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


Variant generikus interfészek létrehozása (C#)

Az interfészek általános típusparamétereit kovariantként vagy contravariantként deklarálhatja. A kovariancia lehetővé teszi, hogy a felületi metódusok az általános típusparaméterek által definiáltnál több származtatott visszatérési típussal rendelkezzenek. A contravariance lehetővé teszi, hogy az illesztő metódusok az általános paraméterek által megadottnál kevésbé származtatott argumentumtípusokkal rendelkezzenek. A kovariant vagy contravariant általános típusparaméterekkel rendelkező általános felületet variant-nak nevezzük.

Megjegyzés:

A .NET-keretrendszer 4 bemutatta a variancia támogatását számos meglévő általános interfészhez. A .NET-ben található változatillesztők listájáért lásd: Variancia az általános felületekben (C#)..

Változó generikus interfészek deklarálása

Általános típusú variáns felületeket a in és out kulcsszavak használatával deklarálhatja.

Fontos

ref, inés out a C# paraméterei nem lehetnek variánsok. Az értéktípusok a varianciát sem támogatják.

A kulcsszó használatával out kovariant típusú általános paramétert deklarálhat. A kovariant típusnak meg kell felelnie a következő feltételeknek:

  • A típus csak az interfészmetódusok visszatérési típusaként használatos, és nem használható metódusargumentumok típusaként. Ezt a következő példában szemlélteti, amelyben a típus R kovariantnak van deklarálva.

    interface ICovariant<out R>
    {
        R GetSomething();
        // The following statement generates a compiler error.
        // void SetSomething(R sampleArg);
    
    }
    

    A szabály alól egyetlen kivétel van. Ha van egy kontravariáns általános delegáltja, mint metódusparaméter, a típust általános típusparaméterként használhatja a delegáltnál. Ezt az alábbi példában szereplő típus R szemlélteti. További információ: Variance in Delegates (C#) és Using Variance for Func and Action Generic Delegates (C#).

    interface ICovariant<out R>
    {
        void DoSomething(Action<R> callback);
    }
    
  • A típus nem általános korlátozásként használatos az interfészmetelyekhez. Ezt az alábbi kód szemlélteti.

    interface ICovariant<out R>
    {
        // The following statement generates a compiler error
        // because you can use only contravariant or invariant types
        // in generic constraints.
        // void DoSomething<T>() where T : R;
    }
    

A in kulcsszó használatával deklarálhat egy általános típusparamétert kontravariánsként. A contravariant típus csak metódusargumentumok típusaként használható, az interfészmetódusok visszatérési típusaként nem. A contravariant típus általános korlátozásokhoz is használható. Az alábbi kód bemutatja, hogyan deklarálhat egy contravariant felületet, és hogyan használhat általános kényszert az egyik metódusához.

interface IContravariant<in A>
{
    void SetSomething(A sampleArg);
    void DoSomething<T>() where T : A;
    // The following statement generates a compiler error.
    // A GetSomething();
}

A kovariancia és a contravariance ugyanazon a felületen is támogatott, de különböző típusparaméterek esetén is, ahogy az alábbi kódpéldában látható.

interface IVariant<out R, in A>
{
    R GetSomething();
    void SetSomething(A sampleArg);
    R GetSetSomethings(A sampleArg);
}

Variant Generic Interfaces implementálása

A variáns általános felületeket az osztályokban az invariáns felületekhez használt szintaxissal implementálhatja. Az alábbi példakód bemutatja, hogyan implementálhat kovariantikus felületet egy általános osztályban.

interface ICovariant<out R>
{
    R GetSomething();
}
class SampleImplementation<R> : ICovariant<R>
{
    public R GetSomething()
    {
        // Some code.
        return default(R);
    }
}

A variáns interfészeket megvalósító osztályok invariánsak. Vegyük például az alábbi kódot.

// The interface is covariant.
ICovariant<Button> ibutton = new SampleImplementation<Button>();
ICovariant<Object> iobj = ibutton;

// The class is invariant.
SampleImplementation<Button> button = new SampleImplementation<Button>();
// The following statement generates a compiler error
// because classes are invariant.
// SampleImplementation<Object> obj = button;

Variant Generic Interfaces kiterjesztése

Amikor egy variáns generikus interfészt kiterjeszt, a in és out kulcsszavakat kell használnia annak explicit meghatározásához, hogy a származtatott interfész támogatja-e a varianciát. A fordító nem vonja le a varianciát a kiterjesztett interfészről. Vegyük például az alábbi felületeket.

interface ICovariant<out T> { }
interface IInvariant<T> : ICovariant<T> { }
interface IExtCovariant<out T> : ICovariant<T> { }

Az interfészben az IInvariant<T> általános típusparaméter T invariáns, míg IExtCovariant<out T> a típusparaméter kovariáns, bár mindkét interfész ugyanazt a felületet bővíti. Ugyanez a szabály vonatkozik a contravariant általános típusparaméterekre.

Létrehozhat egy interfészt, amely kiterjeszti mind az általános típusparaméter T kovariant felületét, mind a contravariant felületet, ha a bővítő felületen az általános típusparaméter T invariáns. Ezt az alábbi példakód szemlélteti.

interface ICovariant<out T> { }
interface IContravariant<in T> { }
interface IInvariant<T> : ICovariant<T>, IContravariant<T> { }

Ha azonban egy általános típusú paraméter T kovariantnak van deklarálva egy felületen, akkor nem deklarálhatja azt a kiterjesztett felületen, vagy fordítva. Ezt az alábbi példakód szemlélteti.

interface ICovariant<out T> { }
// The following statement generates a compiler error.
// interface ICoContraVariant<in T> : ICovariant<T> { }

A kétértelműség elkerülése

A variant generic interfészek implementálása esetén a variancia néha kétértelműséghez vezethet. Ezt a kétértelműséget el kell kerülni.

Ha például explicit módon implementálja ugyanazt a variáns általános felületet különböző általános típusparaméterekkel egy osztályban, az kétértelműséget okozhat. A fordító ebben az esetben nem okoz hibát, de nincs megadva, hogy melyik felület implementációját választja ki futásidőben. Ez a kétértelműség apró hibákhoz vezethet a kódban. Tekintse meg a következő kód példáját.

// Simple class hierarchy.
class Animal { }
class Cat : Animal { }
class Dog : Animal { }

// This class introduces ambiguity
// because IEnumerable<out T> is covariant.
class Pets : IEnumerable<Cat>, IEnumerable<Dog>
{
    IEnumerator<Cat> IEnumerable<Cat>.GetEnumerator()
    {
        Console.WriteLine("Cat");
        // Some code.
        return null;
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        // Some code.
        return null;
    }

    IEnumerator<Dog> IEnumerable<Dog>.GetEnumerator()
    {
        Console.WriteLine("Dog");
        // Some code.
        return null;
    }
}
class Program
{
    public static void Test()
    {
        IEnumerable<Animal> pets = new Pets();
        pets.GetEnumerator();
    }
}

Ebben a példában nincs meghatározva, hogyan választ a pets.GetEnumerator metódus Cat és Dog között. Ez problémákat okozhat a kódban.

Lásd még