使用了泛型接口,我们不再像以前那样去处理一些方法,比如:
以前版本中存在这样一个方法:
public interface IComparable<in T>
{
int CompareTo(T other);
}
public class Person: IComparable
{
public int CompareTo(object obj)
{
Person other = obj as Person;
return this.lastname.CompareTo(other.LastName);
}
}
调用CompareTo方法时,需要对obj进行强制类型转换,但是有了泛型之后,就不在使用这用方法了
public class Person: IComparable<Person>
{
public int CompareTo(Person other) =>
LastName.CompareTo(other.LastName);
//...
上面用到了in的参数,其实还有out的参数IEnumerator<out T>.,接下来我们就会讲解这些内容
1、协变和抗变(逆变)
.net 4为泛型接口和泛型委托添加了重要的更改:协变和抗变。
协变:类似于子类到父类的转换称之为协变 Sharp sharp = new Rectange();
逆变:类似于父类到子类的转换称之为逆变 Rectange sharp = new Sharp();
以上只是简单的定义,还没有明确下来,那么究竟如何应用协变和逆变呢?
1.1协变
书中内容看起来着实费劲,此处参考https://blog.csdn.net/sudazf/article/details/17148971
首先明确一个问题,在.net 4.0之前,注意看代码的注释
Sharp sharp = new Rectange();//可以编译通过,以为Rectange继承了Sharp
IEnumerable<Sharp> sharps = new List<Rectange>();//但是不能这样写,因为当时不支持泛型的转换
那么问题来了,在.net 4.0之后为什么就可行了呢?
public interface IEnumerable<out T> : IEnumerable
是因为在T参数中加了一个out 关键字,此out关键字,和方法中的out关键字不同。
下面呢我们来看一下什么时候使用协变。
首先,定义一个接口
public interface ICovariant<T>
{
}
在定义两个类,分别为父类和子类,都继承第一个接口ICovariaant<T>
public class Sharp:ICovariant<Sharp>
{
}
public class Rectange:Sharp,ICovariant<Rectange>
{
}
然后开始调用:
我们看到,让泛型子类,赋值给父类的时候报错,那么我们就需要对接口进行修改,加上out参数,构成泛型的协变
public interface ICovariant<out T>
{
}
此时编译通过。
这样我们就可以说接口ICovariant只是对类型T的协变,也就是说支持泛型子类赋值给泛型父类。
1.2 抗变(逆变)
当我们将 ICovariant<Sharp>的实例变量赋值给 ICovariant<Rectange>实例变量时,则编译不通过。
这是属于父类赋值给子类,属于抗变,那么我们将接口修改为 in 修饰的参数
public interface ICovariant<in T>
{
}
此时编译通过。
1.3逆变和抗变应用的特点。
我们在接口中添加两个方法,第一个方法返回T类型,第二个方法,T类型作为方法的输入参数。
public interface ICovariant<out T>
{
T Method1();
void Method2(T param);
}
这时我们就会发现
将T类型作为方法2的输入参数类型会报错。
报错的原因为,因为类型T为协变,不能用于抗变的位置。这时我们就会发现,out类型只能用于输出类型的修饰(协变),不能用于输入类型的修饰(抗变)。
那么我们将泛型的协变类型改为逆变类型:
果然和我们想的一样,这时in 修饰的T类型,用于方法1的返回类型就会报错。
那么我们该同时使用协变和逆变呢?我们需要将方法分开到两个接口
public interface ICovariant<out T>
{
T Method1();
void Method3(IContravariant<T> param);
}
public interface IContravariant<in T>
{
void Method2(T param);
}