代码改变世界

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

2015-04-09 19:06  阿诚de窝  阅读(927)  评论(0编辑  收藏  举报

委托:

还记得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!