今天来谈谈C#语言中事件的设计问题——这是除了性能问题之外,C#语言设计哲学中另外一个严重的问题——不必要的臃肿。C#事件总共存在以下4类问题:
1. C#事件没有抽象性
事件并非对象的基本元素,并不反映对象的关键抽象。字段反映对象状态,方法反映对象行为,事件反映什么?一个观察-通知的关系结构吗?
2. C#事件没有通用性
绝大多数对象没有事件的设计需求,不会实现事件。
3. C#事件的实现性能比较低下
a.C#事件的背后是一个委托链表(单链表),单链表的遍历调用性能远低于数组链表(List<T>)
b.C#事件默认实现会产生一个委托字段实例,支持不用的委托字段实例是一个性能负担(参见WPF/SL里面的路由事件的改造设计)
4. C#事件没有必要性——这一条是C#事件最大的问题,也是本文的重点
事件只是观察者设计模式的变体。其完全可以用接口来实现。
将所谓常用的设计模式变成语言构造的一部分,是C#设计思想里面又一个的严重错误(第一个严重错误是为了所谓的功能,而不管不问性能成本,在前文《C#会重蹈覆辙吗?系列之2:反射及元数据的性能问题》中对此有详述)。
C#中类似的例子还有:使用foreach和yield支持迭代器模式,使用using支持Dispose设计模式——我就在这里一并批评了,不再另外撰文了。
如果可以在C#语言中内置Observer模式,内置Iterator模式, 内置Dispose模式,那么要不要把Adapter模式也变成C#语言的一部分?要不要把Proxy模式也变成C#语言的一部分?Bridge呢?Composite呢?Strategy呢?.....几百种模式是否都要变成C#语言的一部分???...
另外,在语言中内置设计模式,会使得该模式失去实现上的灵活性、或者损伤性能(比如C#事件就限定了观察者模式必须使用委托来完成事件调用,使用委托链表来表达观察者的对象集合,这种限制使得设计模式失去了其实现上的灵活性)
使用程序库来支持设计模式是编程语言的正道,将模式集成为语言的一部分来耍cool,最后的结果是这个语言越来越臃肿,越来越庞大。希望C#设计师们在这条道路上止步。