Share via


Méthodes d'extension (Visual Basic)

Mise à jour : novembre 2007

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

Notes

Une méthode d'extension peut uniquement être une procédure Sub ou Function. 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 d'extension, <Extension()>, à partir de l'espace de noms System.Runtime.CompilerServices.

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.

Exemple

Description

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

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 méthode d'extension est marquée avec l'attribut d'extension <Extension()>. Le 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 ne peuvent être déclarées que dans des modules. En général, 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. À la place, le module qui contient la méthode d'extension est importé, si nécessaire, pour être placé dans la portée. Une fois que le module qui contient Print est placé dans la portée, la méthode peut être appelée comme une méthode d'instance ordinaire qui ne prend pas d'arguments, telle que ToUpper :

Imports ConsoleApplication2.StringExtensions

Module Module1

    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, PrintAndPunctuate, est également une extension vers String, cette fois définie avec deux paramètres. Le premier paramètre, aString, indique que la méthode d'extension étend String. Le deuxième paramètre, punc, est une chaîne de signes de ponctuation passée comme un argument lorsque la méthode est appelée. La méthode affiche la chaîne suivie des signes 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 affiche Print et PrintAndPunctuate définis et appelés. System.Runtime.CompilerServices est importé dans le module de définition afin d'activer l'accès à l'attribut d'extension.

Code

Imports System.Runtime.CompilerServices

Module StringExtensions

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

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

End Module

Ensuite, les méthodes d'extension sont placées dans la portée 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

Commentaires

Pour que ces méthodes d'extension ou des méthodes similaires puissent être exécutées, il faut qu'elles soient placées dans la portée. Si le module qui contient une méthode d'extension se trouve dans la portée, il apparaît dans IntelliSense et peut être appelé comme 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 qui les appelle. Le compilateur utilise example comme argument envoyé au premier paramètre.

Types qui peuvent être étendus

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

  • Classes (types 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, les paramètres Optional et ParamArray ne peuvent pas être le premier paramètre de la liste de paramètres.

Meilleures pratiques

Les méthodes d'extension fournissent une façon pratique et puissante d'étendre un type existant. Toutefois, pour les utiliser correctement, certains points doivent être considérés. Ces considérations s'appliquent principalement aux auteurs de bibliothèques de classes, mais elles peuvent affecter n'importe quelle application utilisant des méthodes d'extension.

La plupart du temps, 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. Plusieurs événements peuvent se produire dans les classes que vous ne possédez pas, pouvant interférer avec vos méthodes d'extension.

  • Si un membre d'instance accessible existe avec une signature compatible avec les arguments dans l'instruction d'appel, sans conversions restrictives requises d'un argument en un paramètre, la méthode d'instance sera utilisée de préférence à 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 auquel vous vous conformez 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 être prioritaires 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 ou exclure un espace de noms, ou réaliser une sélection parmi les espaces de noms, de manière séparée pour le reste de la bibliothèque.

  • Il peut être plus sûr d'étendre les interfaces plutôt que d'étendre les classes, notamment si vous ne possédez pas l'interface ou la classe. Une modification dans une interface affecte chaque classe implémentée. Par conséquent, l'auteur peut être moins tenté d'ajouter ou de modifier les 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 possible. Dans une hiérarchie de types, si vous sélectionnez un type à partir duquel d'autres types sont dérivés, l'introduction de méthodes d'instance ou d'autres méthodes d'extension peuvent interférer avec les vôtres.

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

Lorsqu'une méthode d'instance dans la portée a une signature qui est compatible avec les arguments d'une instruction appelante, la méthode d'instance est choisie dans la préférence à toute méthode d'extension. La méthode d'instance a même la priorité 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 d'extension ExampleMethod é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 à ExampleMethod dans le code suivant appelle la méthode d'extension, parce que arg1 est de type Long et est compatible uniquement avec le paramètre Long dans la méthode d'extension. Le deuxième appel à ExampleMethod a un argument Integer, arg2, et 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

Maintenant 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 Main appelle la méthode d'instance les deux fois. C'est parce que arg1 et arg2 ont une conversion étendue à Long, et que 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 a le même nom qu'une méthode d'instance, les deux méthodes sont accessibles tant que les signatures ne sont pas en conflit. Par exemple, si la classe ExampleClass contient une méthode nommée ExampleMethod qui ne prend pas d'arguments, une méthode d'extension portant le même nom mais avec une signature différente est autorisée, comme illustré dans le code ci-après.

Imports ConsoleApplication2.ExtensionExample
Module Module1

    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

End Module
Imports System.Runtime.CompilerServices

' Define an extension method named ExampleMethod.
Module ExtensionExample
    <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 des propriétés : si une méthode d'extension a 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é de méthode d'extension

Lorsque deux méthodes d'extension ayant des signatures identiques se trouvent dans la portée et sont accessibles, la méthode prioritaire sera appelée. La priorité d'une méthode d'extension est basée sur le mécanisme utilisé pour placer la méthode dans la portée. La liste suivante présente la hiérarchie de priorité par ordre décroissant.

  1. Méthodes d'extension définies au sein du module actuel.

  2. Méthodes d'extension définies au sein de types de données dans l'espace de noms actuel ou dans l'un de ses parents, les espaces de noms enfants étant prioritaires sur les espaces de noms parents.

  3. Méthodes d'extension définies dans les importations de type dans le fichier en cours.

  4. Méthodes d'extension définies dans les importations d'espaces de noms dans le fichier en cours.

  5. Méthodes d'extension définies dans les importations 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 qualifié complet pour spécifier la méthode que vous appelez. Si la méthode Print dans l'exemple précédent est définie dans un module nommé StringExtensions, le nom qualifié complet est StringExtensions.Print(example) au lieu de example.Print().

Voir aussi

Tâches

Comment : définir des paramètres optionnels pour une procédure

Concepts

Application des attributs

Paramètres et arguments d'une procédure

Paramètres facultatifs

Tableaux de paramètres

Vue d'ensemble des attributs dans Visual Basic

Portée dans Visual Basic

Référence

System.Runtime.CompilerServices

Module, instruction

ExtensionAttribute