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     }

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

  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     }

委托与接口的比较

下列情况使用委托:

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     }

委托小结:

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

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

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

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

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

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

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

时间: 2024-08-27 16:33:21

part01.03 委托与 Lambda 表达式(一):委托的相关文章

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

"Lambda 表达式"是一个匿名函数,它可以包含表达式和语句,用于创建委托或表达式树类型 A. 用Lambda表达式代替匿名方法,复杂冗长的形式 格式:( 显式类型参数列表 )=>{ 语句 } 样例: // 带返回值的委托 Func<int, double, double> a = (m, n) => { return m * n; }; Console.WriteLine(a); Console.WriteLine(a(10, 25.2)); // 不带返回

委托、Lambda表达式、事件系列03,从委托到Lamda表达式

在"委托.Lambda表达式.事件系列02,什么时候该用委托"一文中,使用委托让代码简洁了不少. namespace ConsoleApplication2 { internal delegate bool MyCalculateDelegate(int val); class Program { static void Main(string[] args) { IEnumerable<int> source = new List<int>(){2, 3, 4

委托、Lambda表达式、事件系列07,使用EventHandler委托

谈到事件注册,EventHandler是最常用的. EventHandler是一个委托,接收2个形参.sender是指事件的发起者,e代表事件参数. □ 使用EventHandler实现猜拳游戏 使用EventHandler实现一个猜拳游戏,每次出拳,出剪刀.石头.布这三者的其中一种. 首先抽象出一个被观察者,其中提供了事件,提供了执行事件的方法. public class FistGame { public string FistName { get; set; } public event

委托、Lambda表达式、事件系列05,Action委托与闭包

来看使用Action委托的一个实例: static void Main(string[] args) { int i = 0; Action a = () => i++; a(); a(); Console.WriteLine(i); } 结果是期望能的2.但令人好奇的是:栈上的变量i是如何传递给Action委托的? 反编译进行查看,首先看Main方法对应的IL代码: 再看c_DisplayClass1的IL代码: 从中可以看出:→在托管堆上创建了一个名为c_DisplayClass1的实例→把

委托、Lambda表达式、事件系列04,委托链是怎样形成的, 多播委托

在"委托.Lambda表达式.事件系列01,委托是什么,委托的基本用法,委托的Method和Target属性"中,反编译委托,发现委托都是多播委托. 既然委托是多播委托,我们可以通过"+="把多个方法赋给委托变量,这样就形成了一个委托链, 它是怎样形成的?来看下面的例子: namespace ConsoleApplication3 { internal delegate void MySayDel(string msg); class Program { stati

委托、Lambda表达式、事件系列06,使用Action实现观察者模式

在"实现观察者模式(Observer Pattern)的2种方式"中,曾经通过接口的方式.委托与事件的方式实现过观察者模式.本篇体验使用Action实现此模式. 就举一个足球场上的例子,当裁判吹响终场哨,胜队庆祝,失败队落寞.把裁判看作是被观察者,比赛中的两队看作是观察者. 裁判作为被观察者需要提供一个Action委托供观察者方法注册. public class Referee { public Action DoSth; public void ISayGameOver() { Co

[深入学习C#] 匿名函数、委托和Lambda表达式

匿名函数 匿名函数(Anonymous Function)是表示"内联"方法定义的表达式.匿名函数本身及其内部没有值或者类型,但是可以转换为兼容的委托或者表达式树类型(了解详情).匿名函数转换的计算取决于转换的目标类型:如果是委托类型,则转换计算为引用匿名函数所定义的方法的委托:如果是表达式树类型,则转换将计算以对象结构形式表示方法结构的表达式树.  匿名函数有两种语法风格:Lambda表达式(lambda-expression)和匿名方法表达式(anonymous-method-ex

十二、C# 委托与Lambda表达式(匿名方法的另一种写法)

委托与Lambda表达式 1.委托概述 2.匿名方法 3.语句Lambda 4.表达式Lambda 5.表达式树 一.委托概述 相当于C++当中的方法指针,在C#中使用delegate 委托来提供相同的功能, 它将方法作为对象封装起来,允许在"运行时"间接地绑定一个方法调用. 声明的委托相当于一种自定义的数据类型. 1.背景 冒泡排序 1 static class SimpleSort1 2 { 3 public static void BubbleSort(int[] items)

委托、匿名委托、Lambda 表达式、Expression表达式树之刨根问底

本篇不是对标题所述之概念的入门文章,重点在阐述它们的异同点和应用场景.各位看官,这里就不啰嗦了,直接上代码. 首先定义一个泛型委托类型,如下: public delegate T Function<T>(T a, T b); 实现泛型委托的主体代码,并调用: public static string Add(string a, string b) { return string.Format("{0} #### {1}",a,b); } //实名委托方式 Function&

委托、Lambda表达式和事件

1.1 引用方法      委托是寻址方法的 .NET 版本.委托是类型安全的类.它定义了返回类型和参数的类型.委托类不仅包含对方法的引用,也可以包含对多个方法的引用. Lambda 表达式与委托直接相关.当参数是委托类型时,就可以使用Lambda表达式实现委托引用的方法. 1.2委托      当要把 方法 传送给其他方法时,需要使用 委托.以下两个示例: 启动线程和任务--在C# 中,可以告诉计算机并行运行某些新的执行序列同时运行当前的任务.这种序列就是称为线程,在其中一个基类 System