Wariancja w delegatach (Visual Basic)

Program .NET Framework 3.5 wprowadził obsługę wariancji dla pasujących podpisów metod z typami delegatów we wszystkich delegatach w języku C# i Visual Basic. Oznacza to, że można przypisać do delegatów nie tylko metody, które mają pasujące podpisy, ale także metody zwracające więcej typów pochodnych (kowariancja) lub które akceptują parametry, które mają mniej pochodne typy (kontrawariancję) niż określone przez typ delegata. Obejmuje to zarówno delegaty ogólne, jak i nieogólne.

Rozważmy na przykład następujący kod, który ma dwie klasy i dwa delegaty: ogólny i niegeneryczny.

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

Podczas tworzenia delegatów typów SampleDelegate lub SampleDelegate(Of A, R) można przypisać dowolną z następujących metod do tych delegatów.

' 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

Poniższy przykład kodu ilustruje niejawną konwersję między sygnaturą metody a typem delegata.

' 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

Aby uzyskać więcej przykładów, zobacz Using Variance in Delegates (Visual Basic) (Używanie wariancji w delegatach (Visual Basic) i Using Variance for Func and Action Generic Delegates (Visual Basic) (Używanie wariancji w delegatach (Visual Basic) i Using Variance for Func and Action Generic Delegates (Visual Basic).

Wariancja w parametrach typu ogólnego

W programie .NET Framework 4 lub nowszym można włączyć niejawną konwersję między delegatami, aby delegaty ogólne, które mają różne typy określone przez parametry typu ogólnego, można przypisać do siebie, jeśli typy są dziedziczone ze sobą zgodnie z wymaganiami wariancji.

Aby włączyć niejawną konwersję, należy jawnie zadeklarować parametry ogólne w delegatu jako kowariantne lub kontrawariantne przy użyciu słowa kluczowego in lub out .

Poniższy przykład kodu pokazuje, jak utworzyć delegata, który ma kowariantny parametr typu ogólnego.

' 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

Jeśli używasz tylko obsługi wariancji do dopasowywania podpisów metod z typami delegatów i nie używasz in słów kluczowych i out , może się okazać, że czasami można utworzyć wystąpienia delegatów z identycznymi wyrażeniami lub metodami lambda, ale nie można przypisać jednego delegata do innego.

W poniższym przykładzie SampleGenericDelegate(Of String) kodu nie można jawnie przekonwertować na SampleGenericDelegate(Of Object)element , chociaż String dziedziczy element Object. Ten problem można rozwiązać, oznaczając ogólny parametr T za pomocą słowa kluczowego 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

Delegaty ogólne, które mają parametry typu wariantu w programie .NET Framework

Program .NET Framework 4 wprowadził obsługę wariancji dla parametrów typu ogólnego w kilku istniejących delegatach ogólnych:

Aby uzyskać więcej informacji i przykładów, zobacz Using Variance for Func and Action Generic Delegates (Visual Basic)(Używanie wariancji dla func i action Generic Delegates (Visual Basic).

Deklarowanie parametrów typu wariantu w delegatach ogólnych

Jeśli delegat ogólny ma kowariantne lub kontrawariantne parametry typu ogólnego, może być określany jako delegat ogólny wariantu.

Za pomocą słowa kluczowego out można zadeklarować kowariantny parametr typu ogólnego w delegacie ogólnym. Typ kowariantny może być używany tylko jako typ zwracany przez metodę, a nie jako typ argumentów metody. Poniższy przykład kodu pokazuje, jak zadeklarować kowariantnego delegata ogólnego.

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

Można zadeklarować kontrawariant parametru typu ogólnego w delegacie ogólnym przy użyciu słowa kluczowego in . Typ kontrawariantny może być używany tylko jako typ argumentów metody, a nie jako typ zwracany przez metodę. Poniższy przykład kodu przedstawia sposób deklarowania kontrawariantnego delegata ogólnego.

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

Ważne

ByRef parametrów w Visual Basic nie można oznaczyć jako wariantu.

Można również obsługiwać zarówno wariancję, jak i kowariancję w tym samym delegatu, ale dla różnych parametrów typu. Jest to pokazane w następującym przykładzie.

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

Tworzenie wystąpień i wywoływanie delegatów ogólnych wariantu

Możesz utworzyć wystąpienie i wywołać delegatów wariantu tak samo, jak wystąpienia i wywołać niezmienne delegaty. W poniższym przykładzie delegat jest tworzone przez wyrażenie lambda.

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

Łączenie delegatów ogólnych wariantu

Nie należy łączyć delegatów wariantów. Metoda Combine nie obsługuje konwersji delegata wariantu i oczekuje delegatów dokładnie tego samego typu. Może to prowadzić do wyjątku czasu wykonywania podczas łączenia delegatów przy użyciu metody (w języku C# i Visual Basic) lub przy użyciu Combine+ operatora (w języku C#), jak pokazano w poniższym przykładzie kodu.

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)

Wariancja w parametrach typu ogólnego dla typów wartości i odwołań

Wariancja dla parametrów typu ogólnego jest obsługiwana tylko dla typów referencyjnych. Na przykład DVariant(Of Int)nie można niejawnie konwertować na DVariant(Of Object) lub DVariant(Of Long), ponieważ liczba całkowita jest typem wartości.

W poniższym przykładzie pokazano, że wariancja w parametrach typu ogólnego nie jest obsługiwana dla typów wartości.

' 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

Zrelaksowana konwersja delegatów w Visual Basic

Złagodzona konwersja delegatów zapewnia większą elastyczność w dopasowywaniu podpisów metod z typami delegatów. Na przykład pozwala pominąć specyfikacje parametrów i pominąć wartości zwracane przez funkcję podczas przypisywania metody do delegata. Aby uzyskać więcej informacji, zobacz Zrelaksowana konwersja delegatów.

Zobacz też