委托、匿名函数、Lambda表达式和事件的学习

委托:

还记得C++里的函数指针么?大家可以点击这里查看一下以前的笔记。C#的委托和C++中的函数指针效果一致。

当我们需要将函数作为对象进行传递和使用时就需要用到委托。

下面我们看一个例子:

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6
 7 namespace Test
 8 {
 9     class Program
10     {
11         static void Main(string[] args)
12         {
13             //声明委托的实例
14             ProcessDelegate pd;
15
16             //指定委托的函数
17             pd = Multiply;
18             Console.WriteLine(pd(12, 6));
19
20             //指定委托的函数
21             pd = Divide;
22             Console.WriteLine(pd(12, 6));
23
24             //将委托作为参数传递
25             Func1(pd);
26             //将函数直接作为参数传递
27             Func1(Multiply);
28
29             Console.ReadKey();
30         }
31
32         /// <summary>
33         /// 声明一个委托.
34         /// </summary>
35         delegate double ProcessDelegate(double param1, double param2);
36
37         static double Multiply(double param1, double param2)
38         {
39             return param1 * param2;
40         }
41
42         static double Divide(double param1, double param2)
43         {
44             return param1 / param2;
45         }
46
47         /// <summary>
48         /// 参数为委托类型的函数.
49         /// </summary>
50         static void Func1(ProcessDelegate pd)
51         {
52             Console.WriteLine(pd(55, 11));
53         }
54     }
55 }

运行的结果如下:

1 72
2 2
3 5
4 605

初始化定义和委托推断:

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6
 7 namespace Test
 8 {
 9     class Program
10     {
11         delegate string GetAString();
12
13         static void Main(string[] args)
14         {
15             int x = 40;
16             //这里我称为初始化定义, 注意不能写成 x.ToString()
17             GetAString method = new GetAString(x.ToString);
18             Console.WriteLine(method());
19
20             x = 123;
21             //通过委托推断可以简化代码编写, 注意不能写成 x.ToString()
22             GetAString method2 = x.ToString;
23             Console.WriteLine(method2());
24
25             Console.ReadKey();
26         }
27     }
28 }

多播委托:

多播委托支持“+”“-”操作符,可以添加多个方法到同一个委托中,当委托被执行时,并不会按照添加的顺序依次调用函数,调用顺序是无法保证的。

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6
 7 namespace Test
 8 {
 9     class Program
10     {
11         delegate void DoubleOp(double value);
12
13         public static void Func1(double value)
14         {
15             double result = value * 2;
16             Console.WriteLine("Func1 value: {0}, result: {1}", value, result);
17         }
18
19         public static void Func2(double value)
20         {
21             double result = value * value;
22             Console.WriteLine("Func2 value: {0}, result: {1}", value, result);
23         }
24
25         static void Main(string[] args)
26         {
27             DoubleOp op = Func1;
28             //添加一个方法
29             op += Func2;
30
31             op(1);
32             Console.WriteLine();
33             op(2);
34             Console.WriteLine();
35
36             //去掉一个方法
37             op -= Func1;
38
39             op(3);
40
41             Console.ReadKey();
42         }
43     }
44 }

下面是运行的结果:

1 Func1 value: 1, result: 2
2 Func2 value: 1, result: 1
3
4 Func1 value: 2, result: 4
5 Func2 value: 2, result: 4
6
7 Func2 value: 3, result: 9

匿名函数:

使用时直接进行定义的函数。

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6
 7 namespace Test
 8 {
 9     class Program
10     {
11         delegate string GetString(float num);
12
13         static void Main(string[] args)
14         {
15             //定义匿名函数
16             GetString m = delegate(float num)
17             {
18                 return num.ToString();
19             };
20             Func(m, 10.5f);
21
22             //直接传递匿名函数
23             Func(delegate(float num)
24             {
25                 num *= 2.0f;
26                 return num.ToString();
27             }, 20.5f);
28
29             Console.ReadKey();
30         }
31
32         static void Func(GetString method, float num)
33         {
34             Console.WriteLine("Func: " + method(num));
35         }
36     }
37 }

下面是运行的结果:

1 Func: 10.5
2 Func: 41

Lambda表达式:

