委托定义
委托是一个或多个函数指针的抽象。C# 为了类型安全,默认不支持指针,只能以委托的形式来实现函数指针的概念。委托提供了一种定义和执行回调的方法。通过委托可以将函数视为数据,作为参数进行传递。委托有以下特征:
- 委托继承 System.MulticastDelegate 类
- 目标方法与委托声明须一致
- 委托可以指向静态或实例方法
- 一旦创建委托对象,就可以在运行时动态调用它指向的方法
- 委托可以同步和异步调用方法
委托的声明和使用 委托声明与抽象方法一模一样,只需将 abstract
关键字换成 delegate
。
public delegate int Operation(int x,int y);
在这里我们就声明好了一个委托类型。可以理解为这是一份委托协议,里面规定了要传进来的方法必须是两个整数且返回一个整数。而C# 编译器执行到这一行时,它会定义一个继承自 MulticastDelegate 的类,该类还实现了一个 Invoke 方法,该方法与委托声明中描述的方法完全相同。我们可以通过 ILDASM.exe 来看下它最终的编译。选择工具>命令行>开发者命令提示,在弹出的 CMD 输入 ildasm 就弹出 IL DASM 窗体,在 IL DASM 菜单栏选择 文件>打开,找到项目的 dll 。结果如下图
接下我们定义一个目标方法。如下 :
/// <summary>
/// 作为参数传递的方法
/// 它与声明的 Operation 参数一致且返回类型也一样
/// </summary>
static int Add(int a,int b)
{
//逻辑处理
return a+b;
}
一定要注意,目标方法与委托声明的要一致。接着我们将目标方法插入到给定的委托对象,只需将方法名称传递给委托构造函数,如下:
Operation operation = new Operation(Add);
这时,就可以使用直接函数调用来调用指向的成员,如下:
static void Main(string[] args)
{
//将目标方法插入到委托对象中
Operation operation = new Operation(Add);
//或者
Operation operation1 = Add
//使用直接函数调用来调用指向的成员
int result=operation(2,5);
//输出 7
Console.WriteLine(result);
//也可以直接调用 Invoke 方法
result=operation1.Invoke(4,5);
//输出 9
Console.WriteLine(result);
Console.Read();
}
方法当作参数进行传递,代码如下:
public delegate int Operation(int x, int y);
static int Add(int a, int b)
{
return a + b;
}
static void Main(string[] args)
{
Sum(Add);
Console.Read();
}
static void Sum(Operation operation)
{
if (operation != null)
{
//逻辑处理,得到两个数据进行求和
int a = 2;
int b = 5;
var result = operation(a, b);
Console.WriteLine("{0} + {1} = {2}", a, b, result);
}
}
委托数组创建委托数组与定义数组相似,这里就看代码:
public delegate void Operation(int x, int y);
static void Add(int a,int b)
{
Console.WriteLine("{0} + {1} = {2}", a, b, a + b);
}
static void Multiple(int a, int b)
{
Console.WriteLine("{0} * {1} = {2}", a, b, a * b);
}
static void Main(string[] args)
{
Operation[] operation =
{
new Operation(Add),
new Operation(Multiple)
};
foreach(Operation op in operation)
{
op(4, 7);
op(2, 6);
op(5, 5);
}
Console.Read();
}
输出的结果如下:
4 + 7 = 11
2 + 6 = 8
5 + 5 = 10
4 * 7 = 28
2 * 6 = 12
5 * 5 = 25
匿名方法
匿名方法就是无名方法。不需要创建新方法就可以完成功能,所以没必要单独创建一个方法。匿名方法在编码时提供了一种更简洁、更方便的方法。使用委托关键字和无名方法体来定义匿名方法。
class Program
{
// 定义一个委托
delegate void Operation();
static void Main(string[] args)
{
// 创建实例
Operation opt = delegate
{
Console.WriteLine("匿名方法");
};
opt();
Console.ReadLine();
}
}
此代码将匿名方法分配给委托。匿名方法不能有参数和返回值。匿名方法降低了代码的复杂性,尤其是项目使用过委托的开发者,就知道匿名方法的好处。当然,使用匿名方法不会提高代码性能,编译器仍会定义一个方法。如下图框起来的是编译器生成的匿名方法
多播委托
多播委托看到也有叫委托链。多播委托类似于虚拟容器,其存储着调用多个函数引用的列表。它以 FIFO(先进先出)的顺序依次调用每个方法。当你向委托对象添加多个方法时,只需使用 += 运算符,而不是直接赋值。
public delegate void Operation(int x, int y);
static void Add(int a,int b)
{
Console.WriteLine("{0} + {1} = {2}", a, b, a + b);
}
static void Multiple(int a, int b)
{
Console.WriteLine("{0} * {1} = {2}", a, b, a * b);
}
static void Main(string[] args)
{
Operation operation = Add;
operation += Multiple;
operation(4, 7);
Console.Read();
//输出
//2 + 5 = 7
//2 * 5 = 10
}
这里先执行 Add 方法,再执行 Multiple 方法。这是函数指针添加到多播委托的顺序。要从多播委托中删除函数引用,使用 -= 运算符,该运算符允许调用者从委托对象调用列表中动态删除方法。
Operation operation = Add;
operation -= Multiple;
这里会执行 Add 方法,不会执行 Multiple ,因为我们使用 -= 运算符从多播委托取消了。一个委托调用多个方法可能会导致出现问题。如果委托调用的方法之一引发异常,则整个委托将会被中止。可以通过 Delegate 类,它定义了一个GetInvocationList 方法,该方法返回一个 Delegate 对象数组。
public delegate void Operation(int x, int y);
static void Add(int a, int b)
{
//Console.WriteLine("{0} + {1} = {2}", a, b, a + b);
throw new Exception("错误");
}
static void Multiple(int a, int b)
{
Console.WriteLine("{0} * {1} = {2}", a, b, a * b);
}
static void Main(string[] args)
{
Operation operation = Add;
operation += Multiple;
Delegate[] dels = operation.GetInvocationList();
foreach (Operation del in dels)
{
try
{
del(2, 5);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
Console.Read();
//输出
//错误
//2 * 5 = 10
}
综合示例
上面是委托的基本使用,可能还体会不到它有无尽的好处,这里我们看一个综合示例。不使用委托:
class Program
{
public static void SpeakEnglish()
{
Console.WriteLine("hello , bala bala bala bala bala bala.");
}
public static void SpeakChinse()
{
Console.WriteLine("你好,你会说中文吗?会滴!");
}
public static void Speak(string ln)
{
switch (ln)
{
case "zh-cn":
SpeakChinse();
break;
case "en":
SpeakEnglish();
break;
}
}
static void Main(string[] args)
{
Speak("zh-cn");
Speak("en");
}
}
上面这个代码也没什么毛病,如果现在要多增加一种语言,比如说法语,西班牙语等。这时怎么办,我们要增加一个说法语的方法,然后再增加一个 case,但这样扩展起来是不是很麻烦~而且还破坏开放封闭原则。我们使用委托方式改造下:
public class Language
{
public delegate void Speak();
public void Say(Speak speak)
{
speak();
}
}
class Program
{
public static void SpeakEnglish()
{
Console.WriteLine("hello , bala bala bala bala bala bala.");
}
public static void SpeakChinse()
{
Console.WriteLine("你好,你会说中文吗?会滴!");
}
public static void SpeakFrench()
{
Console.WriteLine("Bonjour, c'est moi qui parle!");
}
static void Main(string[] args)
{
Language language = new Language();
language.Say(SpeakChinse);
language.Say(SpeakEnglish);
language.Say(SpeakFrench);
}
}
没有了 swich-case ,取而代之是委托,扩展也很方便了有木有。今天的委托就到这了。
最后,祝大家学习愉快!
声明:文中观点不代表本站立场。本文传送门:https://eyangzhen.com/218368.html