Erstellen varianter generischer Schnittstellen (C#)Creating Variant Generic Interfaces (C#)

Sie können generische Typparameter in Schnittstellen als Kovariante oder als Kontravariante deklarieren.You can declare generic type parameters in interfaces as covariant or contravariant. Kovarianz ermöglicht Schnittstellenmethoden, stärker abgeleitete Rückgabetypen zu verwenden, als durch die generischen Typparameter definiert.Covariance allows interface methods to have more derived return types than that defined by the generic type parameters. Kontravarianz ermöglicht Schnittstellenmethoden, Argumenttypen zu verwenden, die weniger stark abgeleitet sind, als durch die generischen Parameter angegeben.Contravariance allows interface methods to have argument types that are less derived than that specified by the generic parameters. Eine generische Schnittstelle mit ko- oder kontravarianten generischen Typparametern wird als variant bezeichnet.A generic interface that has covariant or contravariant generic type parameters is called variant.

Hinweis

In .NET Framework 4 wurde die Varianzunterstützung für mehrere vorhandene generische Schnittstellen eingeführt..NET Framework 4 introduced variance support for several existing generic interfaces. Die Liste der varianten Schnittstellen in .NET Framework finden Sie unter Varianz in generischen Schnittstellen (C#).For the list of the variant interfaces in the .NET Framework, see Variance in Generic Interfaces (C#).

Deklarieren von varianten generischen SchnittstellenDeclaring Variant Generic Interfaces

Sie können variante generische Schnittstellen deklarieren, indem Sie die in- und out-Schlüsselwörter für generische Typparameter verwenden.You can declare variant generic interfaces by using the in and out keywords for generic type parameters.

Wichtig

ref- und out-Parameter in C# können nicht variant sein.ref and out parameters in C# cannot be variant. Auch Werttypen unterstützen keine Varianz.Value types also do not support variance.

Sie können einen generischen Typparameter mithilfe des Schlüsselworts out als Kovariante deklarieren.You can declare a generic type parameter covariant by using the out keyword. Der kovariante Typ muss die folgenden Bedingungen erfüllen:The covariant type must satisfy the following conditions:

  • Der Typ wird nur als Rückgabetyp von Schnittstellenmethoden verwendet, und nicht als Typ von Methodenargumenten.The type is used only as a return type of interface methods and not used as a type of method arguments. Dies wird im folgenden Beispiel veranschaulicht, in dem der Typ R als Kovariante deklariert wird.This is illustrated in the following example, in which the type R is declared covariant.

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

    Es gibt allerdings eine Ausnahme zu dieser Regel.There is one exception to this rule. Wenn Sie einen kontravarianten generischen Delegaten als Methodenparameter angegeben haben, können Sie den Typ als generischen Typparameter für den Delegaten verwenden.If you have a contravariant generic delegate as a method parameter, you can use the type as a generic type parameter for the delegate. Dies wird im folgenden Beispiel von Typ R veranschaulicht.This is illustrated by the type R in the following example. Weitere Informationen finden Sie unter Varianz in Delegaten (C#) und Verwenden von Varianz für die generischen Delegaten Func und Action (C#).For more information, see Variance in Delegates (C#) and Using Variance for Func and Action Generic Delegates (C#).

    interface ICovariant<out R>  
    {  
        void DoSomething(Action<R> callback);  
    }  
    
  • Der Typ wird nicht als generische Einschränkung für die Schnittstellenmethoden verwendet.The type is not used as a generic constraint for the interface methods. Der folgende Code veranschaulicht dies.This is illustrated in the following code.

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

Sie können einen generischen Typparameter mithilfe des Schlüsselworts in als Kovariante deklarieren.You can declare a generic type parameter contravariant by using the in keyword. Der kontravariante Typ kann nur als Typ von Methodenargumenten und nicht von Schnittstellenmethoden verwendet werden.The contravariant type can be used only as a type of method arguments and not as a return type of interface methods. Der kontravariante Typ kann auch für generische Einschränkungen verwendet werden.The contravariant type can also be used for generic constraints. Der folgende Code zeigt, wie eine kontravariante Schnittstelle deklariert und eine generische Einschränkung für eine ihrer Methoden verwendet wird.The following code shows how to declare a contravariant interface and use a generic constraint for one of its methods.

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

Bei unterschiedlichen Typparametern ist jedoch auch die Verwendung verschiedener Ko- und Kontravarianzen in der gleichen Schnittstelle möglich, wie im folgenden Codebeispiel veranschaulicht wird.It is also possible to support both covariance and contravariance in the same interface, but for different type parameters, as shown in the following code example.

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

Implementieren von varianten generischen SchnittstellenImplementing Variant Generic Interfaces

Sie können variante generische Schnittstellen in Klassen implementieren, indem Sie die gleiche Syntax verwenden, die für invariante Schnittstellen verwendet wird.You implement variant generic interfaces in classes by using the same syntax that is used for invariant interfaces. Im folgenden Codebeispiel wird veranschaulicht, wie eine kovariante Schnittstelle in einer generischen Klasse implementiert wird.The following code example shows how to implement a covariant interface in a generic class.

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

Klassen, die variante Schnittstellen implementieren, sind nicht variant.Classes that implement variant interfaces are invariant. Betrachten Sie hierzu den folgenden Beispielcode:For example, consider the following 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 SchnittstellenExtending Variant Generic Interfaces

Wenn Sie eine variante generische Schnittstelle erweitern, müssen Sie mit den in- und out-Schlüsselwörtern explizit angeben, ob Varianz von der abgeleiteten Schnittstelle unterstützt wird.When you extend a variant generic interface, you have to use the in and out keywords to explicitly specify whether the derived interface supports variance. Der Compiler leitet eine Varianz nicht von der Schnittstelle ab, die erweitert wird.The compiler does not infer the variance from the interface that is being extended. Beachten Sie z.B. die folgenden Schnittstellen.For example, consider the following interfaces.

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 nicht variant, während in IExtCovariant<out T> der Typparameter kovariant ist, obwohl beide Schnittstellen die gleiche Schnittstelle erweitern.In the IInvariant<T> interface, the generic type parameter T is invariant, whereas in IExtCovariant<out T> the type parameter is covariant, although both interfaces extend the same interface. Die gleiche Regel gilt für kontravariante generische Typparameter.The same rule is applied to contravariant generic type parameters.

Sie können eine Schnittstelle erstellen, die sowohl die Schnittstelle mit dem kovarianten generischen Typparameter T als auch die Schnittstelle mit dem kontravarianten Typparameter implementiert, wenn der generische Typparameter T in der erweiternden Schnittstelle nicht variant ist.You can create an interface that extends both the interface where the generic type parameter T is covariant and the interface where it is contravariant if in the extending interface the generic type parameter T is invariant. Dies wird im folgenden Codebeispiel veranschaulicht.This is illustrated in the following code example.

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

Wenn der generische Typparameter T jedoch in einer Schnittstelle als Kovariante deklariert wird, können Sie ihn nicht in der erweiternden Schnittstelle als Kontravariante deklarieren oder umgekehrt.However, if a generic type parameter T is declared covariant in one interface, you cannot declare it contravariant in the extending interface, or vice versa. Dies wird im folgenden Codebeispiel veranschaulicht.This is illustrated in the following code example.

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

Vermeiden von MehrdeutigkeitenAvoiding Ambiguity

Wenn Sie variante generische Schnittstellen implementieren, kann Varianz manchmal zu Mehrdeutigkeit führen.When you implement variant generic interfaces, variance can sometimes lead to ambiguity. Dies sollte vermieden werden.This should be avoided.

Wenn Sie z.B. die gleiche variante generische Schnittstelle explizit mit unterschiedlichen generischen Typparametern in einer Klasse implementieren, kann dies zu Mehrdeutigkeit führen.For example, if you explicitly implement the same variant generic interface with different generic type parameters in one class, it can create ambiguity. Der Compiler erzeugt in diesem Fall keinen Fehler, aber es wird nicht angegeben, welche Schnittstellenimplementierung zur Laufzeit ausgewählt wird.The compiler does not produce an error in this case, but it is not specified which interface implementation will be chosen at runtime. Dies kann zu schwer erkennbaren Fehlern im Code führen.This could lead to subtle bugs in your code. Betrachten Sie folgendes Codebeispiel.Consider the following code example.

// 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 ist nicht angegeben, wie die pets.GetEnumerator-Methode zwischen Cat und Dog auswählt.In this example, it is unspecified how the pets.GetEnumerator method chooses between Cat and Dog. Dies könnte Probleme im Code verursachen.This could cause problems in your code.

Siehe auchSee Also

Varianz in generischen SchnittstellenVariance in Generic Interfaces (C#)
Verwenden von Varianz für die generischen Delegaten Func und Action (C#)Using Variance for Func and Action Generic Delegates (C#)