Métodos de extensión (Visual Basic)Extension Methods (Visual Basic)

Métodos de extensión permiten a los desarrolladores agregar funcionalidad personalizada a los tipos de datos que ya están definidos sin crear un nuevo tipo derivado.Extension methods enable developers to add custom functionality to data types that are already defined without creating a new derived type. Métodos de extensión permiten escribir un método que se pueda llamar como si fuera un método de instancia del 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.

ComentariosRemarks

Un método de extensión solo puede ser un Sub procedimiento o un Function procedimiento.An extension method can be only a Sub procedure or a Function procedure. No se puede definir una propiedad de extensión, campo o evento.You cannot define an extension property, field, or event. Todos los métodos de extensión se deben marcar con el atributo de extensión <Extension()> desde el System.Runtime.CompilerServices espacio de nombres.All extension methods must be marked with the extension attribute <Extension()> from the System.Runtime.CompilerServices namespace.

El primer parámetro en una definición de método de extensión especifica qué tipo de datos extiende el método.The first parameter in an extension method definition specifies which data type the method extends. Cuando se ejecuta el método, el primer parámetro se enlaza a la instancia del tipo de datos que invoca el método.When the method is run, the first parameter is bound to the instance of the data type that invokes the method.

EjemploExample

DescripciónDescription

En el ejemplo siguiente se define un Print extensión a la String tipo de datos.The following example defines a Print extension to the String data type. Usa el método Console.WriteLine para mostrar una cadena.The method uses Console.WriteLine to display a string. El parámetro de la Print método aString, Establece que el método extiende la String clase.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

Tenga en cuenta que la definición del método de extensión se marca con el atributo de extensión <Extension()>.Notice that the extension method definition is marked with the extension attribute <Extension()>. Marcar el módulo en el que se define el método es opcional, pero se debe marcar cada método de extensión.Marking the module in which the method is defined is optional, but each extension method must be marked. System.Runtime.CompilerServices debe importarse con el fin de obtener acceso al atributo de extensión.System.Runtime.CompilerServices must be imported in order to access the extension attribute.

Métodos de extensión se pueden declarar únicamente dentro de los módulos.Extension methods can be declared only within modules. Normalmente, el módulo en el que se define un método de extensión no es el mismo módulo que lo en el que se llama.Typically, the module in which an extension method is defined is not the same module as the one in which it is called. En su lugar, se importa el módulo que contiene el método de extensión, si es necesario que, para volver a ponerlo en el ámbito.Instead, the module that contains the extension method is imported, if it needs to be, to bring it into scope. Después del módulo que contiene Print está en ámbito, se puede llamar al método como si fuera un método de instancia ordinario que no toma ningún 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

El ejemplo siguiente, PrintAndPunctuate, también es una extensión String, esta vez definida con dos parámetros.The next example, PrintAndPunctuate, is also an extension to String, this time defined with two parameters. El primer parámetro, aString, Establece que el método de extensión extiende String.The first parameter, aString, establishes that the extension method extends String. El segundo parámetro, punc, está pensado para ser una cadena de signos de puntuación que se pasa como argumento cuando se llama al método.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. El método muestra la cadena seguida de los signos de puntuación.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

Se llama al método enviando un argumento de cadena para punc: example.PrintAndPunctuate(".")The method is called by sending in a string argument for punc: example.PrintAndPunctuate(".")

El ejemplo siguiente muestra Print y PrintAndPunctuate definen y se invocan.The following example shows Print and PrintAndPunctuate defined and called. System.Runtime.CompilerServices se importa en el módulo de definición para permitir el acceso al atributo de extensión.System.Runtime.CompilerServices is imported in the definition module in order to enable access to the extension attribute.

CódigoCode

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  

A continuación, los métodos de extensión se incluyen en el ámbito y se llama.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  

ComentariosComments

Todo lo que son necesaria que sea capaz de ejecutar estos comandos o métodos de extensión similares es que ser en el ámbito.All that is required to be able to run these or similar extension methods is that they be in scope. Si el módulo que contiene un método de extensión está en ámbito, está visible en IntelliSense y pueden llamarse como si fuera un método de instancia normal.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.

