C#拾遗系列(1):委托

简介:

一、委托概述

委托具有以下特点:

  • 委托类似于 C++ 函数指针,但它们是类型安全的。

  • 委托允许将方法作为参数进行传递。

  • 委托可用于定义回调方法。

  • 委托可以链接在一起;例如,可以对一个事件调用多个方法。

  • 方法不必与委托签名完全匹配。(委托中的协变和逆变)

  • C# 2.0 版引入了匿名方法的概念,此类方法允许将代码块作为参数传递,以代替单独定义的方法。C# 3.0 引入了 Lambda 表达式,利用它们可以更简练地编写内联代码块。匿名方法和 Lambda 表达式(在某些上下文中)都可编译为委托类型。这些功能统称为匿名函数

二、委托申明和使用示例

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

 

//Description: 委托三种声明方式.net1.0到.net3.0

 

namespace NetTest

    public class TestDelegateNet1To3

    {

        #region 不带返回值的委托

        delegate void TestDelegate(string s);

        public void TestAllDelegate()

        {

            //.net 1.0

            TestDelegate en = new TestDelegate(M);

            en("jack");

            TestDelegate china = China;

            china("wangdeshui");

 

            //.net 2.0

            TestDelegate delegateNet2 = delegate(string s) { Console.Out.WriteLine(s + ":这是一个2.0的方式"); };

            delegateNet2("2.0 delegate declare");

 

            //.net 3.0

            TestDelegate delegateNet3 = (string s) => { Console.Out.WriteLine(s + ":这是3.0的方式"); };

            delegateNet3("3.0 delegate declare");

 

            TestReturn(); 

 

            Func<intbool> d = (n) => { return n > 10 ? true : false; };

            Console.Out.WriteLine("func<int, bool>:" + d(20));

 

 

        }

 

        void M(string s)

        {

            Console.Out.WriteLine(s + ":Good Morning");

        }

 

        void China(string s)

        {

            Console.Out.WriteLine(s + ":早上好");

        }

        #endregion

 

 

        #region 带返回值的委托测试

 

        delegate bool TestDelegateReturn(int i);

        void TestReturn()

        {

            TestDelegateReturn Net10 = new TestDelegateReturn(IsBig);

            bool i = Net10(11);

            Console.Out.WriteLine("Net10:" + i);

 

            TestDelegateReturn Net20 = delegate(int j)

            {

                return (j > 10 ? true : false);

 

            };

 

            bool test20 = Net20(5);

            Console.Out.WriteLine("Net20:" + test20);

 

            TestDelegateReturn Net30 = (int k) => { return (k > 10 ? true : false); };

            bool test30 = Net30(50);

            Console.Out.WriteLine("Net30:" + test30);

 

        }

 

        bool IsBig(int i)

        {

            return i > 10 ? true : false;

 

        }

 

        #endregion

    }

}

 

三、多播委托

调用委托时,它可以调用多个方法。这称为多路广播。若要向委托的方法列表(调用列表)中添加额外的方法,只需使用加法运算符或加法赋值运算符(“+”或“+=”)添加两个委托。

注意:

作为委托参数传递的方法必须与委托声明具有相同的签名。
委托实例可以封装静态或实例方法。
尽管委托可以使用 out 参数,但建议您不要将其用于多路广播事件委托,因为您无法知道哪个委托将被调用。

多播委托传递值类型时,对参数的修改不会传到下一个
多播委托传递引用类型时,对引用参数的修改会传到下一个

示例:

