Примечание
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Платформа .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
Универсальные делегаты с параметрами типа variant в .NET Framework
Платформа .NET Framework 4 ввела поддержку для вариативности параметров обобщённого типа в нескольких уже существующих обобщённых делегатах.
Action
делегаты из пространства имен System, например, Action<T> и Action<T1,T2>Func
делегаты из пространства имен System, например, Func<TResult> и Func<T,TResult>Делегат Predicate<T>
Делегат Comparison<T>
Делегат Converter<TInput,TOutput>
Дополнительные сведения и примеры см. в разделе «Использование вариативности для обобщённых делегатов 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
Создание экземпляров и вызов вариантных универсальных делегатов.
Вы можете создавать экземпляры вариантных делегатов и вызывать их точно так же, как создавать и вызывать экземпляры инвариантных делегатов. В следующем примере делегат создается при помощи лямбда-выражения.
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
Упрощённое преобразование делегатов обеспечивает большую гибкость при сопоставлении сигнатур методов с типами делегатов. Например, он позволяет опустить спецификации параметров и опустить возвращаемые значения функции при назначении метода делегату. Дополнительные сведения см. в разделе "Упрощенное преобразование делегатов".