Partager via


Variance dans les délégués (C# et Visual Basic)

.NET Framework 3.5 et Visual Studio 2008 offrent la prise en charge de la variance pour faire correspondre les signatures de méthode aux types délégués pour tous les délégués dans C# et Visual Basic. Cela signifie que vous pouvez assigner aux délégués pas uniquement les méthodes ayant des signatures correspondantes, mais également des méthodes qui retournent des types plus dérivés (covariance) ou qui acceptent des paramètres ayant des types moins dérivés (contravariance) que ceux spécifiés par le type délégué. Cela inclut à la fois des délégués génériques et non génériques.

Par exemple, considérez le code suivant, qui a deux classes et deux délégués : générique et non générique.

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

Lorsque vous créez des délégués des types SampleDelegate ou SampleGenericDelegate<A, R> (SampleDelegate(Of A, R) en Visual Basic), vous pouvez assigner l'une des méthodes suivantes à ces délégués.

' 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(); }

L'exemple de code suivant illustre la conversion implicite entre la signature de méthode et le type délégué.

' 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;

Pour obtenir d'autres exemples, consultez Utilisation de la variance dans les délégués (C# et Visual Basic) et Utilisation de la variance pour les délégués génériques Func et Action (C# et Visual Basic).

Variance dans les paramètres de type générique

Dans .NET Framework 4, vous pouvez activer la conversion implicite entre les délégués, afin que les délégués génériques ayant des types différents spécifiés par les paramètres de type générique puissent être assignés les uns aux autres, si les types sont hérités les uns des autres comme requis par la variance.

Pour activer la conversion implicite, vous devez déclarer explicitement les paramètres génériques dans un délégué comme covariant ou contravariant à l'aide du mot clé in ou out.

L'exemple de code suivant indique comment vous pouvez créer un délégué ayant un paramètre de type générique covariant.

' 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;           
}

Si vous utilisez uniquement la prise en charge de la variance pour faire correspondre les signatures de méthode aux types délégués et que vous n'utilisez pas les mots clés in et out, vous pouvez trouver que quelquefois vous pouvez instancier des délégués avec des expressions ou méthodes lambda identiques, mais que vous ne pouvez pas assigner un délégué à un autre.

Dans l'exemple de code suivant, SampleGenericDelegate<String> ne peut pas être converti explicitement en SampleGenericDelegate<Object> (SampleGenericDelegate(Of String) en SampleGenericDelegate(Of Object) en Visual Basic), bien que String hérite de Object. Vous pouvez résoudre ce problème en marquant le paramètre générique T avec le mot clé 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;



}

Délégués génériques ayant des paramètres de type variant dans le .NET Framework

.NET Framework 4 offre la prise en charge des paramètres de type générique dans plusieurs délégués génériques existants :

Pour plus d'informations et d'exemples, consultez Utilisation de la variance pour les délégués génériques Func et Action (C# et Visual Basic).

Déclaration de paramètres de type variant dans les délégués génériques

Si un délégué générique possède des paramètres de type générique covariant ou contravariant, il peut être désigné sous le nom de délégué générique variant.

Vous pouvez déclarer un covariant de paramètre de type générique dans un délégué générique à l'aide du mot clé out. Le type covariant peut être utilisé uniquement comme type de retour de méthode et non comme type d'arguments de méthode. L'exemple de code suivant indique comment déclarer un délégué générique covariant.

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

Vous pouvez déclarer un contravariant de paramètre de type générique dans un délégué générique à l'aide du mot clé in. Le type contravariant peut être utilisé uniquement comme type d'arguments de méthode et non comme type de retour de méthode. L'exemple de code suivant indique comment déclarer un délégué générique contravariant.

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

Important

Les paramètres ByRef en Visual Basic et ref et out en C# ne peuvent pas être marqués comme variants.

Il est également possible de prendre en charge à la fois la variance et la covariance dans le même délégué, mais pour des paramètres de type différents. L'exemple suivant le démontre.

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

Instanciation et appel de délégués génériques variants

Vous pouvez instancier et appeler des délégués variants de la même façon que vous instanciez et appelez des délégués invariants. Dans l'exemple suivant, le délégué est instancié par une expression lambda.

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

Combinaison des délégués génériques variants

Vous ne devez pas combiner les délégués variants. La méthode Combine ne prend pas en charge la conversion des délégués variants et nécessite le même type pour tous les délégués. Cela peut provoquer une exception runtime lorsque vous combinez les délégués à l'aide de la méthode Combine (en C# et Visual Basic) ou à l'aide de l'opérateur + (en C#), comme l'illustre l'exemple de code suivant.

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

Variance dans les paramètres de type générique pour les types valeur et référence

La variance pour les paramètres de type générique est prise en charge uniquement pour les types référence. Par exemple, DVariant<int> (DVariant(Of Int) en Visual Basic) ne peut pas être converti implicitement en DVariant<Object> ou DVaraint<long> (DVariant(Of Object) ou DVaraint(Of Long) en Visual Basic) parce que l'entier est un type valeur.

L'exemple suivant montre que la variance dans les paramètres de type générique n'est pas prise en charge pour les types valeur.

' 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;            
}

Conversion souple des délégués dans Visual Basic

La conversion souple des délégués, introduite dans Visual Basic 2008, permet davantage de flexibilité dans la correspondance entre les signatures de méthode et les types délégués. Par exemple, elle vous permet d'omettre des spécifications de paramètre et des valeurs de retour de fonction lorsque vous assignez une méthode à un délégué. Pour plus d'informations, consultez Conversion simplifiée des délégués (Visual Basic).

Voir aussi

Tâches

Comment : combiner des délégués (délégués multicast) (Guide de programmation C#)

Référence

Utilisation de la variance pour les délégués génériques Func et Action (C# et Visual Basic)

Concepts

Génériques dans le .NET Framework