Extension Methods

Eccomi quì per parlarvi degli Extension Methods, Funzioni atte ad estendere i metodi di qualsiasi oggetto, sia esso un oggetto del Framework o una vostra Classe.

I "Metodi di Estensione", possono essere applicati soltanto per creare Sub o Function e non possono essere adoperati per creare nuove Proprietà.

Essi vanno creati all'interno di Moduli, devono avere specificato l'attributo <Extension()> ed è necessario importare lo spazio dei nomi System.Runtime.CompilerServices.

Anche se questi metodi sono Shared, in realtà essi verranno eseguiti come Metodi di istanza.

Vediamo come si crea un metodo di estensione

codice:
Imports System.Runtime.CompilerServices
Module Module1
<Extension()> _
  Public Sub NomeMetodo(ByVal Param1 As Object)
        ......
  End Sub
End Module
Facciamo attenzione al primo parametro passato, esso in realtà identifica il tipo di oggetto che andremo ad estendere con questo metodo e servirà contestualmente per ricevere l'istanza dell'oggetto che richiamerà il metodo.

Facciamo un esempio pratico:

codice:
Imports System.Runtime.CompilerServices
Module ExtensionModule
 <Extension()> _
 Public Sub ToLog (ByVal Sender As String)
  Dim fs As System.IO.FileStream
  Dim sw As System.IO.StreamWriter
    'Verifico se esiste la cartella dei log, altrimenti la creo
    If Not System.IO.Directory.Exists(Application.StartupPath & "\Log\") Then
      System.IO.Directory.CreateDirectory(Application.StartupPath & "\Log\")
    End If
    'Verifico se il file esiste, altrimenti lo creo
    fs = New System.IO.FileStream(Application.StartupPath & "\Log\Log.txt", FileMode.OpenOrCreate, FileAccess.ReadWrite)
    sw = New System.IO.StreamWriter(fs)
    sw.Close()
    fs.Close()
    'Scrivo su file
    fs = New System.IO.FileStream(Application.StartupPath & "\Log\Log.txt", FileMode.Append, FileAccess.Write)
    sw = New System.IO.StreamWriter(fs)  
    sw.Write(String.Format("{0}: {1}", Now.ToString, Sender.Tostring))
    sw.Close()
    fs.Close()
 End Sub
End Module
Come possiamo vedere, ho creato un Metodo di Estensione per l'oggetto di tipo String (visto che il primo parametro è di Tipo String) che serve per salvare su file il contenuto dell'istanza che richiamerà tale metodo. (avrei potuto evitare di usare il .ToString essendo già una stringa, ma l'ho volutamente inserito per rendere l'idea)

Tale Extension verrà richiamata utilizzando semplicemente:
codice:
Dim s As String = "Contenuto di prova"
s.ToLog
Che produrra nel file Log una riga contenente:
28/01/2010 22.00.00: Contenuto di prova

Le implementazioni di questa Extension potrebbero essere quelle di scrivere su file eventuali errori o loggare determinate operazioni.

Ma se volessimo specificare che si tratta di un errore piuttosto che di un'apertura del Database o di un avvio dell'applicazione, etc..etc..., cosa possiamo fare ?

Possiamo aggiungere un ulteriore parametro al Metodo di Estensione, si perchè i parametri successivi al primo (che identifica il tipo da estendere come detto prima), saranno i parametri che possono essere passati al nuovo "Metodo di Estensione".

Continuiamo con l'esempio precedente:

codice:
Imports System.Runtime.CompilerServices
Module ExtensionModule
 <Extension()> _
 Public Sub ToLog (ByVal Sender As String, ByVal DescEvento As String)
  Dim fs As System.IO.FileStream
  Dim sw As System.IO.StreamWriter
    'Verifico se esiste la cartella dei log, altrimenti la creo
    If Not System.IO.Directory.Exists(Application.StartupPath & "\Log\") Then
      System.IO.Directory.CreateDirectory(Application.StartupPath & "\Log\")
    End If
    'Verifico se il file esiste, altrimenti lo creo
    fs = New System.IO.FileStream(Application.StartupPath & "\Log\Log.txt", FileMode.OpenOrCreate, FileAccess.ReadWrite)
    sw = New System.IO.StreamWriter(fs)
    sw.Close()
    fs.Close()
    'Scrivo su file
    fs = New System.IO.FileStream(Application.StartupPath & "\Log\Log.txt", FileMode.Append, FileAccess.Write)
    sw = New System.IO.StreamWriter(fs)  
    sw.Write(String.Format("{0}: {1} - {2}", Now.ToString, DescEvento.ToString, Sender.Tostring))
    sw.Close()
    fs.Close()
 End Sub
End Module
Il suo utilizzo per esempio:
codice:
Try
 ..... 
Catch Ex As System.Exception
 Ex.Message.ToLog("Error")
End Try
Che produrrà all'interno del file la seguente riga:
28/01/2010 22.00.00: Error - MessaggioErrore

Conclusioni


Se volete estendere qualche oggetto senza voler derivare da esso creando una nuova Classe, questi metodi fanno al caso vostro e non hanno limiti di utilizzo visto che possono essere utilizzati con qualsiasi Tipo di oggetto, sia che esso faccia parte del Framework o che sia una vostra Classe.
Potreste voler creare un vostro tipo di "metodo di confronto" personalizzato per oggetti Generics.List oppure delle conversioni di Tipo (da String as Integer e viceversa), in questo caso vi basta usare Function che vi ritornerà il valore da voi convertito...

Un pò come fà il metodo Integer.TryParse insomma, però richiamato direttamente dalla Stringa che si intende convertire

codice:
Dim s As String = "5"
Dim num As Integer = s.ToInt32
A voi le possibili implementazioni.