共用方式為


委派中的變數 (C#)

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

當您建立 SampleDelegateSampleGenericDelegate<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 或更新版本中,您可以啟用委派之間的隱式轉換,這樣不同泛型型別參數所指定類型的泛型委派就可以相互指派,但前提是這些類型根據變異性需求必須是彼此的繼承類型。

若要啟用隱含轉換,您必須使用 inout 關鍵詞,明確地將委派中的泛型參數宣告為 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;
}  

如果您只使用變異數支援來比對方法簽章與委派類型,而且不使用 inout 關鍵詞,您可能會發現有時候您可以使用相同的 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 引進了數個現有泛型委派中泛型型別參數的變異數支援:

如需詳細資訊和範例,請參閱使用 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# 中的 、 inout 參數不能標示為 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;
}  

另請參閱