Gli
"Extension Methods" sono una caratteristica sintattica introdotta dai
compilatori C# 3.0 e VB 9.0 (che NON è .NET 3.0, e qui scusatemi l'excursus ma
siamo alle solite, si rischia sempre di fare confusione con questi numeri di
versione "sovrapposti").
Sottolineo
il termine "sintattica" perchè non hanno nulla a che vedere con il
runtime, nè strutturalmente nè funzionalmente.
Molto
semplicemente, invece, sono state aggiunte regole di risoluzione.
Mi spiego,
prendendo ad esempio il codice qui sotto:
String[]
codici = new String[] {"11", "12", "13"};
Object
soloQualcheCodice = codici.Where(.....)
Quando
incontra lo statement codici.Where(), il compilatore segue le consuete regole
di lookup in modo da individuare il metodo corretto da invocare.
Regole
che, per come siamo abituati a conoscerle, in questo caso falliscono !
Non esiste
alcun metodo public instance Where in String[], nè in alcuna delle sue classi
di base in gerarchia.
Gli
extension methods sfruttano, fondamentalmente, nuove regole di lookup aggiunte
al compilatore: il quale, falliti i tentativi “standard”, va a verificare che
non esista un metodo “valido” in qualche altro posto.
L’altro
posto è una qualche classe statica, avente un metodo statico “compatibile”, e
in scope dal punto di chiamata (nel namespace corrente e su a salire in base
alle direttive using).
“Compatibile”
in questo contesto significa che, data l’invocazione (pseudo-codice):
T
istanza = ...
Int32
i = istanza.Metodo()
se non
esiste un metodo utilizzabile il compilatore cerca (in base alle regole
accennate in precedenza) un metodo con firma:
static
Int32 Metodo(this T istanza, )
E se lo
trova emette la chiamata ad esso.
Da notare che
comunque, in caso di overload, i metodi di istanza hanno la precedenza
Per
esempio, dati:
class
Esempio
{
public void Scrivi (Object o) {}
}
e
public
static class Estensione
{
public static void Scrivi(this Esempio e, Int32 numero) {}
}
Una
chiamata a:
Esempio
e = new Esempio();
e.Scrivi(10);
viene
risolta invocando la versione instance, per quanto meno “specializzata”.
E, piccola
considerazione per rispondere ad una domanda che mi è stata posta in più di un’occasione:
gli extension methods NON sono un modo per ereditare da classi sealed !
Sono
concetti parecchio differenti.
A mio
parere (si accettano ovviamente critiche e suggerimenti) farne un uso estensivo
per, che so, definire mille nuove funzionalità della classe String non è la
migliore delle cose.
Soprattutto
dal punto di vista della leggibilità e della chiarezza del codice per chi, quel
codice, non lo ha scritto.
Continuo a
preferire una chiamata esplicita, tipo
StringHelpers.DecodeString(“ciao”)
Piuttosto che
“ciao”.Decode()
Ovvio che
il secondo metodo è più conciso e diretto, ma forse è anche meno leggibile per il
lettore “occasionale” del nostro codice.