Métodos de extensão (Visual Basic)Extension Methods (Visual Basic)

Os métodos de extensão permitem que os desenvolvedores adicionem funcionalidade personalizada a tipos de dados que já estão definidos sem a criação de um novo tipo derivado.Extension methods enable developers to add custom functionality to data types that are already defined without creating a new derived type. Os métodos de extensão possibilitam escrever um método que pode ser chamado como se fosse um método de instância do tipo existente.Extension methods make it possible to write a method that can be called as if it were an instance method of the existing type.

ComentáriosRemarks

Um método de extensão pode ser apenas um procedimento Sub ou um procedimento Function.An extension method can be only a Sub procedure or a Function procedure. Você não pode definir uma propriedade de extensão, um campo ou um evento.You cannot define an extension property, field, or event. Todos os métodos de extensão devem ser marcados com o atributo de extensão <Extension> do namespace System.Runtime.CompilerServices e devem ser definidos em um módulo.All extension methods must be marked with the extension attribute <Extension> from the System.Runtime.CompilerServices namespace and must be defined in a Module. Se um método de extensão for definido fora de um módulo, o compilador Visual Basic gerará o erro BC36551, "os métodos de extensão podem ser definidos somente em módulos".If an extension method is defined outside a module, the Visual Basic compiler generates error BC36551, "Extension methods can be defined only in modules".

O primeiro parâmetro em uma definição de método de extensão especifica o tipo de dados que o método estende.The first parameter in an extension method definition specifies which data type the method extends. Quando o método é executado, o primeiro parâmetro é associado à instância do tipo de dados que invoca o método.When the method is run, the first parameter is bound to the instance of the data type that invokes the method.

O atributo Extension só pode ser aplicado a um Visual Basic Module, Subou Function.The Extension attribute can only be applied to a Visual Basic Module, Sub, or Function. Se você aplicá-lo a um Class ou a um Structure, o compilador Visual Basic gerará o erro BC36550, o atributo "extensão" poderá ser aplicado somente às declarações "módulo", "sub" ou "função".If you apply it to a Class or a Structure, the Visual Basic compiler generates error BC36550, "'Extension' attribute can be applied only to 'Module', 'Sub', or 'Function' declarations".

ExemploExample

O exemplo a seguir define uma extensão Print para o tipo de dados String.The following example defines a Print extension to the String data type. O método usa Console.WriteLine para exibir uma cadeia de caracteres.The method uses Console.WriteLine to display a string. O parâmetro do método Print, aString, estabelece que o método estende a classe String.The parameter of the Print method, aString, establishes that the method extends the String class.

Imports System.Runtime.CompilerServices

Module StringExtensions

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

End Module

Observe que a definição do método de extensão está marcada com o atributo de extensão <Extension()>.Notice that the extension method definition is marked with the extension attribute <Extension()>. Marcar o módulo no qual o método é definido é opcional, mas cada método de extensão deve ser marcado.Marking the module in which the method is defined is optional, but each extension method must be marked. System.Runtime.CompilerServices deve ser importada para acessar o atributo de extensão.System.Runtime.CompilerServices must be imported in order to access the extension attribute.

Os métodos de extensão podem ser declarados somente dentro de módulos.Extension methods can be declared only within modules. Normalmente, o módulo no qual um método de extensão é definido não é o mesmo módulo que aquele em que ele é chamado.Typically, the module in which an extension method is defined is not the same module as the one in which it is called. Em vez disso, o módulo que contém o método de extensão é importado, se precisar, para colocá-lo no escopo.Instead, the module that contains the extension method is imported, if it needs to be, to bring it into scope. Depois que o módulo que contém Print estiver no escopo, o método poderá ser chamado como se fosse um método de instância comum que não aceite nenhum argumento, como ToUpper:After the module that contains Print is in scope, the method can be called as if it were an ordinary instance method that takes no arguments, such as ToUpper:

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

