.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>
に変換することはできませんが、 String
は Object
を継承します。 この問題を解決するには、ジェネリック パラメーター 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 でバリアント型パラメーターを持つジェネリック デリゲート
.NET Framework 4 では、いくつかの既存のジェネリック デリゲートでジェネリック型パラメーターの分散サポートが導入されました。
Action
System名前空間からのデリゲート (例: Action<T>とAction<T1,T2>Func
System名前空間からのデリゲート (例: Func<TResult>とFunc<T,TResult>Predicate<T> デリゲート
Comparison<T> デリゲート
詳細と例については、「 Func および Action Generic Delegate の分散の使用 (C#)」を参照してください。
ジェネリック デリゲートでのバリアント型パラメーターの宣言
ジェネリック デリゲートに共変または反変のジェネリック型パラメーターがある場合は、 バリアント ジェネリック デリゲートと呼ばれます。
out
キーワードを使用して、ジェネリック デリゲートで共変のジェネリック型パラメーターを宣言できます。 共変型は、メソッドの戻り値の型としてのみ使用でき、メソッド引数の型として使用することはできません。 次のコード例は、共変のジェネリック デリゲートを宣言する方法を示しています。
public delegate R DCovariant<out R>();
in
キーワードを使用して、ジェネリック デリゲートでジェネリック型パラメーター反変を宣言できます。 反変型は、メソッドの戻り値の型としてではなく、メソッド引数の型としてのみ使用できます。 次のコード例は、反変ジェネリック デリゲートを宣言する方法を示しています。
public delegate void DContravariant<in A>(A a);
Von Bedeutung
ref
、 in
、および 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;
}
こちらも参照ください
.NET