public class TestMultiCastDelegate

    {

        public delegate void Del(string message);

        delegate void DelRef(Person p);

        void MethodA(string s)

        {

            Console.Out.WriteLine("Method A:"+s);

        }

 

        void MethodB(string s)

        {

            s = "good";

            Console.Out.WriteLine("Method B:" + s);

        }

 

        void MethodC(string s)

        {

            Console.Out.WriteLine("Method C:" + s);

        }

 

        /*

        多播委托传递值类型时,对参数的修改不会传到下一个

        多播委托传递引用类型时,对引用参数的修改会传到下一个

        *

        * */

 

        public void Test()

        {

            //多播委托

            Del d1 = MethodA;

            Del d2 = MethodB;

            Del d3=MethodC;

            Del AllDelegateMethod = d1 + d2;

            AllDelegateMethod += d3;

            AllDelegateMethod("Jack");

            /*

            output:

            ConsoleA: Jack

            ConsoleB: Jack

            ConsoleC:Jack           

            */

            Console.Out.WriteLine("---------------------");

            AllDelegateMethod -= d3;

            AllDelegateMethod("wangds");

 

 

            //测试引用类型

            DelRef dref1 = MethodRefA;

            DelRef dref2 = MethodRefB;

            DelRef Alldref = dref1 + dref2;

            Person p = new Person { Address = "USA", Name = "America" };

            Alldref(p);

        }

 

 

        //内嵌类

 

        class Person

        {

            public string Name { getset; }

            public string Address { get;  set; }

 

        }

 

        void MethodRefA(Person p)

        {

            Console.Out.WriteLine(p.Name + "," + p.Address);

            Console.Out.WriteLine("----------MethodRefA---------");

            p.Name = "Jack";

            p.Address = "China";

 

            Console.Out.WriteLine(p.Name + "," + p.Address);

            Console.Out.WriteLine("----------MethodRefA---------");

        }

 

        void MethodRefB(Person p)

        {

            Console.Out.WriteLine("--------MethodRefB----------");

            Console.Out.WriteLine(p.Name + "," + p.Address);

            Console.Out.WriteLine("--------MethodRefB----------");

        }

    }

四、委托中的协变和逆变

委托中的协变: 就是允许方法的返回类型是委托的返回类型的子类

委托中的逆变,方法的参数是委托的参数的基类

示例:

#region 委托中的协变: 就是允许方法的返回类型是委托的返回类型的子类

    class Mammals

    {

    }

 

    class Dogs : Mammals

    {

    }

 

    class XieBianDelegate

    {

        // Define the delegate.

        public delegate Mammals HandlerMethod();

 

        public Mammals FirstHandler()

        {

            return null;

        }

 

        public Dogs SecondHandler()

        {

            return null;

        }

 

        void Test()

        {

            HandlerMethod handler1 = FirstHandler;

 

            // Covariance allows this delegate.

            HandlerMethod handler2 = SecondHandler;

        }

    }

    #endregion

 

 

    #region   委托中的逆变,方法的参数是委托的参数的基类

    class NiBianDelegate

    {

        public delegate Dogs HandlerNibianMethod(Dogs dogs);

        public Dogs FirstHandlerNibian(Mammals mammals)

        {

            return null;

        }

 

        void Test()

        {

            Dogs dogs = new Dogs();

            HandlerNibianMethod handNibian1 = FirstHandlerNibian;

 

        }

 

    }

    #endregion

五、委托综合示例(来自MSDN)

下面的示例阐释声明、实例化和使用委托。BookDB 类封装一个书店数据库,它维护一个书籍数据库。它公开 ProcessPaperbackBooks 方法,该方法在数据库中查找所有平装书,并对每本平装书调用一个委托。使用的 delegate 类型名为 ProcessBookDelegate。Test 类使用该类打印平装书的书名和平均价格。

委托的使用促进了书店数据库和客户代码之间功能的良好分隔。客户代码不知道书籍的存储方式和书店代码查找平装书的方式。书店代码也不知道找到平装书后将对平装书执行什么处理。

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

 

namespace Bookstore

{

    using System.Collections;

 

    // Describes a book in the book list:

    public struct Book

    {

        public string Title;        // Title of the book.

        public string Author;       // Author of the book.

        public decimal Price;       // Price of the book.

        public bool Paperback;      // Is it paperback?

 

        public Book(string title, string author, decimal price, bool paperBack)

        {

            Title = title;

            Author = author;

            Price = price;

            Paperback = paperBack;

        }

    }

 

    // Declare a delegate type for processing a book:

    public delegate void ProcessBookDelegate(Book book);

 

    // Maintains a book database.

    public class BookDB

    {

        // List of all books in the database:

        ArrayList list = new ArrayList();

 

        // Add a book to the database:

        public void AddBook(string title, string author, decimal price, bool paperBack)

        {

            list.Add(new Book(title, author, price, paperBack));

        }

 

        // Call a passed-in delegate on each paperback book to process it:

        public void ProcessPaperbackBooks(ProcessBookDelegate processBook)

        {

            foreach (Book b in list)

            {

                if (b.Paperback)

                    // Calling the delegate:

                    processBook(b);

            }

        }

    }

}

 

 

 

 

