C# 委托

委托定义

委托是一个或多个函数指针的抽象。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

联系我们
联系我们
分享本页
返回顶部