首页 > 代码库 > part01.03 委托与 Lambda 表达式(一):委托

part01.03 委托与 Lambda 表达式(一):委托

delegate 是表示对具有特定参数列表和返回类型的方法的引用类型。

委托最大的作用就是为 类的事件 绑定 事件处理程序

可将任何可访问类或结构中与委托类型匹配的任何方法分配给委托。该方法可以是静态方法,也可以是实例方法。这样便能通过编程方式来更改方法调用,还可以向现有类中插入新代码。

将方法作为参数进行引用的能力使委托成为定义回调方法的理想选择。

将方法作为对象封装起来,允许在运行时间接地绑定一个方法调用。

在一定程度上可以把委托类型看成是定义了一个方法的接口,而委托的实例看成是对这个接口的实现。

实现一个委托很简单,主要有三个步骤:相关代码说明

技术分享
 1 /// <summary>
 2     /// 定义一个委托方法(类型)
 3     /// 1.声明一个 delegate 对象,它应当与你想要传递的方法具有 相同的参数和返回值类型
 4     /// </summary>
 5     /// <param name="i"></param>
 6     /// <param name="j"></param>
 7     delegate void DelegateMethod(int i, double j);
 8 
 9     /// <summary>
10     /// 一个普通的类
11     /// </summary>
12     class SomeClass
13     {
14         public void PrintDigit(int m,double n)
15         {
16             Console.Write(m * n + " ");
17         }
18 
19         public static void Func()
20         {
21             var a = new SomeClass();
22 
23             //  使用 SomeClass 的实例方法实例化委托
24             //  2.创建 delegate 对象,并将你想要传递的函数作为参数传入
25             //  1) var b=new DelegateMethod(实例名.方法名);
26             //  2) var b=new DelegateMethod(类名.方法名);
27             var b = new DelegateMethod(a.PrintDigit);
28 
29             //  此时委托实例执行的是 a.PintDigit
30             //  3.在要实现异步调用的地方,通过上一步创建的对象来调用方法
31             //  b(向方法传递的参数);
32             b(1, 2.5);
33 
34             Console.ReadKey();
35         }
36     }
View Code

委托应用的一个简单场景:相关代码说明

技术分享
  1 class Program
  2     {
  3         static void Main(string[] args)
  4         {
  5             DelegateClass.ToDelegate();
  6         }
  7     }
  8     /// <summary>
  9     /// 10     /// </summary>
 11     public class Book
 12     {
 13         public string Title;
 14         public string Author;
 15         public decimal Price;
 16         public bool Paperback;  //  是否是平装书
 17 
 18         public Book(string title, string author, decimal price, bool paperBack)
 19         {
 20             Title = title;
 21             Author = author;
 22             Price = price;
 23             Paperback = paperBack;
 24         }
 25     }
 26 
 27     /// <summary>
 28     /// 书籍存储器
 29     /// </summary>
 30     public class BookDB
 31     {
 32         /// <summary>
 33         /// 声明一个用于处理书籍相关数据的委托方法(类型)
 34         /// 仅仅说明要处理一本书,如何处理,处理结果等都没有任何约束
 35         /// </summary>
 36         /// <param name="book"></param>
 37         public delegate void ProcessBookDelegate(Book book);
 38 
 39         //  定义存储书籍的一个变量集合
 40         List<Book> _BookCollection = new List<Book>();
 41 
 42         /// <summary>
 43         /// 添加书籍
 44         /// </summary>
 45         /// <param name="title"></param>
 46         /// <param name="author"></param>
 47         /// <param name="price"></param>
 48         /// <param name="paperBack"></param>
 49         public void AddBook(string title, string author, decimal price, bool paperBack)
 50         {
 51             _BookCollection.Add(new Book(title, author, price, paperBack));
 52         }
 53 
 54         /// <summary>
 55         /// 定义一个处理平装书的方法
 56         /// </summary>
 57         /// <param name="processBook">传入的是一个方法,前面定义的一个委托方法“实例”</param>
 58         public void ProcessPaperbackBooks(ProcessBookDelegate processBook)
 59         {
 60             foreach (Book b in _BookCollection)
 61             {
 62                 if (b.Paperback)
 63                 {
 64                     //  调用委托示例来处理每一本书
 65                     processBook(b);
 66                 }
 67             }
 68         }
 69     }
 70 
 71     /// <summary>
 72     /// 书籍价格计数器
 73     /// </summary>
 74     public class PriceTotaller
 75     {
 76         int _CountBooks = 0;    //  数量  
 77         decimal _PriceBooks = 0.0m;     //  平均单价
 78 
 79         /// <summary>
 80         /// 处理传入的书籍
 81         /// 与前面定义的委托方法 public delegate void ProcessBookDelegate(Book book)的传入值和返回值都是一样的,所以,可以把它看成是 ProcessBookDelegate 的实例
 82         /// </summary>
 83         /// <param name="book"></param>
 84         internal void AddBookToTotal(Book book)
 85         {
 86             _CountBooks += 1;
 87             _PriceBooks += book.Price;
 88         }
 89 
 90         internal decimal AveragePrice()
 91         {
 92             return _PriceBooks / _CountBooks;
 93         }
 94     }
 95 
 96     /// <summary>
 97     /// 用于演示说明委托基本应用原理的例子(声明、实例化和使用委托)
 98     /// </summary>
 99     public class DelegateClass
