Perché abbiamo bisogno della nuova parola chiave e perché è il comportamento predefinito da hide e non sovrascrivere?

Stavo guardando questo post del blog e ho avuto le seguenti domande:

  • Perché abbiamo bisogno della new parola chiave, è solo per specificare che un metodo di class base è nascosto. Voglio dire, perché ne abbiamo bisogno? Se non usiamo la parola chiave override , non nascondiamo il metodo della class base?
  • Perché l’impostazione predefinita in C # è nascosta e non sostituita? Perché i progettisti l’hanno implementata in questo modo?

Buone domande Permettetemi di ri-dichiararli.

Perché è legale hide un metodo con un altro metodo?

Lasciatemi rispondere a questa domanda con un esempio. Hai un’interfaccia da CLR v1:

 interface IEnumerable { IEnumerator GetEnumerator(); } 

Super. Ora in CLR v2 hai generici e pensi “uomo, se solo avessimo avuto generici in v1 avrei reso questa un’interfaccia generica, ma non l’ho fatto, dovrei rendere qualcosa compatibile con questo ora che è generico in modo che Ottengo i benefici dei generici senza perdere la retrocompatibilità con il codice che si aspetta IEnumerable. ”

 interface IEnumerable : IEnumerable { IEnumerator .... uh oh 

Come chiamerai il metodo GetEnumerator di IEnumerable ? Ricorda che vuoi hide GetEnumerator sull’interfaccia di base non generica. Non vuoi mai che quella cosa venga chiamata, a meno che tu non sia esplicitamente in una situazione di compatibilità all’indietro.

Questo da solo giustifica il metodo nascosto. Per ulteriori pensieri sulle giustificazioni del metodo nascosto vedere il mio articolo sull’argomento .

Perché nascondersi senza “nuovo” causa un avvertimento?

Perché vogliamo portarlo alla tua attenzione che stai nascondendo qualcosa e potresti farlo accidentalmente. Ricorda, potresti hide qualcosa accidentalmente a causa di una modifica alla class base eseguita da qualcun altro, piuttosto che modificando la tua class derivata.

Perché nascondersi senza “nuovo” un avvertimento piuttosto che un errore?

Stessa ragione. Potresti hide qualcosa accidentalmente perché hai appena preso una nuova versione di una class base. Questo succede sempre. FooCorp crea una class base B. BarCorp crea una class derivata D con un metodo Bar, perché ai suoi clienti piace quel metodo. FooCorp lo vede e dice hey, questa è una buona idea, possiamo mettere questa funzionalità nella class base. Lo fanno e spediscono una nuova versione di Foo.DLL, e quando BarCorp preleva la nuova versione, sarebbe bello se gli venisse detto che il loro metodo ora nasconde il metodo della class base.

Vogliamo che questa situazione sia un avvertimento e non un errore perché renderlo un errore significa che questa è un’altra forma del fragile problema della class base . C # è stato progettato con cura in modo che quando qualcuno apporta una modifica a una class base, gli effetti sul codice che utilizza una class derivata siano ridotti al minimo.

Perché si nasconde e non sovrascrive il default?

Perché l’override virtuale è pericoloso . L’override virtuale consente alle classi derivate di modificare il comportamento del codice che è stato compilato per utilizzare le classi di base. Fare qualcosa di pericoloso come fare un override dovrebbe essere qualcosa che fai consapevolmente e deliberatamente , non per caso.

Se il metodo nella class derivata è preceduto dalla nuova parola chiave, il metodo viene definito indipendente dal metodo nella class base

Tuttavia, se non si specifica né la nuova né la sovrascrittura, l’output risultante è uguale a quello specificato dall’utente, ma verrà visualizzato un avviso del compilatore (poiché potrebbe non essere consapevole del fatto che si nasconde un metodo nel metodo della class base, o in effetti si potrebbe aver voluto ignorarlo, e semplicemente dimenticato di includere la parola chiave).

In questo modo è ansible evitare errori e mostrare esplicitamente ciò che si vuole fare e rende più leggibile il codice, in modo che si possa facilmente comprendere il codice.

Vale la pena notare che l’ unico effetto di new in questo contesto è quello di sopprimere un avvertimento. Non c’è alcun cambiamento nella semantica.

Quindi una risposta è: abbiamo bisogno di new per segnalare al compilatore che il nascondiglio è intenzionale e di eliminare l’avvertimento.

La domanda successiva è: Se non vuoi / non puoi sovrascrivere un metodo, perché dovresti introdurre un altro metodo con lo stesso nome? Perché hide è essenzialmente un conflitto di nomi. E ovviamente lo eviterai nella maggior parte dei casi.

L’unica buona ragione per cui posso pensare di nascondermi intenzionalmente è quando un nome ti viene forzato da un’interfaccia.

In C # i membri sono sigillati per impostazione predefinita e non è ansible sovrascriverli (a meno che non siano contrassegnati con le parole chiave virtual o abstract ) e ciò per motivi di prestazioni . Il nuovo modificatore viene utilizzato per hide esplicitamente un membro ereditato.

Se l’override era predefinito senza specificare la parola chiave override , potresti sovrascrivere accidentalmente alcuni metodi della tua base solo a causa dell’uguaglianza del nome.

La strategia del compilatore Net è quella di emettere avvisi se qualcosa potrebbe andare storto, solo per essere sicuri, quindi in questo caso se l’override era predefinito, dovrebbe esserci un avvertimento per ogni metodo sovrascritto – qualcosa come “avviso: controlla se vuoi veramente per sovrascrivere ‘.

La mia ipotesi sarebbe principalmente dovuta all’ereditarietà dell’interfaccia multipla. Utilizzando interfacce discrete sarebbe molto ansible che due interfacce distinte utilizzino la stessa firma del metodo. Consentire l’uso della new parola chiave ti consentirebbe di creare queste diverse implementazioni con una class, invece di dover creare due classi distinte.

AggiornatoEric mi ha dato un’idea su come migliorare questo esempio.

 public interface IAction1 { int DoWork(); } public interface IAction2 { string DoWork(); } public class MyBase : IAction1 { public int DoWork() { return 0; } } public class MyClass : MyBase, IAction2 { public new string DoWork() { return "Hi"; } } class Program { static void Main(string[] args) { var myClass = new MyClass(); var ret0 = myClass.DoWork(); //Hi var ret1 = ((IAction1)myClass).DoWork(); //0 var ret2 = ((IAction2)myClass).DoWork(); //Hi var ret3 = ((MyBase)myClass).DoWork(); //0 var ret4 = ((MyClass)myClass).DoWork(); //Hi } } 

Come notato, il metodo / property hiding rende ansible cambiare le cose su un metodo o una proprietà che altrimenti non potrebbero essere prontamente modificati. Una situazione in cui ciò può essere utile è consentire a una class ereditata di avere proprietà di lettura-scrittura che sono di sola lettura nella class base. Ad esempio, supponiamo che una class base abbia un gruppo di proprietà di sola lettura chiamate Value1-Value40 (ovviamente, una class reale userebbe nomi migliori). Un discendente sealed di questa class ha un costruttore che prende un object della class base e copia i valori da lì; la class non consente che vengano modificati dopo. Un diverso, ereditabile, discendente dichiara una proprietà di lettura-scrittura chiamata Valore1-Valore40 che, quando letto, si comporta come le versioni della class base ma, quando viene scritta, consente la scrittura dei valori. L’effetto netto sarà quel codice che vuole un’istanza della class base che sa non cambierà mai può creare un nuovo object della class di sola lettura, che può copiare i dati da un object passato senza doversi preoccupare se quell’object è di sola lettura o lettura-scrittura.

Un fastidio con questo approccio – forse qualcuno può darmi una mano – è che non conosco un modo per ombreggiare e sovrascrivere una particolare proprietà all’interno della stessa class. Qualcuno dei linguaggi CLR lo consente (io uso vb 2005)? Sarebbe utile se l’object della class base e le sue proprietà fossero astratte, ma ciò richiederebbe una class intermedia per sovrascrivere le proprietà Value1 in Value40 prima che una class discendente potesse ombreggerle.

È corretto che la nuova parola chiave nasconda esplicitamente un membro dalla class base in modo che i chiamanti finiscano per utilizzare il membro nella sottoclass piuttosto che la versione della superclass. Come menziona anche il post di questo blog, puoi anche farlo implicitamente.

È diverso rispetto a sostituire un membro della class base; per fare ciò il membro della class base deve essere contrassegnato come astratto o virtuale. Quindi non tutti i membri di una class base possono essere ignorati. Supponiamo che stai sottoclassi una class dall’assemblaggio di qualcun altro e non hai accesso a, o non vuoi, tornare indietro e adattare il codice per rendere i membri virtuali. Senza la nuova parola chiave verrai bloccato.

Puoi discutere se è meglio avere la parola chiave ed essere espliciti (la mia opinione) o farlo semplicemente implicitamente.