Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Sie können generische Typparameter in Schnittstellen als kovariant oder kontravariant deklarieren. Mit der Kovarianz können Schnittstellenmethoden über mehr abgeleitete Rückgabetypen verfügen als die von den generischen Typparametern definierten. Contravariance ermöglicht es Schnittstellenmethoden, Argumenttypen zu haben, die weniger abgeleitet sind als die von den generischen Parametern angegebenen. Eine generische Schnittstelle mit kovarianten oder kontravarianten generischen Typparametern wird als Variante bezeichnet.
Hinweis
.NET Framework 4 hat die Varianzunterstützung für mehrere vorhandene generische Schnittstellen eingeführt. Eine Liste der Variantenschnittstellen in .NET finden Sie unter Varianz in generischen Schnittstellen (C#).
Deklarieren von generischen Variant-Schnittstellen
Sie können varianten generische Schnittstellen mithilfe der Schlüsselwörter in
und out
für generische Typparameter deklarieren.
Von Bedeutung
ref
, in
und out
Parameter in C# können keine Variante sein. Werttypen unterstützen auch keine Varianz.
Sie können eine generische Typparameterkovariant mithilfe des out
Schlüsselworts deklarieren. Der kovariante Typ muss die folgenden Bedingungen erfüllen:
Der Typ wird nur als Rückgabetyp von Schnittstellenmethoden verwendet und nicht als Typ von Methodenargumenten verwendet. Dies wird im folgenden Beispiel veranschaulicht, in dem der Typ
R
kovariant deklariert wird.interface ICovariant<out R> { R GetSomething(); // The following statement generates a compiler error. // void SetSomething(R sampleArg); }
Es gibt eine Ausnahme für diese Regel. Wenn Sie über einen kontravarianten generischen Delegaten als Methodenparameter verfügen, können Sie den Typ als generischen Typparameter für den Delegaten verwenden. Dies wird durch den Typ
R
im folgenden Beispiel veranschaulicht. Weitere Informationen finden Sie unter Varianz in Delegaten (C#) und Verwenden von Varianz für die generischen Delegaten Func und Action (C#).interface ICovariant<out R> { void DoSomething(Action<R> callback); }
Der Typ wird nicht als generische Einschränkung für die Schnittstellenmethoden verwendet. Dies wird im folgenden Code veranschaulicht.
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; }
Sie können einen generischen Typparameter contravariant mithilfe des Schlüsselworts in
deklarieren. Der kontravariante Typ kann nur als Typ von Methodenargumenten und nicht als Rückgabetyp von Schnittstellenmethoden verwendet werden. Der kontravariante Typ kann auch für generische Einschränkungen verwendet werden. Der folgende Code zeigt, wie eine kontravariante Schnittstelle deklariert und eine generische Einschränkung für eine seiner Methoden verwendet wird.
interface IContravariant<in A>
{
void SetSomething(A sampleArg);
void DoSomething<T>() where T : A;
// The following statement generates a compiler error.
// A GetSomething();
}
Es ist auch möglich, sowohl Kovarianz als auch Kontravarianz in derselben Schnittstelle zu unterstützen, aber für verschiedene Typparameter, wie im folgenden Codebeispiel gezeigt.
interface IVariant<out R, in A>
{
R GetSomething();
void SetSomething(A sampleArg);
R GetSetSomethings(A sampleArg);
}
Implementieren von varianten generischen Schnittstellen
Sie implementieren generische Variantenschnittstellen in Klassen, indem Sie dieselbe Syntax verwenden, die für invariante Schnittstellen verwendet wird. Das folgende Codebeispiel zeigt, wie eine kovariante Schnittstelle in einer generischen Klasse implementiert wird.
interface ICovariant<out R>
{
R GetSomething();
}
class SampleImplementation<R> : ICovariant<R>
{
public R GetSomething()
{
// Some code.
return default(R);
}
}
Klassen, die Variantenschnittstellen implementieren, sind invariant. Betrachten Sie beispielsweise den folgenden Code.
// 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;
Erweitern von varianten generischen Schnittstellen
Wenn Sie eine generische Variante-Schnittstelle erweitern, müssen Sie die in
und out
Schlüsselwörter verwenden, um explizit anzugeben, ob die abgeleitete Schnittstelle Abweichung unterstützt. Der Compiler leitet nicht die Varianz von der Schnittstelle ab, die erweitert wird. Betrachten Sie beispielsweise die folgenden Schnittstellen.
interface ICovariant<out T> { }
interface IInvariant<T> : ICovariant<T> { }
interface IExtCovariant<out T> : ICovariant<T> { }
In der IInvariant<T>
Schnittstelle ist der generische Typparameter T
invariant, während im IExtCovariant<out T>
Typparameter kovariant ist, obwohl beide Schnittstellen dieselbe Schnittstelle erweitern. Die gleiche Regel wird auf kontravariante generische Typparameter angewendet.
Sie können eine Schnittstelle erstellen, die sowohl die Schnittstelle erweitert, bei der der generische Typparameter T
kovariant ist, als auch die Schnittstelle, bei der sie kontravariant ist, wenn in der Erweiterungsschnittstelle der generische Typparameter T
invariant ist. Dies wird im folgenden Codebeispiel veranschaulicht.
interface ICovariant<out T> { }
interface IContravariant<in T> { }
interface IInvariant<T> : ICovariant<T>, IContravariant<T> { }
Wenn jedoch ein generischer Typparameter T
in einer Schnittstelle kovariant deklariert wird, können Sie ihn nicht kontravariant in der Erweiterungsschnittstelle oder umgekehrt deklarieren. Dies wird im folgenden Codebeispiel veranschaulicht.
interface ICovariant<out T> { }
// The following statement generates a compiler error.
// interface ICoContraVariant<in T> : ICovariant<T> { }
Vermeiden von Mehrdeutigkeit
Wenn Sie generische Variantenschnittstellen implementieren, kann die Varianz manchmal zu Mehrdeutigkeit führen. Solche Mehrdeutigkeiten sollten vermieden werden.
Wenn Sie z. B. explizit dieselbe generische Variante mit unterschiedlichen generischen Typparametern in einer Klasse implementieren, kann dies mehrdeutig sein. Der Compiler erzeugt in diesem Fall keinen Fehler, aber es wird nicht angegeben, welche Schnittstellenimplementierung zur Laufzeit ausgewählt wird. Diese Mehrdeutigkeit könnte zu subtilen Fehlern in Ihrem Code führen. Betrachten Sie folgendes Codebeispiel.
// 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();
}
}
In diesem Beispiel wird nicht festgelegt, wie die pets.GetEnumerator
Methode zwischen Cat
und Dog
auswählt. Dies kann zu Problemen in Ihrem Code führen.