Megjegyzés
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhat bejelentkezni vagy módosítani a címtárat.
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhatja módosítani a címtárat.
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
Rkovariantnak 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
Rszemlé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.