O exemplo a seguir, PrintAndPunctuate, também é uma extensão para String, desta vez definido com dois parâmetros.The next example, PrintAndPunctuate, is also an extension to String, this time defined with two parameters. O primeiro parâmetro, aString, estabelece que o método de extensão estende String.The first parameter, aString, establishes that the extension method extends String. O segundo parâmetro, punc, deve ser uma cadeia de caracteres de sinais de pontuação que é passada como um argumento quando o método é chamado.The second parameter, punc, is intended to be a string of punctuation marks that is passed in as an argument when the method is called. O método exibe a cadeia de caracteres seguida pelas marcas de pontuação.The method displays the string followed by the punctuation marks.

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

O método é chamado enviando um argumento de cadeia de caracteres para punc: example.PrintAndPunctuate(".")The method is called by sending in a string argument for punc: example.PrintAndPunctuate(".")

O exemplo a seguir mostra Print e PrintAndPunctuate definidos e chamados.The following example shows Print and PrintAndPunctuate defined and called. System.Runtime.CompilerServices é importado no módulo de definição para habilitar o acesso ao atributo de extensão.System.Runtime.CompilerServices is imported in the definition module in order to enable access to the extension attribute.

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

Em seguida, os métodos de extensão são trazidos para o escopo e chamados:Next, the extension methods are brought into scope and called:

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

Tudo o que é necessário para poder executar esses ou métodos de extensão semelhantes é que eles estão no escopo.All that is required to be able to run these or similar extension methods is that they be in scope. Se o módulo que contém um método de extensão estiver no escopo, ele ficará visível no IntelliSense e poderá ser chamado como se fosse um método de instância comum.If the module that contains an extension method is in scope, it is visible in IntelliSense and can be called as if it were an ordinary instance method.

Observe que, quando os métodos são invocados, nenhum argumento é enviado para o primeiro parâmetro.Notice that when the methods are invoked, no argument is sent in for the first parameter. O parâmetro aString nas definições de método anteriores é associado a example, a instância de String que os chama.Parameter aString in the previous method definitions is bound to example, the instance of String that calls them. O compilador usará example como o argumento enviado para o primeiro parâmetro.The compiler will use example as the argument sent to the first parameter.

Se um método de extensão for chamado para um objeto definido como Nothing, o método de extensão será executado.If an extension method is called for an object that is set to Nothing, the extension method executes. Isso não se aplica a métodos de instância comuns.This does not apply to ordinary instance methods. Você pode verificar explicitamente se há Nothing no método de extensão.You can explicitly check for Nothing in the extension method.

Tipos que podem ser estendidosTypes that can be extended

Você pode definir um método de extensão na maioria dos tipos que podem ser representados em uma Visual Basic lista de parâmetros, incluindo o seguinte:You can define an extension method on most types that can be represented in a Visual Basic parameter list, including the following:

  • Classes (tipos de referência)Classes (reference types)
  • Estruturas (tipos de valor)Structures (value types)
  • InterfacesInterfaces
  • DelegadosDelegates
  • Argumentos ByRef e ByValByRef and ByVal arguments
  • Parâmetros de método genéricoGeneric method parameters
  • MatrizesArrays

Como o primeiro parâmetro especifica o tipo de dados que o método de extensão estende, ele é necessário e não pode ser opcional.Because the first parameter specifies the data type that the extension method extends, it is required and cannot be optional. Por esse motivo, parâmetros de Optional e parâmetros de ParamArray não podem ser o primeiro parâmetro na lista de parâmetros.For that reason, Optional parameters and ParamArray parameters cannot be the first parameter in the parameter list.

Os métodos de extensão não são considerados na associação tardia.Extension methods are not considered in late binding. No exemplo a seguir, a instrução anObject.PrintMe() gera uma exceção MissingMemberException, a mesma exceção que você veria se a segunda definição de método de extensão PrintMe fosse excluída.In the following example, the statement anObject.PrintMe() raises a MissingMemberException exception, the same exception you would see if the second PrintMe extension method definition were deleted.

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

Práticas recomendadasBest practices

