.NET 4.0 “Covariance”和“Contravariance”趣话
来源:互联网 发布:哆点校园mac版 编辑:程序博客网 时间:2024/05/17 12:21
面向对象的程序中,我们知道基类变量可以引用子类对象,比如List<T>派生自IEnumerable<T>,所以,以下这句绝无问题: IEnumerable<Parent> P = new List<Parent>(); 现在假设Parent类有一个子类,取名Child。 class Parent { } class Child : Parent { } 请看以下“错误的”代码: IEnumerable<Parent> P = new List<Child>(); 虽然基类变量可以引用子类对象,但上述代码在.NET 4.0之前无法通过编译。 现在有趣的事情发生了,你会发现,同样的代码在.NET 4.0和Visual Studio 2010中则可以顺利通过编译。 这是怎么回事? 请看一下IEnumerable<T >的声明: public interface IEnumerable<out T> : IEnumerable { IEnumerator<T> GetEnumerator(); } 这里面多了一个“神秘”的out关键字。正是因为它,才发生那些过去不可能发生的情况。 在.NET 4.0中,如果一个泛型接口(或泛型委托)的类型参数前有一个out关键字,那么,此类型参数“子类/父类对象通吃”。这种特性称为“Covariance”。 再来看.NET基类库中的泛型委托Action<T>的定义: public delegate void Action< in T>(T obj); 请注意其中有一个“in”关键字,由于前面看到了“out”的介绍,敏感的读者一定会估计这个“in”里可能有点名堂,来看个例子。 以下代码定义了一个接收基类Parent对象的方法: static void ParentFunc(Parent p) { //……(代码略) } 我们发现,在.NET4.0中,此方法可以传给一个以子类Child作为类型参数的Action委托! Action<Child> del = ParentFunc; 由此我们知道,在.NET 4.0中,如果一个泛型委托(或泛型接口)的类型参数前有一个in关键字,那么,“定义为子类型的in类型参数可以接收对应位置的定义为父类型参数的方法”,这句话实在是太别扭了,但请读者原谅我的汉语水平。 这种特性被称为“Contravariance”。 提示: 别问我“Covariance”和“Contravariance”和这两个词如何翻译,我也不知道,大家等着中文MSDN出来,看看微软的牛人们怎么将这两个词翻译为汉语吧。 为便于记忆,可以总结为两句话: 类型参数前有“in”的,基类可以传入给子类,叫“Contravariance”。 类型参数前有“out”的,子类可以传出给父类,叫“Covariance”。 比较“变态”的是有些委托可以同时“in”和“out”,请看.NET基类库中的Func<T,TResult>的定义: public delegate TResult Func<in T, out TResult>(T arg); 于是,我们可以写出以下完全正确但却让人“昏菜”的代码: Func<Child, Parent> func = MyFunc; 其中,MyFunc方法定义如下: static Child MyFunc(Parent p) { return p as Child; } 警告: 在实际开发中别写这样的代码,如果你这么做了,我担保你一定会被需要维护你代码的同事痛扁一顿! 依据上述原则,你完全可以使用in和out关键字定义支持“Covariance”和“Contravariance”特性的泛型接口与泛型委托。
不过,如果没有特殊需求,你还是直接用基类库中的现有接口和委托就行了,不要滥用“Covariance”和“Contravariance”特性。
对了,如果一个泛型接口(或泛型委托)声明为支持“Covariance”或“Contravariance”特性,我们就将它们统称为“variant”的泛型接口(或泛型委托)。
事实上,Covariance和Contravariance不仅适用于泛型接口和泛型委托,同样适用于非泛型的委托。
例如,以下代码定义了一个MyDelegate委托,注意它返回一个Parent对象:
public delegate Parent MyDelegate();
则我们可以写出以下代码:
MyDelegate del = delegate()
{
return new Child();
};
需要注意的是,上述使用“匿名方法”给委托变量赋值仅适用于“Covariance”,如果要用于“Contravariance”,你必须老老实实地写一个独立的函数,再赋值给委托变量。
好了,来看一个“真正有点用”的实例吧。
请看一个“使用同一个函数同时响应鼠标和键盘操作”的示例程序ContravarianceExample:
示例程序定义了一个键盘和鼠标事件响应函数: private void MultiHandler(object sender, System.EventArgs e) { if (e is KeyEventArgs) lblInfo.Text = string.Format("您敲了{0}键", (e as KeyEventArgs).KeyCode.ToString()); if(e is MouseEventArgs) lblInfo.Text = "您按了鼠标上的键"; } 示例程序的奇特之处在于,此函数可以直接挂接到按钮的KeyDown和MouseClick事件上! btnTestCovariance.MouseClick += MultiHandler; btnTestCovariance.KeyDown += MultiHandler; 让我们分析一下“后台”到底发生了什么事情。 首先,我们注意到MouseClick事件和KeyDown事件的参数拥有以下继承关系:
再来看一下KeyDown事件和MouseClick事件的定义:
public event KeyEventHandler KeyDown;
public event MouseEventHandler MouseClick;
请注意两个事件其实都是委托类型的变量。以下是两个事件委托的定义:
public delegate void KeyEventHandler(object sender, KeyEventArgs e);
public delegate void MouseEventHandler(object sender, MouseEventArgs e)
可以看到,这两个委托的定义,其参数都是EventArgs的子类。所以,依据Contravariance特性,它们可以接收参数定义为父类EventArgs的方法。这正是MultiHandler可以直接作为统一的事件响应函数挂接到键盘和鼠标事件的原因。
===================
下载示例程序(http://files.cnblogs.com/bitfan/ContravarianceExample.rar)
转载来至 http://blog.csdn.net/bitfan/archive/2010/01/25/5255425.aspx
提示:“Covariance”和“Contravariance”应该翻译为协变和逆变
- .NET 4.0 “Covariance”和“Contravariance”趣话
- .NET 4.0 “Covariance”和“Contravariance”趣话
- Covariance and Contravariance
- Covariance, Contravariance and Invariance
- Invariance, covariance and contravariance
- Covariance and Contravariance in Java
- .Net 2.0 新功能:委托中的协变与逆变(Covariance and Contravariance in Delegates)
- Covariance and Contravariance in Generics(泛型中的协变和逆变)
- 对协变和逆变的理解(Contravariance and Covariance)
- Delegate(委托)中的Covariance(协变)和Contravariance(逆变)
- 协变(Covariance)与逆变(Contravariance)
- contravariance
- 协变(covariance),逆变(contravariance)与不变(invariance)
- covariance
- Covariance
- 【转】趣话WebSocket、long poll 和 ajax轮询
- “图灵”趣话
- “图灵”趣话
- hash表
- BF算法
- 链表
- KMP算法
- 人生哲理 之 驴子的故事
- .NET 4.0 “Covariance”和“Contravariance”趣话
- System.Data.OracleClient 需要 Oracle 客户端软件 8.1.7 或更高版本
- Silverlight 自定义控件的继承问题2
- MS-SQL2005服务器登录名、角色、数据库用户、角色、架构的关系
- 我把IT给哲学了
- 蛮力法
- 如何查询SQL2005的连接数
- 记单词
- 分治法