Metodi di estensione (Visual Basic)Extension Methods (Visual Basic)

I metodi di estensione consentono agli sviluppatori di aggiungere funzionalità personalizzate ai tipi di dati già definiti senza creare un nuovo tipo derivato.Extension methods enable developers to add custom functionality to data types that are already defined without creating a new derived type. I metodi di estensione consentono di scrivere un metodo che può essere chiamato come se fosse un metodo di istanza del tipo esistente.Extension methods make it possible to write a method that can be called as if it were an instance method of the existing type.

OsservazioniRemarks

Un metodo di estensione può essere solo una routine Sub o una procedura di Function.An extension method can be only a Sub procedure or a Function procedure. Non è possibile definire una proprietà, un campo o un evento di estensione.You cannot define an extension property, field, or event. Tutti i metodi di estensione devono essere contrassegnati con l'attributo di estensione <Extension> dallo spazio dei nomi System.Runtime.CompilerServices e devono essere definiti in un modulo.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 un metodo di estensione viene definito all'esterno di un modulo, il Visual Basic compilatore genera l'errore BC36551, "i metodi di estensione possono essere definiti solo nei moduli".If an extension method is defined outside a module, the Visual Basic compiler generates error BC36551, "Extension methods can be defined only in modules".

Il primo parametro di una definizione di metodo di estensione specifica il tipo di dati che il metodo estende.The first parameter in an extension method definition specifies which data type the method extends. Quando viene eseguito il metodo, il primo parametro viene associato all'istanza del tipo di dati che richiama il metodo.When the method is run, the first parameter is bound to the instance of the data type that invokes the method.

È possibile applicare l'attributo Extension solo a una Visual Basic Module, Subo Function.The Extension attribute can only be applied to a Visual Basic Module, Sub, or Function. Se lo si applica a una Class o a un Structure, il compilatore Visual Basic genera l'errore BC36550, l'attributo "extension" può essere applicato solo alle dichiarazioni ' Module ',' Sub ' o ' Function ' ".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".

EsempioExample

Nell'esempio seguente viene definita un'estensione Print per il tipo di dati String.The following example defines a Print extension to the String data type. Il metodo usa Console.WriteLine per visualizzare una stringa.The method uses Console.WriteLine to display a string. Il parametro del metodo di Print, aString, stabilisce che il metodo estende la classe di 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

Si noti che la definizione del metodo di estensione è contrassegnata con l'attributo extension <Extension()>.Notice that the extension method definition is marked with the extension attribute <Extension()>. Contrassegnare il modulo in cui è definito il metodo è facoltativo, ma ogni metodo di estensione deve essere contrassegnato.Marking the module in which the method is defined is optional, but each extension method must be marked. per accedere all'attributo di estensione, è necessario importare System.Runtime.CompilerServices.System.Runtime.CompilerServices must be imported in order to access the extension attribute.

I metodi di estensione possono essere dichiarati solo all'interno di moduli.Extension methods can be declared only within modules. In genere, il modulo in cui è definito un metodo di estensione non corrisponde a quello in cui viene chiamato.Typically, the module in which an extension method is defined is not the same module as the one in which it is called. Al contrario, il modulo che contiene il metodo di estensione viene importato, se necessario, per riportarlo nell'ambito.Instead, the module that contains the extension method is imported, if it needs to be, to bring it into scope. Quando il modulo che contiene Print si trova nell'ambito, il metodo può essere chiamato come se fosse un metodo di istanza normale che non accetta argomenti, ad esempio 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

L'esempio successivo, PrintAndPunctuate, è anche un'estensione per String, questa volta definito con due parametri.The next example, PrintAndPunctuate, is also an extension to String, this time defined with two parameters. Il primo parametro, aString, stabilisce che il metodo di estensione estende String.The first parameter, aString, establishes that the extension method extends String. Il secondo parametro, punc, deve essere una stringa di segni di punteggiatura che viene passata come argomento quando viene chiamato il metodo.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. Il metodo Visualizza la stringa seguita dai segni di punteggiatura.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

Il metodo viene chiamato inviando in un argomento stringa per punc: example.PrintAndPunctuate(".")The method is called by sending in a string argument for punc: example.PrintAndPunctuate(".")

Nell'esempio seguente vengono illustrati Print e PrintAndPunctuate definiti e chiamati.The following example shows Print and PrintAndPunctuate defined and called. System.Runtime.CompilerServices viene importato nel modulo di definizione per consentire l'accesso all'attributo di estensione.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

Successivamente, i metodi di estensione vengono inseriti nell'ambito e chiamati: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

Tutto ciò che è necessario per poter eseguire questi o metodi di estensione simili è che si trovino nell'ambito.All that is required to be able to run these or similar extension methods is that they be in scope. Se il modulo che contiene un metodo di estensione è nell'ambito, è visibile in IntelliSense e può essere chiamato come se fosse un metodo di istanza comune.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.