Os métodos de extensão fornecem uma maneira conveniente e eficiente de estender um tipo existente.Extension methods provide a convenient and powerful way to extend an existing type. No entanto, para usá-los com êxito, há alguns pontos a serem considerados.However, to use them successfully, there are some points to consider. Essas considerações se aplicam principalmente a autores de bibliotecas de classes, mas podem afetar qualquer aplicativo que use métodos de extensão.These considerations apply mainly to authors of class libraries, but they might affect any application that uses extension methods.

Geralmente, os métodos de extensão que você adiciona aos tipos que não são de sua propriedade são mais vulneráveis do que os métodos de extensão adicionados aos tipos que você controla.Most generally, extension methods that you add to types that you do not own are more vulnerable than extension methods added to types that you control. Várias coisas podem ocorrer em classes que não são de sua propriedade que podem interferir nos métodos de extensão.A number of things can occur in classes you do not own that can interfere with your extension methods.

  • Se existir algum membro de instância acessível que tenha uma assinatura compatível com os argumentos na instrução de chamada, sem conversões redutoras necessárias do argumento para o parâmetro, o método de instância será usado em preferência para qualquer método de extensão.If any accessible instance member exists that has a signature that is compatible with the arguments in the calling statement, with no narrowing conversions required from argument to parameter, the instance method will be used in preference to any extension method. Portanto, se um método de instância apropriado for adicionado a uma classe em algum momento, um membro de extensão existente que você depende poderá se tornar inacessível.Therefore, if an appropriate instance method is added to a class at some point, an existing extension member that you rely on may become inaccessible.

  • O autor de um método de extensão não pode impedir que outros programadores gravem métodos de extensão conflitantes que possam ter precedência sobre a extensão original.The author of an extension method cannot prevent other programmers from writing conflicting extension methods that may have precedence over the original extension.

  • Você pode melhorar a robustez colocando métodos de extensão em seu próprio namespace.You can improve robustness by putting extension methods in their own namespace. Os consumidores da sua biblioteca podem, então, incluir um namespace ou excluí-lo ou selecionar entre namespaces, separadamente do restante da biblioteca.Consumers of your library can then include a namespace or exclude it, or select among namespaces, separately from the rest of the library.

  • Pode ser mais seguro estender interfaces do que estender classes, especialmente se você não possuir a interface ou classe.It may be safer to extend interfaces than it is to extend classes, especially if you do not own the interface or class. Uma alteração em uma interface afeta cada classe que a implementa.A change in an interface affects every class that implements it. Portanto, talvez seja menos provável que o autor adicione ou altere métodos em uma interface.Therefore, the author may be less likely to add or change methods in an interface. No entanto, se uma classe implementar duas interfaces que têm métodos de extensão com a mesma assinatura, nenhum método de extensão será visível.However, if a class implements two interfaces that have extension methods with the same signature, neither extension method is visible.

  • Estenda o tipo mais específico que você pode.Extend the most specific type you can. Em uma hierarquia de tipos, se você selecionar um tipo do qual muitos outros tipos são derivados, haverá camadas de possibilidades para a introdução de métodos de instância ou outros métodos de extensão que possam interferir com o seu.In a hierarchy of types, if you select a type from which many other types are derived, there are layers of possibilities for the introduction of instance methods or other extension methods that might interfere with yours.

Métodos de extensão, métodos de instância e propriedadesExtension methods, instance methods, and properties

Quando um método de instância no escopo tem uma assinatura que é compatível com os argumentos de uma instrução de chamada, o método de instância é escolhido de preferência para qualquer método de extensão.When an in-scope instance method has a signature that is compatible with the arguments of a calling statement, the instance method is chosen in preference to any extension method. O método de instância tem precedência mesmo se o método de extensão for uma correspondência melhor.The instance method has precedence even if the extension method is a better match. No exemplo a seguir, ExampleClass contém um método de instância chamado ExampleMethod que tem um parâmetro do tipo Integer.In the following example, ExampleClass contains an instance method named ExampleMethod that has one parameter of type Integer. O método de extensão ExampleMethod estende ExampleClasse tem um parâmetro do tipo Long.Extension method ExampleMethod extends ExampleClass, and has one parameter of 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

