• 协变与逆变


    迁移 https://huangshubi.github.io/2020/02/14/%E5%8D%8F%E5%8F%98%E4%B8%8E%E9%80%86%E5%8F%98/

    记录 官方文档的协变与逆变学习过程。

    使用举例

    协变与逆变能够实现数组类型、委托类型和泛型接口参数的隐式引用转换。

    1、委托类型

    namespace ConsoleApp4
    {
        class Program
        {        
            static void Main(string[] args)
            {
                Func<Bird> birdFunc = () => new Bird();
                Func<Animal> animalFunc = () => new Animal(); 
                animalFunc = birdFunc; 
                //协变 Func参数使用了out关键字
                Animal animal = animalFunc(); 
                          
                Action<Animal> animalAction = (t) => { };
                Action<Bird> birdAction = animalAction;
                //逆变 Action参数使用了in关键字
                birdAction(new Bird()); 
            }
        }
        class Animal { }
        class Bird : Animal { }   
    }

    如果泛型接口或委托的泛型参数被声明为协变或逆变,该泛型接口或委托则被称为“变体”。

    错误示范:

    List<Object> list = new List<string>();
    //实现变体接口的类仍是固定类,这样是无法转换的。
    
    IEnumerable<int> integers = new List<int>();
    IEnumerable<Object> objects = integers; //只能用于引用类型
    

     创建变体泛型接口

    通过对泛型类型参数使用out关键字,将参数声明为协变。

    interface ICovariant<out R>
    {
        R GetSomething();
        void DoSomething(Action<R> callback);  //逆变参数
    }
    
     class Implementation<R> : ICovariant<R>
     {
         public void DoSomething(Action<R> callback)
         {
             throw new NotImplementedException();
         }
    
         public R GetSomething()
         {
             throw new NotImplementedException();
         }
     }

    通过对泛型类型参数使用in关键字,将参数声明为逆变。

    interface IContravariant<in A>
    {
        void SetSomething(A sampleArg);
        void DoSomething<T>() where T : A;   //逆变参数可以使用约束,协变不可以      
    }
    

     同一个接口,可以同时有逆变参数和协变参数,例如Func<in T,out TResult>

    派生变体泛型接口

    派生变体泛型接口,仍需使用in、out关键字来显示指定是否支持变体。

    interface ICovariant<out R>
    {
        R GetSomething();
        void DoSomething(Action<R> callback);  //逆变参数
    }
    interface  IExtCovariant<out R> : ICovariant<R> //协变参数
    {
    }
    interface IExtCovariantOne<R> : ICovariant<R> //固定参数
    {
    }
    如果父接口参数声明为逆变,则派生接口只能和父相同,或者声明为固定参数。
     
  • 相关阅读:
    数据可视化需要简化编程
    设计模式之工厂模式
    LinCode落单的数
    怎样安装解压版MySQL
    程序阅读:简单C++学生信息管理系统
    中缀式变后缀式
    jquery动态创建表格
    Android笔记——Activity中的回传数据案例(装备选择)
    A mail sent to Google chromium.org Groups for Help
    Eclipse导入MyEclipseproject(web项目显示为java项目解决的方法)
  • 原文地址:https://www.cnblogs.com/bibi-feiniaoyuan/p/12368677.html
Copyright © 2020-2023  润新知