100     {
101         public static void ToDelegate()
102         {
103             //  创建书籍数据存储器
104             BookDB bookDB = new BookDB();
105 
106             //  添加书籍
107             AddBooks(bookDB);
108 
109             Console.WriteLine("所有平装书的书名:");
110 
111             //  打印平装书名
112             bookDB.ProcessPaperbackBooks(new BookDB.ProcessBookDelegate(PrintTitle));
113 
114             PriceTotaller totaller = new PriceTotaller();
115             bookDB.ProcessPaperbackBooks(new BookDB.ProcessBookDelegate(totaller.AddBookToTotal));
116             Console.WriteLine("平装书的平均价格是:¥{0}", totaller.AveragePrice());
117 
118             Console.ReadKey();
119         }
120 
121         /// <summary>
122         /// 打印书籍的标题
123         /// </summary>
124         /// <param name="book">书籍</param>
125         static void PrintTitle(Book book)
126         {
127             Console.WriteLine("   {0}", book.Title);
128         }
129 
130         /// <summary>
131         /// 初始化书籍存储器的存储内容
132         /// </summary>
133         /// <param name="bookDB"></param>
134         static void AddBooks(BookDB bookDB)
135         {
136             bookDB.AddBook("C#程序设计", "黄大哥", 70.99m, true);
137             bookDB.AddBook("PHP设计", "黄大哥", 70.99m, true);
138             bookDB.AddBook("计算机操作系统", "黄大哥", 70.99m, false);
139             bookDB.AddBook("C程序设计", "黄大哥", 70.99m, false);
140         }
141     }
View Code

委托与接口的比较

下列情况使用委托:

1.当使用事件设计模式时。

2.当封装静态方法可取时。

3.当调用方不需要访问实现该方法的对象中的其他属性、方法和接口时。

4.需要方便的组合。

5.当类可能需要该方法的多个实现时。

下列情况使用接口:

1.当存在一组可能被调用的相关方法时。

2.当类只需要方法的单个实现时。

3.当使用接口的类想要将该接口强制转换为其他接口或类类型时。

4.当正在实现的方法链接到类的类型或标识时:例如比较方法。

委托的组合操作:

1.可以使用 + 运算符将它们分配给另一个有需要的委托实例。

2.可以使用 - 运算符从组合的委托移除组件委托。

3.组合的委托可调用组成它的那些委托。

4.只有相同类型的委托才可以组合。

相关代码:

技术分享
 1  class Program
 2     {
 3         #region 委托的组合使用
 4         /// <summary>
 5         /// 除了方法语义上表明将传入的字符串读出来外,并没有在代码上给出任何约束
 6         /// </summary>
 7         /// <param name="s">待处理字符串</param>
 8         delegate void VoiceDelagate(string s);
 9 
10         #region 这两个方法都可以作为 VoiceDelegate 的实例
11         public static void Hello(string s)
12         {
13             Console.WriteLine("hello" + s);
14         }
15 
16         public static void GoodBye(string s)
17         {
18             Console.WriteLine("goodbye" + s);
19         }
20 
21         #endregion
22 
23         static void Main(string[] args)
24         {
25             var a = new VoiceDelagate(Hello);
26             var b = new VoiceDelagate(GoodBye);
27             var c = a + b;
28             var d = c - a;
29 
30             a("调用委托实例 a 的结果");
31             b("调用委托实例 b 的结果");
32             c("C");
33             d("D");
34             Console.ReadKey();
35         }
36     }
View Code

委托小结:

1.委托通过特定的返回值和一组输入参数,将行为方法封装起来,成为一个特殊的类型,与具有单一方法的接口类似。

2.在声明为委托类型时,相应的类型签名决定了哪些方法可以用于创建委托实例,以及哪些签名可以引用。

3.创建一个委托实例,需要一个方法以及这个调用这个方法的目标场所(实例方法)。

4.委托实例是不可变的。

5.每个委托实例内部都包含一个可供引用操作的列表。

6.委托类型可以混合在一起,也可以从其中一个实例删除另一个实例。

7.事件不是委托实例,而是配对的 add/remove 方法(犹如类属性的 getters/setters )

 

part01.03 委托与 Lambda 表达式(一):委托