Quali sono alcuni usi pratici del "nuovo" modificatore in C # rispetto al nascondersi?


21

Un collega e io stavamo esaminando il comportamento della newparola chiave in C # in quanto si applica al concetto di nascondersi. Dalla documentazione :

Utilizzare il nuovo modificatore per nascondere esplicitamente un membro ereditato da una classe base. Per nascondere un membro ereditato, dichiararlo nella classe derivata usando lo stesso nome e modificarlo con il nuovo modificatore.

Abbiamo letto la documentazione e capiamo cosa fa sostanzialmente e come lo fa. Ciò su cui non siamo riusciti a capire è perché dovresti farlo in primo luogo. Il modificatore è stato lì dal 2003, e abbiamo entrambi lavorato con .Net per un periodo più lungo di quello e non è mai arrivato.

Quando questo comportamento sarebbe necessario in senso pratico (ad es. Come applicato a un caso aziendale)? È questa una caratteristica che è sopravvissuta alla sua utilità o è ciò che fa semplicemente abbastanza inusuale in quello che facciamo (in particolare facciamo moduli web e applicazioni MVC e alcuni WinForms e WPF a fattore ridotto)? Nel provare questa parola chiave e giocarci abbiamo trovato alcuni comportamenti che permettono che sembrino un po 'pericolosi se usati in modo improprio.

Sembra un po 'aperto, ma stiamo cercando un caso d'uso specifico che può essere applicato a un'applicazione aziendale che trova utile questo particolare strumento.


