Partager via


Méthodes d’extension (Visual Basic)

Les méthodes d’extension permettent aux développeurs d’ajouter des fonctionnalités personnalisées aux types de données déjà définis sans créer de nouveau type dérivé. Les méthodes d’extension permettent d’écrire une méthode qui peut être appelée comme s’il s’agissait d’une méthode d’instance du type existant.

Remarques

Une méthode d’extension ne peut être qu’une Sub procédure ou une Function procédure. Vous ne pouvez pas définir une propriété d’extension, un champ ou un événement. Toutes les méthodes d’extension doivent être marquées avec l’attribut <Extension> d’extension de l’espace System.Runtime.CompilerServices de noms et doivent être définies dans un module. Si une méthode d’extension est définie en dehors d’un module, le compilateur Visual Basic génère une erreur BC36551, « Les méthodes d’extension ne peuvent être définies que dans les modules ».

Le premier paramètre d’une définition de méthode d’extension spécifie le type de données étendu par la méthode. Lorsque la méthode est exécutée, le premier paramètre est lié à l’instance du type de données qui appelle la méthode.

L’attribut Extension ne peut être appliqué qu’à un Visual Basic Module, Subou Function. Si vous l’appliquez à un ou à un ClassStructure, le compilateur Visual Basic génère des erreurs BC36550, l’attribut « Extension » ne peut être appliqué qu’aux déclarations « Module », « Sub » ou « Function ».

Exemple :

L’exemple suivant définit une Print extension au type de String données. La méthode utilise Console.WriteLine pour afficher une chaîne. Le paramètre de la Print méthode, aStringétablit que la méthode étend la String classe.

Imports System.Runtime.CompilerServices

Module StringExtensions

    <Extension()> 
    Public Sub Print(ByVal aString As String)
        Console.WriteLine(aString)
    End Sub

End Module

Notez que la définition de la méthode d’extension est marquée avec l’attribut <Extension()>d’extension . Marquage du module dans lequel la méthode est définie est facultatif, mais chaque méthode d’extension doit être marquée. System.Runtime.CompilerServices doit être importé pour accéder à l’attribut d’extension.

Les méthodes d’extension peuvent être déclarées uniquement dans les modules. En règle générale, le module dans lequel une méthode d’extension est définie n’est pas le même que celui dans lequel elle est appelée. Au lieu de cela, le module qui contient la méthode d’extension est importé, s’il doit l’être, pour l’intégrer dans l’étendue. Une fois que le module qui contient Print est dans l’étendue, la méthode peut être appelée comme s’il s’agissait d’une méthode d’instance ordinaire qui ne prend aucun argument, par ToUpperexemple :

Module Class1

    Sub Main()

        Dim example As String = "Hello"
        ' Call to extension method Print.
        example.Print()

        ' Call to instance method ToUpper.
        example.ToUpper()
        example.ToUpper.Print()

    End Sub

End Module

L’exemple suivant, PrintAndPunctuateest également une extension à String, cette fois définie avec deux paramètres. Le premier paramètre, aStringétablit que la méthode d’extension s’étend String. Le deuxième paramètre, puncest destiné à être une chaîne de marques de ponctuation passées en tant qu’argument lorsque la méthode est appelée. La méthode affiche la chaîne suivie des marques de ponctuation.

<Extension()> 
Public Sub PrintAndPunctuate(ByVal aString As String, 
                             ByVal punc As String)
    Console.WriteLine(aString & punc)
End Sub

La méthode est appelée en envoyant un argument de chaîne pour punc: example.PrintAndPunctuate(".")

L’exemple suivant montre Print et définit et PrintAndPunctuate appelé. System.Runtime.CompilerServices est importé dans le module de définition pour permettre l’accès à l’attribut d’extension.

Imports System.Runtime.CompilerServices

Module StringExtensions

    <Extension()>
    Public Sub Print(aString As String)
        Console.WriteLine(aString)
    End Sub

    <Extension()>
    Public Sub PrintAndPunctuate(aString As String, punc As String)
        Console.WriteLine(aString & punc)
    End Sub
End Module

Ensuite, les méthodes d’extension sont introduites dans l’étendue et appelées :

Imports ConsoleApplication2.StringExtensions

Module Module1

    Sub Main()
        Dim example As String = "Example string"
        example.Print()

        example = "Hello"
        example.PrintAndPunctuate(".")
        example.PrintAndPunctuate("!!!!")
    End Sub
End Module

Tout ce qui est nécessaire pour pouvoir exécuter ces méthodes d’extension ou similaires est qu’elles sont dans l’étendue. Si le module qui contient une méthode d’extension est dans l’étendue, il est visible dans IntelliSense et peut être appelé comme s’il s’agissait d’une méthode d’instance ordinaire.

