基本概念:在 C# 中,协变和逆变能够实现数组类型、委托类型和泛型类型参数的隐式引用转换。
主要遵循的原则是里氏替换原则,即父类可以装子类。比如string变成object。
用途:协变和逆变是修饰泛型类型参数的,协变是out,逆变是in,修饰泛型类型参数,只有泛型接口和泛型委托能使用。
返回值和参数:
用out修饰的泛型只能作为返回值使用;
delegate T TestOut();
用in修饰的泛型只能作为参数使用。
delegate void TestIn(T t);
结合里氏替换原则理解:
class Father { }
class Son:Father { }
internal class Program
{
static void Main(string[] args)
{
//结合里氏替换原则理解
//里氏替换原则:父类装子类
//协变out:修饰委托delegate泛型,只能作为返回值使用
TestOut<Son> testOutSon = () =>
{
return new Son();
};
//委托是装“格式相同”的函数,如果不用out,此处代码会报错。因为TestOut<Father>和TestOut<Son>的泛型不同。
//但实际上TestOut<Father>泛型是Father类,TestOut<Son>泛型是Son类,即泛型存在一个父子关系,
//所以,使用out修饰委托delegate的泛型,系统会自己判断返回值是否存在父子关系,即是否可以使用里氏替换原则。
//那么,实际执行TestOut<Father>时,返回值是Son,是Father的子类,满足里氏替换原则,
//所以,存在父子关系的,且用out修饰委托泛型的可以使TestOut<Father>装TestOut<Son>。
TestOut<Father> testOutFather = testOutSon;
Father father = testOutFather();
//逆变in:修饰委托delegate泛型,只能作为参数使用。
//委托TestIn<Son>的泛型与委托TestIn<Father>存在父子关系,
//用in修饰泛型T,按照规则,作为看似委托TestIn<Son>装委托TestIn<Father>,
//实际上执行TestIn<Son>时是将Son作为参数传入Father,还是满足里氏替换原则的。
TestIn<Father> testInfather = (Father father) => { };
TestIn<Son> testInSon = testInfather;
testInSon(new Son());//实际上调用的testInfather
}
}