Tenga en cuenta que cuando se invocan los métodos, se envía ningún argumento el primer parámetro.Notice that when the methods are invoked, no argument is sent in for the first parameter. Parámetro aString en el método anterior definiciones se enlaza a example, la instancia de String que los llama.Parameter aString in the previous method definitions is bound to example, the instance of String that calls them. El compilador usará example como el argumento que se envía al primer parámetro.The compiler will use example as the argument sent to the first parameter.

Si se llama a un método de extensión para un objeto que se establece en Nothing, ejecuta el método de extensión.If an extension method is called for an object that is set to Nothing, the extension method executes. Esto no es aplicable a los métodos normales de la instancia.This does not apply to ordinary instance methods. Puede comprobar explícitamente si Nothing en el método de extensión.You can explicitly check for Nothing in the extension method.

Tipos que se pueden extenderTypes That Can Be Extended

Puede definir un método de extensión en la mayoría de los tipos que se puede representar en una lista de parámetros de Visual Basic, incluidas las siguientes:You can define an extension method on most types that can be represented in a Visual Basic parameter list, including the following:

  • Clases (tipos de referencia)Classes (reference types)

  • Estructuras (tipos de valor)Structures (value types)

  • InterfacesInterfaces

  • DelegadosDelegates

  • Argumentos ByRef y ByValByRef and ByVal arguments

  • Parámetros de método genéricoGeneric method parameters

  • MatricesArrays

Puesto que el primer parámetro especifica el tipo de datos que el método de extensión extiende, es necesario y no puede ser opcional.Because the first parameter specifies the data type that the extension method extends, it is required and cannot be optional. Por ese motivo, Optional parámetros y ParamArray parámetros no pueden ser el primer parámetro de la lista de parámetros.For that reason, Optional parameters and ParamArray parameters cannot be the first parameter in the parameter list.

No se consideran los métodos de extensión de enlace más tarde.Extension methods are not considered in late binding. En el ejemplo siguiente, la instrucción anObject.PrintMe() provoca un MissingMemberException excepción, la misma excepción que vería si el segundo PrintMe definición de método de extensión se eliminaron.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

Procedimientos recomendadosBest Practices

Métodos de extensión proporcionan una manera eficaz y conveniente para extender un tipo existente.Extension methods provide a convenient and powerful way to extend an existing type. Sin embargo, para poder utilizarlos correctamente, hay algunos puntos a tener en cuenta.However, to use them successfully, there are some points to consider. Estas consideraciones se aplican principalmente a los autores de bibliotecas de clases, pero podrían afectar a cualquier aplicación que use métodos de extensión.These considerations apply mainly to authors of class libraries, but they might affect any application that uses extension methods.

Más en general, los métodos de extensión que se agregan a los tipos que no es propietario son más vulnerables a los métodos de extensión que se agregan a los tipos que usted 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. Pueden ocurrir varias cosas en las clases que no es propietario que pueden interferir con sus métodos de extensión.A number of things can occur in classes you do not own that can interfere with your extension methods.

  • Si existe cualquier miembro de instancia accesible con una firma que es compatible con los argumentos de la instrucción de llamada con ningún argumento necesarias al parámetro las conversiones de restricción, se usará el método de instancia con preferencia a cualquier método de extensión.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. Por lo tanto, si se agrega un método de instancia adecuado para una clase en algún momento, un miembro existente de extensión que dependen de sea inaccesible.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.

  • El autor de un método de extensión no puede impedir que otros programadores escribir métodos de extensión conflictivos que pueden tener prioridad sobre la extensión original.The author of an extension method cannot prevent other programmers from writing conflicting extension methods that may have precedence over the original extension.

  • Puede mejorar la solidez colocando los métodos de extensión en su propio espacio de nombres.You can improve robustness by putting extension methods in their own namespace. Los consumidores de la biblioteca, a continuación, pueden incluir un espacio de nombres o excluirlo o seleccionar entre los espacios de nombres, por separado del resto de la biblioteca.Consumers of your library can then include a namespace or exclude it, or select among namespaces, separately from the rest of the library.

  • Puede ser más seguro extender interfaces que es extender clases, especialmente si no posee la interfaz o clase.It may be safer to extend interfaces than it is to extend classes, especially if you do not own the interface or class. Un cambio en una interfaz afecta a todas las clases que lo implementa.A change in an interface affects every class that implements it. Por lo tanto, el autor puede ser menos probable agregar o cambiar métodos en una interfaz.Therefore, the author may be less likely to add or change methods in an interface. Sin embargo, si una clase implementa dos interfaces que tienen métodos de extensión con la misma firma, ninguno de los métodos extensión está visible.However, if a class implements two interfaces that have extension methods with the same signature, neither extension method is visible.

  • Extender el tipo más específico que puede.Extend the most specific type you can. En una jerarquía de tipos, si selecciona un tipo del que se derivan muchos otros tipos, hay niveles de posibilidades para la introducción de los métodos de instancia u otros métodos de extensión que podrían interferir con el suyo.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.