Lambda表达式可以用来简化匿名函数的写法,如果把上面的示例改为Lambda表达试则如下:

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6
 7 namespace Test
 8 {
 9     class Program
10     {
11         delegate string GetString(float num);
12
13         static void Main(string[] args)
14         {
15             //使用表达式进行简写
16             GetString m = num =>
17                 {
18                     return num.ToString();
19                 };
20             Func(m, 10.5f);
21
22             //直接传递表达试
23             Func(num =>
24                 {
25                     num *= 2.0f;
26                     return num.ToString();
27                 }, 20.5f);
28
29             Console.ReadKey();
30         }
31
32         static void Func(GetString method, float num)
33         {
34             Console.WriteLine("Func: " + method(num));
35         }
36     }
37 }

Lambda表达式可以去掉函数参数的类型,因为该类型编译器可以从上下文中获得。如果存在多个参数则需要添加括号,如下:

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6
 7 namespace Test
 8 {
 9     class Program
10     {
11         delegate string GetString(float num, int num2);
12
13         static void Main(string[] args)
14         {
15             //使用表达式进行简写
16             GetString m = (num, num2) =>
17                 {
18                     return num.ToString() + "," + num2.ToString();
19                 };
20             Func(m, 10.5f, 100);
21
22             //直接传递表达试
23             Func((num, num2) =>
24                 {
25                     num *= 2.0f;
26                     num += num2;
27                     return num.ToString();
28                 }, 20.5f, 200);
29
30             Console.ReadKey();
31         }
32
33         static void Func(GetString method, float num, int num2)
34         {
35             Console.WriteLine("Func: " + method(num, num2));
36         }
37     }
38 }

下面是运行的结果:

1 Func: 10.5,100
2 Func: 241

如果代码仅有一行还可以省略return和大括号,如下:

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6
 7 namespace Test
 8 {
 9     class Program
10     {
11         delegate string GetString(float num, int num2);
12
13         static void Main(string[] args)
14         {
15             //使用表达式进行简写
16             GetString m = (num, num2) => num.ToString() + "," + num2.ToString();
17             Func(m, 10.5f, 100);
18
19             //直接传递表达式
20             Func((num, num2) => num.ToString() + "," + num2.ToString(), 20.5f, 200);
21
22             Console.ReadKey();
23         }
24
25         static void Func(GetString method, float num, int num2)
26         {
27             Console.WriteLine("Func: " + method(num, num2));
28         }
29     }
30 }

下面是运行的结果:

1 Func: 10.5,100
2 Func: 20.5,200

这里引入了一个新的知识点协变和抗变,大家可以自行搜索,或者查看协变和抗变的文章点击这里

事件:

C#里的事件使用event关键字定义,无需实例化就可以使用,可以将其看做一个特殊的委托对象,下面我们看看一个例子。

EventDispatcher.cs(用来定义和发送特定事件的类):

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6
 7 namespace Test
 8 {
 9     /// <summary>
10     /// 事件发送类.
11     /// </summary>
12     class EventDispatcher
13     {
14         /// <summary>
15         /// 定义特定的事件委托, 注意不要有返回值.
16         /// </summary>
17         /// <param name="sender">发送者.</param>
18         /// <param name="args">附带的参数.</param>
19         public delegate void MyEventHandler(object sender, MyEventArgs args);
20
21         /// <summary>
22         /// 事件对象, 所有的回调都可以添加到该对象上, 不需要实例化就能使用.
23         /// </summary>
24         public event MyEventHandler onCustom;
25
26         /// <summary>
27         /// 构造函数.
28         /// </summary>
29         public EventDispatcher()
30         {
31         }
32
33         /// <summary>
34         /// 发送一个自定义事件.
35         /// </summary>
36         /// <param name="data">数据.</param>
37         public void dispatchCustom(String data)
38         {
39             onCustom(this, new MyEventArgs(data));
40         }
41     }
42
43     /// <summary>
44     /// 自定义事件参数类.
45     /// </summary>
46     class MyEventArgs : EventArgs
47     {
48         public String data;
49
50         public MyEventArgs(String data)
51         {
52             this.data = data;
53         }
54     }
55 }

