Partager via


Création d’interfaces génériques variant (Visual Basic)

Vous pouvez déclarer des paramètres de type générique dans les interfaces en tant que covariant ou contravariant. La covariance permet aux méthodes d’interface d’avoir plus de types de retour dérivés que ceux définis par les paramètres de type générique. La contravariance permet aux méthodes d’interface d’avoir des types d’arguments moins dérivés que ceux spécifiés par les paramètres génériques. Une interface générique qui a des paramètres de type générique covariant ou contravariant est appelée variante.

Remarque

.NET Framework 4 a introduit la prise en charge des variances pour plusieurs interfaces génériques existantes. Pour obtenir la liste des interfaces variant dans .NET Framework, consultez Variance dans les interfaces génériques (Visual Basic).

Déclaration d’interfaces génériques variant

Vous pouvez déclarer des interfaces génériques de variantes à l’aide des mots clés et in des out paramètres de type générique.

Important

ByRef les paramètres de Visual Basic ne peuvent pas être variant. Les types valeur ne prennent pas non plus en charge la variance.

Vous pouvez déclarer un paramètre de type générique covariant à l’aide du out mot clé. Le type covariant doit remplir les conditions suivantes :

  • Le type est utilisé uniquement comme type de retour de méthodes d’interface et non utilisé comme type d’arguments de méthode. Ceci est illustré dans l’exemple suivant, dans lequel le type R est déclaré covariant.

    Interface ICovariant(Of Out R)
        Function GetSomething() As R
        ' The following statement generates a compiler error.
        ' Sub SetSomething(ByVal sampleArg As R)
    End Interface
    

    Il existe une exception à cette règle. Si vous avez un délégué générique contravariant comme paramètre de méthode, vous pouvez utiliser le type comme paramètre de type générique pour le délégué. Ceci est illustré par le type R dans l’exemple suivant. Pour plus d’informations, consultez Variance dans les délégués (Visual Basic) et Utilisation de la variance pour les délégués génériques Func et Action (Visual Basic).

    Interface ICovariant(Of Out R)
        Sub DoSomething(ByVal callback As Action(Of R))
    End Interface
    
  • Le type n’est pas utilisé comme contrainte générique pour les méthodes d’interface. Ceci est illustré dans le code suivant.

    Interface ICovariant(Of Out R)
        ' The following statement generates a compiler error
        ' because you can use only contravariant or invariant types
        ' in generic constraints.
        ' Sub DoSomething(Of T As R)()
    End Interface
    

Vous pouvez déclarer un paramètre de type générique contravariant à l’aide du in mot clé. Le type contravariant peut être utilisé uniquement comme type d’arguments de méthode et non comme type de retour de méthodes d’interface. Le type contravariant peut également être utilisé pour les contraintes génériques. Le code suivant montre comment déclarer une interface contravariante et utiliser une contrainte générique pour l’une de ses méthodes.

Interface IContravariant(Of In A)
    Sub SetSomething(ByVal sampleArg As A)
    Sub DoSomething(Of T As A)()
    ' The following statement generates a compiler error.
    ' Function GetSomething() As A
End Interface

Il est également possible de prendre en charge la covariance et la contravariance dans la même interface, mais pour différents paramètres de type, comme indiqué dans l’exemple de code suivant.

Interface IVariant(Of Out R, In A)
    Function GetSomething() As R
    Sub SetSomething(ByVal sampleArg As A)
    Function GetSetSomething(ByVal sampleArg As A) As R
End Interface

Dans Visual Basic, vous ne pouvez pas déclarer d’événements dans des interfaces variant sans spécifier le type de délégué. En outre, une interface variant ne peut pas avoir de classes imbriquées, d’énumérations ou de structures, mais elle peut avoir des interfaces imbriquées. Ceci est illustré dans le code suivant.

Interface ICovariant(Of Out R)
    ' The following statement generates a compiler error.
    ' Event SampleEvent()
    ' The following statement specifies the delegate type and
    ' does not generate an error.
    Event AnotherEvent As EventHandler

    ' The following statements generate compiler errors,
    ' because a variant interface cannot have
    ' nested enums, classes, or structures.

    'Enum SampleEnum : test : End Enum
    'Class SampleClass : End Class
    'Structure SampleStructure : Dim value As Integer : End Structure

    ' Variant interfaces can have nested interfaces.
    Interface INested : End Interface
End Interface

Implémentation d’interfaces génériques variant

Vous implémentez des interfaces génériques variant dans des classes à l’aide de la même syntaxe que celle utilisée pour les interfaces invariantes. L’exemple de code suivant montre comment implémenter une interface covariante dans une classe générique.

