协变和逆变 (C#)

在 C# 中,协变和逆变使数组类型、委托类型和泛型类型参数实现隐式引用转换。 协变会保留赋值兼容性,而逆变会反转赋值兼容性。

以下代码演示了分配兼容性、协变和逆变之间的差异。

// Assignment compatibility.
string str = "test";  
// An object of a more derived type is assigned to an object of a less derived type.
object obj = str;  
  
// Covariance.
IEnumerable<string> strings = new List<string>();  
// An object that is instantiated with a more derived type argument
// is assigned to an object instantiated with a less derived type argument.
// Assignment compatibility is preserved.
IEnumerable<object> objects = strings;  
  
// Contravariance.
// Assume that the following method is in the class:
static void SetObject(object o) { }
Action<object> actObject = SetObject;  
// An object that is instantiated with a less derived type argument
// is assigned to an object instantiated with a more derived type argument.
// Assignment compatibility is reversed.
Action<string> actString = actObject;  

数组的协变使派生程度更大的类型的数组能够隐式转换为派生程度更小的类型的数组。 但这一操作不是类型安全的,如以下代码示例所示。

object[] array = new String[10];  
// The following statement produces a run-time exception.  
// array[0] = 10;  

方法组的协变和逆变支持允许将方法签名与委托类型匹配。 这样,不仅可以将具有匹配签名的方法分配给委托,还可以分配与委托类型指定的派生类型相比,返回派生程度更大的类型的方法(协变)或接受具有派生程度更小的类型的参数的方法(逆变)。 有关详细信息,请参阅委托中的变体 (C#)使用委托中的变体 (C#)

下面的代码示例显示了对方法组的协变和逆变支持。

static object GetObject() { return null; }  
static void SetObject(object obj) { }  
  
static string GetString() { return ""; }  
static void SetString(string str) { }  
  
static void Test()  
{  
    // Covariance. A delegate specifies a return type as object,  
    // but you can assign a method that returns a string.  
    Func<object> del = GetString;  
  
    // Contravariance. A delegate specifies a parameter type as string,  
    // but you can assign a method that takes an object.  
    Action<string> del2 = SetObject;  
}  

在 .NET Framework 4 及更高版本中,C# 支持泛型接口和委托中的协变和逆变,并允许隐式转换泛型类型参数。 要获取更多信息,请参阅泛型接口中的协变和逆变(C#)委托中的协变和逆变(C#)。

下面的代码示例演示泛型接口的隐式引用转换。

IEnumerable<String> strings = new List<String>();  
IEnumerable<Object> objects = strings;  

泛型接口或委托在其泛型参数声明为协变或逆变时,被称为变体。 使用 C# 可以创建自己的变体接口和委托。 有关详细信息,请参阅创建变体泛型接口(C#)委托的变体性(C#)

标题 DESCRIPTION
泛型接口中的方差 (C#) 讨论泛型接口中的协变和逆变,并提供 .NET 中的变体泛型接口列表。
创建 Variant 泛型接口 (C#) 演示如何创建自定义变体接口。
在泛型集合的接口中使用变体 (C#) IEnumerable<T>IComparable<T>接口中显示协变和逆变支持如何帮助你重用代码。
委托中的变体 (C#) 讨论泛型和非泛型委托中的协变和逆变,并在 .NET 中提供变体泛型委托的列表。
使用委托中的变体 (C#) 演示如何在非泛型委托中使用协变和逆变支持,将方法签名与委托类型匹配。
对 Func 和 Action 泛型委托使用变体 (C#) 演示 Func 委托和 Action 委托中对协变和逆变的支持如何帮助重复使用代码。