委托
C# 中的委托(Delegate)类似于 C 或 C++ 中函数的指针。委托(Delegate) 是存有对某个方法的引用的一种引用类型变量。引用可在运行时被改变。
委托(Delegate)特别用于实现事件和回调方法。所有的委托(Delegate)都派生自 System.Delegate 类。
声明委托(Delegate)
委托声明决定了可由该委托引用的方法。委托可指向一个与其具有相同标签的方法。
1 delegate 函数返回类型 委托名 (<方法参数列表>);
实例化委托(Delegate)
委托对象必须使用 new 关键字来创建,且与一个特定的方法有关。当创建委托时,传递到 new 语句的参数就像方法调用一样书写,但是不带有参数。
1 委托类型 实例名 = new 委托类型 (<注册方法>);
通过委托实例来调用方法,执行委托实例就等同于执行注册方法。
匿名函数初始化委托
为初始化委托而专门定义方法较为麻烦,通常调用委托实例初始化时赋值的方法,而不直接调用方法本身。
格式如下:
1 delegate 委托(函数)返回类型 委托类型(函数参数列表);
2
3 委托类型 委托实例= new 委托类型(delegate(<函数参数列表:类型 形参名,类型 形参名...>)
4 {
5 //函数体
6 });
或者省去new关键字。
1 delegate 委托(函数)返回类型 委托类型(函数参数列表);
2
3 委托类型 委托实例= delegate(<函数参数列表:类型 形参名,类型 形参名...>)
4 {
5 //函数体
6 };
委托的多播(Multicasting of a Delegate)
委托对象可使用 "+" 运算符进行合并。一个合并委托调用它所合并的两个委托。只有相同类型的委托可被合并。"-" 运算符可用于从合并的委托中移除组件委托。
使用委托的这个有用的特点,您可以创建一个委托被调用时要调用的方法的调用列表。这被称为委托的 多播(multicasting),也叫组播。
利用多播可以将委托中的函数类似数据一样扩展或消减。

1 namespace _9_delegate_2_20170801
2 {
3 class TestDelegate
4 {
5 public static void PrintfA()
6 {
7 Console.WriteLine("PrintfA !");
8 }
9
10 public static void PrintfB()
11 {
12 Console.WriteLine("PrintfB !");
13 }
14 }
15 delegate void Mydelegate();
16
17 class Program
18 {
19 static void Main(string[] args)
20 {
21 // 创建委托实例
22 Mydelegate d;
23 Mydelegate da = new Mydelegate(TestDelegate.PrintfA);
24 Mydelegate db = new Mydelegate(TestDelegate.PrintfB);
25 d = da;
26 d += db;
27 // 调用多播
28 Console.WriteLine("调用委托实例d ==da+db:");
29 d();
30 d -= da;
31 Console.WriteLine("调用委托实例d ==db:");
32 d();
33 }
34 }
35 }
委托的好处
1、操作函数更加灵活,就像使用变量一样方便,具有动态性,可避免程序中大量的使用分支语句。
2、与C++,C中的函数指针相比,委托是面向对象、类型安全、可靠的受控对象。委托能保证指向一个安全有效不会越界的储存函数的地址。
3、与C++,C中的函数指针相比,指针只能指向静态函数,委托可以引用静态函数也可以引用非静态成员函数。
当程序必须调用一个方法来执行某个操作,但编译无法确定是什么方法时,就可以使用委托。
Action委托和Func委托
除了我们自己定义的委托之外,系统还给我们提供过来一个内置的委托类型,Action和Func。
Action委托引用了一个void返回类型的方法,T表示方法参数。
Action<参数类型列表> Action委托实例 = 方法签名;
Func引用了一个带有一个返回值的方法,它可以传递0或者多到16个参数类型,和一个返回类型
Func<参数类型列表,返回类型> Func实例名 = 方法签名;
在调用时,Func类委托和Action类委托都一样:
实例名(参数);
一个Action委托的使用例子:

1 class Program
2 {
3 static void Main(string[] args)
4 {
5 Action<int, string> eatAction = Eat;
6 eatAction(5, "苹果");
7 Action<int, string> palceAction = Place;
8 palceAction(10, "香蕉");
9
10 Action<int, string> doAction;
11 doAction = eatAction;
12 doAction += palceAction;
13 doAction(8,"西瓜");
14 }
15 private static void Eat(int num, string name)
16 {
17 Console.WriteLine($"我吃了{num}个{name}");
18 }
19 private static void Place(int num, string name)
20 {
21 Console.WriteLine($"我放了{num}个{name}");
22 }
23 }
Lambda表达式
从C#3.0开始,可以使用Lambda表达式代替匿名方法。只要有委托参数类型的地方就可以使用Lambda表达式。Lamdba表达式方便了委托的。
通过一个例子:

1 class Program
2 {
3 delegate string PrintfInfo(string name, string dowhat);
4 static void Main(string[] args)
5 {
6 //不用Lambda表达式的Func-------
7 Func<int, int, int> plus = delegate(int arg1, int arg2)
8 {
9 return arg1 + arg2;
10 };
11
12 //用Lambda表达式的Action--------
13 Action<int, int> printf = (arg1, arg2) => Console.WriteLine($"输入的数{arg1},{arg2}");
14
15 //用Lambdab表达式的一般委托
16 PrintfInfo printfInfo = (name, dowhat) =>
17 {
18 string s = name + "想去" + dowhat;
19 return s;
20 };
21
22
23 Console.WriteLine(plus(99, 1));//输出Func
24 printf(99, 1);//输出Action
25 Console.WriteLine(printfInfo("小明","吃KFC"));//输出delegate
26 }
27 }
可以总结出来:
委托的使用格式一般为:
委托类型 委托实例名 = ( 参数名列表 A, B , C ) => { 函数体 } ;
需要注意:
“=”和“=>”无法省略,“()”和“{}”在特殊情况可以省略,比如:
1 Action<int> printf =a=> Console.WriteLine($"输入的数:{a}"); //一个参数且一行代码的函数体
2 Action printf =()=> Console.WriteLine($"输入的数:{a}"); //无参数且一行代码的函数体
通过Lambda表达式可以访问Lambda表达式块外部的变量。这是一个非常好的功能,但如果不能正确使用,也会非常危险。
示例:
1 int somVal = 5; 2 3 Func<int,int> f = x=>x+somVal; 4 5 Console.WriteLine(f(3));//8 6 7 somVal = 7; 8 9 Console.WriteLine(f(3));//10
这个方法的结果,不但受到参数的控制,还受到somVal变量的控制,结果不可控,容易出现编程问题,用的时候要谨慎。
事件
事件(event)基于委托,为委托提供了一个发布/订阅机制,我们可以说事件是一种具有特殊签名的委托。
什么是事件?事件(Event)是类或对象向其他类或对象通知发生的事情的一种特殊签名的委托.
事件的声明
public event 委托类型 事件名;
事件使用event关键词来声明,他的返回类值是一个委托类型。
通常事件的命名,以名字+Event 作为他的名称,在编码中尽量使用规范命名,增加代码可读性。
来源:https://www.cnblogs.com/craft0625/p/7266474.html
