• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    公众号

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

原作者: [db:作者] 来自: [db:来源] 收藏 邀请

本文分为两部分,首先说明什么是协变与逆变,之后使用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就是出括号做返回值,这样来记。

 使用Action<in T>举例子来加深理解的深度:

首先定义两个Action如下:

public Action<Animal> an;
public Action<Cat> cat;

然后定义两个方法:

 public void ceshi1(Animal an) { }
 public void ceshi2(Cat an) { }

我书写如下代码,大家一起来理解一下,看看写的是否对:

1)an+=ceshi1

2)an+=ceshi2

3)cat+=ceshi1

4)cat+=ceshi2

答案是:1.3.4都是对的  2是错的

因为2式中 an是Animal,测试2是Cat,在逆变情形下,需要左边实现或者继承右边

而实际情况是Cat继承了Animal,因此3对,2不对。

杰杰觉得这个很有用,希望跟大家一起分享。

 


鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
上一篇:
C#GIF图片的分解以及合成发布时间:2022-07-10
下一篇:
c#窗体实现验证码发布时间:2022-07-10
热门推荐
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap