委托中的变体(Visual Basic)Variance in Delegates (Visual Basic)

.NET Framework 3.5 介绍了在C#和 Visual Basic 的所有委托中匹配方法签名与委托类型的差异支持。.NET Framework 3.5 introduced variance support for matching method signatures with delegate types in all delegates in C# and Visual Basic. 这表明不仅可以将具有匹配签名的方法分配给委托,还可以将返回派生程度较大的派生类型的方法分配给委托(协变),或者如果方法所接受参数的派生类型所具有的派生程度小于委托类型指定的程度(逆变),也可将其分配给委托。This means that you can assign to delegates not only methods that have matching signatures, but also methods that return more derived types (covariance) or that accept parameters that have less derived types (contravariance) than that specified by the delegate type. 这包括泛型委托和非泛型委托。This includes both generic and non-generic delegates.

例如,思考以下代码,该代码具有两个类和两个委托:泛型和非泛型。For example, consider the following code, which has two classes and two delegates: generic and non-generic.

Public Class First
End Class

Public Class Second
    Inherits First
End Class

Public Delegate Function SampleDelegate(ByVal a As Second) As First
Public Delegate Function SampleGenericDelegate(Of A, R)(ByVal a As A) As R

创建 SampleDelegateSampleDelegate(Of A, R) 类型的委托时,可以将以下任一方法分配给这些委托。When you create delegates of the SampleDelegate or SampleDelegate(Of A, R) types, you can assign any one of the following methods to those delegates.

' Matching signature.
Public Shared Function ASecondRFirst(
    ByVal second As Second) As First
    Return New First()
End Function

' The return type is more derived.
Public Shared Function ASecondRSecond(
    ByVal second As Second) As Second
    Return New Second()
End Function

' The argument type is less derived.
Public Shared Function AFirstRFirst(
    ByVal first As First) As First
    Return New First()
End Function

' The return type is more derived
' and the argument type is less derived.
Public Shared Function AFirstRSecond(
    ByVal first As First) As Second
    Return New Second()
End Function

以下代码示例说明了方法签名与委托类型之间的隐式转换。The following code example illustrates the implicit conversion between the method signature and the delegate type.

' Assigning a method with a matching signature
' to a non-generic delegate. No conversion is necessary.
Dim dNonGeneric As SampleDelegate = AddressOf ASecondRFirst
' Assigning a method with a more derived return type
' and less derived argument type to a non-generic delegate.
' The implicit conversion is used.
Dim dNonGenericConversion As SampleDelegate = AddressOf AFirstRSecond

' Assigning a method with a matching signature to a generic delegate.
' No conversion is necessary.
Dim dGeneric As SampleGenericDelegate(Of Second, First) = AddressOf ASecondRFirst
' Assigning a method with a more derived return type
' and less derived argument type to a generic delegate.
' The implicit conversion is used.
Dim dGenericConversion As SampleGenericDelegate(Of Second, First) = AddressOf AFirstRSecond

有关更多示例,请参阅在委托中使用变体(Visual Basic)对 Func 和 Action 泛型委托使用变体(Visual Basic)For more examples, see Using Variance in Delegates (Visual Basic) and Using Variance for Func and Action Generic Delegates (Visual Basic).

泛型类型参数中的变体Variance in Generic Type Parameters

在 .NET Framework 4 和更高版本中,你可以启用委托之间的隐式转换,以便可以将具有不同类型的泛型委托分配给不同的类型,前提是这些类型从彼此继承时是必需的变动.In .NET Framework 4 and later you can enable implicit conversion between delegates, so that generic delegates that have different types specified by generic type parameters can be assigned to each other, if the types are inherited from each other as required by variance.

若要启用隐式转换,必须使用 inout 关键字将委托中的泛型参数显式声明为协变或逆变。To enable implicit conversion, you must explicitly declare generic parameters in a delegate as covariant or contravariant by using the in or out keyword.

以下代码示例演示了如何创建一个具有协变泛型类型参数的委托。The following code example shows how you can create a delegate that has a covariant generic type parameter.

' Type T is declared covariant by using the out keyword.
Public Delegate Function SampleGenericDelegate(Of Out T)() As T
Sub Test()
    Dim dString As SampleGenericDelegate(Of String) = Function() " "
    ' You can assign delegates to each other,
    ' because the type T is declared covariant.
    Dim dObject As SampleGenericDelegate(Of Object) = dString
End Sub

如果仅使用变体支持来匹配方法签名和委托类型,且不使用 inout 关键字,则可能会发现有时可以使用相同的 lambda 表达式或方法实例化委托,但不能将一个委托分配给另一个委托。If you use only variance support to match method signatures with delegate types and do not use the in and out keywords, you may find that sometimes you can instantiate delegates with identical lambda expressions or methods, but you cannot assign one delegate to another.

在下面的代码示例中,虽然 String 继承 ObjectSampleGenericDelegate(Of String) 无法显式转换为 SampleGenericDelegate(Of Object)In the following code example, SampleGenericDelegate(Of String) can't be explicitly converted to SampleGenericDelegate(Of Object), although String inherits Object. 可以使用 T 关键字标记 泛型参数 out 解决此问题。You can fix this problem by marking the generic parameter T with the out keyword.

Public Delegate Function SampleGenericDelegate(Of T)() As T
Sub Test()
    Dim dString As SampleGenericDelegate(Of String) = Function() " "

    ' You can assign the dObject delegate
    ' to the same lambda expression as dString delegate
    ' because of the variance support for
    ' matching method signatures with delegate types.
    Dim dObject As SampleGenericDelegate(Of Object) = Function() " "

    ' The following statement generates a compiler error
    ' because the generic type T is not marked as covariant.
    ' Dim dObject As SampleGenericDelegate(Of Object) = dString

