C# 泛型可变性

泛型的可变性只能修饰接口或者委托的类型参数;

可变性分为协变性与逆变性,出于好记忆的角度,out协变可以理解为和谐的变化,in逆变可以理解为不和谐的变化。其格式为:

interface MyList<out T>{}
//或者
private delegate A MyDelegate<out A, in B>(B tmp);

委托的泛型可变性,在类型参数为返回值或者形参的时候,in/out可以不写,因为它们在函数申明中所做的成分(返回值/实参)就已经决定了它们是in/out了。也就是说,上面这委托申明中的in和out写与不写是一致的。

泛型可变性的意义,是规定了不同泛型实例间的继承关系(这里说继承关系或许不太妥),可以达成类似以下效果:

MyList<Animal> animalLst = new MyList<Dog>();

当然MyList作为接口是无法直接实例化的,这里只是想表达泛型可变性带给我们的功能。如果没有泛型可变性,MyList<Animal>与MyList<Dog>是完全两个不同的类型,他们各自的声明无法关联彼此的实例,但是泛型可变性带给我们了这种可能性。

以下是一则完整的委托泛型可变性实例:

private class GrandParent{}
private class Parent : GrandParent{}
private class Child : Parent{}

//以下这一句委托声明,可以想成,委托的签名的要求是
//传一个B类型(或者其子类)实参进去,返回值用A类型(或者其父类)变量来接
//参数类型是B类型,实参当然可以传递B类型的子类型的实例
//返回类型是A类型,返回值当然可以用A类型的父类型的变量来接
private delegate A MyDelegate<out A, in B>(B tmp);

//上面委托声明与以下声明是完全一致的,类型参数所做成分就决定了它们的可变性:
private delegate A MyDelegate<A, B>(B tmp);

//以下这一句委托实例的声明,就是要求:
//第一个泛型参数Parent,指明myEvent返回一个类型为Parent的对象,自然返回其子类也是OK的
//第二个泛型参数Parent,指明委托实例myEvent调用时,需要传入一个类型为Parent的对象,自然形参是其父类也是OK的
//基于以上两点,Main函数中,MyFunc赋值给myEvent委托实例是合法的。

private event MyDelegate<Parent, Parent> myEvent;

private Child MyFunc(GrandParent c)
{
    return null;
}

private void Main()
{
    myEvent = MyFunc;
    Parent ppp= myEvent0.Invoke(new Parent());
}

泛型可变性与LSP(里氏代换原则)是有相似之处的:

里氏替换原则包含以下4层含义:

  • 子类可以实现父类的抽象方法,但是不能覆盖父类的非抽象方法。
  • 子类中可以增加自己特有的方法。
  • 当子类覆盖或实现父类的方法时,方法的前置条件(即方法的形参)要比父类方法的输入参数更宽松。
  • 当子类的方法实现父类的抽象方法时,方法的后置条件(即方法的返回值)要比父类更严格。

里氏代换原则的3、4两点,与具有泛型可变性的委托在结果上是一致的,至于其有没有深层次关联,请各位自己再想想吧。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值