2015-10-10 28 views
6

Jon Skeet wychowany ten problem raz na jego filmy (choć nie dostarczyła z odpowiedzią).ukrywanie właściwość z klasy pochodnej

Powiedzmy, że mamy klasę o nazwie Person i klasy osoba ma właściwość Nazwa

Następnie mamy kolejną klasę Szpiega. Oczywiście Szpieg to Osoba, więc wywodzimy się z klasy Osoba.

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

public class Spy : Person 
{ 

} 

Nie chcemy, aby ludzie wiedzieli nazwę Szpiega tak, że chcemy to dać błąd kompilacji:

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

lub albo:

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

jak możemy zapobiegać tego rodzaju wydarzeniom?

+0

Możliwy duplikat [Jak mogę ukryć własność publiczną klasy podstawowej w klasie pochodnej] (http://stackoverflow.com/questions/1443886/how-can-can-i-hide-a-base-class-public- klasa własnościowa) –

+3

Jeśli szpieg odmówi podania swojego nazwiska, mogłoby to prowadzić do podejrzeń, że jest szpiegiem, byłoby lepiej, gdyby szpieg podał fałszywe nazwisko, takie jak "Jan Kowalski". – user1620220

+1

Usuń to: _wymagamy tego, aby dać błąd kompilacji_. W przeciwnym razie zaakceptowana odpowiedź nie odpowiada na pytanie. –

Odpowiedz

6

Wpisz Name właściwość virtual w klasie podstawowej Person. W wyprowadzonej klasie Spy nadpisujemy właściwość i throw Exception w programie pobierającym.

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; 
     } 
    } 
} 

Ale zamiast rzucać wyjątek, Chciałbym zaproponować coś podobnego

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

Bo łamie LSP (wspomniane w innym odpowiedź). Co to oznacza tutaj (tylko przykład) jest zawsze mogę zrobić jak

Person x = new Spy(); 

i przekazać je do innej metody, która może być jak

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

Metoda ta jest nieświadomy z jakiegoś szpiega wędrując po stadionie, rozbija się, wykonując prosty uczciwy obowiązek!

Edit

Wystarczy, aby było jasne, to name=********** jest nadal nie jest dobrym rozwiązaniem. To po prostu uratuje od wyjątków! Później można znaleźć wiele Osób przemierzających kod z nazwą **********, co spowoduje późniejsze niespodzianki i inne problemy.

Lepszym rozwiązaniem byłby lepszy projekt. Sprawdź odpowiedź Nathana, by uzyskać jakąś wskazówkę.

+1

Przyjemnie, ale spowoduje to błąd czasu wykonania, a nie błąd kompilacji. –

+0

Tak, masz rację. Ale to idzie najbliżej tego, co op może z łatwością i skutecznie wprowadzić w kod C#. Po prostu usunięcie właściwości z hierarchii klas (dając błąd kompilacji) nie jest obsługiwane w .Net AFAIK. Proszę mnie poprawić, jeśli się mylę :) –

+1

Twój "**********" pobierający jest czymś, co byłoby dla mnie bardzo zaskakujące w przypadku ustawionej właściwości string. Nie jestem wielkim fanem niespodzianek w ogóle. Wyjątkiem, którego szukasz, jest 'NotSupportedException', ale jeśli czytasz IL ostrożnie, okazuje się, że są to podklasy' MyDesignIsTerribleExceptions' i należy ich unikać. Uważam, że odpowiedź na to pytanie to właściwy projekt, a nie ten "jak-to-ja-strzelam-w-stopie-w-czasie pracy". –

1

Można ukryć podstawową metodę klasy lub własności z new słowa kluczowego:

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

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

Nie da Ci kompilacji błędów, i nadal będzie w stanie uzyskać dostęp do właściwości klasy bazowej Name jeśli oddasz.

Jeśli możesz zmodyfikować klasę podstawową, zmień właściwość na wirtualną. Następnie można go przesłonić w klasie pochodnej, klasie i rzucić wyjątek nawet przy wywołaniach polimorficznych.

Wszystko to będzie działać w czasie wykonywania, nic nie da się z tym zrobić podczas kompilacji.

I jak wspomnieli inni w swoich odpowiedziach, taki projekt przełamuje zasadę zastępowania Liskova i należy go unikać.

+1

Więc jeśli mam metodę o nazwie 'IntegrateForName', która ma' Spy', nigdy się nie złamie. Ale jeśli przejdę "Szpiega" do metody "AsknicelyForName", która zajmie "Osobę", powiedzą mi wszystko? Przypuszczam, że po prostu pokazuje, że tortury nigdy nie działają. –

+0

Dobry przykład. Cóż, z obecnym projektem nie można zrobić nic więcej. Następnym krokiem jest uświadomienie sobie, że w tym kontekście Szpieg nie jest Osobą (jak napisałeś w swojej odpowiedzi). –

6

Jeśli część bycia osobą jest ujawniając swoją nazwę: Spy nie są ludzie

Mając Spy dziedziczą osoby łamie Liskov substitution principle: Obiekt może być zastąpiona jego podtypu.

Jeśli Spys nie ujawni swojego imienia i nazwiska, nie powinien być osobą w kontekście projektu. Może go zaprojektować inaczej:

public interface IPerson 
{ 
    void EatLunch(); 
} 

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

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

Przykładem tego biednego projektu w realnym świecie jest NetworkStream. Implementuje właściwość Position, rzucając wyjątek NotSupportedException. Kod napisany dla Stream może zostać złamany w czasie wykonywania dla NetworkStream. Nie jestem też fanem tego. Poradnik projektowania: złe rzeczy powinny się zepsuć w czasie kompilacji, a obiekty, które dziedziczą z interfejsów, których nie mogą wdrożyć, są okropne.

1

Nie możesz. Jak już wspomniano, możesz rzucić wyjątek podczas uzyskiwania dostępu do właściwości Nazwa szpiega, ale to się kompiluje. I, o czym już wspomniano, złamałoby to zasadę substytucji Liskov i chciałbym dodać również zasadę otwartą.

Powiązane problemy