Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
Os métodos de extensão permitem que os desenvolvedores adicionem funcionalidade personalizada aos tipos de dados que já estão definidos sem criar um novo tipo derivado. Os métodos de extensão possibilitam a gravação de um método que pode ser chamado como se fosse um método de instância do tipo existente.
Observações
Um método de extensão pode ser apenas um procedimento Sub
ou um procedimento Function
. Não é possível definir uma propriedade de extensão, um campo ou um evento. Todos os métodos de extensão devem ser marcados com o atributo <Extension>
de extensão do System.Runtime.CompilerServices namespace e devem ser definidos em um Módulo. Se um método de extensão for definido fora de um módulo, o compilador do Visual Basic gerará um erro BC36551" "Os métodos de extensão só poderão ser definidos em módulos".
O primeiro parâmetro em uma definição de método de extensão especifica qual tipo de dados o método estende. Quando o método é executado, o primeiro parâmetro é associado à instância do tipo de dados que invoca o método.
O Extension
atributo só pode ser aplicado a um Visual Basic Module
Sub
ou Function
. Se você a aplicar a um Class
ou a um Structure
, o compilador do Visual Basic gerará o erro BC36550, "'Extension' pode ser aplicado somente às declarações 'Module', 'Sub' ou 'Function'".
Exemplo
O exemplo a seguir define uma Print
extensão para o String tipo de dados. O método usa Console.WriteLine
para exibir uma string. O parâmetro do Print
método estabelece aString
que o método estende a String classe.
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 <Extension()>
de extensão. Marcar o módulo no qual o método é definido é opcional, mas cada método de extensão deve ser marcado.
System.Runtime.CompilerServices deve ser importado para acessar o atributo de extensão.
Os métodos de extensão só podem ser declarados em módulos. Normalmente, o módulo no qual um método de extensão é definido não é o mesmo módulo que aquele no qual ele é chamado. Em vez disso, o módulo que contém o método de extensão é importado, se necessário, para colocá-lo no escopo. Depois que o módulo que contém Print
está no escopo, o método pode ser chamado como se fosse um método de instância comum que não aceita argumentos, como 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 próximo exemplo, PrintAndPunctuate
também é uma extensão para String, desta vez definida com dois parâmetros. O primeiro parâmetro, aString
estabelece que o método de extensão se estende String. O segundo parâmetro, punc
, destina-se a ser uma cadeia de caracteres de marcas de pontuação que é passada como um argumento quando o método é chamado. O método exibe a cadeia de caracteres seguida pelas marcas de pontuação.
<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(".")
O exemplo a seguir mostra Print
e PrintAndPunctuate
definidos e chamados.
System.Runtime.CompilerServices é importado no módulo de definição para habilitar o acesso ao atributo de extensão.
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 colocados no escopo e chamados:
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 estes métodos de extensão ou semelhantes é que estejam no escopo. Se o módulo que contém um método de extensão estiver no escopo, ele estará visível no IntelliSense e poderá ser chamado como se fosse um método de instância comum.
Observe que, quando os métodos são invocados, nenhum argumento é enviado para o primeiro parâmetro. O parâmetro aString
nas definições de método anteriores está associado à example
, a instância de String
que as chama. O compilador usará example
como o argumento enviado para o primeiro parâmetro.
Se um método de extensão for chamado para um objeto definido como Nothing
, o método de extensão será executado. Isso não se aplica a métodos de instância comuns. Você pode verificar Nothing
explicitamente no método de extensão.
Tipos que podem ser estendidos
Você pode definir um método de extensão na maioria dos tipos que podem ser representados em uma lista de parâmetros do Visual Basic, incluindo o seguinte:
- Classes (tipos de referência)
- Estruturas (tipos de valor)
- Interfaces
- Delegados
- Argumentos ByRef e ByVal
- Parâmetros de método genérico
- matrizes
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. Por esse motivo, Optional
parâmetros e ParamArray
parâmetros não podem ser o primeiro parâmetro na lista de parâmetros.
Os métodos de extensão não são considerados na associação tardia. No exemplo a seguir, a instrução anObject.PrintMe()
gera uma MissingMemberException exceção, a mesma exceção que você verá se a definição do segundo PrintMe
método de extensão foi excluída.
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 recomendadas
Os métodos de extensão fornecem uma maneira conveniente e poderosa de estender um tipo existente. No entanto, para usá-los com êxito, há alguns pontos a serem considerados. Essas considerações se aplicam principalmente aos autores de bibliotecas de classes, mas podem afetar qualquer aplicativo que use métodos de extensão.
Geralmente, os métodos de extensão que você adiciona aos tipos que você não possui são mais vulneráveis do que os métodos de extensão adicionados aos tipos que você controla. Várias coisas podem ocorrer em classes que você não possui que podem interferir em seus métodos de extensão.
Se houver algum membro de instância acessível que tenha uma assinatura compatível com os argumentos na instrução de chamada, sem conversões de restrição necessárias de argumento para parâmetro, o método de instância será usado em preferência para qualquer método de extensão. Portanto, se um método de instância apropriado for adicionado a uma classe em algum momento, um membro de extensão existente no qual você depende poderá se tornar inacessível.
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.
Você pode melhorar a robustez colocando métodos de extensão em seu próprio namespace. Os consumidores da biblioteca podem incluir um namespace ou excluí-lo ou selecionar entre namespaces, separadamente do restante da biblioteca.
Pode ser mais seguro estender interfaces do que estender classes, especialmente se você não possui a interface ou a classe. Uma alteração em uma interface afeta todas as classes que a implementam. Portanto, o autor pode ser menos propenso a adicionar ou alterar métodos em uma 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 ficará visível.
Estenda o tipo mais específico que puder. Em uma hierarquia de tipos, se você selecionar um tipo do qual muitos outros tipos são derivados, há camadas de possibilidades para a introdução de métodos de instância ou outros métodos de extensão que podem interferir no seu.
Métodos de extensão, métodos de instância e propriedades
Quando um método de instância dentro do escopo tem uma assinatura compatível com os argumentos de uma instrução de chamada, o método de instância é escolhido em preferência a qualquer método de extensão. O método de instância tem precedência mesmo que o método de extensão seja uma correspondência melhor. No exemplo a seguir, ExampleClass
contém um método de instância chamado ExampleMethod
que tem um parâmetro do tipo Integer
. O método ExampleMethod
de extensão se estende ExampleClass
e tem um parâmetro do tipo 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. A segunda chamada para ExampleMethod
tem um argumento Integer
, arg2
, e chama o método de instância.
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:
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 ambas as vezes. 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.
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. No entanto, quando um método de extensão tem o mesmo nome de um método de instância, mas as assinaturas não entram em conflito, ambos os métodos podem ser acessados. Por exemplo, se a classe ExampleClass
contiver um método nomeado ExampleMethod
que não usa argumentos, métodos de extensão com o mesmo nome, mas assinaturas diferentes são permitidos, conforme mostrado no código a seguir.
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:
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 ele estende, o método de extensão não estará visível e não poderá ser acessado.
Precedência do método de extensão
Quando dois métodos de extensão que têm assinaturas idênticas estiverem no escopo e acessíveis, aquele com precedência mais alta será invocado. A precedência de um método de extensão baseia-se no mecanismo usado para colocar o método no escopo. A lista a seguir mostra a hierarquia de precedência, da mais alta para a mais baixa.
Métodos de extensão definidos dentro do módulo atual.
Métodos de extensão definidos dentro de tipos de dados no namespace atual ou em qualquer um dos pais dele, com namespaces filho com precedência maior do que namespaces pai.
Métodos de extensão definidos dentro de qualquer tipo de importação no arquivo atual.
Métodos de extensão definidos dentro de qualquer importação de namespace no arquivo atual.
Métodos de extensão definidos dentro de qualquer importação de tipo no nível do projeto.
Métodos de extensão definidos dentro de qualquer importação de namespace no nível do projeto.
Se a precedência não resolver a ambiguidade, você poderá usar o nome totalmente qualificado para especificar o método que você está chamando. Se o Print
método no exemplo anterior for definido em um módulo chamado StringExtensions
, o nome totalmente qualificado será StringExtensions.Print(example)
em vez de example.Print()
.
Consulte também
- System.Runtime.CompilerServices
- ExtensionAttribute
- Métodos de Extensão
- Instrução do Módulo
- Parâmetros e Argumentos de Procedimento
- parâmetros opcionais
- Matrizes de Parâmetros
- Visão geral de atributos
- Escopo no Visual Basic