.NET Framework 3.5 引進了與 C# 中所有委派中委派類型比對方法簽章的變異數支援。 這表示您可以不僅指派給具有相符簽章的方法,還可以指派給回傳更具衍生型別(協變)的方法,或者接受比委派類型所指定的衍生型別較少的參數(逆變)的方法。 這包括泛型和非泛型委派。
例如,請考慮下列程序代碼,其具有兩個類別和兩個委派:泛型和非泛型。
public class First { }
public class Second : First { }
public delegate First SampleDelegate(Second a);
public delegate R SampleGenericDelegate<A, R>(A a);
當您建立 SampleDelegate 或 SampleGenericDelegate<A, R> 類型的委派時,您可以將下列任一種方法指派給這些委派。
// Matching signature.
public static First ASecondRFirst(Second second)
{ 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(); }
下列程式代碼範例說明方法簽章與委派類型之間的隱含轉換。
// 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;
如需更多範例,請參閱 在委派中使用變數 (C#) 和使用 Func 和 Action 泛型委派的變數 (C#) 。
泛型類型參數中的變異數
在 .NET Framework 4 或更新版本中,您可以啟用委派之間的隱式轉換,這樣不同泛型型別參數所指定類型的泛型委派就可以相互指派,但前提是這些類型根據變異性需求必須是彼此的繼承類型。
若要啟用隱含轉換,您必須使用 in 或 out 關鍵詞,明確地將委派中的泛型參數宣告為 covariant 或 contravariant。
下列程式代碼範例示範如何建立具有covariant泛型型別參數的委派。
// 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;
}
如果您只使用變異數支援來比對方法簽章與委派類型,而且不使用 in 和 out 關鍵詞,您可能會發現有時候您可以使用相同的 Lambda 表達式或方法具現化委派,但無法將一個委派指派給另一個委派。
在下列程式代碼範例中,SampleGenericDelegate<String>雖然 繼承 SampleGenericDelegate<Object>,但無法明確轉換成 StringObject 。 您可以透過使用T關鍵字來標記泛型參數out,以修正此問題。
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;
}
在 .NET 中具有 Variant 類型參數的泛型委派
.NET Framework 4 引進了數個現有泛型委派中泛型型別參數的變異數支援:
Action委派來自 System 命名空間,例如 Action<T> 和 Action<T1,T2>Func委派來自 System 命名空間,例如 Func<TResult> 和 Func<T,TResult>
如需詳細資訊和範例,請參閱使用 Func 和 Action 泛型委派的變數(C#)。
在泛型委派中宣告變體類型參數
如果泛型委派具有 covariant 或 contravariant 泛型型別參數,它可以稱為 Variant 泛型委派。
您可以在泛型委派中使用 out 關鍵詞宣告協變的泛型型別參數。 covariant 型別只能當做方法傳回型別使用,而不是做為方法自變數的類型。 下列程式代碼範例示範如何宣告 covariant 泛型委派。
public delegate R DCovariant<out R>();
您可以使用 關鍵詞,在泛型委派 in 中宣告泛型型別參數反變數。 反變數類型只能當做方法自變數的類型使用,而不是做為方法傳回型別。 下列程式代碼範例示範如何宣告反變數泛型委派。
public delegate void DContravariant<in A>(A a);
這很重要
refC# 中的 、 in和 out 參數不能標示為 variant。
您也可以在同一個委派中同時支援變數和共變數,但針對不同的類型參數。 下列範例會顯示這一點。
public delegate R DVariant<in A, out R>(A a);
實例化和調用變體泛型委派
您可以實例化和叫用變形委派,就像實例化和叫用不變委派一樣。 在下列範例中,委派是由 Lambda 運算式具現化。
DVariant<String, String> dvariant = (String str) => str + " ";
dvariant("test");
結合變異泛型委派
請勿合併 Variant 委派。 方法 Combine 不支援 Variant 委派轉換,而且預期委派的類型完全相同。 當您使用 Combine 方法或使用 + 運算符結合委派時,這可能會導致運行時間例外狀況,如下列程式代碼範例所示。
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);
實值型別和參考型別的泛型參數變異性
泛型型別參數的變異數僅支持參考型別。 例如, DVariant<int> 無法隱含轉換成 DVariant<Object> 或 DVariant<long>,因為整數是實值型別。
下列範例示範泛型型別參數的型變不支援實值型別。
// 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;
}