Si noti che quando vengono richiamati i metodi, per il primo parametro non viene inviato alcun argomento.Notice that when the methods are invoked, no argument is sent in for the first parameter. Il parametro aString nelle definizioni di metodo precedenti è associato a example, ovvero l'istanza di String che li chiama.Parameter aString in the previous method definitions is bound to example, the instance of String that calls them. Il compilatore utilizzerà example come argomento inviato al primo parametro.The compiler will use example as the argument sent to the first parameter.

Se viene chiamato un metodo di estensione per un oggetto impostato su Nothing, il metodo di estensione viene eseguito.If an extension method is called for an object that is set to Nothing, the extension method executes. Questa operazione non si applica ai metodi di istanza ordinari.This does not apply to ordinary instance methods. È possibile verificare in modo esplicito il Nothing nel metodo di estensione.You can explicitly check for Nothing in the extension method.

Tipi che possono essere estesiTypes that can be extended

È possibile definire un metodo di estensione per la maggior parte dei tipi che possono essere rappresentati in un elenco di parametri Visual Basic, inclusi i seguenti:You can define an extension method on most types that can be represented in a Visual Basic parameter list, including the following:

  • Classi (tipi di riferimento)Classes (reference types)
  • Strutture (tipi di valore)Structures (value types)
  • InterfacceInterfaces
  • DelegatiDelegates
  • Argomenti ByRef e ByValByRef and ByVal arguments
  • Parametri del metodo genericoGeneric method parameters
  • MatriciArrays

Poiché il primo parametro specifica il tipo di dati che il metodo di estensione estende, è obbligatorio e non può essere facoltativo.Because the first parameter specifies the data type that the extension method extends, it is required and cannot be optional. Per questo motivo, i parametri Optional e i parametri di ParamArray non possono essere il primo parametro nell'elenco di parametri.For that reason, Optional parameters and ParamArray parameters cannot be the first parameter in the parameter list.

I metodi di estensione non vengono considerati nell'associazione tardiva.Extension methods are not considered in late binding. Nell'esempio seguente l'istruzione anObject.PrintMe() genera un'eccezione MissingMemberException, ovvero la stessa eccezione che si verifica se la seconda definizione del metodo di estensione PrintMe è stata eliminata.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

Procedure consigliateBest practices

I metodi di estensione forniscono un metodo pratico e potente per estendere un tipo esistente.Extension methods provide a convenient and powerful way to extend an existing type. Tuttavia, per usarli correttamente, è necessario prendere in considerazione alcuni aspetti.However, to use them successfully, there are some points to consider. Queste considerazioni si applicano principalmente agli autori di librerie di classi, ma potrebbero influire su qualsiasi applicazione che utilizza i metodi di estensione.These considerations apply mainly to authors of class libraries, but they might affect any application that uses extension methods.

In generale, i metodi di estensione aggiunti ai tipi di cui non si è proprietari sono più vulnerabili dei metodi di estensione aggiunti ai tipi che si controllano.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. Nelle classi che non si possiede possono verificarsi numerose operazioni che possono interferire con i metodi di estensione.A number of things can occur in classes you do not own that can interfere with your extension methods.

  • Se esiste un membro di istanza accessibile che dispone di una firma compatibile con gli argomenti nell'istruzione chiamante, senza conversioni verso un tipo di argomento più piccolo richiesto dall'argomento al parametro, il metodo di istanza verrà utilizzato in modo preferenziale per qualsiasi metodo di estensione.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. Pertanto, se un metodo di istanza appropriato viene aggiunto a una classe in un determinato punto, un membro di estensione esistente su cui si basa la dipendenza potrebbe diventare inaccessibile.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.

  • L'autore di un metodo di estensione non può impedire ad altri programmatori di scrivere metodi di estensione in conflitto che possono avere la precedenza sull'estensione originale.The author of an extension method cannot prevent other programmers from writing conflicting extension methods that may have precedence over the original extension.

  • È possibile migliorare la robustezza inserendo i metodi di estensione nel relativo spazio dei nomi.You can improve robustness by putting extension methods in their own namespace. I consumer della libreria possono quindi includere uno spazio dei nomi o escluderlo oppure selezionare tra gli spazi dei nomi, separatamente dal resto della libreria.Consumers of your library can then include a namespace or exclude it, or select among namespaces, separately from the rest of the library.

  • Potrebbe essere più sicuro estendere le interfacce anziché estendere le classi, specialmente se non si è proprietari dell'interfaccia o della classe.It may be safer to extend interfaces than it is to extend classes, especially if you do not own the interface or class. Una modifica in un'interfaccia influiscono su ogni classe che la implementa.A change in an interface affects every class that implements it. Pertanto, l'autore potrebbe avere meno probabilità di aggiungere o modificare i metodi in un'interfaccia.Therefore, the author may be less likely to add or change methods in an interface. Tuttavia, se una classe implementa due interfacce con metodi di estensione con la stessa firma, il metodo di estensione non è visibile.However, if a class implements two interfaces that have extension methods with the same signature, neither extension method is visible.

  • Estendere il tipo più specifico possibile.Extend the most specific type you can. In una gerarchia di tipi, se si seleziona un tipo da cui derivano molti altri tipi, esistono livelli di possibilità per l'introduzione di metodi di istanza o altri metodi di estensione che potrebbero interferire con l'utente.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.

