C# 协变 逆变
逆变,协变
定义:
逆变 :指能够使用比原始指定的派生类型的 派生程度更小(不太具体的)的类型。
协变 :能够使用比原始指定的派生类型的 派生程度更大(更具体的)的类型。
看了上面一脸懵逼,下面会解释
示例
这是用来讲解例子的两个类
public class Father
{
public string Name { get; set; }
}
public class Son : Father
{
public int Age { get; set; }
}
协变 out
Father f = new Son();
先来看这一句 这是我们熟悉的, 因为Son继承自Father, 所以编译可以通过。
我们可以认为:
Father(基类) 相对于 Son(子类) 是派生程度更大(更具体的)的类型。 具体,代表一个父类可以有多个子类。所以相对而言基类更具体 子类不太具体
Son(子类) 相对于 Father(基类) 是派生程度更小(不太具体的)的类型
再来看这一句
List<Son> sons = new List<Son>();
List<Father> fathers = sons; // error
为什么为编译失败呢,因为编译器知道 Father 和 Son 是继承关系,但是不知道List< Father > 和 List< Son > 是什么关系。
List<Son> sons = new List<Son>();
IEnumerable<Father> fathers = sons; // success
这是协变,可以允许使用比原指定类型派生程度更大(更具体的)的类型。
逆变 in
逆变也称为不正常的变化
比如Father转为Son,这肯定不行。
static void Main(string[] args)
{
ISay<Father> fatherSay = new FatherSay();
ISay<Son> son1 = fatherSay;
son1.Say(new Son()); // new Son()这里就是 传的是派生程度更低的类型, 而指定的是父类,我对父类的操作肯定也可以传递到子类
}
public interface ISay<in T>
{
void Say(T t);
}
public class FatherSay : ISay<Father>
{
public void Say(Father t)
{
Console.WriteLine(t.Name);
}
}
定义ISay接口 我指定的类型是Father 既然father有say().Name 为什么Son不能呢,所以使用逆变.
这是逆变 可以使用比原指定类型派生程度更小(不太具体的)的类型。
其实就是希望当作为返回的时候,期望的是派生程度更高的类型,你指定一个子类,我希望他能返回,他的父类,或者父类的父类。这样我才能保证输出的正确,比如我返回个父类,你却非要子类,而子类里入上例有一个属于自己的age属性,所以就不满足
而作为参数类型,我希望传的是派生程度更低的类型,因为你指定的是父类,我对父类的操作肯定也可以传递到子类。而为什么不能传派生程度更高的类型,比如你指定的是子类,但是传的是父类, 那你怎么保证对子类的操作,会传递到父类。
相当于
协变: Father f() => new Son(); 所以作为返回值时可以更具体
逆变:指定父类Father Say(Father f)=> f.Name我传入子类Say(Son s) => s.Name一定满足, 而如果我指定子类Son Say(Son s) => s.Age
然后传入父类 Say(Father f) => f.Age ?可以看到父类是没有age的 所以逆变我们需要传入派生程度更低(不具体)的类型