Varianz bei Delegaten (C#)Variance in Delegates (C#)

Mit .NET Framework 3.5 wurde die Unterstützung von Varianz eingeführt, um Methodensignaturen und Delegattypen in allen Delegaten in C# vergleichen zu können..NET Framework 3.5 introduced variance support for matching method signatures with delegate types in all delegates in C#. Das bedeutet, dass Sie Delegaten nicht nur Methoden mit übereinstimmenden Signaturen zuweisen können, sondern auch Methoden, die mehrere abgeleitete Typen zurückgeben (Kovarianz) oder die Parameter akzeptieren, die über weniger abgeleitete Typen verfügen, als durch den Delegattyp angegeben wurde (Kontravarianz).This means that you can assign to delegates not only methods that have matching signatures, but also methods that return more derived types (covariance) or that accept parameters that have less derived types (contravariance) than that specified by the delegate type. Dies umfasst generische und nicht generische Delegaten.This includes both generic and non-generic delegates.

Betrachten Sie beispielsweise folgenden Code, der zwei Klassen und zwei Delegaten aufweist: generisch und nicht generisch.For example, consider the following code, which has two classes and two delegates: generic and non-generic.

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

Beim Erstellen von Delegaten vom Typ SampleDelegate oder SampleGenericDelegate<A, R> können Sie diesen Delegaten eine der folgenden Methoden zuweisen.When you create delegates of the SampleDelegate or SampleGenericDelegate<A, R> types, you can assign any one of the following methods to those delegates.

// Matching signature.  
public static First ASecondRFirst(Second first)  
{ 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(); }  

Das folgende Codebeispiel veranschaulicht die implizite Konvertierung zwischen der Methodensignatur und dem Delegattyp.The following code example illustrates the implicit conversion between the method signature and the delegate type.

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

Weitere Beispiele finden Sie unter Using Variance in Delegates (C#) (Verwenden von Varianz bei Delegaten (C#)) und Using Variance for Func and Action Generic Delegates (C#) (Verwenden von Varianz für die generischen Delegaten Func und Action (C#)).For more examples, see Using Variance in Delegates (C#) and Using Variance for Func and Action Generic Delegates (C#).

Varianz in generischen TypparameternVariance in Generic Type Parameters

In .NET Framework 4 oder höher können Sie die implizierte Konvertierung zwischen Delegaten aktivieren. Das bedeutet, dass generische Delegaten, die über verschiedene von generischen Typparametern angegebene Typen verfügen, sich gegenseitig zugewiesen werden können, wenn die Typen voneinander geerbt werden. Dies ist für die Varianz erforderlich.In .NET Framework 4 or later you can enable implicit conversion between delegates, so that generic delegates that have different types specified by generic type parameters can be assigned to each other, if the types are inherited from each other as required by variance.

Sie müssen einen generischen Parameter in einem Delegaten mithilfe der Schlüsselwörter in oder out explizit als kovariant oder kontravariant deklarieren, um die implizite Konvertierung zu aktivieren.To enable implicit conversion, you must explicitly declare generic parameters in a delegate as covariant or contravariant by using the in or out keyword.

Im folgenden Codebeispiel wird veranschaulicht, wie Sie einen Delegaten erstellen können, der über einen kovarianten generischen Typparameter verfügt.The following code example shows how you can create a delegate that has a covariant generic type parameter.

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

Wenn Sie die Unterstützung von Varianz nur verwenden, um Methodensignaturen mit Delegaten zu vergleichen und nicht die Schlüsselwörter in und out verwenden, kann es möglicherweise passieren, dass Sie zwar Delegate mit identischen Lambdaausdrücken oder -Methoden instanziieren, aber keinen Delegaten einem anderen zuweisen können.If you use only variance support to match method signatures with delegate types and do not use the in and out keywords, you may find that sometimes you can instantiate delegates with identical lambda expressions or methods, but you cannot assign one delegate to another.

Im folgenden Codebeispiel kann SampleGenericDelegate<String> nicht expliziert in SampleGenericDelegate<Object> konvertiert werden, obwohl String Object erbt.In the following code example, SampleGenericDelegate<String> cannot be explicitly converted to SampleGenericDelegate<Object>, although String inherits Object. Sie können dieses Problem beheben, indem Sie den generischen Parameter T mit dem Schlüsselwort out markieren.You can fix this problem by marking the generic parameter T with the out keyword.

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;  

}  

Generische Delegaten mit varianten Typparametern in .NET FrameworkGeneric Delegates That Have Variant Type Parameters in the .NET Framework

Mit .NET Framework 4 wurde die Unterstützung von Varianz für generische Typparameter in verschiedenen vorhandenen generischen Delegaten eingeführt:.NET Framework 4 introduced variance support for generic type parameters in several existing generic delegates:

Weitere Informationen und Beispiele finden Sie unter Using Variance for Func and Action Generic Delegates (C#) (Verwenden von Varianz für die generischen Delegaten Func und Action (C#)).For more information and examples, see Using Variance for Func and Action Generic Delegates (C#).

Angeben varianter Typparameter in generischen DelegatenDeclaring Variant Type Parameters in Generic Delegates

Wenn ein generischer Delegat über kovariante oder kontravariante generische Typparameter verfügt, kann er als varianter generischer Delegat bezeichnet werden.If a generic delegate has covariant or contravariant generic type parameters, it can be referred to as a variant generic delegate.

Sie können einen generischen Typparameter mithilfe des Schlüsselworts out in einem generischen Delegaten als kovariant deklarieren.You can declare a generic type parameter covariant in a generic delegate by using the out keyword. Der kovariante Typ kann nur als Typ von Methodenrückgaben und nicht von Methodenargumenten verwendet werden.The covariant type can be used only as a method return type and not as a type of method arguments. Das folgende Codebeispiel zeigt, wie Sie einen kovarianten generischen Delegaten deklarieren.The following code example shows how to declare a covariant generic delegate.

public delegate R DCovariant<out R>();  

Sie können einen generischen Typparameter mithilfe des Schlüsselworts in in einem generischen Delegaten als kontravariant deklarieren.You can declare a generic type parameter contravariant in a generic delegate by using the in keyword. Der kontravariante Typ kann nur als Typ von Methodenrückgaben und nicht von Methodenargumenten verwendet werden.The contravariant type can be used only as a type of method arguments and not as a method return type. Das folgende Codebeispiel zeigt, wie Sie einen kontravarianten generischen Delegaten deklarieren.The following code example shows how to declare a contravariant generic delegate.

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

Wichtig

Die Parameter ref und out in C# können nicht als variant markiert werden.ref and out parameters in C# can't be marked as variant.

Es ist auch möglich, Varianz und Kovarianz im gleichen Delegaten, aber für verschiedene Typparameter, zu unterstützen.It is also possible to support both variance and covariance in the same delegate, but for different type parameters. Dies wird im folgenden Beispiel gezeigt.This is shown in the following example.

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

Instanziieren und Aufrufen von varianten generischen DelegatenInstantiating and Invoking Variant Generic Delegates

Sie können variante Delegaten genau wie invariante Delegaten instanziieren und aufrufen.You can instantiate and invoke variant delegates just as you instantiate and invoke invariant delegates. Im folgenden Beispiel wird der Delegat durch einen Lambdaausdruck instanziiert.In the following example, the delegate is instantiated by a lambda expression.

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

Kombinieren von varianten generischen DelegatenCombining Variant Generic Delegates

Variante Delegaten sollten nicht kombiniert werden.You should not combine variant delegates. Die Methode Combine unterstützt keine Konvertierung von varianten Delegaten und erwartet, dass Delegaten vom exakt gleichen Typ sind.The Combine method does not support variant delegate conversion and expects delegates to be of exactly the same type. Es kann zu einer Laufzeitausnahme führen, wenn Sie Delegaten entweder mit der Methode Combine oder dem Operator + kombinieren, wie im folgenden Codebeispiel gezeigt wird.This can lead to a run-time exception when you combine delegates either by using the Combine method or by using the + operator, as shown in the following code example.

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

Varianz in generischen Typparametern für Wert- und ReferenztypenVariance in Generic Type Parameters for Value and Reference Types

Varianz für generische Typparameter wird nur für Referenztypen unterstützt.Variance for generic type parameters is supported for reference types only. DVariant<int> kann z.B. nicht implizit in DVariant<Object> oder DVariant<long> konvertiert werden, da es sich bei „Integer“ um einen Werttyp handelt.For example, DVariant<int> can't be implicitly converted to DVariant<Object> or DVariant<long>, because integer is a value type.

Das folgende Beispiel veranschaulicht, dass Varianz in generischen Typparametern für Werttypen nicht unterstützt wird.The following example demonstrates that variance in generic type parameters is not supported for value types.

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

Siehe auchSee Also

GenerikaGenerics
Verwenden von Varianz für die generischen Delegaten Func und Action (C#)Using Variance for Func and Action Generic Delegates (C#)
How to: Combine Delegates (Multicast Delegates) (Vorgehensweise: Kombinieren von Delegaten (Multicastdelegaten))How to: Combine Delegates (Multicast Delegates)