Поделиться через


Вариативность в делегатах (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 Generic Delegates (Visual Basic)".

Вариативность в параметрах универсального типа

В платформа .NET Framework 4 и более поздних версий можно включить неявное преобразование между делегатами, чтобы универсальные делегаты, имеющие разные типы, заданные параметрами универсального типа, можно назначить друг другу, если типы наследуются друг от друга, как это требуется в дисперсии.

Чтобы включить неявное преобразование, необходимо явно объявить универсальные параметры в делегате как ковариантные или контравариантные с помощью ключевого слова in или out.

В следующем примере кода показано, как создать делегат, который имеет ковариантный параметр универсального типа.

' 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

Если поддержка вариативности используется только для сопоставления сигнатур методов с типами делегатов, а ключевые слова in и out не используются, можно создать экземпляры делегатов с одинаковыми лямбда-выражениями или методами, но нельзя назначить один делегат другому.

В следующем примере SampleGenericDelegate(Of String) кода невозможно явно преобразовать в SampleGenericDelegate(Of Object), хотя String наследуется Object. Эту проблему можно устранить, пометив универсальный параметр 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 generic делегатов (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

Создание экземпляра и вызов вариативных универсальных методов-делегатов

Создание экземпляра и вызов вариативных делегатов возможен только при создании экземпляра и вызове инвариантных делегатов. В следующем примере создается экземпляр делегата с помощью лямбда-выражения.

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

Объединение вариативных универсальных методов-делегатов

Не следует объединять вариантные делегаты. Метод Combine не поддерживает преобразование вариантных делегатов и ожидает делегаты того же самого типа. Это может привести к исключению во время выполнения при объединении делегатов с помощью метода (в C# и Visual Basic) или с помощью Combine+ оператора (в 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), поскольку integer является типом значения.

В следующем примере показано, что вариативность в параметрах универсального типа не поддерживается для типов значения.

' 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

Расслабленное преобразование делегатов обеспечивает большую гибкость при сопоставлении сигнатур методов с типами делегатов. Например, он позволяет опустить спецификации параметров и опустить возвращаемые значения функции при назначении метода делегату. Дополнительные сведения см. в разделе "Расслабленное преобразование делегатов".

См. также