Share via


Afwijking in gemachtigden (C#)

.NET Framework 3.5 heeft variantieondersteuning geïntroduceerd voor overeenkomende methodehandtekeningen met gedelegeerdentypen in alle gemachtigden in C#. Dit betekent dat u niet alleen methoden kunt toewijzen aan gedelegeerden die overeenkomende handtekeningen hebben, maar ook methoden die meer afgeleide typen (covariantie) retourneren of parameters accepteren die minder afgeleide typen (contravariantie) hebben dan die zijn opgegeven door het gemachtigdetype. Dit omvat zowel algemene als niet-algemene gemachtigden.

Denk bijvoorbeeld aan de volgende code, die twee klassen en twee gemachtigden heeft: algemeen en niet-algemeen.

public class First { }  
public class Second : First { }  
public delegate First SampleDelegate(Second a);  
public delegate R SampleGenericDelegate<A, R>(A a);  

Wanneer u gemachtigden van de SampleDelegate of SampleGenericDelegate<A, R> typen maakt, kunt u een van de volgende methoden toewijzen aan deze gemachtigden.

// 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(); }  

In het volgende codevoorbeeld ziet u de impliciete conversie tussen de methodehandtekening en het type gemachtigde.

// 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;  

Zie Afwijking gebruiken in Gemachtigden (C#) en Variantie gebruiken voor Func en Actie algemene gedelegeerden (C#) voor meer voorbeelden.

Afwijking in algemene typeparameters

In .NET Framework 4 of hoger kunt u impliciete conversie tussen gemachtigden inschakelen, zodat algemene gemachtigden met verschillende typen die zijn opgegeven door algemene typeparameters aan elkaar kunnen worden toegewezen, als de typen van elkaar worden overgenomen zoals vereist door variantie.

Als u impliciete conversie wilt inschakelen, moet u expliciet algemene parameters in een gemachtigde declareren als covariant of contravariant met behulp van het in of out trefwoord.

In het volgende codevoorbeeld ziet u hoe u een gemachtigde kunt maken met een parameter voor het algemene type covariant.

// 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;
}  

Als u alleen variantieondersteuning gebruikt om methodehandtekeningen te vergelijken met gedelegeerdentypen en de in trefwoorden out niet gebruikt, kunt u soms gedelegeerden instantiëren met identieke lambda-expressies of -methoden, maar u kunt geen gedelegeerde aan een andere gemachtigde toewijzen.

In het volgende codevoorbeeld SampleGenericDelegate<String> kan niet expliciet worden geconverteerd naar SampleGenericDelegate<Object>, hoewel String overgenomen Object. U kunt dit probleem oplossen door de algemene parameter T te markeren met het out trefwoord.

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;  
  
}  

Algemene gemachtigden met parameters voor varianttypen in .NET

.NET Framework 4 heeft variantieondersteuning geïntroduceerd voor algemene typeparameters in verschillende bestaande algemene gemachtigden:

Zie Afwijkingen gebruiken voor Func en Actie algemene gedelegeerden (C#) voor meer informatie en voorbeelden.

Parameters voor varianttype declareren in algemene gemachtigden

Als een algemene gemachtigde covariant of contravariant algemene typeparameters heeft, kan deze worden aangeduid als een variant algemene gemachtigde.

U kunt een algemene parametercovariant in een algemene gemachtigde declareren met behulp van het out trefwoord. Het covarianttype kan alleen worden gebruikt als retourtype voor een methode en niet als een type methodeargumenten. In het volgende codevoorbeeld ziet u hoe u een covariant algemene gemachtigde declareert.

public delegate R DCovariant<out R>();  

U kunt een algemene parameterparameter contravariant in een algemene gemachtigde declareren met behulp van het in trefwoord. Het contravarianttype kan alleen worden gebruikt als een type methodeargumenten en niet als retourtype methode. In het volgende codevoorbeeld ziet u hoe u een algemene gemachtigde voor contravariant declareert.

public delegate void DContravariant<in A>(A a);  

Belangrijk

ref, inen out parameters in C# kunnen niet worden gemarkeerd als variant.

Het is ook mogelijk om zowel variantie als covariantie in dezelfde gemachtigde te ondersteunen, maar voor verschillende typeparameters. Dit wordt weergegeven in het volgende voorbeeld.

public delegate R DVariant<in A, out R>(A a);  

Algemene gedelegeerden instantiëren en aanroepen van varianten

U kunt variantendelegeringen instantiëren en aanroepen, net zoals u invariante gedelegeerden instantieert en aanroept. In het volgende voorbeeld wordt de gemachtigde geïnstantieerd door een lambda-expressie.

DVariant<String, String> dvariant = (String str) => str + " ";  
dvariant("test");  

Variant algemene gemachtigden combineren

Combineer variantdelegen niet. De Combine methode biedt geen ondersteuning voor conversie van variantdelegen en verwacht dat gemachtigden precies hetzelfde type hebben. Dit kan leiden tot een runtime-uitzondering wanneer u gemachtigden combineert met behulp van de Combine methode of met behulp van de + operator, zoals wordt weergegeven in het volgende codevoorbeeld.

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);  

Variantie in algemene typeparameters voor waarde- en verwijzingstypen

Variantie voor algemene typeparameters wordt alleen ondersteund voor referentietypen. Kan bijvoorbeeld DVariant<int> niet impliciet worden geconverteerd naar DVariant<Object> of DVariant<long>, omdat een geheel getal een waardetype is.

In het volgende voorbeeld ziet u dat variantie in algemene typeparameters niet wordt ondersteund voor waardetypen.

// 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;
}  

Zie ook