在C# 4.0中引入的协变和逆变是多态性的扩展,它们允许对数组类型、委托类型和泛型类型参数进行隐式引用转换。协变保持了赋值兼容性,而逆变则逆转了它。简而言之,协变和逆变是C#语言中用于增强类型灵活性和表达力的特性。
在C# 4.0之前,这种灵活性是有限的。以下面的继承关系为例:
class GrandFather {}
class Son : GrandFather {}
class GrandSon : GrandFather {}
根据多态性,可以将子类实例赋值给父类引用:
GrandFather father = new Son();
father = new GrandSon();
然而,在C# 4.0之前,以下代码会抛出异常:
IEnumerable<GrandFather> fathers = new List<Son>();
这似乎违反了多态性原则。但在C# 4.0及更高版本中,上述代码可以正常工作,因为引入了协变和逆变。
协变允许将派生类型隐式转换为基类型,而逆变则允许将基类型隐式转换为派生类型。在数组协变中,可以将派生类型的数组隐式转换为基类型的数组,但这并不是类型安全的。例如:
object[] strArray = new string[10];
strArray[0] = 1;
在上面的例子中,尽管数组是string类型的,但尝试将一个整数值赋给数组的某个元素,这在编译时不会抛出异常,但在运行时会抛出异常。
委托协变也称为方法组变体。根据MSDN文档,可以将具有匹配签名的方法分配给委托,也可以将返回更派生类型的方法或接受更少派生类型参数的方法分配给委托。这包括泛型和非泛型委托。例如:
static string Parental() {
return "Grandfather name is: Lala Bhagwan Das";
}
Func<object> parentalLambda = () => Parental();
Func<object> parentalFunc = Parental;
在上面的代码中,创建了一个返回string的委托,但可以分配一个返回object的方法。
逆变允许将参数类型声明为string的委托分配给接受object参数的方法。例如:
static string ParentalObject(object obj) {
// method signature
}
Action<String> parentalDel = ParentalObject;
在上面的代码中,委托指定了参数类型为string,但可以分配一个接受object的方法。