2015-10-10 17 views
6

Jon Skeet ha sollevato questo problema una volta nei suoi video (sebbene non fornisse una risposta).nascondere la proprietà dalla classe derivata

Diciamo che abbiamo una classe denominata persona e la classe persona ha Nome proprietà

Poi abbiamo un'altra classe, Spy. Ovviamente una Spia è una Persona quindi deriverà dalla classe Persona.

public class Person 
{ 
    public string Name { get; set; } 
} 

public class Spy : Person 
{ 

} 

Non vogliamo che la gente sappia il nome della spia in modo vorremmo questo per dare un errore di compilazione:

static void ReportSpy(Spy spy) { 
    string name = spy.Name; 
} 

o uno:

static void ReportSpy(Spy spy) 
{ 
    Person spyAsPerson = spy; 
    string name = spyAsPerson.Name; 
} 

Come potremmo impedire che questo tipo di cose accada?

+0

Possibile duplicato di [Come nascondere una proprietà pubblica della classe base nella classe derivata] (http://stackoverflow.com/questions/1443886/how-can-i-hide-a-base-class-public- proprietà-nella-classe derivata) –

+3

Se una spia rifiuta di dare il proprio nome, porterebbe al sospetto che siano una spia, sarebbe meglio se la spia avesse dato un nome falso, come "John Doe". – user1620220

+1

Rimuovi questo: _ vorremmo dare un errore di compilazione_. Altrimenti, la risposta accettata non risponde alla domanda. –

risposta

6

Rendere Name proprietà virtual nella classe Person di base. Nella classe derivata Spy, sovrascrivere la proprietà e throw Exception in getter.

public class Person 
{ 
    public virtual string Name { get; set; } 
} 

public class Spy : Person 
{ 
    public override string Name 
    { 
     get 
     { 
      throw new Exception("You never ask for a spy's name!"); 
     } 
     set 
     { 
      base.Name = value; 
     } 
    } 
} 

Ma, invece di lanciare un'eccezione, io suggerirei qualcosa come

get 
{ 
    return "**********"; 
} 

Perché, si rompe LSP (detto in un'altra risposta). Ciò significa qui (solo un esempio) è, posso sempre fare come

Person x = new Spy(); 

e passarlo a un altro metodo, che potrebbe essere come

void RegisterForNextBallGame(Person p) 
{ 
    playerList.Add(p.Name); 
} 

Questo metodo di essere all'oscuro della una spia girovagando per lo stadio, si schianta mentre svolge un semplice e onesto dovere!

Modifica

Giusto per chiarire, questo è name=**********ancora non una soluzione giusta. Salverà solo dalle eccezioni! Più tardi, si potrebbero trovare molte persone che percorrono il codice con il nome ********** che causerà successive sorprese e altri problemi.

Una soluzione migliore sarebbe un design migliore. Controlla la risposta di Nathan per avere qualche suggerimento.

+1

Bello, ma questo genererà un errore di runtime, non un errore di compilazione. –

+0

Sì, hai ragione. Ma questo è più vicino a ciò che op può facilmente e con successo implementare in un codice C#. La semplice rimozione di una proprietà da una gerarchia di classi (dando errore di compilazione) non è supportata in .Net AFAIK. Per favore correggimi se sbaglio :) –

+1

Il tuo getter "**********" è qualcosa che troverei molto sorprendente per una proprietà stringa get-set. Non sono un grande fan delle sorprese in generale. L'eccezione che stai cercando è una 'NotSupportedException', ma se leggi l'IL attentamente, si scopre che si tratta di una sottoclasse' MyDesignIsTerribleExceptions' e dovrebbe essere evitata. Credo che la risposta alla domanda sia la progettazione corretta, piuttosto che questa roba "come fare-sparare-io-in-piedi-a-runtime". –

1

È possibile nascondere metodo della classe base o una proprietà con new parola chiave:

public class Person 
{ 
    public string Name { get; set; } 
} 

public class Spy : Person 
{ 
    public new string Name 
    { 
     get { throw new InvalidOperationException(); } 
    } 
} 

Non vi darà la compilazione errori, e sarete ancora in grado di accedere alla proprietà della classe base Name se lanci.

Se è possibile modificare la classe base, modificare la proprietà in virtuale. Quindi puoi sovrascriverlo nella derivata e nella classe e generare eccezioni anche con chiamate polimorfiche.

Tutto ciò funzionerà in fase di esecuzione, nulla che si possa fare al momento della compilazione.

E come altri hanno menzionato nelle loro risposte, tale progettazione rompe il principio di sostituzione di Liskov e dovrebbe essere evitato.

+1

Quindi, se ho un metodo chiamato 'IntegrateForName' che richiede una' Spia', non si romperanno mai. Ma se passo il 'Spy' al metodo' AsknicelyForName' che prende un 'Person', mi diranno tutto? Suppongo che serva solo a dimostrare che la tortura non funziona mai. –

+0

Bel esempio. Bene, non puoi fare molto di più con il design attuale. Il passo successivo è rendersi conto che in questo contesto, Spia non è una persona (come hai scritto nella tua risposta). –

6

Se parte di essere una persona è rivelare il tuo nome: spia di non sono persone

Avere Spy ereditare da persona rompe il Liskov substitution principle: Un oggetto può essere sostituito con il suo sottotipo.

Se Spys non rivela il proprio nome, non dovrebbero essere Persone nel contesto del proprio progetto. Forse si potrebbe progettare in modo diverso:

public interface IPerson 
{ 
    void EatLunch(); 
} 

public interface INameDisclosingPerson : IPerson 
{ 
    string Name {get; set; } 
} 

public interface ISpy : IPerson 
{ 
    void DrinkCocktail(); 
    Package MakeDrop(); 
} 

Un esempio di questa povera di design nel mondo reale è NetworkStream. Implementa la proprietà Position lanciando NotSupportedException. Quindi il codice che scrivi per un Stream potrebbe interrompersi in fase di esecuzione per lo NetworkStream. Anche io non sono un fan di questo. Una guida alla progettazione: le cose sbagliate dovrebbero rompersi alla compilazione e gli oggetti che ereditano da interfacce che non possono implementare sono terribili.

1

Non è possibile. Come già accennato potresti lanciare un'eccezione quando accederai alla proprietà Nome di Spy, ma questa sarebbe comunque compilata. E, come già accennato, ciò spezzerebbe il principio di sostituzione di Liskov e, vorrei aggiungere, anche il principio di apertura chiusa.

Problemi correlati