Notez que lorsque les méthodes sont appelées, aucun argument n’est envoyé pour le premier paramètre. Le paramètre aString dans les définitions de méthode précédentes est lié à example, l’instance de String ce dernier les appelle. Le compilateur utilise example comme argument envoyé au premier paramètre.

Si une méthode d’extension est appelée pour un objet défini Nothingsur , la méthode d’extension s’exécute. Cela ne s’applique pas aux méthodes d’instance ordinaires. Vous pouvez rechercher Nothing explicitement dans la méthode d’extension.

Types pouvant être étendus

Vous pouvez définir une méthode d’extension sur la plupart des types qui peuvent être représentés dans une liste de paramètres Visual Basic, y compris les éléments suivants :

  • Classes (types de référence)
  • Structures (types valeur)
  • Interfaces
  • Délégués
  • Arguments ByRef et ByVal
  • Paramètres de méthode générique
  • Tableaux

Étant donné que le premier paramètre spécifie le type de données étendu par la méthode d’extension, il est requis et ne peut pas être facultatif. Pour cette raison, Optional les paramètres et ParamArray les paramètres ne peuvent pas être le premier paramètre de la liste des paramètres.

Les méthodes d’extension ne sont pas prises en compte dans la liaison tardive. Dans l’exemple suivant, l’instruction anObject.PrintMe() déclenche une MissingMemberException exception, la même exception que celle que vous verrez si la deuxième PrintMe définition de méthode d’extension a été supprimée.

Option Strict Off
Imports System.Runtime.CompilerServices

Module Module4

    Sub Main()
        Dim aString As String = "Initial value for aString"
        aString.PrintMe()

        Dim anObject As Object = "Initial value for anObject"
        ' The following statement causes a run-time error when Option
        ' Strict is off, and a compiler error when Option Strict is on.
        'anObject.PrintMe()
    End Sub

    <Extension()> 
    Public Sub PrintMe(ByVal str As String)
        Console.WriteLine(str)
    End Sub

    <Extension()> 
    Public Sub PrintMe(ByVal obj As Object)
        Console.WriteLine(obj)
    End Sub

End Module

Meilleures pratiques

Les méthodes d’extension offrent un moyen pratique et puissant d’étendre un type existant. Toutefois, pour les utiliser avec succès, il existe quelques points à prendre en compte. Ces considérations s’appliquent principalement aux auteurs de bibliothèques de classes, mais elles peuvent affecter toute application qui utilise des méthodes d’extension.

En règle générale, les méthodes d’extension que vous ajoutez aux types que vous ne possédez pas sont plus vulnérables que les méthodes d’extension ajoutées aux types que vous contrôlez. Un certain nombre de choses peuvent se produire dans les classes que vous ne possédez pas qui peuvent interférer avec vos méthodes d’extension.

  • Si un membre d’instance accessible existe qui a une signature compatible avec les arguments de l’instruction appelante, sans conversions restrictives requises d’argument en paramètre, la méthode d’instance sera utilisée en préférence pour toute méthode d’extension. Par conséquent, si une méthode d’instance appropriée est ajoutée à une classe à un moment donné, un membre d’extension existant sur lequel vous vous appuyez peut devenir inaccessible.

  • L’auteur d’une méthode d’extension ne peut pas empêcher d’autres programmeurs d’écrire des méthodes d’extension en conflit qui peuvent avoir la priorité sur l’extension d’origine.

  • Vous pouvez améliorer la robustesse en plaçant les méthodes d’extension dans leur propre espace de noms. Les consommateurs de votre bibliothèque peuvent ensuite inclure un espace de noms ou l’exclure, ou sélectionner parmi les espaces de noms, séparément du reste de la bibliothèque.

  • Il peut être plus sûr d’étendre les interfaces qu’il ne s’agit d’étendre des classes, en particulier si vous ne possédez pas l’interface ou la classe. Une modification dans une interface affecte chaque classe qui l’implémente. Par conséquent, l’auteur peut être moins susceptible d’ajouter ou de modifier des méthodes dans une interface. Toutefois, si une classe implémente deux interfaces qui ont des méthodes d’extension avec la même signature, aucune méthode d’extension n’est visible.

  • Étendez le type le plus spécifique que vous pouvez. Dans une hiérarchie de types, si vous sélectionnez un type à partir duquel de nombreux autres types sont dérivés, il existe des couches de possibilités pour l’introduction de méthodes d’instance ou d’autres méthodes d’extension susceptibles d’interférer avec le vôtre.

Méthodes d’extension, méthodes d’instance et propriétés

Lorsqu’une méthode d’instance dans l’étendue a une signature compatible avec les arguments d’une instruction appelante, la méthode d’instance est choisie en préférence pour n’importe quelle méthode d’extension. La méthode d’instance est prioritaire même si la méthode d’extension est une meilleure correspondance. Dans l’exemple suivant, ExampleClass contient une méthode d’instance nommée ExampleMethod qui a un paramètre de type Integer. La méthode ExampleMethod d’extension s’étend ExampleClasset a un paramètre de type Long.

