Variação em delegados (Visual Basic)
O .NET Framework 3.5 introduziu o suporte à variância para correspondência de assinaturas de método com tipos delegados em todos os delegados em C# e Visual Basic. Isso significa que você pode atribuir aos delegados não apenas métodos que tenham assinaturas correspondentes, mas também métodos que retornem mais tipos derivados (covariância) ou que aceitem parâmetros que tenham menos tipos derivados (contravariância) do que os especificados pelo tipo 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
Ao criar delegados dos SampleDelegate
tipos 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 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 Usando variância em delegados (Visual Basic) e Usando variância para delegados genéricos de ação e func (Visual Basic).
Variância nos parâmetros de tipo genéricos
No .NET Framework 4 e posterior, você pode habilitar a conversão implícita entre delegados, para que delegados genéricos que tenham tipos diferentes especificados por parâmetros de tipo genéricos possam ser atribuídos uns aos outros, se os tipos forem herdados uns dos outros, conforme exigido pela variância.
Para habilitar a conversão implícita, você deve declarar explicitamente parâmetros genéricos em um delegado como covariante ou contravariante usando a in
palavra-chave 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 apenas o suporte de variância para corresponder assinaturas de método com tipos delegados e não usar as in
palavras-chave e out
, poderá achar que, às vezes, pode instanciar delegados com expressões ou métodos lambda idênticos, mas não pode atribuir um delegado a outro.
No exemplo de código a seguir, SampleGenericDelegate(Of String)
não pode ser explicitamente convertido em SampleGenericDelegate(Of Object)
, embora String
herde Object
. Você pode corrigir esse problema marcando o parâmetro T
genérico com a out
palavra-chave.
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 de variante no .NET Framework
O .NET Framework 4 introduziu o suporte à variância para parâmetros de tipo genéricos em vários delegados genéricos existentes:
Action
delegados do System namespace, por exemplo, Action<T> e Action<T1,T2>Func
delegados do System namespace, por exemplo, Func<TResult> e Func<T,TResult>O Predicate<T> delegado
O Comparison<T> delegado
O Converter<TInput,TOutput> delegado
Para obter mais informações e exemplos, consulte Usando variância para Func e delegados genéricos de ação (Visual Basic).
Declarando parâmetros de tipo de variante em delegados genéricos
Se um delegado genérico tiver parâmetros de tipo genérico covariante ou contravariante, ele pode ser referido como um delegado genérico variante.
Você pode declarar uma covariante de parâmetro de tipo genérico em um delegado genérico usando a out
palavra-chave. 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 uma contravariante de parâmetro de tipo genérico em um delegado genérico usando a in
palavra-chave. 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
ByRef
parâmetros no Visual Basic não podem ser marcados como variante.
Também é possível suportar variância e covariância no mesmo delegado, mas para parâmetros de tipo diferentes. 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 de variantes
Você pode instanciar e invocar delegados variantes da mesma forma que instancia e invoca delegados invariantes. No exemplo a seguir, o delegado é instanciado por uma expressão lambda.
Dim dvariant As DVariant(Of String, String) = Function(str) str + " "
dvariant("test")
Combinando Delegados Genéricos de Variantes
Você não deve combinar delegados variantes. O Combine método não suporta conversão de delegados de variante e espera que os delegados sejam exatamente do mesmo tipo. Isso pode levar a uma exceção de tempo de execução quando você combina delegados usando o Combine método (em C# e Visual Basic) ou usando o +
operador (em C#), conforme mostrado no 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)
Variância em parâmetros de tipo genéricos para tipos de valor e referência
A variância para parâmetros de tipo genéricos é suportada 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)
, porque inteiro é um tipo de valor.
O exemplo a seguir demonstra que a variância em parâmetros de tipo genéricos não é suportada 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 relaxado no Visual Basic
A conversão relaxada de delegados permite mais flexibilidade na correspondência de assinaturas de método com tipos de delegados. Por exemplo, ele permite omitir especificações de parâmetros e omitir valores de retorno de função quando você atribui um método a um delegado. Para obter mais informações, consulte Conversão de delegado relaxada.