CSDN代码 | GitHub代码
协变(covariant)和逆变(contravariant)是C#4.0新增的概念。
协变
首先,我们都知道C#中父类可以充当子类来使用,就像我们在泛型中说到的object是所有类型的父类,那么以下变量赋值时不会报错:
string str = string.Empty;
object obj = str;
如果换一种方式来表达:
创建一个Human类:
using System;
using System.Collections.Generic;
namespace GenericPracitce
{
public class Human
{
public string name { get; set; }
}
}
创建一个子类Chinese:
using System;
using System.Collections.Generic;
namespace GenericPracitce
{
public class Chinese:Human
{
public string age { get; set; }
}
}
在Main()方法中调用:
using System;
using System.Collections.Generic;
namespace GenericPracitce
{
public class Program
{
static void Main(string[] args)
{
//声明Human,和Chinese类,不会有问题
Human human = new Human();
Chinese chinese = new Chinese();
//Chinese是Human的子类,所以声明不会有问题
Human shuman = new Chinese();
//声明List
List<Human> listHuman = new List<Human>();
List<Chinese> listChinese = new List<Chinese>();
//在这里,会出现错误
List<Human> list = new List<Chinese>();
}
}
}
这里的Human虽然是Chinese的父类,但是如果将Human,和Chinese当做List类型来用,那么这里的2个List就不再有继承关系。没有继承关系在声明的时候就会出错。
这时候就需要使用协变:
IEnumerable<Human> List1 = new List<Chinese>();
查看IEnumerable定义:
可以看到协变类似于一个泛型接口,但是不同的是在泛型接口的T前面有一个out关键字修饰(这里的out不是ref的那个out),这就是协变,协变的定义为左边声明的是基类,右边可以声明基类以及基类的子类。协变可以直白的理解为string->object,out关键字可以单纯的理解为输出,作为返回值
根据协变的规则,也可以自定义协变:
using System;
namespace GenericPracitce
{
public interface ICustomCovariant<out T>
{
T Get();
}
public class CustomCovariant<T>:ICustomCovariant<T>
{
public T Get()
{
return default(T);
}
}
}
=========================================================================
//Main调用
ICustomCovariant<Human> custom = new CustomCovariant<Chinese>();
逆变
逆变可以直白的看为object->string,协变的关键字是out,逆变的关键字是in,在你变种in只能作为传入值不能作为返回值。
using System;
namespace GenericPracitce
{
public interface ICustomContravariant<in T>
{
void Get(T t);
}
public class CustomContravariant<T>:ICustomContravariant<T>
{
public void Get(T t)
{
}
}
}
=========================================================================
//Main调用
ICustomContravariant<Chinese> custom = new CustomContravariant<Human>();
我们可以看出协变和逆变是相反的协变可以看作string->object,逆变可以看作object->string。