Propiedades, métodos de instancia y métodos de extensiónExtension Methods, Instance Methods, and Properties

Cuando un método de instancia en el ámbito tiene una firma que es compatible con los argumentos de una instrucción de llamada, se elige el método de instancia con preferencia a cualquier método de extensión.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. El método de instancia tiene prioridad incluso si el método de extensión es una coincidencia mejor.The instance method has precedence even if the extension method is a better match. En el ejemplo siguiente, ExampleClass contiene un método de instancia denominado ExampleMethod que tiene un parámetro de tipo Integer.In the following example, ExampleClass contains an instance method named ExampleMethod that has one parameter of type Integer. Método de extensión ExampleMethod extiende ExampleClass, y tiene un parámetro de 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

La primera llamada a ExampleMethod en el código siguiente llama al método de extensión, porque arg1 es Long y sólo es compatible con la Long parámetro del método de extensión.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. La segunda llamada a ExampleMethod tiene un Integer argumento, arg2, y llama al método de instancia.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

Ahora invierta los tipos de datos de los parámetros en los dos 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

Esta vez el código en Main llama al método dos veces.This time the code in Main calls the instance method both times. Esto es porque ambos arg1 y arg2 tiene una conversión de ampliación a Long, y el método de instancia tiene prioridad sobre el método de extensión en ambos 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

Por lo tanto, un método de extensión no puede reemplazar un método de instancia existente.Therefore, an extension method cannot replace an existing instance method. Sin embargo, cuando un método de extensión tiene el mismo nombre que un método de instancia pero las firmas no entran en conflicto, se pueden acceder ambos métodos.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 ejemplo, si clase ExampleClass contiene un método denominado ExampleMethod que toma ningún argumento, los métodos de extensión con el mismo nombre pero se permiten firmas diferentes, como se muestra en el código siguiente.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

La salida de este código es como sigue:The output from this code is as follows:

Extension method

Instance method

La situación es más fácil con propiedades: si un método de extensión tiene el mismo nombre que una propiedad de la clase que extiende, el método de extensión no está visible y no es accesible.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.

Prioridad del método de extensiónExtension Method Precedence

Cuando dos métodos de extensión que tienen firmas idénticas están en ámbito y accesible, se invocará el uno con prioridad más alta.When two extension methods that have identical signatures are in scope and accessible, the one with higher precedence will be invoked. Prioridad de un método de extensión se basa en el mecanismo utilizado para poner el método en el ámbito.An extension method's precedence is based on the mechanism used to bring the method into scope. En la lista siguiente se muestra la jerarquía de precedencia, de mayor a menor.The following list shows the precedence hierarchy, from highest to lowest.

  1. Métodos de extensión definidos dentro del módulo actual.Extension methods defined inside the current module.

  2. Métodos de extensión tipos definidos por el dentro de datos en el espacio de nombres actual o cualquiera de sus elementos primarios, con espacios de nombres secundarios tienen mayor prioridad que los espacios de nombres primario.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 extensión definidos dentro de cualquier tipo de importaciones en el archivo actual.Extension methods defined inside any type imports in the current file.

  4. Métodos de extensión definidos dentro de cualquier importación de espacio de nombres en el archivo actual.Extension methods defined inside any namespace imports in the current file.

  5. Métodos de extensión definidos dentro de cualquier tipo de nivel de proyecto de importación.Extension methods defined inside any project-level type imports.

  6. Métodos de extensión definidos dentro de cualquier importación de espacio de nombres de nivel de proyecto.Extension methods defined inside any project-level namespace imports.

Si la prioridad no resuelve la ambigüedad, puede usar el nombre completo para especificar el método que está llamando.If precedence does not resolve the ambiguity, you can use the fully qualified name to specify the method that you are calling. Si el Print método en el ejemplo anterior se define en un módulo denominado StringExtensions, el nombre completo es StringExtensions.Print(example) en lugar 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().

Vea tambiénSee also