zoukankan      html  css  js  c++  java
  • C#中协变与抗变(逆变)

      泛型在.NET 2.0中正式的引入。在使用泛型的过程中,联系上面向对象的继承性。往往很容易想当然敲出类似以下代码

    List<Animal> animalLst=new List<Dog>;

    显然这样编译是不通过的。虽然Dog和Animal之间有继承性,但是List<Animal>和List<Dog>这两个类之间并没有继承性。如果要解决这样的问题,用上协变与抗变(逆变),它们统称为变体。是.NET 4.0引入的新特性,但是早在.NET 2.0就引入了。

      变体适用于泛型接口与泛型委托,在我们声明泛型借口或泛型委托时,加上out或in关键字就能实现。

    • out是用于协变,这就好比子类通过隐式转换成基类的情形,这么平淡的转换,与“协”的感觉类似;
    • in是用于抗变,类似于基类强制转换成子类,带有一些负面的感觉的,于是“逆”或者“抗”关联上了。

    两者可以说得上是用于整条类的继承链上转换工作的,但是方向不同。

    那么先来看看协变

        delegate T AnimalAction1<out T>();
    
        interface IProduce<out T>
        {
            T TypeIns { get; }
    
            T CreteNewTypeIns();
        }

      out这个关键字使人联想到“输出”,既然是出的话,那么协变的类型只能用于方法的返回值或者是属性的get,如果变量作为方法的参数(即使是带out关键字的参数)和属性的set,那么编译会报错。那么在FCL里面是协变的接口和委托如下

    IQueryable<out T>
    IEnumerator<out T>
    IGrouping<out TKey,out TElement>
    
    Converter<in TInput,out TOutput>

    看上去都是返回泛型T的。

    再看看抗变

        delegate void AnimalBark<in T>(T animal);
    
        interface IRunnable<in T>
        {
            T TypeIns { set; }
    
            void CanRun(T t);
        }

    既然与协变相反,那么它就代表着“输入”,抗变类型就只能用于方法的参数和属性的set。在FCL里抗变的接口和委托如下

    IComparer<in T>
    IEqualityComparer<in T>
    IComparable<in T>
    
    System.Action<in T>
    System.Func<Out Tresult>
    Predicate<in T>
    Comparison<in T>
    Converter<in TInput,out TOutput>

      可是变体不适用于上面的List<T>,因为这个泛型类在声明的时候并没有用上out关键字,就算是用上了也不现实,因为对于List<T>这个这个泛型类来说,它本身就存在着输入与输出两种行为,Add,Remove等方法就要利用到参数的输入,同时它本身又能通过索引器来获取某个所以值下的元素,既有out又有in的泛型相互矛盾,定义不出来。
      本文因看见某位园有写了一篇相同主题的文章而写的,想着不久前某位同事也叫我探讨过变体,当时我看了不久就忘了,这回看到那位园友的博文,我想想我还是也记录一下吧!不放博客园首页了。

  • 相关阅读:
    EasyUI datagrid动态生成列
    EasyUI easyui-combobox实现数据联动
    EasyUI中datagrid的基本用法
    Oracle update 执行更新操作后的数据恢复
    SqlHelper类
    oracle drop table(表)数据恢复方法
    C#微信公众号——本地调试
    git ignore 总结
    maya cmds pymel 选择 uv area(uv 面积) 为0 的面
    maya cmds pymel selectType() 选择类型切换
  • 原文地址:https://www.cnblogs.com/HopeGi/p/3536587.html
Copyright © 2011-2022 走看看