Prima legge dell'informatica moderna, vera in praticamente tutti i linguaggi OO degli ultimi 20 anni:Tutti le classi derivano da Object.
Detto questo il resto ne segue. Se Object ha N metodi pubblici, la classe derivata avrà quei metodi pubblici.Generalizzando se una classe Foo ha 2 metodi pubblici, 1 private, 3 protected e 5 internal, la sua classe figlia Boo che metodi vedrà al suo interno?
E, non meno importante, che metodi vedranno gli oggetti che vi vogliono accedere tramite una istanza e l'operatore punto?Case 1:I due metodi pubblici saranno visti come tali anche nella derivata, perciò accessibili dall'esterno da qualunque classe del runtime.Case 2:Il metodo private sarà nascosto alla stessa istanza di classe derivata, perciò ancora di più all'esterno.Case 3:I metodi protected saranno "diventati" private nella classe derivata, con conseguente perdita di visibilità all'esterno.Case 4:I metodi internal (dualmente al modificatore default di Java), saranno visibili solo al namespace della classe.
Che metodi ha Object, che quindi ereditano tutte le classi del runtime?
Virtual, Override e NewPer un javista come me questo oscuro mondo dell'ereditarietà .NET è stato un duro colpo, in passato.Ora ho capito che il 90% dei problemi che mi ponevo erano proprio perchè volevo applicare una concezione Java ad un altro mondo di sviluppo, ovviamente a torto. Il senso dei metodi virtuali è tanto profondo quanto è complessa la soluzione che si intende implementare.Partiamo dagli esempi per inferire la teoria.
class Veicolo { public void A() { Console.WriteLine("Classe Veicolo"); } } class Auto:Veicolo { public void A() { Console.WriteLine("Classe Auto"); } }
Veicolo a = new Veicolo(); a.A(); //Stamperà ovviamente "Classe Veicolo" Auto b = new Auto(); b.A(); //Stamperà "Classe Auto" Veicolo c = new Auto(); c.A(); //Stampa "Classe Veicolo" perchè la reference è a Veicolo e non c'è override
class Veicolo { public virtual void A() { Console.WriteLine("Classe Veicolo"); } } class Auto:Veicolo { public override void A() { Console.WriteLine("Classe Auto"); } } Veicolo c = new Auto(); c.A(); //Stampa "Classe Auto" perchè la reference "sa" che deve andare a ricercare l'implementazione più specializzata di cui dispone, nell'albero dei figli.
class Auto:Veicolo { public new void A() { Console.WriteLine("Classe Auto"); } } Veicolo c = new Auto(); c.A(); //Stampa "Classe Veicolo" perchè la figlia ha volutamente interrotto la catena di riscrittura premessa dal padre.
Metodi di estensioneUna cosa magica e sempre affascinante sono gli extension methods, che ci permettono di "aggiungere" metodi a classi qualsiasi, sealed o no, interne o esterne al nostro progetto.Sarebbe carino chiedersi se un numero sia primo o no. Però invece che creare la solita classe di metodi statici vorremmo che, anche per una questione di correttezza semantica, il controllo del numero sia un metodo che espone il numero stesso.Perciò, senza poter estendere la classe intero (o senza volerlo fare), scriviamo questo codice:
public static bool IsPrime(this int number) { //controllo dei divisori return true; }
int number = 10; Console.WriteLine(number.IsPrime());
Implementazione Esplicita vs. NormaleIn realtà la cosa "strana" è rappresentata dall'implementazioni esplicita che antepone ai nomi dei metodi, l'interfaccia di riferimento, creando così un modo per nascondere i metodi alle reference dell'oggetto attuale, a meno che non lo si referenzi da una reference di tipo interfaccia, per esempio:
interface Wingable { void Fly(); } class Bird : Wingable { public void Fly() { throw new NotImplementedException(); } }
Bird b = new Bird(); b.Fly(); //corretto perchè l'implementazione è implicita
class Bird : Wingable { void Wingable.Fly() { throw new NotImplementedException(); } }
Wingable b = new Bird(); b.Fly(); //corretto perchè l'implementazione è esplicita
Nota sul Boxing e sugli operatori is e as:Il boxing consiste nell'incapsulare un oggetto value in un oggetto reference. Ovvero tipicamente copiarlo nello heap.L'operatore is restituisce true se l'operando di sinistra è del tipo specificato a destra, false altrimenti.L'operatore as fa il soft-casting dell'oggetto a sinistra al tipo specificato a destra; se il casting non va a buon fine, restituisce null.