• C# 变体(variance)


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

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

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

    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继承此类,根据多态性,以下代码是可行的:

    Animals cat = new Cat();
    Animals dog = new Dog();

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

    IAnimals<Animals> animals;
    animals=new AnimalsAdmin<Dog>();//父类是IAnimals<Dog>
    animals= new AnimalsAdmin<Cat>();//父类是IAnimals<Cat>

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

    IAnimals<Animals> animals;
    animals=new AnimalsAdmin<Animals>();

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

    协变:

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

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

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

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

    抗变:

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

    AnimalsAdmin<Cat> cAdmin = new AnimalsAdmin<Cat>();
    Type type = cAdmin.GetAnimalType(new AnimalsType<Animals>());

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

    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)

       这是我的公众号二维码,获取最新文章,请关注此号

  • 相关阅读:
    Vue单页面应用
    MVVM模式理解
    Ajax原生四大步骤
    Vue 全家桶介绍
    原生js的dom操作
    vs2015+opencv3.3.1+ maxflow-v3.01 c++实现Yuri Boykov 的Interactive Graph Cuts
    c++迭代递归实现汉诺塔(5种迭代方法满足你)
    opencv3.3.1+vs2015+c++实现直接在图像上画掩码,保存掩码图片
    声明函数指针、回调函数、函数对象------c++程序设计基础、编程抽象与算法策略
    C++/C语言的标准库函数与运算符的区别new/delete malloc/free
  • 原文地址:https://www.cnblogs.com/charlesmvp/p/13413456.html
Copyright © 2020-2023  润新知