基于.Net中的协变与逆变的深入分析

 更新时间:2021年9月22日 10:15  点击:2165

关于协变和逆变要从面向对象继承说起。继承关系是指子类和父类之间的关系;子类从父类继承所以子类的实例也就是父类的实例。比如说Animal是父类,Dog是从Animal继承的子类;如果一个对象的类型是Dog,那么他必然是Animal。
协变逆变正是利用继承关系不同参数类型或返回值类型 的委托或者泛型接口之间做转变。我承认这句话很绕,如果你也觉得绕不妨往下看看。
如果一个方法要接受Dog参数,那么另一个接受Animal参数的方法肯定也可以接受这个方法的参数,这是Animal向Dog方向的转变是逆变。如果一个方法要求的返回值是Animal,那么返回Dog的方法肯定是可以满足其返回值要求的,这是Dog向Animal方向的转变是协变。
由子类向父类方向转变是协变 协变用于返回值类型用out关键字
由父类向子类方向转变是逆变 逆变用于方法的参数类型用in关键字
协变逆变中的协逆是相对于继承关系的继承链方向而言的。
一. 数组的协变:

复制代码 代码如下:

Animal[] animalArray = new Dog[]{};

上面一行代码是合法的,声明的数组数据类型是Animal,而实际上赋值时给的是Dog数组;每一个Dog对象都可以安全的转变为Animal。Dog向Animal方法转变是沿着继承链向上转变的所以是协变
二. 委托中的协变和逆变
1.委托中的协变
复制代码 代码如下:

//委托定义的返回值是Animal类型是父类
public delegate Animal GetAnimal();
//委托方法实现中的返回值是Dog,是子类
static Dog GetDog(){return new Dog();}
//GetDog的返回值是Dog, Dog是Animal的子类;返回一个Dog肯定就相当于返回了一个Animal;所以下面对委托的赋值是有效的
GetAnimal getMethod = GetDog;

2.委托中的逆变
复制代码 代码如下:

//委托中的定义参数类型是Dog
public delegate void FeedDog(Dog target);
//实际方法中的参数类型是Animal
static void FeedAnimal(Animal target){}
// FeedAnimal是FeedDog委托的有效方法,因为委托接受的参数类型是Dog;而FeedAnimal接受的参数是animal,Dog是可以隐式转变成Animal的,所以委托可以安全的的做类型转换,正确的执行委托方法;
FeedDog feedDogMethod = FeedAnimal;

定义委托时的参数是子类,实际上委托方法的参数是更宽泛的父类Animal,是父类向子类方向转变,是逆变
三. 泛型委托的协变和逆变:
1. 泛型委托中的逆变
如下委托声明:
复制代码 代码如下:

public delegate void Feed<in T>(T target);

Feed委托接受一个泛型类型T,注意在泛型的尖括号中有一个in关键字,这个关键字的作用是告诉编译器在对委托赋值时类型T可能要做逆变
复制代码 代码如下:

//先声明一个T为Animal的委托
Feed<Animal> feedAnimalMethod = a=>Console.WriteLine(“Feed animal lambda”);
//将T为Animal的委托赋值给T为Dog的委托变量,这是合法的,因为在定义泛型委托时有in关键字,如果把in关键字去掉,编译器会认为不合法
Feed<Dog> feedDogMethod = feedAnimalMethod;

2. 泛型委托中的协变
如下委托声明:
复制代码 代码如下:

public delegate T Find<out T>();

Find委托要返回一个泛型类型T的实例,在泛型的尖括号中有一个out关键字,该关键字表明T类型是可能要做协变的
复制代码 代码如下:

//声明Find<Dog>委托
Find<Dog> findDog = ()=>new Dog();

//声明Find<Animal>委托,并将findDog赋值给findAnimal是合法的,类型T从Dog向Animal转变是协变
Find<Animal> findAnimal = findDog;

四. 泛型接口中的协变和逆变:
泛型接口中的协变逆变和泛型委托中的非常类似,只是将泛型定义的尖括号部分换到了接口的定义上。
1.泛型接口中的逆变
如下接口定义:
复制代码 代码如下:

public interface IFeedable<in T>
{
void Feed(T t);
}

接口的泛型T之前有一个in关键字,来表明这个泛型接口可能要做逆变
如下泛型类型FeedImp<T>,实现上面的泛型接口;需要注意的是协变和逆变关键字in,out是不能在泛型类中使用的,编译器不允许
复制代码 代码如下:

public class FeedImp<T>:IFeedable<T>
{
    public void Feed(T t){
        Console.WriteLine(“Feed Animal”);
    }
}

来看一个使用接口逆变的例子:
复制代码 代码如下:

IFeedable<Dog> feedDog = new FeedImp<Animal>();

上面的代码将FeedImp<Animal>类型赋值给了IFeedable<Dog>的变量;Animal向Dog转变了,所以是逆变
2.泛型接口中的协变
如下接口的定义:
复制代码 代码如下:

public interface IFinder<out T>
{
    T Find();
}

泛型接口的泛型T之前用了out关键字来说明此接口是可能要做协变的;如下泛型接口实现类
复制代码 代码如下:

public class Finder<T>:IFinder<T> where T:new()
{
    public T Find(){
        return new T();
    }
}
//使用协变,IFinder的泛型类型是Animal,但是由于有out关键字,我可以将Finder<Dog>赋值给它
IFinder<Animal> finder = new Finder<Dog>();

协变和逆变的概念不太容易理解,可以通过实际代码思考理解。这么绕的东西到底有用吗?答案是肯定的,通过协变和逆变可以更好的复用代码。复用是软件开发的一个永恒的追求。

[!--infotagslink--]

相关文章

  • ASP.NET购物车实现过程详解

    这篇文章主要为大家详细介绍了ASP.NET购物车的实现过程,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2021-09-22
  • .NET Core下使用Kafka的方法步骤

    这篇文章主要介绍了.NET Core下使用Kafka的方法步骤,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-09-22
  • 在ASP.NET 2.0中操作数据之七十二:调试存储过程

    在开发过程中,使用Visual Studio的断点调试功能可以很方便帮我们调试发现程序存在的错误,同样Visual Studio也支持对SQL Server里面的存储过程进行调试,下面就让我们看看具体的调试方法。...2021-09-22
  • Win10 IIS 安装.net 4.5的方法

    这篇文章主要介绍了Win10 IIS 安装及.net 4.5及Win10安装IIS并配置ASP.NET 4.0的方法,本文给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下...2021-09-22
  • 详解.NET Core 3.0 里新的JSON API

    这篇文章主要介绍了详解.NET Core 3.0 里新的JSON API,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-09-22
  • .net数据库操作框架SqlSugar的简单入门

    这篇文章主要介绍了.net数据库操作框架SqlSugar的简单入门,帮助大家更好的理解和学习使用.net技术,感兴趣的朋友可以了解下...2021-09-22
  • ASP.NET Core根据环境变量支持多个 appsettings.json配置文件

    这篇文章主要介绍了ASP.NET Core根据环境变量支持多个 appsettings.json配置文件,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-09-22
  • 记一次EFCore类型转换错误及解决方案

    这篇文章主要介绍了记一次EFCore类型转换错误及解决方案,帮助大家更好的理解和学习使用asp.net core,感兴趣的朋友可以了解下...2021-09-22
  • .NET C#利用ZXing生成、识别二维码/条形码

    ZXing是一个开放源码的,用Java实现的多种格式的1D/2D条码图像处理库,它包含了联系到其他语言的端口。这篇文章主要给大家介绍了.NET C#利用ZXing生成、识别二维码/条形码的方法,文中给出了详细的示例代码,有需要的朋友们可以参考借鉴。...2020-06-25
  • 详解ASP.NET Core 中基于工厂的中间件激活的实现方法

    这篇文章主要介绍了ASP.NET Core 中基于工厂的中间件激活的实现方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2021-09-22
  • C#使用Ado.Net更新和添加数据到Excel表格的方法

    这篇文章主要介绍了C#使用Ado.Net更新和添加数据到Excel表格的方法,较为详细的分析了OLEDB的原理与使用技巧,可实现较为方便的操作Excel数据,需要的朋友可以参考下...2020-06-25
  • asp.net通过消息队列处理高并发请求(以抢小米手机为例)

    这篇文章主要介绍了asp.net通过消息队列处理高并发请求(以抢小米手机为例),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-09-22
  • ASP.NET 2.0中的数据操作:使用两个DropDownList过滤的主/从报表

    在前面的指南中我们研究了如何显示一个简单的主/从报表, 该报表使用DropDownList和GridView控件, DropDownList填充类别,GridView显示选定类别的产品. 这类报表用于显示具有...2016-05-19
  • ASP.NET单选按钮控件RadioButton常用属性和方法介绍

    RadioButton又称单选按钮,其在工具箱中的图标为 ,单选按钮通常成组出现,用于提供两个或多个互斥选项,即在一组单选钮中只能选择一个...2021-09-22
  • 详解.NET Core 使用HttpClient SSL请求出错的解决办法

    这篇文章主要介绍了.NET Core 使用HttpClient SSL请求出错的解决办法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧...2021-09-22
  • Python调用.NET库的方法步骤

    这篇文章主要介绍了Python调用.NET库的方法步骤,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-05-09
  • ASP.NET中iframe框架点击左边页面链接 右边显示链接页面内容

    这篇文章主要介绍了ASP.NET中iframe框架点击左边页面链接,右边显示链接页面内容的实现代码,感兴趣的小伙伴们可以参考一下...2021-09-22
  • 创建一个完整的ASP.NET Web API项目

    ASP.NET Web API具有与ASP.NET MVC类似的编程方式,ASP.NET Web API不仅仅具有一个完全独立的消息处理管道,而且这个管道比为ASP.NET MVC设计的管道更为复杂,功能也更为强大。下面创建一个简单的Web API项目,需要的朋友可以参考下...2021-09-22
  • ASP.NET连接MySql数据库的2个方法及示例

    这篇文章主要介绍了ASP.NET连接MySql数据库的2个方法及示例,使用的是MySQL官方组件和ODBC.NET,需要的朋友可以参考下...2021-09-22
  • Asp.Net使用Bulk实现批量插入数据

    这篇文章主要介绍了Asp.Net使用Bulk实现批量插入数据的方法,对于进行asp.net数据库程序设计非常有借鉴价值,需要的朋友可以参考下...2021-09-22