0%

浅谈C#委托机制

前言

在学习软件构造过程中听说到了 java的委托实现,于是让我想起曾经接触到 C# 时的委托机制了。
但是由于当初没有细究,所以对“委托机制”一问三不知。
于是再次试着搞清楚这个较为神秘的机制,然而最终来说,委托机制其实并不是什么难懂的机制,只要简单的上手试一试,就会进一步了解。
本篇将会十分浅显易懂的探究 C# 委托机制

认识

委托机制,就是将函数名作为函数的参数来使用。

就好比,我想要烤面包,但是我太忙不太想烤面包,于是我去找一位面包师傅。告诉他我需要的面包个数,口感等,接下来他会帮我做好面包,送货上门。(例子可能不恰当)

而说到将函数名作为函数的参数来使用,其实在 C语言 就已经存在了(过去我闲着无聊试着看能不能将函数作为参数进行传递,结果发现可以。。。)。

C语言可以将函数名作为参数传递,其实质是每个函数存在自己的存储空间,函数名指向此函数的地址。因此 C语言 通过指针就能获取到函数的指针(地址),由于指针是一个值类型所以可以作为函数参数来传递,这就是指针 的强大。

据我估计 C++ 也是拥有委托机制的。而 java 没有委托机制,其实是因为 java 在设计中将地址操作隐藏了,这也使得 java 没有指针的概念。

顺带一提 C# 也是存在指针的。

示例演示

理解了 委托 的概念,接下来在 C# 中来操作使用。

C# 使用委托的关键是 delegate 的关键字。这个关键字会将一个函数的声明的函数名作为一个相当于数据类型的存在,这样使得被 delegate 修饰的函数声明的函数名作为参数传递。

演示一

  1. 首先我们声明一个委托类型,其结构也就是需要委托的函数的结构。
1
2
3
// 这里我们将其称为 委托类型
// 委托类型所委托的函数的特点是 返回值为void,需要一个 string 类的参数
public delegate void DelegateMethod(string code);
  1. 然后我们定义用来使用委托的函数,也就是我们希望在这个函数中使用其他函数(委托函数)。

static关键词并不是必须的,这里只是因为方法定义在 Main函数所在的类中,为了方便调用而已。

1
2
3
4
5
6
// 参数 code 是委托函数的参数
// 参数 DelegateMethod method 就是我们定义的委托类型参数
public static void ShowCode(string code, DelegateMethod method){
// 调用方法
method(code);
}
  1. 定义委托函数,函数的数据(返回)类型参数列表 必须 与委托类型一致
1
2
3
4
// 委托者函数 1 
public static void CodeTime(string code){
Console.WriteLine("时间代码:" + code);
}
1
2
3
4
// 委托者函数 2
public static void CodeDate(string code){
Console.WriteLine("日期代码:" + code);
}
  1. 在主函数尝试
1
2
3
4
static void Main(string[] args){
// 尝试传递 codeTime 的方法
ShowCode("12:11:00",CodeTime);
}
  1. 结果
1
时间代码:12:11:00

演示二

其实在 Main 函数中除了以上的用法,委托类型变量也是可以赋值的。

  • 主函数代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    static void Main(string[] args){
    // 尝试传递 codeTime 的方法
    ShowCode("12:11:00",CodeTime);

    // 定义委托类型的变量
    DelegateMethod delegateMethod;
    // 赋值
    delegateMethod = CodeTime;
    // 传值
    ShowCode("13:12:00",delegateMethod);
    }
  • 结果

1
时间代码:13:12:00

演示三

其实 C# 的一个特性,使得委托类型可以像 string 一样来操作。

  • 主函数中,委托类型变量使用 += 来添加
1
2
3
4
5
6
7
8
9
// 定义委托类型的变量
DelegateMethod delegateMethod;
// 定义委托类型的变量
delegateMethod = CodeTime;
// 进一步添加
delegateMethod += CodeDate;

ShowCode("14:00:00",delegateMethod);

  • 结果
1
2
时间代码:14:00:00
日期代码:14:00:00

其实既然 += 可以使用,那么 -= 也是可以使用的

  • 试着使用 -=
1
2
3
4
5
6
7
8
9
10
11
// 定义委托类型的变量
DelegateMethod delegateMethod;
// 定义委托类型的变量
delegateMethod = CodeTime;
// 进一步添加
delegateMethod += CodeDate;
// 试着删除第一个
delegateMethod -= CodeTime;

ShowCode("14:00:00",delegateMethod);

  • 结果确实如我们所想
1
日期代码:14:00:00

注意:需要注意的是,不能一开始就使用 += 的操作符,这是错误的。

演示四

对于演示三,我们可以像操作 string 一样使用 += ,但是这样也不是很靠谱,如果修改代码时,将 += 代替了初始化赋值部分那么就会报错。

于是可以已开始就对委托类型实例化对象,这是一个非空的构造函数

  • 创建实例化
1
2
3
4
5
// 构造函数必须有参数,
DelegateMethod delegateMethod = new DelegateMethod(CodeTime) ;

ShowCode("20:50:11", delegateMethod);

总结

优点

  • 很明显的优点就是,当我处理某些事件时,无需使用 if-else 来判断我们要使用的函数,利用委托机制我们只需要考虑选择需要的函数进行传值即可。
  • 其实在代码复用方面也很实用。
  • 减少了代码间的耦合,增强了内聚性。

缺点

  • 能看出,如果要代理的函数参数列表与返回值类型不同的话,那么可能要创建多个委托类型,增加代码结构的复杂性。
  • 如果某个方法要重写,那么会牵扯到这个委托类型。

结语

于是乎,委托机制就是这么好理解,再往深层理解,可以考虑与指针联系在一起。

其实从中也能看出,我们定义的“委托类型”,其实更像是一种接口,也像是指针,只有参数和返回值类型匹配到,就可被委托。