// Using the Bookstore classes:

namespace BookTestClient

{

    using Bookstore;

 

    // Class to total and average prices of books:

    class PriceTotaller

    {

        int countBooks = 0;

        decimal priceBooks = 0.0m;

 

        internal void AddBookToTotal(Book book)

        {

            countBooks += 1;

            priceBooks += book.Price;

        }

 

        internal decimal AveragePrice()

        {

            return priceBooks / countBooks;

        }

    }

 

    // Class to test the book database:

  public  class TestBookDB

    {

        // Print the title of the book.

        void PrintTitle(Book b)

        {

            System.Console.WriteLine("   {0}", b.Title);

        }

 

        // Execution starts here.

        public void Test()

        {

            BookDB bookDB = new BookDB();

 

            // Initialize the database with some books:

            AddBooks(bookDB);

 

            // Print all the titles of paperbacks:

            System.Console.WriteLine("Paperback Book Titles:");

 

            // Create a new delegate object associated with the static

            // method Test.PrintTitle:

            bookDB.ProcessPaperbackBooks(PrintTitle);

 

            // Get the average price of a paperback by using

            // a PriceTotaller object:

            PriceTotaller totaller = new PriceTotaller();

 

            // Create a new delegate object associated with the nonstatic

            // method AddBookToTotal on the object totaller:

            bookDB.ProcessPaperbackBooks(totaller.AddBookToTotal);

 

            System.

本文转自敏捷的水博客园博客,原文链接http://www.cnblogs.com/cnblogsfans/archive/2008/06/11/1217381.html如需转载请自行联系原作者


王德水

相关文章
|
21天前
|
C#
C#拾遗补漏之goto跳转语句
在我们日常工作中常用的C#跳转语句有break、continue、return,但是还有一个C#跳转语句很多同学可能都比较的陌生就是goto,今天大姚带大家一起来认识一下goto语句及其它的优缺点。
|
21天前
|
开发框架 人工智能 .NET
C#/.NET/.NET Core拾遗补漏合集(持续更新)
在这个快速发展的技术世界中,时常会有一些重要的知识点、信息或细节被忽略或遗漏。《C#/.NET/.NET Core拾遗补漏》专栏我们将探讨一些可能被忽略或遗漏的重要知识点、信息或细节,以帮助大家更全面地了解这些技术栈的特性和发展方向。
|
1月前
|
开发框架 安全 .NET
C# .NET面试系列三:集合、异常、泛型、LINQ、委托、EF!
<h2>集合、异常、泛型、LINQ、委托、EF! #### 1. IList 接口与 List 的区别是什么? IList 接口和 List 类是C#中集合的两个相关但不同的概念。下面是它们的主要区别: <b>IList 接口</b> IList 接口是C#中定义的一个泛型接口,位于 System.Collections 命名空间。它派生自 ICollection 接口,定义了一个可以通过索引访问的有序集合。 ```c# IList 接口包含一系列索引化的属性和方法,允许按索引访问、插入、移除元素等。 由于是接口,它只定义了成员的契约,而不提供具体的实现。类似于 IEnumera
159 2
|
3月前
|
存储 C# C++
C#进阶-委托(Delegrate)
类似于 C 或 C++ 中函数的指针,委托是C#的函数指针,是存有对某个方法的引用的一种引用类型变量。引用可在运行时被改变。本篇文章我们将讲解C#里委托的类型及如何使用。委托的语法第一次接触难免感到陌生,最好的学习方式就是在项目中多去使用,相信会有很多感悟。
27 0
|
4月前
|
Java C# C++
【从Java转C#】第八章:委托、lambda、事件
【从Java转C#】第八章:委托、lambda、事件
|
4月前
|
C#
C#中的委托
C#中的委托
20 0
|
5月前
|
存储 开发框架 .NET
c#委托详解
委托是一种能够将方法作为参数传递、存储方法并且调用方法的类型,它可以让我们写出更加灵活和可扩展的代码。委托通常用于回调 (Callback) 机制,比如在事件处理、异步编程、LINQ 查询等场景中常常会使用委托。它可以将方法作为参数传递给其他方法,从而在需要的时候执行该方法。
28 2
|
7月前
|
C# Windows
C#OOP之十一 委托和事件
C#OOP之十一 委托和事件
36 0
|
8月前
|
安全 C#
C#委托事件的区别
C#委托事件的区别