Megosztás a következőn keresztül:


Eltérés a meghatalmazottakban (C#)

.NET-keretrendszer 3.5-ös verzióban bevezettük a variancia-támogatást a metódusaláírások és a delegálttípusok egyeztetéséhez a C# összes meghatalmazottjában. Ez azt jelenti, hogy a meghatalmazottakhoz nem csak az egyező aláírással rendelkező metódusokat rendelheti hozzá, hanem olyan metódusokat is, amelyek több származtatott típust (kovarianciát) adnak vissza, vagy olyan paramétereket fogadnak el, amelyek kevésbé származtatott típusok (kontravarianciával) rendelkeznek, mint a delegált típus által megadottak. Ide tartoznak az általános és a nem általános meghatalmazottak is.

Vegyük például a következő kódot, amelynek két osztálya és két meghatalmazottja van: általános és nem általános.

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

Az ilyen vagy SampleGenericDelegate<A, R> típusú meghatalmazottak SampleDelegate létrehozásakor az alábbi módszerek bármelyikét hozzárendelheti ezekhez a meghatalmazottakhoz.

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

Az alábbi példakód a metódus aláírása és a delegált típusa közötti implicit konverziót szemlélteti.

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

További példákért tekintse meg a Variance használata a meghatalmazottakban (C#) és a Variance használata func és action generic delegates (C#) esetén című témakört.

Variancia az általános típusparaméterekben

A 4-es vagy újabb .NET-keretrendszer engedélyezheti a meghatalmazottak közötti implicit konverziót, így az általános típusparaméterek által meghatározott különböző típusú általános meghatalmazottak egymáshoz rendelhetők, ha a típusok a variancia által megkövetelt módon öröklődnek egymástól.

Az implicit átalakítás engedélyezéséhez explicit módon kell deklarálnia az általános paramétereket egy meghatalmazottban kovariantként vagy contravariantként a vagy out a in kulcsszó használatával.

Az alábbi példakód bemutatja, hogyan hozhat létre olyan meghatalmazottat, aki kovariáns általános típusparaméterrel rendelkezik.

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

Ha csak a variancia-támogatást használja a metódusaadékok delegálási típusokkal való egyeztetéséhez, és nem használja a kulcsszavakat és out a in delegáltakat, előfordulhat, hogy néha azonos lambda-kifejezésekkel vagy metódusokkal is példányosíthatja a meghatalmazottakat, de nem rendelhet hozzá egy meghatalmazottat egy másikhoz.

A következő kód példában SampleGenericDelegate<String> nem konvertálható SampleGenericDelegate<Object>explicit módon , bár String örökli Object. Ezt a problémát az általános paraméter T kulcsszóval való megjelölésével out háríthatja el.

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

A .NET-ben variant típusú paraméterekkel rendelkező általános meghatalmazottak

.NET-keretrendszer 4 számos meglévő általános meghatalmazott esetében bevezette az általános típusparaméterek varianciájának támogatását:

További információkért és példákért lásd: Variance for Func and Action Generic Delegates (C#) (Variance for Func and Action Generic Delegates, C#).

Variant type parameters deklarálása általános meghatalmazottakban

Ha egy általános delegált rendelkezik kovariant vagy contravariant általános típusparaméterekkel, akkor ezt nevezhetjük variáns általános delegáltnak.

Egy általános típusú paraméter kovariantot deklarálhat egy általános meghatalmazottban a out kulcsszó használatával. A kovariant típus csak metódus-visszatérési típusként használható, metódusargumentum-típusként nem. Az alábbi példakód bemutatja, hogyan deklarálhat kovariantikus általános meghatalmazottat.

public delegate R DCovariant<out R>();  

A kulcsszó használatával deklarálhat egy általános típusparamétert contravariant-ként egy in általános delegáltban. A contravariant típus csak metódusargumentumok típusaként használható, metódusvisszautasítási típusként nem. Az alábbi példakód bemutatja, hogyan deklarálhat egy contravariant általános meghatalmazottat.

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

Fontos

ref, inés out a C# paraméterei nem jelölhetők meg variánsként.

A varianciát és a kovarianciát is támogathatja ugyanabban a delegáltban, de különböző típusparaméterek esetén. Ez az alábbi példában látható.

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

Variant Generic-meghatalmazottak példányosítása és meghívása

A változatdelegáltak példányosítása és meghívása ugyanúgy lehetséges, mint az invariáns meghatalmazottak példányosítása és meghívása. Az alábbi példában a meghatalmazottat egy lambda kifejezés hozza létre.

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

Variant Generic Delegates kombinálása

Ne egyesítse a változatdelegáltakat. A Combine metódus nem támogatja a változatdelegáltak konvertálását, és elvárja, hogy a meghatalmazottak pontosan azonos típusúak legyenek. Ez futásidejű kivételhez vezethet, ha a meghatalmazottakat a metódus vagy az Combine+ operátor használatával kombinálja, ahogyan az az alábbi kód példában látható.

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

Érték- és referenciatípusok általános típusparamétereinek varianciája

Az általános típusparaméterek varianciája csak referenciatípusok esetén támogatott. Nem lehet például implicit módon átalakítani vagy DVariant<long>átalakítaniDVariant<Object>, DVariant<int> mert az egész szám értéktípus.

Az alábbi példa azt mutatja be, hogy az általános típusparaméterek varianciája nem támogatott az értéktípusok esetében.

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

Lásd még