次の方法で共有


デリゲートの分散 (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;
}

こちらも参照ください