9
Forse l'hai letto anche tu, ma c'è un articolo interessante di Eric Lippert (sviluppatore del compilatore C #) sul perché il metodo nascosto è stato aggiunto a C #: blogs.msdn.com/b/ericlippert/archive/2008/05/21/… . Questo risponde a una parte della tua domanda, ma non ho un business case pronto per te, quindi l'ho inserito in un commento.
Jalayn,

3
@Jalayn: il business case è discusso da Eric in questo post: blogs.msdn.com/b/ericlippert/archive/2004/01/07/…
Brian

Risposte:


22

Puoi usarlo per imitare la covarianza del tipo di ritorno. Spiegazione di Eric Lippert . Eric fornisce questo codice di esempio:

abstract class Enclosure
{
    protected abstract Animal GetContents();
    public Animal Contents() { return this.GetContents(); }
}
class Aquarium : Enclosure
{
    public new Fish Contents() { ... }
    protected override Animal GetContents() { return this.Contents(); }
}

Questa è una soluzione. public override Fish Contents() { ... }non è legale, nonostante sia sicuro.

In generale, è consigliabile non utilizzare il metodo nascosto, in quanto è fonte di confusione per i consumatori della classe (l'esempio specifico di cui sopra non soffrono di questo problema). Basta nominare il nuovo metodo in modo diverso se non si desidera sovrascrivere un metodo esistente.

Una probabile situazione del mondo reale in cui occorrerebbe nascondere il metodo è se il provider di una classe base ha aggiunto un metodo generico che era già stato aggiunto a una classe derivata. Un programma del genere compilerà (e fornirà avvisi) senza la nuova parola chiave, ma l'aggiunta newdice: "So che la mia versione di questo metodo sta sostituendo la versione della classe base. Questo è orribile e confuso, ma ne siamo bloccati". È ancora meglio che forzare la classe derivata a rinominare il loro metodo.

Il solo fatto di considerare il metodo derivato come una sostituzione provocherebbe problemi. Ignorando qualsiasi preoccupazione con l'implementazione del compilatore, il nuovo metodo è semanticamente diverso dal metodo di base, ma il polimorfismo farebbe chiamare il nuovo metodo quando viene chiesto di chiamare un metodo con lo stesso nome.

Questa situazione è discussa in dettaglio in questo post di Eric Lippert.


1
+1 per newessere un indicatore di "questo è orribile e confuso".
Avner Shahar-Kashtan,

Penso che l'esempio di Eric sia molto utile, se non altro perché mi trovo in una situazione simile ... Ho una classe base che implementa un metodo non banale che restituisce TBase e una classe derivata che dovrebbe restituire un TDerived. In questo caso, sembra un male necessario.
Kyle Baran,

4

Penso che sia lì nel caso in cui potresti averne bisogno per fare qualcosa a cui i progettisti del linguaggio potrebbero non aver pensato. C # è stato per molti versi una reazione alle prime versioni di Java. E una cosa che java ha fatto è stata quella di picchiare esplicitamente gli sviluppatori per eliminare le possibilità che gli sviluppatori si sparassero ai piedi. C # ha adottato un approccio leggermente diverso e ha dato agli sviluppatori un po 'più di potenza nel consentire agli sviluppatori qualche opportunità in più per spararsi ai piedi. Un esempio è la unsafeparola chiave. Questa newparola chiave è un'altra.

Ora, probabilmente non è così utile come, unsafema una volta entrati in una specifica di lingua è difficile uscire da una specifica di lingua.


5
Java non ha novità perché Java considera tutti i metodi come virtuali. Secondo Eric Lippert, la motivazione per supportare il nuovo è quella di risolvere il fragile problema della classe base. L'esistenza di nuovi è necessaria per l'uso commerciale nel mondo reale, non solo per l'uso di biblioteche di basso livello. Java non avere nuovi (e non avere metodi non virtuali) significa che se una classe base introduce un nuovo metodo che è già in uso all'interno delle classi derivate, il codice esistente potrebbe rompersi, nonostante il fatto che lo sviluppatore della classe base dovrebbe essere autorizzato essere ignari del codice che lo consuma.
Brian,

3

Sta dicendo al lettore che "Ho deliberatamente nascosto l'implementazione di questo metodo della classe base", al contrario di accidentalmente.


5
Questo mi sembra chiedere la domanda. L'OP sa cosa fa ma vuole sapere perché.
Brian,

0

È possibile che si desideri che il membro precedente sia disponibile tramite l'altro nome:

class VehicleClass
{
  public int AnyProperty
  {
    get; set;
  }

  public int AnyFunction() { return 0; }
} // VehicleClass

class IntermediateClass : VehicleClass
{
  public int PreviousAnyProperty
  {
    get { return AnyProperty; }
    set { AnyProperty = value  }
  }

  public int PreviousAnyFunction() { return AnyFunction(); }
} // IntermediateClass 

class CarClass : IntermediateClass
{
  public new int AnyProperty
  {
    get ; set ;
  }

  public new int AnyFunction() { return 5; }
} // class CarClass

class ExampleClass
{

  public static void Main()
  {
    using (CarClass MyCar = new CarClass())
    {
      int AnyInt1 = MyCar.PreviousAnyProperty;
      MyCar.PreviousAnyProperty = 7;

      int AnyInt2 = MyCar.PreviousAnyFunction();

      int AnyInt3 = MyCar.AnyProperty;
      MyCar.AnyProperty = 45;

      int AnyInt4 = MyCar.AnyFunction();
    }
  } // static void Main()

} // class CarClass

Saluti.


Conosco bene come funziona, ma la mia domanda è davvero: Perché vorresti avere il membro precedente disponibile con un altro nome? Questo rompe tutti i tipi di contratti, rende inutili alcuni pezzi generici.
Joel Etherton,

@Joel Etherton: Come forse saprai, a volte gli sviluppatori devono "scegliere" altre persone che programmano il codice. E potremmo non essere autorizzati a modificare, forse estendere le classi. E potrebbe essere necessario utilizzare entrambi, il membro precedente e quello nuovo.
Umlcat,

0

L'ho trovato abbastanza utile durante la creazione di una suite di test per il codice legacy. Mi permette di nascondere dipendenze esterne, come interrogare un database all'interno di un metodo utilizzato da altri metodi. Per poter testare gli altri metodi, posso nascondere la logica originale che dipende da risorse esterne senza la necessità di aggiungere la parola chiave virtuale a tali metodi nelle classi di test.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.