Class ExampleClass
    ' Define an instance method named ExampleMethod.
    Public Sub ExampleMethod(ByVal m As Integer)
        Console.WriteLine("Instance method")
    End Sub
End Class

<Extension()> 
Sub ExampleMethod(ByVal ec As ExampleClass, 
                  ByVal n As Long)
    Console.WriteLine("Extension method")
End Sub

Le premier appel au ExampleMethod code suivant appelle la méthode d’extension, car arg1 il est Long compatible uniquement avec le Long paramètre de la méthode d’extension. Le deuxième appel à avoir ExampleMethod un Integer argument, arg2et il appelle la méthode d’instance.

Sub Main()
    Dim example As New ExampleClass
    Dim arg1 As Long = 10
    Dim arg2 As Integer = 5

    ' The following statement calls the extension method.
    example.exampleMethod(arg1)
    ' The following statement calls the instance method.
    example.exampleMethod(arg2)
End Sub

À présent, inversez les types de données des paramètres dans les deux méthodes :

Class ExampleClass
    ' Define an instance method named ExampleMethod.
    Public Sub ExampleMethod(ByVal m As Long)
        Console.WriteLine("Instance method")
    End Sub
End Class

<Extension()> 
Sub ExampleMethod(ByVal ec As ExampleClass, 
                  ByVal n As Integer)
    Console.WriteLine("Extension method")
End Sub

Cette fois, le code dans appelle Main la méthode d’instance deux fois. Cela est dû au fait que les deux arg1 et arg2 ont une conversion étendue en Long, et la méthode d’instance est prioritaire sur la méthode d’extension dans les deux cas.

Sub Main()
    Dim example As New ExampleClass
    Dim arg1 As Long = 10
    Dim arg2 As Integer = 5

    ' The following statement calls the instance method.
    example.ExampleMethod(arg1)
    ' The following statement calls the instance method.
    example.ExampleMethod(arg2)
End Sub

Par conséquent, une méthode d’extension ne peut pas remplacer une méthode d’instance existante. Toutefois, lorsqu’une méthode d’extension porte le même nom qu’une méthode d’instance, mais que les signatures ne sont pas en conflit, les deux méthodes sont accessibles. Par exemple, si la classe ExampleClass contient une méthode nommée ExampleMethod qui ne prend aucun argument, les méthodes d’extension portant le même nom, mais différentes signatures sont autorisées, comme indiqué dans le code suivant.

Imports System.Runtime.CompilerServices

Module Module3

    Sub Main()
        Dim ex As New ExampleClass
        ' The following statement calls the extension method.
        ex.ExampleMethod("Extension method")
        ' The following statement calls the instance method.
        ex.ExampleMethod()
    End Sub

    Class ExampleClass
        ' Define an instance method named ExampleMethod.
        Public Sub ExampleMethod()
            Console.WriteLine("Instance method")
        End Sub
    End Class

    <Extension()> 
    Sub ExampleMethod(ByVal ec As ExampleClass, 
                  ByVal stringParameter As String)
        Console.WriteLine(stringParameter)
    End Sub

End Module

La sortie de ce code est la suivante :

Extension method
Instance method

La situation est plus simple avec les propriétés : si une méthode d’extension porte le même nom qu’une propriété de la classe qu’elle étend, la méthode d’extension n’est pas visible et n’est pas accessible.

Priorité des méthodes d’extension

Lorsque deux méthodes d’extension qui ont des signatures identiques sont dans l’étendue et accessibles, celle avec une priorité plus élevée est appelée. La priorité d’une méthode d’extension est basée sur le mécanisme utilisé pour placer la méthode dans l’étendue. La liste suivante montre la hiérarchie de précédence, de la plus haute à la plus basse.

  1. Méthodes d’extension définies à l’intérieur du module actuel.

  2. Méthodes d’extension définies à l’intérieur des types de données dans l’espace de noms actuel ou l’un de ses parents, avec des espaces de noms enfants ayant une priorité plus élevée que les espaces de noms parents.

  3. Méthodes d’extension définies à l’intérieur de n’importe quel type importe dans le fichier actif.

  4. Méthodes d’extension définies à l’intérieur d’un espace de noms importe dans le fichier actif.

  5. Méthodes d’extension définies à l’intérieur d’une importation de type au niveau du projet.

  6. Méthodes d’extension définies dans les importations d’espaces de noms au niveau du projet.

Si la priorité ne résout pas l’ambiguïté, vous pouvez utiliser le nom complet pour spécifier la méthode que vous appelez. Si la Print méthode de l’exemple précédent est définie dans un module nommé StringExtensions, le nom complet est StringExtensions.Print(example) au lieu de example.Print().

Voir aussi