次の方法で共有


デリゲートの分散 (C#)

.NET Framework 3.5 では、C# のすべてのデリゲートでメソッド シグネチャとデリゲート型を照合するための差異のサポートが導入されました。 つまり、一致するシグネチャを持つメソッドだけでなく、より多くの派生型 (共変性) を返すメソッド、またはデリゲート型で指定されたよりも派生型 (反変性) が少ないパラメーターを受け入れるメソッドにもデリゲートを割り当てることができます。 これには、ジェネリック デリゲートと非ジェネリック デリゲートの両方が含まれます。

たとえば、ジェネリックと非ジェネリックの 2 つのクラスと 2 つのデリゲートを持つ次のコードを考えてみましょう。

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 Generic Delegate の分散の使用 (C#)」を参照してください。

ジェネリック型パラメーターの変性

.NET Framework 4 以降では、デリゲート間で暗黙的な変換を有効にして、ジェネリック型パラメーターで指定された異なる型を持つジェネリック デリゲートを相互に割り当てることができるようにすることができます(型が分散の必要に応じて相互に継承される場合)。

暗黙的な変換を有効にするには、 in キーワードまたは out キーワードを使用して、デリゲート内のジェネリック パラメーターを共変または反変として明示的に宣言する必要があります。

次のコード例は、共変ジェネリック型パラメーターを持つデリゲートを作成する方法を示しています。

// 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 キーワードを使用しない場合は、同じラムダ式またはメソッドを使用してデリゲートをインスタンス化できますが、デリゲートを別のデリゲートに割り当てることができない場合があります。

次のコード例では、 SampleGenericDelegate<String> を明示的に SampleGenericDelegate<Object> に変換することはできませんが、 StringObjectを継承します。 この問題を解決するには、ジェネリック パラメーター Tout キーワードでマークします。

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 でバリアント型パラメーターを持つジェネリック デリゲート

.NET Framework 4 では、いくつかの既存のジェネリック デリゲートでジェネリック型パラメーターの分散サポートが導入されました。

詳細と例については、「 Func および Action Generic Delegate の分散の使用 (C#)」を参照してください。

ジェネリック デリゲートでのバリアント型パラメーターの宣言

ジェネリック デリゲートに共変または反変のジェネリック型パラメーターがある場合は、 バリアント ジェネリック デリゲートと呼ばれます。

out キーワードを使用して、ジェネリック デリゲートで共変のジェネリック型パラメーターを宣言できます。 共変型は、メソッドの戻り値の型としてのみ使用でき、メソッド引数の型として使用することはできません。 次のコード例は、共変のジェネリック デリゲートを宣言する方法を示しています。

public delegate R DCovariant<out R>();  

in キーワードを使用して、ジェネリック デリゲートでジェネリック型パラメーター反変を宣言できます。 反変型は、メソッドの戻り値の型としてではなく、メソッド引数の型としてのみ使用できます。 次のコード例は、反変ジェネリック デリゲートを宣言する方法を示しています。

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

Von Bedeutung

refin、および C# の out パラメーターをバリアントとしてマークすることはできません。

同じデリゲートで分散と共分散の両方をサポートすることもできますが、型パラメーターが異なります。 これを次の例に示します。

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

バリアント ジェネリック デリゲートのインスタンス化と呼び出し

インバリアント デリゲートをインスタンス化して呼び出すのと同じように、バリアント デリゲートをインスタンス化して呼び出すことができます。 次の例では、デリゲートはラムダ式によってインスタンス化されます。

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

バリアント ジェネリック デリゲートの組み合わせ

バリアント デリゲートを組み合わせないでください。 Combine メソッドは、バリアント デリゲート変換をサポートせず、デリゲートがまったく同じ型であることが想定されています。 次のコード例に示すように、 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;
}  

こちらも参照ください