Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
O .NET Framework 3.5 introduziu o suporte a variação para assinaturas de método correspondentes com tipos de delegados em todos os delegados do C#. Isso significa que você pode atribuir aos delegados não apenas 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 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 { }
public class Second : First { }
public delegate First SampleDelegate(Second a);
public delegate R SampleGenericDelegate<A, R>(A a);
Ao criar delegados dos tipos SampleDelegate
ou SampleGenericDelegate<A, R>
, você pode atribuir qualquer um dos métodos a seguir a esses delegados.
// Matching signature.
public static First ASecondRFirst(Second second)
{ 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 delegado.
// 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 informações, consulte Usando variação em delegados (C#) e Usando variação para os delegados genéricos Func e Action (C#).
Variação em parâmetros de tipo genérico
No .NET Framework 4 ou 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érico possam ser atribuídos uns aos outros, se os tipos forem herdados uns dos outros, conforme exigido pela variação.
Para habilitar a conversão implícita, você deve declarar explicitamente os parâmetros genéricos em um delegado como covariantes ou contravariantes usando a palavra-chave in
ou a palavra-chave out
.
O exemplo de código a seguir mostra como você pode criar um delegado que tenha um parâmetro de tipo genérico covariante.
// 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 apenas o suporte à variação para corresponder assinaturas de método com tipos delegados e não usar as palavras-chave in
e out
, você pode achar que às vezes pode instanciar delegados com métodos ou expressões lambda idênticas, mas não pode atribuir um delegado a outro.
No exemplo de código a seguir, SampleGenericDelegate<String>
não é possível converter SampleGenericDelegate<Object>
explicitamente, embora String
herda Object
. Você pode corrigir esse problema marcando o parâmetro T
genérico com a out
palavra-chave.
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;
}
Delegados genéricos que têm parâmetros de tipo variante no .NET
O .NET Framework 4 introduziu suporte de variação para parâmetros de tipo genérico em vários delegados genéricos existentes:
Action
delega do namespace System, por exemplo, Action<T> e Action<T1,T2>Func
delega do namespace System, por exemplo, Func<TResult> e Func<T,TResult>O delegado Predicate<T>
O delegado Comparison<T>
O delegado Converter<TInput,TOutput>
Para obter mais informações e exemplos, consulte Usando variação para delegados genéricos Func e Action (C#).
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 chamado de delegado genérico variante.
Você pode declarar um 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 R DCovariant<out 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 void DContravariant<in A>(A a);
Importante
ref
, in
e out
os parâmetros em C# não podem ser marcados como variante.
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 R DVariant<in A, out R>(A a);
Instanciando e invocando delegados genéricos variantes
Você pode instanciar e invocar delegados variantes assim como instanciar e invocar delegados invariáveis. No exemplo a seguir, o delegado é instanciado por uma expressão lambda.
DVariant<String, String> dvariant = (String str) => str + " ";
dvariant("test");
Combinando delegados genéricos variantes
Não combine 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 levar a uma exceção em tempo de execução quando você combina delegados, seja pelo método Combine ou pelo operador +
, conforme mostrado no exemplo de código a seguir.
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 em parâmetros de tipo genérico para tipos de valor e referência
Há suporte para variação para parâmetros de tipo genérico somente para tipos de referência. Por exemplo, DVariant<int>
não pode ser convertido implicitamente em DVariant<Object>
ou DVariant<long>
, porque inteiro é um tipo de valor.
O exemplo a seguir demonstra que não há suporte para variação em parâmetros de tipo genérico para tipos de valor.
// 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;
}