您的位置:首页 > 其它

协变(covariant)和逆变(contravariant)

2014-01-21 16:15 363 查看
我们知道子类转换到父类,在C#中是能够隐式转换的。这种子类到父类的转换就是协变

而另外一种类似于父类转向子类的变换,可以简单的理解为“逆变”。

上面对逆变的简单理解有些牵强,因为协变和逆变只能针对接口和代理类型。而父类和子类之间不存在这种逆变的概念。

协变和[b]逆变的本质都是子类安全的转到父类的过程。[/b]

下面就来加深下印象,先定义两个类Car和Baoma

public class Car
{
}

public class Baoma : Car
{
}


明显Baoma(宝马)是Car的子类

1,先来看看协变

协变在C#中要用out关键字标明,用这个关键字就表示参数T只能用于函数,属性等的返回值。

//协变(covariant)
public interface ICo<out T>
{
T Test1();
}


//协变(covariant)
ICo<Car> ico1 = null;
ICo<Baoma> ico2 = null;
ico1 = ico2;//子类-->父类
Car car = ico1.Test1();//实际调用ico2.Test1()返回Baoma类型,当然可以赋值给父类Car
//ico2 = ico1;//error
//Baoma baoma = ico2.Test1();//实际调用ico1.Test1()返回Car类型,赋值给子类Baoma,显然不能保证类型安全


2,在来看看逆变

逆变在C#中用in关键字标明,表明参数T只能用于函数的参数。

//逆变(contravariant)
public interface IContra<in T>
{
void Test1(T t);
}


//逆变(contravariant)
IContra<Car> icontra1 = null;
IContra<Baoma> icontral2 = null;
//icontra1 = icontral2;//error
//icontra1.Test(new Car()); 实际调用IContra<Baoma>.Test(Baoma),参数Car不能安全的转换为Baoma
icontral2 = icontra1;//看似很奇怪,但这里不是Car->Baoma,而是IContra<Car>->IContra<Baoma>
icontral2.Test1(new Baoma());//实际调用IContra<Car>.Test(Car),参数Baoma能安全的转换为Car


上面的代码中已经标注详细了调用的过程,对于理解很有帮助。下面分析两种复杂一点的过程。

3,带有逆变或协变的接口作为函数参数

public interface IFoo<in T>
{
void Test(T t);
}
public interface IBar<out T>//这里T必须用out,而不是in
{
void Test(IFoo<T> foo);
}


IFoo<Car> ifoo1 = null;
IBar<Car> ibar1 = null;
IBar<Baoma> ibar2 = null;
ibar1 = ibar2;//协变
ibar1.Test(ifoo1);//实际调用IBar<Baoma>的Test(IFoo<Baoma>),
//要求IFoo<Car>要转换到IFoo<Baoma>,这就需要IFoo对T逆变,即用in关键字


引用装配脑袋对这种情况的归纳:

//如果一个接口需要对T协变,那么这个接口所有方法的参数类型必须支持对T的反变。
//同理我们也可以看出,如果接口要支持对T反变,那么接口中方法的参数类型都必须支持对T协变才行。
//这就是方法参数的协变-反变互换原则。

4,带有逆变或协变的接口作为函数的返回值

public interface IFoo<in T>
{
void Test(T t);
}
public interface IBar2<in T>
{
IFoo<T> Test();
}


IBar2<Car> a = null;
IBar2<Baoma> b = null;
b = a;//逆变
IFoo<Baoma> ibaoma = b.Test();//实际调用IBar2<Car>.Test返回IFoo<Car>类型,IFoo<Car>要转换成IFoo<Baoma>
//需要IFoo对T逆变,这种函数返回值的转换方向是一致的。


引用装配脑袋对这种情况的归纳:

//如果一个接口需要对T进行协变或反变,那么这个接口所有方法的返回值类型必须支持对T同样方向的协变或反变。
//这就是方法返回值的协变-反变一致原则。

以上仅仅是个人理解归纳,关于这个概念的理解,可以参看园子里的这两篇文章,写的非常详细。

装配脑袋的:

/article/4797502.html

还有这篇,用图的方式很好理解:

/article/5272772.html

  
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: