デリゲートの分散 (C# および Visual Basic)

.NET Framework 3.5 および Visual Studio 2008 では、C# と Visual Basic のすべてのデリゲートにおいて、メソッド シグネチャをデリゲート型に一致させる変性のサポートが導入されています。 つまり、一致するシグネチャを持つメソッドだけでなく、デリゲート型で指定された型よりも強い派生型 (共変性) を返すメソッドや、弱い派生型 (反変性) のパラメーターを受け取るメソッドを、デリゲートに割り当てることができます。 これには、汎用デリゲートと非汎用デリゲートの両方が含まれます。

たとえば、次のコードについて考えます。このコードには、2 つのクラスと、汎用と非汎用の 2 つのデリゲートが含まれています。

Public Class First
End Class

Public Class Second
    Inherits First
End Class

Public Delegate Function SampleDelegate(ByVal a As Second) As First
Public Delegate Function SampleGenericDelegate(Of A, R)(ByVal a As A) As R
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> (Visual Basic では SampleDelegate(Of A, R)) 型のデリゲートを作成する場合、それらのデリゲートには、次のいずれかのメソッドを割り当てることができます。

' Matching signature.
Public Shared Function ASecondRFirst(
    ByVal second As Second) As First
    Return New First()
End Function

' The return type is more derived.
Public Shared Function ASecondRSecond(
    ByVal second As Second) As Second
    Return New Second()
End Function

' The argument type is less derived.
Public Shared Function AFirstRFirst(
    ByVal first As First) As First
    Return New First()
End Function

' The return type is more derived 
' and the argument type is less derived.
Public Shared Function AFirstRSecond(
    ByVal first As First) As Second
    Return New Second()
End Function
// Matching signature.
public static First ASecondRFirst(Second first)
{ 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.
Dim dNonGeneric As SampleDelegate = AddressOf 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.
Dim dNonGenericConversion As SampleDelegate = AddressOf AFirstRSecond

' Assigning a method with a matching signature to a generic delegate.
' No conversion is necessary.
Dim dGeneric As SampleGenericDelegate(Of Second, First) = AddressOf ASecondRFirst
' Assigning a method with a more derived return type 
' and less derived argument type to a generic delegate.
' The implicit conversion is used.
Dim dGenericConversion As SampleGenericDelegate(Of Second, First) = AddressOf AFirstRSecond
// 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# および Visual Basic)」および「Func および Action 汎用デリゲートでの分散の使用 (C# および Visual Basic)」を参照してください。

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

.NET Framework 4 では、デリゲート間で暗黙の型変換を有効にできます。そのため、ジェネリック型パラメーターで異なる型が指定された汎用デリゲートどうしでも、型が変性の要件を満たすように互いに継承されていれば、それらの汎用デリゲートを相互に割り当てることができます。

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

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

' Type T is declared covariant by using the out keyword.
Public Delegate Function SampleGenericDelegate(Of Out T)() As T
Sub Test()
    Dim dString As SampleGenericDelegate(Of String) = Function() " "
    ' You can assign delegates to each other,
    ' because the type T is declared covariant.
    Dim dObject As SampleGenericDelegate(Of Object) = dString
End Sub
// 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 キーワードを使用しない場合は、一致するラムダ式またはメソッドによってデリゲートをインスタンス化できることもありますが、デリゲートを別のデリゲートに割り当てることはできません。

次のコード例では、String は Object を継承していますが、SampleGenericDelegate<String> から SampleGenericDelegate<Object> に (Visual Basic では SampleGenericDelegate(Of String) から SampleGenericDelegate(Of Object) に) 明示的に変換することはできません。 ジェネリック パラメーター T を out キーワードでマークすると、この問題を修正できます。

Public Delegate Function SampleGenericDelegate(Of T)() As T
Sub Test()
    Dim dString As SampleGenericDelegate(Of String) = Function() " "

    ' 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.
    Dim dObject As SampleGenericDelegate(Of Object) = Function() " "

    ' The following statement generates a compiler error
    ' because the generic type T is not marked as covariant.
    ' Dim dObject As SampleGenericDelegate(Of Object) = dString


End Sub
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 Framework のバリアント型パラメーターを持つ汎用デリゲート

.NET Framework 4 では、既存のいくつかの汎用デリゲートのジェネリック型パラメーターに対して、変性のサポートが導入されています。

使用例を含む詳細については、「Func および Action 汎用デリゲートでの分散の使用 (C# および Visual Basic)」を参照してください。

汎用デリゲートのバリアント型パラメーターの宣言

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

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

Public Delegate Function DCovariant(Of Out R)() As R
public delegate R DCovariant<out R>();

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

Public Delegate Sub DContravariant(Of In A)(ByVal a As A)
public delegate void DContravariant<in A>(A a);

重要

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

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

Public Delegate Function DVariant(Of In A, Out R)(ByVal a As A) As R
public delegate R DVariant<in A, out R>(A a);

バリアント汎用デリゲートのインスタンス化と呼び出し

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

Dim dvariant As DVariant(Of String, String) = Function(str) str + " "
dvariant("test")
DVariant<String, String> dvariant = (String str) => str + " ";
dvariant("test");

バリアント汎用デリゲートの結合

バリアント デリゲートの結合は推奨されません。 Combine メソッドはバリアント デリゲートの変換をサポートしていないため、デリゲートが厳密に同じ型である必要があります。 そのため、次のコード例に示すように、Combine メソッド (C# および Visual Basic) または + 演算子 (C#) を使用してデリゲートを結合すると、実行時例外が発生する可能性があります。

Dim actObj As Action(Of Object) = Sub(x) Console.WriteLine("object: {0}", x)
Dim actStr As Action(Of String) = Sub(x) Console.WriteLine("string: {0}", x)

' The following statement throws an exception at run time.
' Dim actCombine = [Delegate].Combine(actStr, actObj)
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> (Visual Basic では DVariant(Of Int)) を DVariant<Object> または DVaraint<long> (Visual Basic では DVariant(Of Object) または DVaraint(Of Long)) に暗黙的に変換することはできません。

次の例は、値型ではジェネリック型パラメーターの変性がサポートされないことを示しています。

' The type T is covariant.
Public Delegate Function DVariant(Of Out T)() As T
' The type T is invariant.
Public Delegate Function DInvariant(Of T)() As T
Sub Test()
    Dim i As Integer = 0
    Dim dInt As DInvariant(Of Integer) = Function() i
    Dim dVaraintInt As DVariant(Of Integer) = Function() 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.
    ' Dim dObject As DInvariant(Of Object) = dInt
    ' Dim dLong As DInvariant(Of Long) = dInt
    ' Dim dVaraintObject As DInvariant(Of Object) = dInt
    ' Dim dVaraintLong As DInvariant(Of Long) = dInt
End Sub
// 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;            
}

Visual Basic における厳密でないデリゲート変換

厳密でないデリゲート変換は、Visual Basic 2008 で導入された機能で、メソッド シグネチャとデリゲート型のより柔軟な対応付けを実現します。 たとえば、パラメーターの指定を省略したり、メソッドをデリゲートに割り当てるときに関数の戻り値を省略したりできるようになります。 詳細については、「厳密でないデリゲート変換 (Visual Basic)」を参照してください。

参照

処理手順

方法 : デリゲートを結合する (マルチキャスト デリゲート) (C# プログラミング ガイド)

参照

Func および Action 汎用デリゲートでの分散の使用 (C# および Visual Basic)

その他の技術情報

.NET Framework におけるジェネリック