C# 委托Delegate

委托


 

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     }
Action委托例子

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     }
Lambda的使用例子

可以总结出来:

 委托的使用格式一般为:

委托类型 委托实例名  =  ( 参数名列表 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