A primeira chamada para ExampleMethod no código a seguir chama o método de extensão, porque arg1 é Long e é compatível apenas com o parâmetro Long no método de extensão.The first call to ExampleMethod in the following code calls the extension method, because arg1 is Long and is compatible only with the Long parameter in the extension method. A segunda chamada para ExampleMethod tem um argumento Integer, arg2e chama o método de instância.The second call to ExampleMethod has an Integer argument, arg2, and it calls the instance method.

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

Agora inverta os tipos de dados dos parâmetros nos dois métodos:Now reverse the data types of the parameters in the two methods:

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

Desta vez, o código em Main chama o método de instância duas vezes.This time the code in Main calls the instance method both times. Isso ocorre porque tanto arg1 quanto arg2 têm uma conversão de ampliação para Long, e o método de instância tem precedência sobre o método de extensão em ambos os casos.This is because both arg1 and arg2 have a widening conversion to Long, and the instance method takes precedence over the extension method in both cases.

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

Portanto, um método de extensão não pode substituir um método de instância existente.Therefore, an extension method cannot replace an existing instance method. No entanto, quando um método de extensão tem o mesmo nome que um método de instância, mas as assinaturas não entram em conflito, ambos os métodos podem ser acessados.However, when an extension method has the same name as an instance method but the signatures do not conflict, both methods can be accessed. Por exemplo, se a classe ExampleClass contiver um método chamado ExampleMethod que não usa argumentos, os métodos de extensão com o mesmo nome, mas assinaturas diferentes, serão permitidos, conforme mostrado no código a seguir.For example, if class ExampleClass contains a method named ExampleMethod that takes no arguments, extension methods with the same name but different signatures are permitted, as shown in the following code.

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

A saída desse código é a seguinte:The output from this code is as follows:

Extension method
Instance method

A situação é mais simples com propriedades: se um método de extensão tiver o mesmo nome que uma propriedade da classe que estende, o método de extensão não será visível e não poderá ser acessado.The situation is simpler with properties: if an extension method has the same name as a property of the class it extends, the extension method is not visible and cannot be accessed.

Precedência do método de extensãoExtension method precedence

Quando dois métodos de extensão que têm assinaturas idênticas estiverem no escopo e acessíveis, aquele com maior precedência será invocado.When two extension methods that have identical signatures are in scope and accessible, the one with higher precedence will be invoked. A precedência de um método de extensão é baseada no mecanismo usado para colocar o método no escopo.An extension method's precedence is based on the mechanism used to bring the method into scope. A lista a seguir mostra a hierarquia de precedência, da mais alta para a mais baixa.The following list shows the precedence hierarchy, from highest to lowest.

  1. Métodos de extensão definidos dentro do módulo atual.Extension methods defined inside the current module.

  2. Métodos de extensão definidos dentro de tipos de dados no namespace atual ou qualquer um de seus pais, com namespaces filho com precedência maior do que namespaces pai.Extension methods defined inside data types in the current namespace or any one of its parents, with child namespaces having higher precedence than parent namespaces.

  3. Métodos de extensão definidos dentro de qualquer tipo importado no arquivo atual.Extension methods defined inside any type imports in the current file.

  4. Métodos de extensão definidos dentro de qualquer importação de namespace no arquivo atual.Extension methods defined inside any namespace imports in the current file.

  5. Métodos de extensão definidos dentro de qualquer importação de tipo de nível de projeto.Extension methods defined inside any project-level type imports.

  6. Métodos de extensão definidos dentro de qualquer importação de namespace em nível de projeto.Extension methods defined inside any project-level namespace imports.

Se a precedência não resolver a ambiguidade, você poderá usar o nome totalmente qualificado para especificar o método que está chamando.If precedence does not resolve the ambiguity, you can use the fully qualified name to specify the method that you are calling. Se o método Print no exemplo anterior for definido em um módulo chamado StringExtensions, o nome totalmente qualificado será StringExtensions.Print(example) em vez de example.Print().If the Print method in the earlier example is defined in a module named StringExtensions, the fully qualified name is StringExtensions.Print(example) instead of example.Print().

Consulte tambémSee also