首先是delegate的设计其实是有一些历史问题的,并不能说是最好的一种设计。
一个典型的问题就在于所有的delegate实例都是MulticastDelegate类型的,但事实上多播委托的使用范围并没有那么大。更有意思的是多播委托本质上是个串行委托,委托方法是一个接一个的执行的。而实际应用场景中我们会遇到并发多播,异步多播,当某个出现错误时继续执行其他方法的多播委托,所有这些都是MulticastDelegate搞不定的。所以变得意义不大。
到今天为止MulticatsDelegate和+=的运算符重载还是多用于事件处理,而事件用默认的多播委托实现还会有可能导致对象不被释放的坑。
其次就是delegate这个概念意义并不大,尽管在强类型语言里面我们的确需要发明一种东西来描述函数签名,并将单个函数签名固化成一种强类型。但绝大多数时候专门去强调这个概念的意义并不大。很多语言都支持这个特性,但是一般可以直接用函数来描述就可以了,不必另外发明一个委托的概念。
另外就是传统的delegate强类型还有一个缺陷,即使两个delegate类型所代表的函数签名是一模一样的,那他们俩也是两个类型。这在实际运用中是个麻烦。如果你需要用到两个函数库,而这两个库分别将某个类型的函数签名定义了一个委托,即使你只需要写一个函数就能满足两个函数库的要求,但你仍然不得不莫名其妙的创建两个委托实例分别给到两个不同类型的委托对象。
这个缺陷直接催生了Func和Action系列的委托。
当Func和Action系列委托出现以及泛型委托类型参数的协变和逆变出现后,我们发现委托这个概念大部分时候变得很多余。
也就是说我们可以轻松地写出很多代码根本用不着了解委托这个概念,我们最终的着眼点还是函数签名。
但是别忘了泛型和匿名方法是C# 2.0才出现的(省略委托实例创建表达式直接用方法名称代替委托实例也是2.0才引入的),而泛型委托类型参数的协变和逆变是C#3.0才出现的,C#一直在发展的过程中。还有大家所说的lambda表达式也是3.0才引入的。
无论怎样,现在设计一个语言在语言内部保留委托的概念是很正常的,但是再花时间去把这个概念作为亮点来介绍,以及专门去学习和阐述是没有什么意义的。
即使是Java,其实那个SAM Type就是委托的别名,或者换句话说delegate就是SAM Type的别名和语法糖。
当然不管怎么样,C#的delegate语法相较于C/C++的函数指针的语法是一个巨大的飞越,而委托这种语法糖也远比所谓的SAM Type直观和简便。
当然我也看到很多人说委托的学习成本太高,我想说其实OOP和强类型编程语言的学习成本本来就略高于平均智商水平,早日发现并作出正确的选择是非常对的。