C#泛型中的协变out与逆变in分析

本文分为两部分,首先说明什么是协变与逆变,之后使用C#自带使用逆变的Action等进行实例演示。

协变与逆变是针对于泛型而言的,为了更加清楚的说明问题,首先我构造两个类如下:

  class Animal { }
  class Cat : Animal { }

 很简单,Animal为基类,Cat为继承的子类,那么接下来的写法大家一起来分析一下写的是否正确:

1  Animal animal1 = new Cat();
2  List<Animal> animals = new List<Cat>();

 答案是:第一行的完全正确,第二行是错误的,C#编译器给我们的错误提示为:

 也就是说虽然Cat继承了Animal,但是List<Cat>并未继承List<Animal>

我们换一种写法:

IEnumerable<Animal> list2 = new List<Cat>();

这样就是对的了,F12查看IEnumerable定义:

 那么这个out是什么意思呢?为什么使用了它,就可以写出并不是看左右整体是否是继承关系才能通过编译呢?

泛型前加out它代表的就是协变,T只能作为返回值,不能作为参数,这就是协变

我们编写一个自定义协变,来理解一下到底什么是协变,如何理解,如何记忆:

#region 自定义协变
        public interface ICustomerListOut<out T>
        {
            T Get();//T只能作为返回值,不能作为参数
        }
        public class CustomerListOut<T> : ICustomerListOut<T>
        {
            public T Get()
            {
                return default(T);
            }
        }
#endregion

使用协变:

   // 使用自定义协变
   ICustomerListOut<Animal> customerList1 = new CustomerListOut<Animal>();
   ICustomerListOut<Animal> customerList2 = new CustomerListOut<Cat>();
   //理解成后面实例的实际类型(Cat)必须是左边(Animal)内部扩展的(即继承了左边的类型或接口)(out)
这么理解:右边必须是左边的扩展(因为是out),即右边需要继承或者实现了左边;上方就是右边的Cat继承了左边的Animal;

对于C#委托相比大家都不是很陌生,如Action<T>,Func<T>等,F12查看定义:

 那么这个in又是什么意思呢?

在泛型前加in,而且T只能作为方法参数,不能作为返回值,这就是逆变

我们编写一个自定义逆变,来理解一下到底什么是逆变,如何理解,如何记忆:

#region 自定义逆变
        //在泛型接口的T前面有一个In关键字修饰,而且T只能方法参数,不能作为返回值类型,这就是逆变
        public interface ICustomerListIn<in T>
        {
            void Show(T t);//T只能作为参数,不能作为返回值
        }
        public class CustomerListIn<T> : ICustomerListIn<T>
        {
            public void Show(T t)
            {
                
            }
        }
#endregion

使用:

 // 使用自定义逆变
ICustomerListIn<Cat> customerListCat1 = new CustomerListIn<Cat>();
ICustomerListIn<Cat> customerListCat2 = new CustomerListIn<Animal>();
//理解成后面实例的实际类型(Animal)必须在左边(Cat)内部已经继承或者实现了的(in)
这么理解:右边必须是左边的基类或者实现的接口(因为是in),即右边需要左边继承或实现了它;上方就是右边的Animal被左边的Cat继承了;

in就是在括号内做参数,out就是出括号做返回值,这样来记。

猜你喜欢

转载自www.cnblogs.com/ningxinjie/p/12674886.html