前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >变体(variance)

变体(variance)

作者头像
宿春磊Charles
发布2022-03-29 09:33:22
4600
发布2022-03-29 09:33:22
举报
文章被收录于专栏:DotNet 致知

上节讲到了泛型,这节延申一下,讲一下变体。

变体(variance)是协变(convariance)和抗变(也说逆变contravariance)的统称。这个概念在.net 4中引入,在.net 2.0中就可以使用,但是比较麻烦,.net 4将这一概念封装成了特性。

讲变体之前,我们先来复习一下多态性。请看如下代码:

代码语言:javascript
复制
class Animals
{
    public virtual void Say(){}
}

class Cat : Animals
{
    public override void Say()
    {
        Console.WriteLine("小猫喵喵叫");
    }
}

class Dog : Animals
{
    public override void Say()
    {
        Console.WriteLine("小狗汪汪汪");
    }
}

interface IAnimals<out T> where T :Animals, new()
{
    void InvokeSay();
}
interface IAnimalsType<in T> where T : Animals
{
    Type GetType();
}

class AnimalsType<T>:IAnimalsType<T> where T : Animals
{
    public Type GetType()
    {
        return typeof(T);
    }
}

class AnimalsAdmin<T> : IAnimals<T> where T : Animals,new()
{
    //此处涉及到反射,不清楚反射的读者请留意后期文章
    //此处只需知道调用了传入实例类的Say()方法即可
    public void InvokeSay()
    {
        Type type = typeof(T);
        T t = new T();
        MethodInfo Say = type.GetMethod("Say");
        Say.Invoke(t, null);
    }
}

有一父类Animals,Cat和Dog继承此类,根据多态性,以下代码是可行的:

代码语言:javascript
复制
Animals cat = new Cat();
Animals dog = new Dog();

有两个接口,分别由两个类继承,先分析其中一个类和接口,下面的代码是编译不过的:

代码语言:javascript
复制
IAnimals<Animals> animals;
animals=new AnimalsAdmin<Dog>();//父类是IAnimals<Dog>
animals= new AnimalsAdmin<Cat>();//父类是IAnimals<Cat>

以上转换,在多态性中看似是可以的,但其实这样的转换不属于多态。多态性是基于类的继承,若两个类没有继承关系,何谈多态,AnimalsAdmin<Dog>和AnimalsAdmin<Cat>的父类和IAnimals<Animals>是平行类型关系,没有继承关系。只有以下代码是可行的:

代码语言:javascript
复制
IAnimals<Animals> animals;
animals=new AnimalsAdmin<Animals>();

而变体,让这样的转换变的可行。

协变:

为了建立他们之间的继承关系,接口IAnimals<T>的类型需要设置为协变,有了协变类型,AnimalsAdmin<Dog>,AnimalsAdmin<Cat>这两个类和IAnimals<Animals>就建立了继承关系(这一点比多态性稍微复杂一些)。那如何设为协变类型呢:

代码语言:javascript
复制
interface IAnimals<out T> where T :Animals, new()
{
    void InvokeSay();
}

T前加关键字out即可。我们来看一下运行结果:

微软的API中也有很多协变的例子:例如IEnumerable泛型接口就是协变性的。

抗变:

了解了协变,那么对于抗变,小伙伴们也能猜出是什么意思,协变是向上转换,那么抗变就是向下转换,请分析另一对接口和类,看如下代码:

代码语言:javascript
复制
AnimalsAdmin<Cat> cAdmin = new AnimalsAdmin<Cat>();
Type type = cAdmin.GetAnimalType(new AnimalsType<Animals>());

没有抗变,以上代码显然是不可行的,因为cAdmin.GetAnimalType的参数需要一个AnimalsType<Cat>类型的,为了使这种转换可行,需将IAnimalsType<T>接口设置为抗变类型:

代码语言:javascript
复制
interface IAnimalsType<in T> where T :Animals
{
    Type GetAnimalType();
}

在T前加关键字in,我们来看一下运行结果:

抗变在.net Framework中有很多用处,IComparable泛型接口就是抗变类型。

通过变体,我们在面向泛型接口编程的时候,就可以借助多态性很灵活的编码。最后注意两点:设置为协变类型的T,只能用作返回类型和属性get访问器的类型,而设置为抗变类型的T只能用作方法的参数。

本节到此结束...

源码文件位于:

https://github.com/Chunlei-Su/PublicDemo/tree/master/C%23/C%23Senior/Variance(%E5%8F%98%E4%BD%93)

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-08-01,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 DotNet 致知 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档