委托中的变体 (Visual Basic)

.NET Framework 3.5 引入了变体支持,用于在 C# 和 Visual Basic 中匹配所有委托的方法签名和委托类型。 这表明不仅可以将具有匹配签名的方法分配给委托,还可以将返回派生程度较大的派生类型的方法分配给委托(协变),或者如果方法所接受参数的派生类型所具有的派生程度小于委托类型指定的程度(逆变),也可将其分配给委托。 这包括泛型委托和非泛型委托。

例如,请考虑以下代码,其中包含两个类和两个委托:泛型和非泛型。

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

创建SampleDelegate或者SampleDelegate(Of A, R)类型的委托时,可以将以下任一方法分配给这些委托。

' 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

下面的代码示例演示了方法签名和委托类型之间的隐式转换。

' 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)

泛型类型参数中的变体

在 .NET Framework 4 及更高版本中,可以启用委托之间的隐式转换,以便在具有泛型类型参数所指定的不同类型按变体的要求继承自对方时,可以将这些类型的泛型委托分配给对方。

要启用隐式转换,必须使用inout关键字将委托中的泛型参数显式声明为协变或逆变。

以下代码示例演示了如何创建一个具有协变泛型类型参数的委托。

' 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 表达式或方法实例化委托,但不能将一个委托分配给另一个委托。

在下面的代码示例中,SampleGenericDelegate(Of String)虽然继承SampleGenericDelegate(Of Object),但无法显式转换为 StringObject 。 可以通过使用T关键字标记泛型参数out来解决此问题。

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 中具有变体类型参数的泛型委托

.NET Framework 4 引入了对多个现有泛型委托中的泛型类型参数的方差支持:

有关详细信息和示例,请参阅对 Func 和 Action 泛型委托使用变体 (Visual Basic)

声明泛型委托中的变体类型参数

如果泛型委托具有协变或逆变泛型类型参数,则可以将其称为 变体泛型委托

可以使用关键字在泛型委托 out 中声明泛型类型参数协变。 协变类型只能用作方法返回类型,不能用作方法参数的类型。 下面的代码示例演示如何声明协变泛型委托。

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

在泛型委托中,可以使用 in 关键字来声明泛型类型参数的逆变。 逆变量类型只能用作方法参数的类型,不能用作方法返回类型。 下面的代码示例演示如何声明逆变泛型委托。

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

重要

ByRef Visual Basic 中的参数不能标记为变体。

可以在同一个委托中支持变体和协变,但这只适用于不同类型的参数。 下面的示例说明了这一点。

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

实例化和调用变体泛型委托

可以实例化和调用变体委托,就像实例化和调用固定委托一样。 在以下示例中,委托通过 lambda 表达式被实例化。

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

合并变体泛型委托

不应合并变体委托。 该方法 Combine 不支持变体委托转换,并且要求委托的类型完全相同。 这可能引发运行时异常:当您使用 Combine 方法(在 C# 和 Visual Basic 中)或使用 + 运算符(仅在 C# 中)组合委托时,如以下代码示例所示。

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)

泛型类型参数中用于值和引用类型的变体

泛型类型参数的变体仅支持引用类型。 例如,DVariant(Of Int)无法隐式转换为DVariant(Of Object)DVariant(Of Long)或,因为整数是值类型。

以下示例演示了泛型类型参数中的变体不支持值类型。

' 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 中的宽松委托转换

轻松的委托转换可以更灵活地将方法签名与委托类型匹配。 例如,它允许在向委托分配方法时省略参数规范和省略函数返回值。 有关详细信息,请参阅宽松委托转换

另请参阅