协变和逆变

泛型接口中的泛型参数可以声明为协变参数或者逆变参数,首先介绍一下泛型接口的协变和逆变:

  •   协变:泛型参数定义的类型只能作为方法的返回类型,不能用作方法参数的类型,且该类型直接或间接地继承自接口方法的返回值类型,称为协变。可以使用关键字out,将泛型类型参数声明为协变参数。
  •   逆变:泛型参数定义的类型只能作为方法参数的类型,不能用作返回类型,且该类型是接口方法的参数类型的基类型,称为逆变。可以使用in关键字,将泛型类型参数声明为逆变参数。

首先我们来定义一个协变接口,代码如下所示:

 具有协变参数的泛型接口
1
interface ISample1<out T>
2 {
3 T Function1();
4 }
5
6 class Sample1<T>:ISample1<T>
7 {
8 public T Function1()
9 {
10 Console.WriteLine("Wroked......");
11 return default(T);
12 }
13 }

上述代码中,ISample1<out T>是一个协变接口,其泛型参数T是一个协变参数,Sample1 <T>泛型类实现了该协变接口,如下代码体现了“协变”:

1 class BaseClass{}
2 class Child:BaseClass{}
3 ISample1<Child> sample1=new ISample<Child>(); <1>
4 ISample1<BaseClass> base1=sample1; <2>
5 ISample1<object> base2=base1; <3>

在上述代码中,<1>中sample1的协变参数的类型为Child(此时是协变实参),也就是<1>中sample1的接口方法返回类型为Child,二<2>中的接口方法返回类型为BaseClass,CLR会自动完成Child到BaseClass的隐式类型转换,此谓“协变”。同理,<3>也是合法的。
    我们特别介绍一下第一段代码中的“default(T)”代码,在这里default关键字用以解决这样一个问题:在无法预参数化类型T是值类型还是引用类型的情况下,使用default关键字,对于引用类型会返回空引用(null),而对于值类型则会返回0。

具有逆变参数的泛型接口
1
interface ISample2<in T>
2 {
3 void Function2(T arg);
4 }
5 class Sample2<T> : ISample2<T>
6 {
7 public void Function2(T arg)
8 {
9 Console.WriteLine(arg);
10 }
11 }


上述代码定义了一个逆变接口,以及一个实现了该接口的类。接下来,首先使用BaseClass作为类型实参声明,并初始化一个变量sample2,然后再使用Child作为类型实参声明一个变量sample3,Child类是BaseClass类的派生类,可以基于逆变的规则将sample2赋给sample3变量

1 ISample2<BaseClass> sample2 = new Sample2<BaseClass>();
2 ISample2<Child> sample3 =sample2;




原文地址:https://www.cnblogs.com/hesitate/p/2436615.html