Interface ICovariant(Of Out R)
    Function GetSomething() As R
End Interface

Class SampleImplementation(Of R)
    Implements ICovariant(Of R)
    Public Function GetSomething() As R _
    Implements ICovariant(Of R).GetSomething
        ' Some code.
    End Function
End Class

Les classes qui implémentent des interfaces variants sont invariantes. Par exemple, prenons le code suivant.

 The interface is covariant.
Dim ibutton As ICovariant(Of Button) =
    New SampleImplementation(Of Button)
Dim iobj As ICovariant(Of Object) = ibutton

' The class is invariant.
Dim button As SampleImplementation(Of Button) =
    New SampleImplementation(Of Button)
' The following statement generates a compiler error
' because classes are invariant.
' Dim obj As SampleImplementation(Of Object) = button

Extension des interfaces génériques variant

Lorsque vous étendez une interface générique de variante, vous devez utiliser les mots clés et in les out mots clés pour spécifier explicitement si l’interface dérivée prend en charge la variance. Le compilateur ne déduit pas la variance de l’interface en cours d’extension. Par exemple, considérez les interfaces suivantes.

Interface ICovariant(Of Out T)
End Interface

Interface IInvariant(Of T)
    Inherits ICovariant(Of T)
End Interface

Interface IExtCovariant(Of Out T)
    Inherits ICovariant(Of T)
End Interface

Dans l’interface Invariant(Of T) , le paramètre T de type générique est invariant, tandis que dans IExtCovariant (Of Out T)le paramètre de type est covariant, bien que les deux interfaces étendent la même interface. La même règle est appliquée aux paramètres de type générique contravariant.

Vous pouvez créer une interface qui étend à la fois l’interface où le paramètre T de type générique est covariant et l’interface où il est contravariant si dans l’interface d’extension, le paramètre T de type générique est invariant. Ceci est illustré dans l’exemple de code suivant.

Interface ICovariant(Of Out T)
End Interface

Interface IContravariant(Of In T)
End Interface

Interface IInvariant(Of T)
    Inherits ICovariant(Of T), IContravariant(Of T)
End Interface

Toutefois, si un paramètre T de type générique est déclaré covariant dans une interface, vous ne pouvez pas le déclarer contravariant dans l’interface d’extension, ou inversement. Ceci est illustré dans l’exemple de code suivant.

Interface ICovariant(Of Out T)
End Interface

' The following statements generate a compiler error.
' Interface ICoContraVariant(Of In T)
'     Inherits ICovariant(Of T)
' End Interface

Éviter l’ambiguïté

Lorsque vous implémentez des interfaces génériques de variantes, la variance peut parfois entraîner une ambiguïté. Cela doit être évité.

Par exemple, si vous implémentez explicitement la même interface générique variant avec différents paramètres de type générique dans une classe, elle peut créer une ambiguïté. Le compilateur ne génère pas d’erreur dans ce cas, mais il n’est pas spécifié quelle implémentation d’interface sera choisie au moment de l’exécution. Cela peut entraîner des bogues subtils dans votre code. Considérez l’exemple de code suivant.

Remarque

Avec Option Strict Off, Visual Basic génère un avertissement du compilateur lorsqu’il existe une implémentation d’interface ambiguë. Avec Option Strict On, Visual Basic génère une erreur du compilateur.

' Simple class hierarchy.
Class Animal
End Class

Class Cat
    Inherits Animal
End Class

Class Dog
    Inherits Animal
End Class

' This class introduces ambiguity
' because IEnumerable(Of Out T) is covariant.
Class Pets
    Implements IEnumerable(Of Cat), IEnumerable(Of Dog)

    Public Function GetEnumerator() As IEnumerator(Of Cat) _
        Implements IEnumerable(Of Cat).GetEnumerator
        Console.WriteLine("Cat")
        ' Some code.
    End Function

    Public Function GetEnumerator1() As IEnumerator(Of Dog) _
        Implements IEnumerable(Of Dog).GetEnumerator
        Console.WriteLine("Dog")
        ' Some code.
    End Function

    Public Function GetEnumerator2() As IEnumerator _
        Implements IEnumerable.GetEnumerator
        ' Some code.
    End Function
End Class

Sub Main()
    Dim pets As IEnumerable(Of Animal) = New Pets()
    pets.GetEnumerator()
End Sub

Dans cet exemple, il n’est pas spécifié comment la pets.GetEnumerator méthode choisit entre Cat et Dog. Cela peut entraîner des problèmes dans votre code.

Voir aussi