End Sub

.NET Framework 中具有变体类型参数的泛型委托Generic Delegates That Have Variant Type Parameters in the .NET Framework

.NET Framework 4 在几个现有泛型委托中引入了泛型类型参数的变体支持:.NET Framework 4 introduced variance support for generic type parameters in several existing generic delegates:

有关详细信息和示例,请参阅对Func 和 Action 泛型委托使用变体(Visual Basic)For more information and examples, see Using Variance for Func and Action Generic Delegates (Visual Basic).

声明泛型委托中的变体类型参数Declaring Variant Type Parameters in Generic Delegates

如果泛型委托具有协变或逆变泛型类型参数,则该委托可被称为“变体泛型委托”。If a generic delegate has covariant or contravariant generic type parameters, it can be referred to as a variant generic delegate.

可以使用 out 关键字声明泛型委托中的泛型类型参数协变。You can declare a generic type parameter covariant in a generic delegate by using the out keyword. 协变类型只能用作方法返回类型,而不能用作方法参数的类型。The covariant type can be used only as a method return type and not as a type of method arguments. 以下代码示例演示了如何声明协变泛型委托。The following code example shows how to declare a covariant generic delegate.

Public Delegate Function DCovariant(Of Out R)() As R

可以使用 in 关键字声明泛型委托中的泛型类型参数逆变。You can declare a generic type parameter contravariant in a generic delegate by using the in keyword. 逆变类型只能用作方法参数的类型,而不能用作方法返回类型。The contravariant type can be used only as a type of method arguments and not as a method return type. 以下代码示例演示了如何声明逆变泛型委托。The following code example shows how to declare a contravariant generic delegate.

Public Delegate Sub DContravariant(Of In A)(ByVal a As A)

重要

Visual Basic 中的 ByRef 参数不能标记为变体。ByRef parameters in Visual Basic can't be marked as variant.

可以在同一个委托中支持变体和协变,但这只适用于不同类型的参数。It is also possible to support both variance and covariance in the same delegate, but for different type parameters. 这在下面的示例中显示。This is shown in the following example.

Public Delegate Function DVariant(Of In A, Out R)(ByVal a As A) As R

实例化和调用变体泛型委托Instantiating and Invoking Variant Generic Delegates

可以像实例化和调用固定委托一样,实例化和调用变体委托。You can instantiate and invoke variant delegates just as you instantiate and invoke invariant delegates. 在以下示例中,该委托通过 lambda 表达式进行实例化。In the following example, the delegate is instantiated by a lambda expression.

Dim dvariant As DVariant(Of String, String) = Function(str) str + " "
dvariant("test")

合并变体泛型委托Combining Variant Generic Delegates

不应合并变体委托。You should not combine variant delegates. Combine 方法不支持变体委托转换,并且要求委托的类型完全相同。The Combine method does not support variant delegate conversion and expects delegates to be of exactly the same type. 当你使用 Combine 方法(在和 Visual Basic 中C# )或使用 + 运算符(在中C#)组合委托时,这可能会导致运行时异常,如下面的代码示例中所示。This can lead to a run-time exception when you combine delegates either by using the Combine method (in C# and Visual Basic) or by using the + operator (in C#), as shown in the following code example.

Dim actObj As Action(Of Object) = Sub(x) Console.WriteLine("object: {0}", x)
Dim actStr As Action(Of String) = Sub(x) Console.WriteLine("string: {0}", x)

' The following statement throws an exception at run time.
' Dim actCombine = [Delegate].Combine(actStr, actObj)

泛型类型参数中用于值和引用类型的变体Variance in Generic Type Parameters for Value and Reference Types

泛型类型参数的变体仅支持引用类型。Variance for generic type parameters is supported for reference types only. 例如,DVariant(Of Int)无法隐式转换为 DVariant(Of Object)DVariant(Of Long),因为 integer 是值类型。For example, DVariant(Of Int)can't be implicitly converted to DVariant(Of Object) or DVariant(Of Long), because integer is a value type.

以下示例演示了泛型类型参数中的变体不支持值类型。The following example demonstrates that variance in generic type parameters is not supported for value types.

' The type T is covariant.
Public Delegate Function DVariant(Of Out T)() As T
' The type T is invariant.
Public Delegate Function DInvariant(Of T)() As T
Sub Test()
    Dim i As Integer = 0
    Dim dInt As DInvariant(Of Integer) = Function() i
    Dim dVariantInt As DVariant(Of Integer) = Function() i

    ' All of the following statements generate a compiler error
    ' because type variance in generic parameters is not supported
    ' for value types, even if generic type parameters are declared variant.
    ' Dim dObject As DInvariant(Of Object) = dInt
    ' Dim dLong As DInvariant(Of Long) = dInt
    ' Dim dVariantObject As DInvariant(Of Object) = dInt
    ' Dim dVariantLong As DInvariant(Of Long) = dInt
End Sub

Visual Basic 中的宽松委托转换Relaxed Delegate Conversion in Visual Basic

通过宽松委托转换,可以更灵活地将方法签名与委托类型相匹配。Relaxed delegate conversion enables more flexibility in matching method signatures with delegate types. 例如,可以在将方法分配给委托时省略参数规范并省略函数返回值。For example, it lets you omit parameter specifications and omit function return values when you assign a method to a delegate. 有关详细信息,请参阅宽松委托转换For more information, see Relaxed Delegate Conversion.

另请参阅See also