协变和逆变(C# 和 Visual Basic)

在 C# 和 Visual Basic 中,协变和逆变允许数组类型、委托类型和泛型类型参数进行隐式引用转换。 协变保留分配兼容性,逆变与之相反。

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

' Assignment compatibility. 
Dim str As String = "test"
' An object of a more derived type is assigned to an object of a less derived type. 
Dim obj As Object = str

' Covariance. 
Dim strings As IEnumerable(Of String) = New List(Of 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. 
Dim objects As IEnumerable(Of Object) = strings

' Contravariance.           
' Assume that there is the following method in the class: 
' Shared Sub SetObject(ByVal o As Object)
' End Sub
Dim actObject As Action(Of Object) = AddressOf 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. 
Dim actString As Action(Of String) = actObject
// 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;

数组的协变允许派生程度更大的类型的数组隐式转换为派生程度更小的类型的数组。 但是此操作不是类型安全的操作,如下面的代码示例所示。

Dim array() As Object = New String(10) {}
' The following statement produces a run-time exception.
' array(0) = 10
object[] array = new String[10];
// The following statement produces a run-time exception.
// array[0] = 10;

对方法组的协变和逆变支持允许将方法签名与委托类型相匹配。 这样,您不仅可以为委托指派具有匹配签名的方法,而且可以指派这样的方法:它们返回与委托类型指定的派生类型相比,派生程度更大的类型(协变),或者接受相比之下,派生程度更小的类型的参数(逆变)。 有关更多信息,请参见委托中的变体(C# 和 Visual Basic)在委托中使用变体(C# 和 Visual Basic)

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

Shared Function GetObject() As Object
    Return Nothing
End Function

Shared Sub SetObject(ByVal obj As Object)
End Sub

Shared Function GetString() As String
    Return ""
End Function

Shared Sub SetString(ByVal str As String)

End Sub

Shared Sub Test()
    ' Covariance. A delegate specifies a return type as object,
    ' but you can assign a method that returns a string.
    Dim del As Func(Of Object) = AddressOf GetString

    ' Contravariance. A delegate specifies a parameter type as string,
    ' but you can assign a method that takes an object.
    Dim del2 As Action(Of String) = AddressOf SetObject
End Sub
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 和 Visual Studio 2010 中,C# 和 Visual Basic 都支持在泛型接口和委托中使用协变和逆变,并允许隐式转换泛型类型参数。 有关更多信息,请参见泛型接口中的变体(C# 和 Visual Basic)委托中的变体(C# 和 Visual Basic)

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

Dim strings As IEnumerable(Of String) = New List(Of String)
Dim objects As IEnumerable(Of Object) = strings
IEnumerable<String> strings = new List<String>();
IEnumerable<Object> objects = strings;

如果泛型接口或委托的泛型参数声明为协变或逆变,则将该泛型接口或委托称为“变体”。 C# 和 Visual Basic 都允许您创建自己的变体接口和委托。 有关更多信息,请参见创建变体泛型接口(C# 和 Visual Basic)委托中的变体(C# 和 Visual Basic)

相关主题

标题

说明

泛型接口中的变体(C# 和 Visual Basic)

讨论泛型接口中的协变和逆变,提供 .NET Framework 中的变体泛型接口列表。

创建变体泛型接口(C# 和 Visual Basic)

演示如何创建自定义变体接口。

在泛型集合的接口中使用变体(C# 和 Visual Basic)

演示 IEnumerable<T> 接口和 IComparable<T> 接口中的协变和逆变支持如何帮助您重用代码。

委托中的变体(C# 和 Visual Basic)

讨论泛型委托和非泛型委托中的协变和逆变,提供 .NET Framework 中的变体泛型委托列表。

在委托中使用变体(C# 和 Visual Basic)

演示如何使用非泛型委托中的协变和逆变支持来匹配方法签名和委托类型。

对 Func 和 Action 泛型委托使用变体(C# 和 Visual Basic)

演示 Func 委托和 Action 委托中的协变和逆变支持如何帮助您重用代码。