什么是委托
委托是一个类,它定义了方法的类型,使得可以将方法当作另一个方法的参数来进行传递,这种将方法动态地赋给参数的做法,可以避免在程序中大量使用If-Else(Switch)语句,同时使得程序具有更好的可扩展性。
委托是一种动态调用方法的类型,属于引用型。
委托是对方法的抽象和封装。委托对象实质上代表了方法的引用(即内存地址)
可以理解为函数的一个包装,它使得c#中的函数可以作为参数来被传递
简单理解是这样的
比如您要管您的孩子,把孩子送进了幼儿园
OK,此时您就把您的孩子委托给了幼儿园
当幼儿园放学,将孩子交还给您的手中则是委托的回调
当然我这里的例子是说异步委托调用,您也可以同步
什么是同步委托调用?
您去银行存钱,把钱给柜员,他们帮你点钞,存款然后给您存折或卡
那么您就相当于把钱委托给了柜员让他去执行存钱的这个函数
委托的定义
1. 以delegate关键字开头。
2. 返回类型+委托类型名+参数列表
public delegate void MyDelegate(int para1, string para2);
委托能包装的方法是有一定限制的,例如能被前面的委托类型MyDelegate包装的方法需要满足以下条件:
1.方法的返回类型必须为void;
2.方法必须有两个参数,并且第一个参数应为int类型,第二个参数为string类型。
总结:可以被委托包装的方法必须满足以下规则:
1.方法的签名必须与委托一致,方法签名包括参数的个数、类型和顺序;
2.方法的返回类型要和委托一致,注意,方法的返回类型不属于方法签名的一部分。
委托的使用
//委托使用的演示
class Program
{
//1.使用delegate关键字来定义一个委托类型
public delegate void MyDelegate(int para1, int para2);
static void Main(string[] args)
{
//2.声明委托变量d
MyDelegate d;
//3.实例化委托类型,传递的方法也可以为静态方法,这里传递的是实例方法
d = new MyDelegate(new Program().Add);
//4.委托类型作为参数传递给另一个方法
MyMethod(d);
Console.ReadKey();
}
void Add(int para1,int para2)
{
int sum = para1 + para2;
Console.WriteLine("两个数的和为:" + sum);
}
private static void MyMethod(MyDelegate mydelegate)
{
//5.在方法中调用委托
mydelegate(1, 2);
}
}
从以上代码可以看出,使用委托的步骤为:定义委托类型—声明委托变量—实例化委托—作为参数传递给方法—调用委托。如下具体分析委托的使用过程。
(1)定义委托类型:public delegate void MyDelegate(int para1, int para2) 其定义方式类似于方法的定义,只是多了一个delegate关键字。
(2)声明委托变量:MyDelegate d;既然委托是一种类型,那么可以使用委托来声明一个委托变量,相当于int a;
(3)实例化委托: d = new MyDelegate(new Program().Add);。第二步只是声明了委托变量,但并没有将它实例化。类的实例化使用new关键字来实现,而委托也属于类类型,所以委托的实例化也使用new关键字来进行的。这里需要注意的是,委托的实例化是用一个方法名(不能带左右括号)作为参数,并且该方法的定义必须符合委托的定义,即该方法的返回类型、参数个数和类型必须与委托定义中的一样。这样,前面3步就好比构造了一个律师对象,而方法InstanceMethod好比是当事人的方法。
(4)作为参数传递给方法:MyMethod(d);。委托使用得在C#中,可以把一个方法作为另一个方法的参数,而委托可以看作是一个包装方法的对象。
(5)在方法中调用委托:MyMethod(d);。委托使用得在c#中,可以把一个方法作为另一个方法的参数,而委托可以看作是一个包装方法的对象。
总结:在使用委托时,需要注意以下几个问题。
1.在第三步中,被传递的方法的定义必须与委托定义相同,即方法的返回类型和参数个数、参数类型都必须与委托相同。并且,传递的是方法名,方法名后不能带有左右括号。
2.在第五步中,委托的调用与方法调用类似,传递的实参类型和个数必须与委托定义一致。
3.由于委托是方法的包装类型,所以对委托的调用也就是对其所包装的的方法的调用,上面第5步时间上是调用了Add方法来对传入的实参进行计算。
为什么要使用委托
委托使得一个方法可以作为另一个方法的参数进行传递,这就是委托最大的作用。使用委托可以将同类型的方法绑定到同一个变量上,当调用此变量时就可以一次调用绑定的方法,很方便。如下例子:
例如我们要实现一个打招呼的方法,而每个国家打招呼的方式都不一样,刚开始我们可能会像下面这样实现打招呼的方法:
public void Greeting(string name,string language)
{
switch (language)
{
case "zh-cn":
ChineseGreeting(name);
break;
case "en-us":
EnglishGreeting(name);
break;
default:
EnglishGreeting(name);
break;
}
}
public void EnglishGreeting(string name )
{
Console.WriteLine("Hello, " + name);
}
public void ChineseGreeting(string name)
{
Console.WriteLine("你好, " + name);
}
若后续我们需要添加德国、日本等打招呼方法,就必须修改Greeting方法内的case语句,来适应新的需求,这样特别不方便。有了委托,我们就可以把函数作为参数,并像如下代码实现Greeting方法:
public delegate void GreetingDelegate(string name);
static void Main(string[] args)
{
//引入委托
Program p = new Program();
p.Greeting("小叶", p.ChineseGreeting);
p.Greeting("Tommy Li", p.EnglishGreeting);
Console.Read();
}
public void Greeting(string name,GreetingDelegate callback)
{
callback(name);
}
public void EnglishGreeting(string name)
{
Console.WriteLine("Hello, " + name);
}
public void ChineseGreeting(string name)
{
Console.WriteLine("你好, " + name);
}
如下,增加德国人打招呼:
class Program
{
public delegate void GreetingDelegate(string name);
static void Main(string[] args)
{
//引入委托
Program p = new Program();
p.Greeting("小叶", p.ChineseGreeting);
p.Greeting("Tommy Li", p.EnglishGreeting);
p.Greeting("Ha Wa", p.GermanyGreeting);
Console.Read();
}
public void Greeting(string name,GreetingDelegate callback)
{
callback(name);
}
public void EnglishGreeting(string name)
{
Console.WriteLine("Hello, " + name);
}
public void ChineseGreeting(string name)
{
Console.WriteLine("你好, " + name);
}
public void GermanyGreeting(string name)
{
Console.WriteLine("Hallo, " + name);
}
}
委托链的使用
委托链其实就是委托类型,只是委托链把多个委托链接在一起而已,也就是说,我们把链接了多个方法的委托称为委托链或多路广播委托。如下:
public delegate void DelegateTest();
static void Main(string[] args)
{
//用静态方法来实例化委托
DelegateTest dtstatic = new DelegateTest(Program.method1);
DelegateTest dtinstance = new DelegateTest(new Program().method2);
//定义一个委托对象,一开始初始化为null,即不代表任何方法。
DelegateTest delegatechain = null;
//使用 “+”符号链接委托,链接多个委托后就成为了委托链
delegatechain += dtstatic;
delegatechain += dtinstance;
//调用委托链
delegatechain();
Console.Read();
}
private static void method1()
{
Console.WriteLine("这是静态方法");
}
//实例方法
private void method2()
{
Console.WriteLine("这是实例方法");
}
从委托链中移除委托
public delegate void DelegateTest();
static void Main(string[] args)
{
//用静态方法来实例化委托
DelegateTest dtstatic = new DelegateTest(Program.method1);
DelegateTest dtinstance = new DelegateTest(new Program().method2);
//定义一个委托对象,一开始初始化为null,即不代表任何方法。
DelegateTest delegatechain = null;
//使用 “+”符号链接委托,链接多个委托后就成为了委托链
delegatechain += dtstatic;
delegatechain += dtinstance;
//使用 “-”运算符 移除委托
delegatechain -= dtstatic;
//调用委托链
delegatechain();
Console.Read();
}
private static void method1()
{
Console.WriteLine("这是静态方法");
}
//实例方法
private void method2()
{
Console.WriteLine("这是实例方法");
}
多播委托
如果要调用多个方法,就需要多次显式调用这个委托。但是委托也可以包含多个方法,这种委托称为多播委托。
如果调用多播委托就可以按顺序连续调用多个方法。为此,委托的签名就必须返回void;否则,就只能得到委托调用的最后一个方法的结果。
例如:
-
Action operations=方法一;operartion+=方法二;
-
也可以 委托1=方法1;委托二=方法2;委托3=委托1+委托2。
所以当调用委托3时,委托1和委托2会同时执行,与第一种实现多播委托一个道理。
但是多播委托还有一个缺陷,一旦一个委托发生异常,其他委托都会停止。为了避免这个问题,应自己迭代方法列表。Delegate类定义GetInvocationList()方法,它返回一个Delegate对象数组。现在可以使用这个委托调用与委托直接相关的
方法,捕获异常,并继续下一次迭代。(.net core 中间件)
匿名方法
到目前为止,要想使委托工作,方法名必须已经存在。但还有另外一种使用委托的方式:匿名方法。
匿名方法(Anonymous methods) 提供了一种传递代码块作为委托参数的技术。匿名方法是没有名称只有主体的方法。
在匿名方法中您不需要指定返回类型,它是从方法主体内的 return 语句推断的。
编写匿名方法的语法
匿名方法是通过使用 delegate 关键字创建委托实例来声明的。例如:
delegate void NumberChanger(int n);
...
NumberChanger nc = delegate(int x)
{
Console.WriteLine("Anonymous Method: {0}", x);
};
代码块 Console.WriteLine("Anonymous Method: {0}", x); 是匿名方法的主体。
委托可以通过匿名方法调用,也可以通过命名方法调用,即,通过向委托对象传递方法参数。
例如:
nc(10);
实例
下面的实例演示了匿名方法的概念:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
using System;
delegate void NumberChanger(int n);
namespace DelegateAppl
{
class TestDelegate
{
static int num = 10;
public static void AddNum(int p)
{
num += p;
Console.WriteLine("Named Method: {0}", num);
}
public static void MultNum(int q)
{
num *= q;
Console.WriteLine("Named Method: {0}", num);
}
static void Main(string[] args)
{
// 使用匿名方法创建委托实例
NumberChanger nc = delegate(int x)
{
Console.WriteLine("Anonymous Method: {0}", x);
};
// 使用匿名方法调用委托
nc(10);
// 使用命名方法实例化委托
nc = new NumberChanger(AddNum);
// 使用命名方法调用委托
nc(5);
// 使用另一个命名方法实例化委托
nc = new NumberChanger(MultNum);
// 使用命名方法调用委托
nc(2);
Console.ReadKey();
}
}
}
当上面的代码被编译和执行时,它会产生下列结果:
匿名方法是用作委托的参数的一段代码,用匿名方法定义委托的语法与前面的定义并没有区别,但在实例化委托时,就有了区别。下面是一个非常简单的代码,它说明了如何使用匿名方法:
Func<string,string> anonDel=delegate(string param)
{
param+=mid;
param+=" and this was added to the string.";
return param;
}
使用匿名方法的规则很明显,它前面是关键字delegate,后面是一个字符串参数.
使用匿名方法
- 声明委托变量时候作为初始化表达式。
- 组合委托时在赋值语句的右边。
- 为委托增加事件时在赋值语句的右边。
匿名方法语法
delegate (parameters ){implementationcode};
关键字 参数 方法体
匿名方法不会声明返回值类型。但是匿名方法返回值类型必须和委托返回值一样。
我们可以使圆括号为空,或省略圆括号来简化匿名方法的参数列表。但是仅在下面两项都为真的情况下才可以这么做。
- 委托的参数列表不包含任何out参数的委托。
- 匿名方法不使用任何参数。
class Program
{
delegate int otherdel(int param);
public static void Main()
{
otherdel del = delegate
{
cleanup();
printMessage();
};
}
}
params参数
如果委托参数包含params参数,那么params关键字就会被匿名方法的参数列表忽略。如下:
delegate int otherdel(int x,params int y);
otherdel del = delegate(int x,int y)
{
};
参考链接:https://www.cnblogs.com/zhan520g/p/10917053.html