2010-11-11 14 views
9

mam takiej sytuacji:Dodawanie setter do własności wirtualnej w C#

public abstract class BaseClass 
{ 
    public abstract string MyProp { get; } 
} 

Teraz dla niektórych klas pochodnych, wartość właściwości jest syntetyzowany wartości, więc nie ma seter:

public class Derived1 : BaseClass 
{ 
    public override string MyProp { get { return "no backing store"; } } 
} 

To działa dobrze. Jednak niektóre z klas pochodnych wymagały bardziej tradycyjnego zaplecza. Jednak, bez względu na to, jak piszę to, jak na automatycznym nieruchomości, lub z wyraźną sklepie podkładu, pojawia się błąd:

public class Derived2 : BaseClass 
{ 
    public override string MyProp { get; private set;} 
} 

public class Derived3 : BaseClass 
{ 
    private string myProp; 
    public override string MyProp 
    { 
     get { return myProp;} 
     private set { myProp = value;} 
    } 
} 

Derived2.MyProp.set ': nie można zastąpić, ponieważ «BaseClass.MyProp» robi nie ma dostępnego zestawu akcesorów

Jak mogę to uruchomić?

+1

Będzie działać, jeśli BaseClass był interfejsem, a nie klasą abstrakcyjną, która byłaby odpowiednia dla tego zmyślonego przykładu, ale być może nie w twoim rzeczywistym kodzie. – Mud

+0

Prawda, klasa BaseClass musi być klasą. Zaimplementował metody, których nie pokazałem. –

+0

Możliwy duplikat [Dlaczego nie można zastąpić właściwości tylko pobierającej i dodać setera?] (Https://stackoverflow.com/questions/82437/why-is-it-impossible-to-override-a-gerter -tylko-właściwości-i-dodawania-ustawiania) –

Odpowiedz

10

Najlepszą rzeczą, jaką można zrobić, to zaimplementować właściwość jako virtual zamiast abstract. Dokonaj get i set bloki dla każdego rzutu NotSupportedException w klasie bazowej i zastąpić zachowanie się odpowiednio w klasach pochodnych:

public virtual string MyProp { 
    get { 
     throw new NotSupportedException(); 
    } 
    set { 
     throw new NotSupportedException(); 
    } 
} 
+2

To, czego nie dostarcza, jest komunikatem czasu kompilacji, jeśli zapomnisz zaimplementować go w klasie pochodnej. Możesz zaimplementować get i ustawić w abstrakcji, a rzut nie zaimplementowany jawnie w klasie pochodnej, aby pokazać, że został uwzględniony. – Chris

3

Zasadniczo nie można. Dodając osobę ustawiającą, zmieniasz definicję właściwości, więc nie "przesłonię" jej właściwości bazowej. Jest tak samo, jak w przypadku próby zastąpienia metody i dodania do niej innego parametru - będą one traktowane jako różne metody (przeciążone). Ponieważ właściwości nie mogą być przeciążone, to nie zadziała.

Będziesz musiał dodać inną metodę ustawiania wartości (być może z dostępnością protected).

+1

Nie sądzę, że to jest analogiczne. Bardziej przypomina to, że 'BaseClass' zadeklarował abstrakcyjną metodę' GetFoo() ', a klasa pochodna dodała metodę' SetFoo() '- rozszerza ją, ale nie zmienia jej w taki sposób, aby naruszać LSP (w przeciwieństwie do zmiany sygnatury metod). –

+0

Zgadzam się z tobą w teorii, ale sposób, w jaki został on zaimplementowany w praktyce .NET jest tym, że * jest * zmianą, ponieważ własność definiuje to, co ma do czynienia, niezależnie od definicji metod get_ i set_, które implementują te akcesory. – EMP

+1

@Ian: Nie zgadzam się. Abstrakcja, którą dają nam właściwości, polega na uzyskiwaniu/ustawianiu właściwości klasy za pomocą składni dostępu do pola. Wewnętrznie są one implementowane jako metody, ale poza tym chodzi o to. Definiując właściwość, mówisz, czy można uzyskać i lub ustawić wartość o różnym dostępie. Narusza on LSP, ponieważ w jednej klasie właściwość jest zdefiniowana tak, aby miała tylko accessor, a następnie w klasie pochodnej, tę samą właściwość, co inny akcesor. –

0

sugestia Bradleya jest dobra, ale jedno Zrobiłem w przypadkach, gdy tylko Setter powinny być wirtualny jest coś zrobić to w ten sposób:

public class Root 
{ 
    private string _MyProp; 
    public string MyProp 
    { 
     get { return _MyProp;} 
     set { _MyProp = SetMyProp(value); } 
    } 
    protected virtual string SetMyProp(string suggestedValue) 
    { 
     return suggestedValue; 
    } 
} 
public class Child 
    : Root 
{ 
    protected override string SetMyProp(string suggestedValue) 
    { 
     string oReturn = base.SetMyProp(suggestedValue); 
     // Do some sort of cleanup here? 
     return oReturn; 
    } 
} 

to wymaga trochę dodatkowej pracy z przodu, ale wydaje się, aby utrzymać wyższy stopień hermetyzacji (np można zapobiec podklasy z nadrzędnymi zachowanie getter, a podklasa nie robi muszą być świadomi tego, że leży on za nimi właściwość e).

+0

Chociaż zgadzam się, że jest to ogólniejsza ogólna praktyka, pytanie Jamesa dotyczy konkretnie właściwości, które mogą nie mieć członka. Zapewnienie jednego w klasie bazowej jest, w jego scenariuszu, marnotrawstwem, ponieważ nie wszystkie klasy pochodne będą tego wymagać. –

+0

Masz rację. Ponownie przeczytałem jego pytanie i myślę, że źle zrozumiałem rzeczy jako pierwsze odejście. Myślałem, że po prostu pyta, jak mieć wirtualnego setera ... Tęskniłem za tym, że niekoniecznie mam w ogóle odsłoniętego setera. – Steven

0

Proponuję unikać właściwości wirtualnych lub abstrakcyjnych. Zamiast tego należy użyć właściwości innej niż wirtualna, która łączy się z chronionymi wirtualnymi lub abstrakcyjnymi metodami get/set. Dzięki temu klasy pochodne mogą przesłonić metody, a także przesłonić właściwość taką, która ma różne modyfikatory dostępu. Ponieważ sama własność podstawowa nie będzie wirtualna, nigdy nie będzie potrzeby jej zastępowania, więc konflikt nazewnictwa z nową wersją nie będzie miał znaczenia.