Quando creiamo una classe che eredita da una classe astratta e quando implementiamo la classe astratta ereditata, perché dobbiamo usare la parola chiave override?
"Perché?" a domande come questa può essere difficile rispondere perché sono vaghe. Presumo che la tua domanda sia "quali argomenti potrebbero essere fatti durante la progettazione del linguaggio per sostenere la posizione richiesta dalla overrideparola chiave ?"
Cominciamo facendo un passo indietro. In alcune lingue, ad esempio Java, i metodi sono virtuali per impostazione predefinita e sovrascritti automaticamente. I progettisti di C # erano consapevoli di questo e lo consideravano un piccolo difetto in Java. C # no "Java con le parti stupide tolte" come alcuni hanno detto, ma i progettisti di C # erano desiderosi di imparare dai punti di progettazione problematici di C, C ++ e Java, e non replicarli in C #.
I progettisti di C # hanno considerato l'override come una possibile fonte di bug; dopo tutto, è un modo per cambiare il comportamento del codice esistente e testato , e questo è pericoloso. L'override non è qualcosa che dovrebbe essere fatto casualmente o per caso; dovrebbe essere progettato da qualcuno che ci pensa intensamente . Ecco perché i metodi non sono virtuali per impostazione predefinita e perché ti viene richiesto di sostituire un metodo.
Questo è il ragionamento di base. Ora possiamo andare in qualche ragionamento più avanzato.
La risposta di StriplingWarrior offre un buon primo taglio nel formulare un argomento più avanzato. L'autore della classe derivata potrebbe non essere informato sulla classe di base, potrebbe essere intenzionato a creare un nuovo metodo e non dovremmo consentire all'utente di eseguire l'override per errore .
Sebbene questo punto sia ragionevole, ci sono una serie di controargomenti, come:
- L'autore di una classe derivata ha la responsabilità di sapere tutto sulla classe base! Stanno riutilizzando quel codice e dovrebbero fare la dovuta diligenza per comprendere a fondo quel codice prima di riutilizzarlo.
- Nel tuo particolare scenario il metodo virtuale è astratto; sarebbe un errore non sovrascriverlo, quindi è improbabile che l'autore stia creando un'implementazione per caso.
Facciamo quindi un argomento ancora più avanzato su questo punto. In quali circostanze l'autore di una classe derivata può essere scusato per non sapere cosa fa la classe base? Bene, considera questo scenario:
- L'autore della classe base crea una classe base astratta B.
- L'autore della classe derivata, in un team diverso, crea una classe D derivata con il metodo M.
- L'autore della classe base comprende che i team che estendono la classe base B dovranno sempre fornire un metodo M, quindi l'autore della classe base aggiunge il metodo astratto M.
- Quando viene ricompilata la classe D, cosa succede?
Ciò che vogliamo che accada è che l'autore di D sia informato che qualcosa di rilevante è cambiato . La cosa rilevante che è cambiata è che M è ora un requisito e che la loro implementazione deve essere sovraccaricata. DM potrebbe aver bisogno di cambiare comportamento una volta che sappiamo che potrebbe essere chiamato dalla classe base. La cosa giusta da fare è non dire in silenzio "oh, DM esiste ed estende BM". La cosa corretta da fare per il compilatore è fallire e dire "hey, autore di D, controlla questa tua ipotesi che non è più valida e correggi il tuo codice se necessario".
Nel tuo esempio, supponi che l' opzioneoverride fosse attiva perché sta sovrascrivendo un metodo astratto. Esistono due possibilità: (1) l'autore del codice intende sovrascrivere un metodo astratto, oppure (2) il metodo prevalente sta prevalendo per caso perché qualcun altro ha cambiato la classe base e il codice ora è sbagliato in qualche modo sottile. Non possiamo distinguere queste possibilità se è facoltativo . SayHellooverride
Ma se overrideè necessario, possiamo distinguere tre scenari. Se c'è un possibile errore nel codice poi overrideè mancante . Se è intenzionalmente ignorato, allora overrideè presente . E se è intenzionalmente non override allora newè presente . Il design di C # ci consente di fare queste sottili distinzioni.
Ricorda che la segnalazione degli errori del compilatore richiede di leggere la mente dello sviluppatore ; il compilatore deve dedurre dal codice errato quale codice corretto probabilmente aveva in mente l'autore e fornire un errore che li indichi nella direzione corretta. Più indizi possiamo fare in modo che lo sviluppatore lasci nel codice ciò che stava pensando, migliore è un lavoro che il compilatore può fare nella segnalazione degli errori e quindi più veloce è possibile trovare e correggere i bug.
Ma più in generale, C # è stato progettato per un mondo in cui il codice cambia . Molte funzionalità di C # che appaiono "dispari" sono in realtà lì perché informano lo sviluppatore quando un presupposto che era valido è diventato non valido perché una classe base è cambiata. Questa classe di bug è chiamata "errori di classe base fragili" e C # ha un numero di mitigazioni interessanti per questa classe di errore.