Metodi di estensione, metodi di istanza e proprietàExtension methods, instance methods, and properties

Quando un metodo di istanza in ambito dispone di una firma compatibile con gli argomenti di un'istruzione chiamante, il metodo di istanza viene scelto in preferenza rispetto a qualsiasi metodo di estensione.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. Il metodo di istanza ha la precedenza anche se il metodo di estensione è una corrispondenza migliore.The instance method has precedence even if the extension method is a better match. Nell'esempio seguente ExampleClass contiene un metodo di istanza denominato ExampleMethod con un parametro di tipo Integer.In the following example, ExampleClass contains an instance method named ExampleMethod that has one parameter of type Integer. Il metodo di estensione ExampleMethod estende ExampleClasse ha un parametro di 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 prima chiamata a ExampleMethod nel codice seguente chiama il metodo di estensione, perché arg1 è Long ed è compatibile solo con il parametro Long nel metodo di estensione.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 seconda chiamata a ExampleMethod dispone di un argomento Integer, arg2e chiama il metodo di istanza.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

Invertire ora i tipi di dati dei parametri nei due metodi: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

Questa volta il codice in Main chiama il metodo di istanza entrambe le volte.This time the code in Main calls the instance method both times. Ciò è dovuto al fatto che sia arg1 che arg2 hanno una conversione verso un tipo di Longpiù ampio e il metodo di istanza ha la precedenza sul metodo di estensione in entrambi i casi.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

Pertanto, un metodo di estensione non può sostituire un metodo di istanza esistente.Therefore, an extension method cannot replace an existing instance method. Tuttavia, quando un metodo di estensione ha lo stesso nome di un metodo di istanza ma le firme non sono in conflitto, è possibile accedere a entrambi i metodi.However, when an extension method has the same name as an instance method but the signatures do not conflict, both methods can be accessed. Se, ad esempio, la classe ExampleClass contiene un metodo denominato ExampleMethod che non accetta argomenti, sono consentiti metodi di estensione con lo stesso nome ma firme diverse, come illustrato nel codice seguente.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

L'output di questo codice è il seguente:The output from this code is as follows:

Extension method
Instance method

La situazione è più semplice con le proprietà: se un metodo di estensione ha lo stesso nome di una proprietà della classe che estende, il metodo di estensione non è visibile e non è possibile accedervi.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.

Precedenza metodo di estensioneExtension method precedence

Quando due metodi di estensione con firme identiche si trovano nell'ambito e sono accessibili, viene richiamato quello con precedenza più alta.When two extension methods that have identical signatures are in scope and accessible, the one with higher precedence will be invoked. La precedenza di un metodo di estensione è basata sul meccanismo utilizzato per portare il metodo nell'ambito.An extension method's precedence is based on the mechanism used to bring the method into scope. Nell'elenco seguente viene illustrata la gerarchia di precedenza, dal più alto al più basso.The following list shows the precedence hierarchy, from highest to lowest.

  1. Metodi di estensione definiti all'interno del modulo corrente.Extension methods defined inside the current module.

  2. I metodi di estensione definiti nei tipi di dati nello spazio dei nomi corrente o in uno qualsiasi dei relativi elementi padre, con spazi dei nomi figlio con precedenza maggiore rispetto agli spazi dei nomi padre.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. Metodi di estensione definiti in qualsiasi importazione di tipi nel file corrente.Extension methods defined inside any type imports in the current file.

  4. Metodi di estensione definiti in qualsiasi importazione di spazi dei nomi nel file corrente.Extension methods defined inside any namespace imports in the current file.

  5. Metodi di estensione definiti in qualsiasi importazione di tipi a livello di progetto.Extension methods defined inside any project-level type imports.

  6. Metodi di estensione definiti in qualsiasi importazione di spazi dei nomi a livello di progetto.Extension methods defined inside any project-level namespace imports.

Se la precedenza non risolve l'ambiguità, è possibile usare il nome completo per specificare il metodo che si sta chiamando.If precedence does not resolve the ambiguity, you can use the fully qualified name to specify the method that you are calling. Se il Print metodo nell'esempio precedente è definito in un modulo denominato StringExtensions, il nome completo viene StringExtensions.Print(example) anziché 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().

Vedere ancheSee also