Program.cs(主程序,测试类):

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6
 7 namespace Test
 8 {
 9     class Program
10     {
11         static void Main(string[] args)
12         {
13             new Program();
14
15             Console.ReadKey();
16         }
17
18         public Program()
19         {
20             EventDispatcher ed = new EventDispatcher();
21
22             //第一种方式
23             EventDispatcher.MyEventHandler meh = new EventDispatcher.MyEventHandler(CustomHandler);
24             ed.onCustom += meh;
25             ed.dispatchCustom("Hello One!");
26             ed.onCustom -= meh;
27
28             //第二种方式
29             ed.onCustom += new EventDispatcher.MyEventHandler(CustomHandler);
30             ed.dispatchCustom("Hello Two!");
31             //下面两种方式都可以删除注册的事件处理函数
32             ed.onCustom -= new EventDispatcher.MyEventHandler(CustomHandler);
33             //ed.onCustom -= CustomHandler;
34
35             //简写方式
36             ed.onCustom += CustomHandler;
37             ed.dispatchCustom("Hello Three!");
38             ed.onCustom -= CustomHandler;
39
40             //匿名函数写法
41             ed.onCustom += delegate(object sender, MyEventArgs args)
42                 {
43                     Console.WriteLine("Event Handler (delegate) : " + args.data);
44                 };
45             ed.dispatchCustom("Hello Four!");
46
47             //Lambda 写法
48             ed.onCustom += (sender, args) =>
49                 {
50                     Console.WriteLine("Event Handler (lambda) : " + args.data);
51                 };
52             ed.dispatchCustom("Hello Five!");
53
54             //简写 Lambda
55             ed.onCustom += (sender, args) => Console.WriteLine("Event Handler (lambda) : " + args.data);
56             ed.dispatchCustom("Hello six!");
57         }
58
59         private void CustomHandler(object sender, MyEventArgs args)
60         {
61             Console.WriteLine("Event Handler : " + args.data);
62         }
63     }
64 }

下面是程序运行的结果:

1 Event Handler : Hello One!
2 Event Handler : Hello Two!
3 Event Handler : Hello Three!
4 Event Handler (delegate) : Hello Four!
5 Event Handler (delegate) : Hello Five!
6 Event Handler (lambda) : Hello Five!
7 Event Handler (delegate) : Hello six!
8 Event Handler (lambda) : Hello six!
9 Event Handler (lambda) : Hello six!
时间: 2024-08-10 19:20:37

委托、匿名函数、Lambda表达式和事件的学习的相关文章

委托-异步调用-泛型委托-匿名方法-Lambda表达式-事件

1. 委托 From: http://www.cnblogs.com/daxnet/archive/2008/11/08/1687014.html 类是对象的抽象,而委托则可以看成是函数的抽象.一个委托代表了具有相同参数列表和返回值的所有函数. [csharp] view plaincopy class Program { delegate int CalculateDelegate(int a, int b); int add(int a, int b) { return a + b; } s

【Unity|C#】基础篇(9)——匿名函数/Lambda表达式

[学习资料] > 在线文档 官方文档:https://docs.microsoft.com/zh-cn/dotnet/csharp/ 菜鸟教程(高级教程):https://www.runoob.com/csharp/csharp-tutorial.html > 视频教程 腾讯学院.Siki学院         > 书籍 <C#图解教程>(第13章):https://www.cnblogs.com/moonache/p/7687551.html [学习内容] > 菜鸟教程

Python匿名函数——lambda表达式

如果要定义的函数很简单,一个return语句就能搞定,可以使用lambda表达式来定义, lambda表达式的语法如下: lambda parameters: expression lambda表达式并不包含return语句,凡是使用函数作为参数或返回值的地方,都可以使用lambda表达式,lambda函数的好处有: 简便,顺手 可以不给函数起名字,某些不需要重用该函数的地方,实质上不需要给函数想一个名字 下面的例1将lambda表达式定义的函数作为内置函数filter的参数, 例1. 将lam

委托、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表达式、事件系列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表达式、事件系列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

委托、Lambda表达式、事件系列02,什么时候该用委托

假设要找出整型集合中小于5的数. static void Main(string[] args) { IEnumerable<int> source = new List<int>(){2, 3, 4, 5, 6, 7, 8, 9,10, 11}; var result = GetNumbersLessThanFive(source); foreach (int n in result) { Console.WriteLine(n); } } static IEnumerable&