Udostępnij za pomocą


Wariancja w delegatach (Visual Basic)

.NET Framework 3.5 wprowadził obsługę wariancji dla dopasowywania sygnatur metod do typów delegatów dla wszystkich delegatów 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 delegatów ogólnych, jak i delegatów nieogólnych.

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 Używanie wariancji w delegatach (Visual Basic) and Używanie wariancji w delegatach ogólnych Func i Action (Visual Basic).

Wariancja w parametrach typu ogólnego

W .NET Framework 4 i nowszych można włączyć funkcję automatycznej konwersji między delegatami, co pozwala na przypisywanie do siebie delegatów ogólnych o różnych typach określonych przez parametry typów ogólnych, jeśli te typy są dziedziczone jeden od drugiego 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 słów kluczowych in i out, może się okazać, że czasami można tworzyć delegaty z identycznymi wyrażeniami lambda lub metodami, ale nie można przypisać jednego delegata do drugiego.

W poniższym przykładzie kodu SampleGenericDelegate(Of String) nie można jawnie przekonwertować na SampleGenericDelegate(Of Object), chociaż String dziedziczy 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).

Deklaracja parametru typu wariancyjnego w delegatach generycznych

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

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 delegacie, ale dla różnych parametrów typu. Jest to pokazane w poniższym 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 generycznych wariantowych

Możesz utworzyć wystąpienie i wywołać delegata wariantu tak samo, jak utworzyć wystąpienie i wywołać delegata niezmiennego. W poniższym przykładzie delegat jest tworzony przez wyrażenie lambda.

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

Łączenie wariantowych delegatów generycznych

Nie należy łączyć delegatów wariantów. Metoda Combine nie obsługuje konwersji delegata z wariantem i oczekuje, że delegaty będą dokładnie tego samego typu. Może to prowadzić do wyjątku czasu wykonywania podczas łączenia delegatów przy użyciu metody Combine (w języku C# i Visual Basic) lub 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 referencyjnych

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 także