深入理解C#(十一)
*第五章(C# 2:进入快速通道的委托(上))
回顾C# 1中我们的做法
总是先定义事件/委托实际要处理的方法,如:
1 | static void LogKeyEvent(object sender, KeyPressEventArgs e) |
然后new一个新的事件/委托实例,把这个方法加到委托列表中,如:
1 | button.KeyPress += new KeyPressEventHandler(LogKeyEvent); |
我们总是要把委托实例实际执行的代码做参数穿给委托实例,这样绕弯子会使代码难以阅读,且使得类中填充了大量只用于委托的方法。
方法组转换
C# 2支持从方法组到一个兼容委托类型的隐式转换。方法组(method group)其实就是一个方法名,可以选择添加一个目标,也就是说和C# 1中创建委托实例使用的表达式相同(含义不同,一个是类型加参数,一个是方法组)。新的隐式转换:
1 | button.KeyPress+=LogkeyEvent; |
一个创建线程的代码:
1 | Thread t=new Thread(MyMethod); |
为什么表达式如LogKeyEvent
属于方法组,因为如果有重载的话,可能不止一个方法适用。隐式转换会将一个方法组转换为具有兼容签名的任意委托类型。
假定有以下两个方法签名:
1 | void MyMethod() |
那么在向一个ThreadStart或EventHandler赋值时,都可以将MyMethod作为方法组使用:
1 | ThreadStart x=MyMethod; |
对于本身已重载成可以获取一个ThreadStart或EventHandler的方法,不能把它作为方法的参数使用。同样,不能利用隐式方法组转换来转换成普通的System.Delegate类型。可用辅助方法、强制转换或中间变量来解决。
协变性和逆变性
委托参数的逆变性
举例说明:
1 | public delegate void EventHandler(object sender,EventArgs e) |
有三个委托类型的签名:KeyPressEventArgs和MouseEventArgs都是从EventArgs派生
利用方法组转换和委托逆变性:
1 | static void LogPlainEvent(object sender, EventArgs e) |
用一个事件处理方法来处理所有事件。
关于事件处理方法的参数说明:
第一个参数是object类型,代表事件来源;第二个参数则负责携带与事件有关的任何额外信息
在有委托参数协变性后,我们可以使用一个具有EventHandler签名的方法,作为符合约定的所有委托类型的操作。
委托返回类型的协变性
举例:
首先声明一个委托类型
1 | delegate Stream StreamFactory(); |
然后声明一个方法返回一个特定的流类型。
1 | static MemoryStream GenerateSampleData() |
利用协变性转换方法组
1 | static void Main() |
注意:委托的返回类型是stream
,但声明的方法的返回类型是MemoryStream。StreamFactory factory = GenerateSampleData;
这句话用到了方法组的转换,并利用返回类型的协变性来允许GenerateSampleData用于StreamFactory,等到调用委托实例时,实际返回的是委托声明的类型,也就是说返回类型已从MemoryStream协变成stream
利用协变性和逆变性,还可以基于一个委托实例来构造另一个委托实例。
1 | EventHandler general=new EventHandler(HandleEvent); |
不兼容的风险
可能发生在派生类中。