Partilhar via


Variação de delegados (C# e Visual Basic)

.NET Framework 3.5 e Visual Studio de 2008 introduziram o suporte de variação para assinaturas de método de correspondência com os tipos de delegados em todos os delegados no C# e Visual Basic. Isso significa que você pode atribuir a delega não apenas os métodos que tenham assinaturas correspondentes, mas também métodos que retornam mais derivado tipos (covariância) ou que aceite parâmetros que têm menos tipos derivados (/ contravariância) do que o especificado pelo tipo delegate. Isso inclui representantes genéricos e não genéricas.

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

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
public class First { }
public class Second : First { }
public delegate First SampleDelegate(Second a);
public delegate R SampleGenericDelegate<A, R>(A a);

Quando você cria os delegados do SampleDelegate ou SampleGenericDelegate<A, R> (SampleDelegate(Of A, R) em Visual Basic) tipos, você pode atribuir qualquer um dos seguintes métodos para 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
// Matching signature.
public static First ASecondRFirst(Second first)
{ return new First(); }

// The return type is more derived.
public static Second ASecondRSecond(Second second)
{ return new Second(); }

// The argument type is less derived.
public static First AFirstRFirst(First first)
{ return new First(); }

// The return type is more derived 
// and the argument type is less derived.
public static Second AFirstRSecond(First first)
{ return new Second(); }

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
// Assigning a method with a matching signature 
// to a non-generic delegate. No conversion is necessary.
SampleDelegate dNonGeneric = 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.
SampleDelegate dNonGenericConversion = AFirstRSecond;

// Assigning a method with a matching signature to a generic delegate.
// No conversion is necessary.
SampleGenericDelegate<Second, First> dGeneric = ASecondRFirst;
// Assigning a method with a more derived return type 
// and less derived argument type to a generic delegate.
// The implicit conversion is used.
SampleGenericDelegate<Second, First> dGenericConversion = AFirstRSecond;

Para obter mais exemplos, consulte Usando a variação delegados (C# e Visual Basic) e Usando a variação para representantes genéricos Func e ação (C# e Visual Basic).

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

No.NET Framework 4, você pode ativar a conversão implícita entre delegados, para que possam ser atribuídos a representantes genéricos que têm tipos diferentes, especificados pelo parâmetro de tipo genérico uns aos outros, se os tipos são herdados uns dos outros, conforme exigido pela variação.

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

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

' 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
// Type T is declared covariant by using the out keyword.
public delegate T SampleGenericDelegate <out T>();

public static void Test()
{
    SampleGenericDelegate <String> dString = () => " ";

    // You can assign delegates to each other,
    // because the type T is declared covariant.
    SampleGenericDelegate <Object> dObject = dString;           
}

Se você usar o suporte de variação somente para coincidir com as assinaturas de método com delegar tipos e não use o in e out palavras-chave, você pode descobrir que às vezes, você pode instanciar delegados com expressões lambda idênticos ou métodos, mas não é possível atribuir um delegado para outro.

No exemplo de código a seguir, SampleGenericDelegate<String> não podem ser convertidas explicitamente para SampleGenericDelegate<Object> (SampleGenericDelegate(Of String) para SampleGenericDelegate(Of Object) em Visual Basic), embora String herda Object. Você pode corrigir esse problema, marcando o parâmetro genérico T com o 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
public delegate T SampleGenericDelegate<T>();

public static void Test()
{
    SampleGenericDelegate<String> dString = () => " ";

    // 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.
    SampleGenericDelegate<Object> dObject = () => " ";

    // The following statement generates a compiler error
    // because the generic type T is not marked as covariant.
    // SampleGenericDelegate <Object> dObject = dString;



}

Representantes genéricos que têm a variante digite parâmetros na.NET Framework

.NET Framework 4 introduz o suporte de variação para os parâmetros de tipo genérico em vários representantes de genéricos existentes:

Para mais informações e exemplos, consulte Usando a variação para representantes genéricos Func e ação (C# e Visual Basic).

Declarar parâmetros de tipo Variant em representantes genéricos

Se um delegado genérico tem covariant ou contravariant parâmetros de tipo genérico, ele pode ser chamado como uma variante genérico delegue.

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

Public Delegate Function DCovariant(Of Out R)() As R
public delegate R DCovariant<out R>();

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

Public Delegate Sub DContravariant(Of In A)(ByVal a As A)
public delegate void DContravariant<in A>(A a);
Observação importanteImportante

ByRefparâmetros de Visual Basic e ref e out parâmetros no C# e não pode ser marcado como variant.

Também é possível oferecer suporte a variação e a covariância no mesmo delegado, mas para os parâmetros de tipo diferente. Isto é mostrado no exemplo a seguir.

Public Delegate Function DVariant(Of In A, Out R)(ByVal a As A) As R
public delegate R DVariant<in A, out R>(A a);

Instanciar e invocando a representantes de variante genéricos

Você pode instanciar e invocar variant delegados exatamente como você instancia e invocar invariável delegados. No exemplo a seguir, o delegado é instanciado por uma expressão lambda.

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

Combinando a representantes de variante genéricos

Não, você deve combinar delegates variant. O Combine método não oferece suporte à conversão de delegado variant e espera que os representantes de exatamente o 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 seguinte exemplo de código.

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)
Action<object> actObj = x => Console.WriteLine("object: {0}", x);
Action<string> actStr = x => Console.WriteLine("string: {0}", x);
// All of the following statements throw exceptions at run time.
// Action<string> actCombine = actStr + actObj;
// actStr += actObj;
// Delegate.Combine(actStr, actObj);

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

É compatível com variação de parâmetros de tipo genérico somente tipos de referência. Por exemplo, DVariant<int> (DVariant(Of Int) em Visual Basic) não pode ser convertido implicitamente DVariant<Object> ou DVaraint<long> (DVariant(Of Object) ou DVaraint(Of Long) em Visual Basic), porque o número inteiro é um tipo de valor.

O exemplo a seguir demonstra essa variação de tipo genérico parâmetros não é suportado 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 dVaraintInt 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 dVaraintObject As DInvariant(Of Object) = dInt
    ' Dim dVaraintLong As DInvariant(Of Long) = dInt
End Sub
// The type T is covariant.
public delegate T DVariant<out T>();

// The type T is invariant.
public delegate T DInvariant<T>();

public static void Test()
{
    int i = 0;
    DInvariant<int> dInt = () => i;
    DVariant<int> dVariantInt = () => 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.
    // DInvariant<Object> dObject = dInt;
    // DInvariant<long> dLong = dInt;
    // DVariant<Object> dVariantObject = dVariantInt;
    // DVariant<long> dVariantLong = dVariantInt;            
}

Conversão de delegado reduzida em Visual Basic

Conversão de delegado reduzida, introduzido no Visual Basic de 2008, permite mais flexibilidade na correspondência de assinaturas de método com tipos delegate. Por exemplo, ele permite omitir as especificações do parâmetro e omitir os valores de retorno da função quando você atribui um método a um delegado. Para obter mais informações, consulte Conversão de delegado reduzida (Visual Basic).

Consulte também

Tarefas

Como: Combinar representantes (Representantes Multicast) (Guia de programação C#)

Referência

Usando a variação para representantes genéricos Func e ação (C# e Visual Basic)

Conceitos

Classes genéricas no .NET Framework