.NET 4.0 “Covariance”和“Contravariance”趣话

本文探讨了.NET4.0中引入的Covariance(协变)与Contravariance(逆变)概念,解释了如何通过out和in关键字使泛型接口和委托支持这些特性,并给出了具体的应用实例。

.NET 4.0 “Covariance”“Contravariance”趣话

面向对象的程序中,我们知道基类变量可以引用子类对象,比如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.0Visual 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;

}

警告:

在实际开发中别写这样的代码,如果你这么做了,我担保你一定会被需要维护你代码的同事痛扁一顿!

依据上述原则,你完全可以使用inout关键字定义支持“Covariance”“Contravariance”特性的泛型接口与泛型委托。

不过,如果没有特殊需求,你还是直接用基类库中的现有接口和委托就行了,不要滥用“Covariance”“Contravariance”特性。

对了,如果一个泛型接口(或泛型委托)声明为支持“Covariance”或“Contravariance”特性,我们就将它们统称为“variant”的泛型接口(或泛型委托)。

事实上,CovarianceContravariance不仅适用于泛型接口和泛型委托同样适用于非泛型的委托

例如,以下代码定义了一个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 = "您按了鼠标上的键";

}

示例程序的奇特之处在于,此函数可以直接挂接到按钮的KeyDownMouseClick事件上!

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

【直流微电网】径向直流微电网的状态空间建模与线性化:一种耦合DC-DC变换器状态空间平均模型的方法 (Matlab代码实现)内容概要:本文介绍了径向直流微电网的状态空间建模与线性化方法,重点提出了一种基于耦合DC-DC变换器状态空间平均模型的建模策略。该方法通过对系统中多个相互耦合的DC-DC变换器进行统一建模,构建出整个微电网的集中状态空间模型,并在此基础上实施线性化处理,便于后续的小信号分析与稳定性研究。文中详细阐述了建模过程中的关键步骤,包括电路拓扑分析、状态变量选取、平均化处理以及雅可比矩阵的推导,最终通过Matlab代码实现模型仿真验证,展示了该方法在动态响应分析控制器设计中的有效性。; 适合人群:具备电力电子、自动控制理论基础,熟悉Matlab/Simulink仿真工具,从事微电网、新能源系统建模与控制研究的研究生、科研人员及工程技术人员。; 使用场景及目标:①掌握直流微电网中多变换器系统的统一建模方法;②理解状态空间平均法在非线性电力电子系统中的应用;③实现系统线性化并用于稳定性分析与控制器设计;④通过Matlab代码复现扩展模型,服务于科研仿真与教学实践。; 阅读建议:建议读者结合Matlab代码逐步理解建模流程,重点关注状态变量的选择与平均化处理的数学推导,同时可尝试修改系统参数或拓扑结构以加深对模型通用性适应性的理解。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值