Variação em delegados (Visual Basic)

O .NET Framework 3.5 introduziu o suporte a variação para assinaturas de método correspondentes com tipos delegados em todos os delegados no C# e no Visual Basic. Isso significa que você pode atribuir a delegados não apenas os métodos que têm assinaturas correspondentes, mas também métodos que retornam tipos mais derivados (covariância) ou que aceitam parâmetros que têm tipos menos derivados (contravariância) do que o especificado pelo tipo de delegado. Isso inclui delegados genéricos e não genéricos.

Por exemplo, considere o código a seguir, que tem duas classes e dois delegados: genérico e não genérico.

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

Quando cria delegados dos tipos SampleDelegate ou SampleDelegate(Of A, R), você pode atribuir qualquer um dos seguintes métodos a esses delegados.

' 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

O exemplo de código a seguir ilustra a conversão implícita entre a assinatura do método e o tipo de delegado.

' 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

Para obter mais exemplos, consulte Usar Variação em Delegados (Visual Basic) e Usar Variação para Delegados Genéricos Func e Action (Visual Basic).

Variação em parâmetros de tipo genérico

No .NET Framework 4 e versões posteriores, você pode habilitar a conversão implícita entre delegados, de modo que delegados genéricos que têm tipos diferentes especificados por parâmetros de tipo genérico podem ser atribuídos uns aos outros, se os tipos forem herdados uns dos outros como é obrigatório de acordo com a variação.

Para habilitar a conversão implícita, você precisa declarar explicitamente os parâmetros genéricos em um delegado como covariante ou contravariante usando a palavra-chave in ou out.

O exemplo de código a seguir mostra como você pode criar um delegado que tem um parâmetro de tipo genérico covariante.

' 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

Se você usar somente o suporte à para fazer a correspondência de assinaturas de método com tipos de delegados e não usar as palavras-chave in e out, você poderá perceber que, às vezes, é possível instanciar delegados com métodos ou expressões lambda idênticas, mas não é possível atribuir um delegado a outro.

No exemplo de código a seguir, SampleGenericDelegate(Of String) não pode ser convertido explicitamente em SampleGenericDelegate(Of Object), embora String herde Object. Você pode corrigir esse problema marcando o parâmetro genérico T com a palavra-chave 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

Delegados genéricos que têm parâmetros de tipo variante no .NET Framework

O .NET Framework 4 introduziu o suporte à variação para parâmetros de tipo genérico em diversos delegados genéricos existentes:

Para obter mais informações e exemplos, consulte Usar Variação para Delegados Genéricos Func e Action (Visual Basic).

Declarando parâmetros de tipo variante em delegados genéricos

Se um delegado genérico tiver parâmetros de tipo genérico covariantes ou contravariantes, ele poderá ser considerado um delegado genérico variante.

Você pode declarar um parâmetro de tipo genérico covariante em um delegado genérico usando a palavra-chave out. O tipo covariante pode ser usado apenas como um tipo de retorno de método e não como um tipo de argumentos de método. O exemplo de código a seguir mostra como declarar um delegado genérico covariante.

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

Você pode declarar um parâmetro de tipo genérico contravariante em um delegado genérico usando a palavra-chave in. O tipo contravariante pode ser usado apenas como um tipo de argumentos de método e não como um tipo de retorno de método. O exemplo de código a seguir mostra como declarar um delegado genérico contravariante.

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

Importante

Os parâmetros ByRef no Visual Basic não podem ser marcados como variantes.

Também é possível dar suporte à variância e à covariância no mesmo delegado, mas para parâmetros de tipo diferente. Isso é mostrado no exemplo a seguir.

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

Instanciando e invocando delegados genéricos variantes

Você pode instanciar e invocar delegados variantes da mesma forma como instancia e invoca delegados invariantes. No exemplo a seguir, um delegado é instanciado por uma expressão lambda.

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

Combinando delegados genéricos variantes

Você não deve combinar delegados variantes. O método Combine não dá suporte à conversão de delegados variantes e espera que os delegados sejam exatamente do mesmo tipo. Isso pode gerar uma exceção de tempo de execução quando você combina delegados usando o método Combine (no C# e no Visual Basic) ou o operador + (in C#), como mostra o exemplo de código a seguir.

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)

Variação em parâmetros de tipo genérico para tipos de referência e valor

A variação para parâmetros de tipo genérico tem suporte apenas para tipos de referência. Por exemplo, DVariant(Of Int) não pode ser convertido implicitamente em DVariant(Of Object) ou DVariant(Of Long), pois inteiro é um tipo de valor.

O exemplo a seguir demonstra que a variação em parâmetros de tipo genérico não tem suporte para tipos de valor.

' 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

Conversão de Delegado Reduzida no Visual Basic

A conversão de delegado reduzida permite mais flexibilidade na correspondência de assinaturas de método com tipos delegados. Por exemplo, permite omitir especificações de parâmetro e valores retornados de função ao atribuir um método a um delegado. Para obter mais informações, consulte Conversão de Delegado Reduzida.

Confira também