Dlaczego następujący kod C # nie jest dozwolony:
public abstract class BaseClass
{
public abstract int Bar { get;}
}
public class ConcreteClass : BaseClass
{
public override int Bar
{
get { return 0; }
set {}
}
}
CS0546 „ConcreteClass.Bar.set”: nie można przesłonić, ponieważ „BaseClass.Bar” nie ma nadpisywalnego akcesorium zestawu
c#
.net
properties
getter-setter
ripper234
źródło
źródło
Foo
która nie robi nic poza opakowaniem chronionejGetFoo
metody abstrakcyjnej . Abstrakcyjny typ pochodny może zasłaniać właściwość niewirtualną właściwością do odczytu i zapisu, która nie robi nic poza zawijaniem wspomnianejGetFoo
metody wraz zSetFoo
metodą abstrakcyjną ; konkretny typ pochodny mógłby zrobić powyższe, ale dostarczyć definicje dlaGetFoo
iSetFoo
.Odpowiedzi:
Ponieważ autor klasy Baseclass wyraźnie zadeklarował, że Bar musi być właściwością tylko do odczytu. Nie ma sensu, aby derywaty przerywały ten kontrakt i zmuszały go do odczytu i zapisu.
Jestem w tej sprawie z Microsoftem.
Powiedzmy, że jestem nowym programistą, któremu powiedziano, że mam kodować w oparciu o wyprowadzenie klasy podstawowej. piszę coś, co zakłada, że Bar nie może zostać zapisany (ponieważ klasa bazowa wyraźnie stwierdza, że jest to właściwość tylko do pobrania). Teraz, po twoim wyprowadzeniu, mój kod może się zepsuć. na przykład
Ponieważ Bar nie może być ustawiony zgodnie z interfejsem BaseClass, BarProvider zakłada, że buforowanie jest rzeczą bezpieczną - ponieważ Bar nie może być modyfikowany. Ale jeśli set był możliwy w wyprowadzeniu, ta klasa może obsługiwać nieaktualne wartości, jeśli ktoś zmodyfikował zewnętrznie właściwość Bar obiektu _source. Chodzi o to, że „ bądź otwarty, unikaj robienia podstępnych rzeczy i zaskakiwania ludzi ”
Aktualizacja : Ilya Ryzhenkov pyta „Dlaczego więc interfejsy nie działają według tych samych zasad?” Hmm ... kiedy o tym myślę, robi się to bardziej mętne.
Interfejs jest kontraktem, który mówi „spodziewaj się, że implementacja będzie miała właściwość odczytu o nazwie Bar”. Osobiście znacznie rzadziej przyjmę założenie tylko do odczytu, gdy zobaczę interfejs. Kiedy w interfejsie widzę właściwość tylko do pobierania, odczytuje się ją jako „Każda implementacja ujawniłaby ten atrybut Bar” ... w klasie bazowej, którą klika jako „Bar jest właściwością tylko do odczytu”. Oczywiście technicznie rzecz biorąc, nie zrywasz kontraktu… robisz więcej. Więc w pewnym sensie masz rację. Na zakończenie powiedziałbym: „utrudnij jak najbardziej nieporozumienie”.
źródło
InvalidOperationException("This instance is read-only")
.Myślę, że głównym powodem jest po prostu to, że składnia jest zbyt wyraźna, aby działać w inny sposób. Ten kod:
jest dość wyraźne, że zarówno opcje, jak
get
iset
są nadpisane. Wset
klasie bazowej nie ma , więc kompilator narzeka. Tak jak nie można przesłonić metody, która nie jest zdefiniowana w klasie bazowej, tak samo nie można przesłonić metody ustawiającej.Możesz powiedzieć, że kompilator powinien odgadnąć twój zamiar i zastosować nadpisanie tylko do metody, którą można przesłonić (tj. Getter w tym przypadku), ale jest to sprzeczne z jedną z zasad projektowania C # - że kompilator nie może odgadnąć twoich zamiarów , ponieważ może zgadnąć źle bez Twojej wiedzy.
Myślę, że następująca składnia może się nieźle sprawdzić, ale jak powtarza Eric Lippert, wdrożenie nawet niewielkiej funkcji, takiej jak ta, nadal wymaga dużego wysiłku ...
lub, w przypadku nieruchomości zaimplementowanych,
źródło
Natknąłem się dzisiaj na ten sam problem i myślę, że mam bardzo ważny powód, by tego chcieć.
Najpierw chciałbym argumentować, że posiadanie właściwości tylko do odczytu niekoniecznie przekłada się na tylko do odczytu. Interpretuję to jako „Z tego interfejsu / abstraktu można uzyskać tę wartość”, co nie oznacza, że niektóre implementacje tego interfejsu / klasy abstrakcyjnej nie będą wymagały od użytkownika / programu do jawnego ustawiania tej wartości. Klasy abstrakcyjne służą do realizacji części potrzebnej funkcjonalności. Nie widzę żadnego powodu, dla którego odziedziczona klasa nie mogłaby dodać setera bez naruszania jakichkolwiek umów.
Poniżej znajduje się uproszczony przykład tego, czego potrzebowałem dzisiaj. Skończyło się na tym, że musiałem dodać setera do mojego interfejsu, aby to obejść. Powodem dodania settera i nie dodawania, powiedzmy, metody SetProp jest to, że jedna konkretna implementacja interfejsu wykorzystywała DataContract / DataMember do serializacji Prop, co byłoby niepotrzebnie skomplikowane, gdybym musiał dodać kolejną właściwość tylko w tym celu serializacji.
Wiem, że to tylko post „mój za 2 centy”, ale czuję, że w przypadku oryginalnego plakatu próba zracjonalizowania tego, że to dobra rzecz, wydaje mi się dziwna, zwłaszcza biorąc pod uwagę, że te same ograniczenia nie mają zastosowania w przypadku dziedziczenia bezpośrednio po berło.
Również wzmianka o używaniu new zamiast override nie ma tutaj zastosowania, po prostu nie działa, a nawet gdyby tak było, nie dałoby to pożądanego rezultatu, a mianowicie wirtualnego gettera opisanego przez interfejs.
źródło
To jest możliwe
tl; dr - Jeśli chcesz, możesz zastąpić metodę ustawiającą tylko metodę pobierania. Po prostu:
Utwórz
new
właściwość, która ma zarówno a, jakget
i a,set
używając tej samej nazwy.Jeśli nie zrobisz nic innego, stara
get
metoda będzie nadal wywoływana, gdy klasa pochodna zostanie wywołana przez jej typ podstawowy. Aby to naprawić, dodajabstract
warstwę pośrednią, która używaoverride
starejget
metody, aby zmusić ją do zwrócenia wyniku nowejget
metody.To pozwala nam zastąpić właściwości z
get
/set
nawet jeśli ich brakowało w ich podstawowej definicji.Jako bonus możesz także zmienić typ zwrotu, jeśli chcesz.
Jeśli podstawową definicją była
get
-only, można użyć bardziej pochodnego typu zwracanego.Jeśli podstawową definicją było
set
-only, możesz użyć mniej pochodnego typu zwracanego.Jeśli podstawową definicją była już
get
/set
, to:możesz użyć bardziej pochodnego typu zwracanego, jeśli
set
ustawisz go -only;możesz użyć mniej pochodnego typu zwracanego, jeśli
get
ustawisz go -only.We wszystkich przypadkach możesz zachować ten sam typ zwrotu, jeśli chcesz. Poniższe przykłady używają tego samego typu zwracanego dla uproszczenia.
Sytuacja: istniejąca
get
tylko własnośćMasz strukturę klas, której nie możesz modyfikować. Może to tylko jedna klasa lub jest to wcześniej istniejące drzewo dziedziczenia. W każdym razie chcesz dodać
set
metodę do właściwości, ale nie możesz.Problem: Can't
override
the-get
only withget
/set
Chcesz
override
z właściwościąget
/set
, ale nie zostanie ona skompilowana.Rozwiązanie: użyj
abstract
warstwy pośredniejChociaż nie można bezpośrednio
override
zget
/set
własności, to można :Utwórz
new
get
/set
property o tej samej nazwie.override
staraget
metoda z akcesorium do nowejget
metody w celu zapewnienia spójności.Więc najpierw napisz
abstract
warstwę pośrednią:Następnie piszesz klasę, która nie skompiluje się wcześniej. Skompiluje się tym razem, ponieważ w rzeczywistości nie jest
override
toget
jedyna właściwość; zamiast tego zastępujesz gonew
słowem kluczowym.Wynik: wszystko działa!
Oczywiście działa to zgodnie z przeznaczeniem
D
.Wszystko nadal działa zgodnie z przeznaczeniem, gdy jest wyświetlane
D
jako jedna z jego klas bazowych, np .A
LubB
. Ale powód, dla którego to działa, może być nieco mniej oczywisty.Jednak definicja klasy bazowej
get
jest nadal ostatecznie zastępowana przez definicję klasy pochodnejget
, więc nadal jest całkowicie spójna.Dyskusja
Ta metoda umożliwia dodawanie
set
metodget
tylko do właściwości. Możesz go również używać do takich rzeczy, jak:Zmień dowolną właściwość na
get
-only,set
-only lubget
-and-set
property, niezależnie od tego, jaka była w klasie bazowej.Zmień zwracany typ metody w klasach pochodnych.
Główną wadą jest to, że jest więcej do zrobienia kodowania i dodatkowy element
abstract class
w drzewie dziedziczenia. Może to być trochę denerwujące w przypadku konstruktorów, które przyjmują parametry, ponieważ muszą one zostać skopiowane / wklejone w warstwie pośredniej.źródło
Zgadzam się, że brak możliwości przesłonięcia metody pobierającej w typie pochodnym jest anty-wzorcem. Tylko do odczytu oznacza brak implementacji, a nie umowę o czystym funkcjonale (sugerowaną przez najwyższą odpowiedź głosową).
Podejrzewam, że Microsoft miał to ograniczenie albo z powodu promowania tego samego błędnego przekonania, albo może z powodu uproszczenia gramatyki; chociaż teraz ten zakres można zastosować do uzyskania lub ustawienia indywidualnie, być może możemy mieć nadzieję, że nadpisanie też będzie.
Błędne przekonanie wskazywane przez odpowiedź w najwyższym głosowaniu, że właściwość tylko do odczytu powinna być w jakiś sposób bardziej „czysta” niż właściwość do odczytu / zapisu, jest absurdalne. Wystarczy spojrzeć na wiele typowych właściwości tylko do odczytu we frameworku; wartość nie jest stałą / czysto funkcjonalną; na przykład DateTime.Now jest tylko do odczytu, ale nie jest czystą wartością funkcjonalną. Próba „buforowania” wartości właściwości tylko do odczytu przy założeniu, że następnym razem zwróci tę samą wartość, jest ryzykowna.
W każdym razie zastosowałem jedną z następujących strategii, aby pokonać to ograniczenie; oba są mniej niż doskonałe, ale pozwolą ci utykać poza tym brakiem języka:
źródło
Być może możesz obejść ten problem, tworząc nową właściwość:
źródło
Rozumiem wszystkie twoje uwagi, ale w rzeczywistości automatyczne właściwości C # 3.0 stają się bezużyteczne w tym przypadku.
Nie możesz zrobić czegoś takiego:
IMO, C # nie powinno ograniczać takich scenariuszy. Deweloper jest odpowiedzialny za jego odpowiednie użycie.
źródło
Problem polega na tym, że z jakiegoś powodu Microsoft zdecydował, że powinny istnieć trzy różne typy właściwości: tylko do odczytu, tylko do zapisu i do odczytu i zapisu, z których tylko jedna może istnieć z daną sygnaturą w danym kontekście; właściwości mogą być zastępowane tylko przez identycznie zadeklarowane właściwości. Aby zrobić to, co chcesz, konieczne byłoby utworzenie dwóch właściwości o tej samej nazwie i podpisie - jedna z nich była tylko do odczytu, a druga do odczytu i zapisu.
Osobiście chciałbym, aby można było znieść całe pojęcie „właściwości”, z wyjątkiem tego, że składnia właściwości mogłaby być używana jako cukier składniowy do wywoływania metod „get” i „set”. Ułatwiłoby to nie tylko opcję „dodaj zestaw”, ale także umożliwiłoby „get” zwrócenie innego typu niż „zestaw”. Chociaż taka zdolność nie byłaby używana zbyt często, czasami może być przydatne, gdyby metoda „get” zwracała obiekt opakowania, podczas gdy „zestaw” mógł akceptować opakowanie lub rzeczywiste dane.
źródło
Oto obejście, aby to osiągnąć za pomocą Reflection:
W ten sposób możemy ustawić wartość obiektu dla właściwości, która jest tylko do odczytu. Może jednak nie działać we wszystkich scenariuszach!
źródło
Powinieneś zmienić tytuł pytania, aby albo wyszczególnić, że twoje pytanie dotyczy wyłącznie nadpisywania własności abstrakcyjnej, albo że twoje pytanie dotyczy generalnie przesłaniania właściwości klasy tylko do pobierania.
Jeśli pierwszy (przesłaniając abstrakcyjną właściwość)
Ten kod jest bezużyteczny. Sama klasa bazowa nie powinna mówić, że jesteś zmuszony do przesłonięcia właściwości Get-Only (być może interfejs). Klasa bazowa zapewnia typowe funkcje, które mogą wymagać określonych danych wejściowych z klasy implementującej. Dlatego wspólna funkcjonalność może wywoływać abstrakcyjne właściwości lub metody. W danym przypadku typowe metody funkcjonalności powinny prosić Cię o zastąpienie metody abstrakcyjnej, takiej jak:
Ale jeśli nie masz nad tym kontroli, a funkcjonalność klasy bazowej odczytuje z własnej właściwości publicznej (dziwne), po prostu zrób to:
Chcę zwrócić uwagę na (dziwny) komentarz: powiedziałbym, że najlepszą praktyką jest, aby klasa nie używała swoich własnych właściwości publicznych, ale używała swoich prywatnych / chronionych pól, gdy one istnieją. Więc to jest lepszy wzór:
Jeśli to drugie (przesłaniając właściwość klasy tylko do pobierania)
Każda nieabstrakcyjna właściwość ma metodę ustawiającą. W przeciwnym razie jest bezużyteczny i nie powinieneś go używać. Microsoft nie musi pozwalać ci robić tego, co chcesz. Powód jest taki: seter istnieje w takiej czy innej formie i możesz łatwo osiągnąć to, co chcesz, Veerryy .
Klasa bazowa lub dowolna klasa, za pomocą której można odczytać właściwość
{get;}
, ma NIEKTÓRE rodzaje odsłoniętej metody ustawiającej dla tej właściwości. Metadane będą wyglądać następująco:Ale implementacja będzie miała dwa końce spektrum złożoności:
Najmniej złożone:
Najbardziej złożone:
Chodzi mi o to, że tak czy inaczej wystawiasz rozgrywającego. W twoim przypadku możesz chcieć przesłonić,
int Bar
ponieważ nie chcesz, aby klasa bazowa sobie z tym poradziła, nie masz dostępu do przeglądu, jak to obsługuje, lub masz za zadanie wbić jakiś kod naprawdę szybko i niegrzecznie wbrew twojej woli .W ostatnim i dawnym (wniosek)
Krótko mówiąc: Microsoft nie musi niczego zmieniać. Możesz wybrać sposób konfiguracji klasy implementującej i, bez konstruktora, użyć wszystkich lub żadnej klasy bazowej.
źródło
Rozwiązanie tylko dla niewielkiego podzbioru przypadków użycia, niemniej jednak: w C # 6.0 metoda ustawiająca „tylko do odczytu” jest dodawana automatycznie dla przesłoniętych właściwości tylko pobierających.
źródło
Ponieważ to złamałoby koncepcję hermetyzacji i ukrywania implementacji. Rozważ przypadek, kiedy tworzysz klasę, wysyłasz ją, a następnie konsument Twojej klasy może ustawić właściwość, dla której pierwotnie zapewniłeś tylko metodę pobierającą. Skutecznie zakłóciłoby to wszystkie niezmienniki twojej klasy, na których możesz polegać w swojej implementacji.
źródło
To nie jest niemożliwe. Musisz po prostu użyć słowa kluczowego „nowe” w swojej usłudze. Na przykład,
Wydaje się, że takie podejście zadowala obie strony tej dyskusji. Użycie „nowego” powoduje zerwanie kontraktu między implementacją klasy bazowej a implementacją podklasy. Jest to konieczne, gdy klasa może mieć wiele kontraktów (za pośrednictwem interfejsu lub klasy bazowej).
Mam nadzieję że to pomoże
źródło
new
nie zastępuje już wirtualnej właściwości podstawowej. W szczególności, jeśli właściwość base jest abstrakcyjna , tak jak w oryginalnym poście, nie jest możliwe użycienew
słowa kluczowego w nie-abstrakcyjnej podklasie, ponieważ musisz zastąpić wszystkich abstrakcyjnych członków.Test t = new Test(); Base b = t; Console.WriteLine(t.BaseProperty)
daje 5,Console.WriteLine(b.BaseProperty)
daje 0, a kiedy twój następca musi to debugować, lepiej odejdź daleko, daleko.Właściwość tylko do odczytu w klasie bazowej wskazuje, że ta właściwość reprezentuje wartość, którą zawsze można określić z poziomu klasy (na przykład wartość wyliczenia pasująca do kontekstu (db-) obiektu). Zatem odpowiedzialność za określenie wartości pozostaje w obrębie klasy.
Dodanie metody ustawiającej spowodowałoby tutaj niezręczny problem: błąd walidacji powinien wystąpić, jeśli ustawisz wartość na cokolwiek innego niż pojedyncza możliwa wartość, którą już ma.
Jednak reguły często mają wyjątki. Jest bardzo możliwe, że na przykład w jednej klasie pochodnej kontekst zawęża możliwe wartości wyliczenia do 3 z 10, ale użytkownik tego obiektu nadal musi zdecydować, który z nich jest poprawny. Klasa pochodna musi delegować odpowiedzialność za określenie wartości na użytkownika tego obiektu. Ważne jest, aby zdać sobie sprawę, że użytkownik tego obiektu powinien być dobrze świadomy tego wyjątku i przyjąć na siebie odpowiedzialność za ustawienie prawidłowej wartości.
Moim rozwiązaniem w tego rodzaju sytuacjach byłoby pozostawienie właściwości tylko do odczytu i dodanie nowej właściwości do odczytu i zapisu do klasy pochodnej w celu obsługi wyjątku. Przesłonięcie oryginalnej właściwości po prostu zwróci wartość nowej właściwości. Nowa właściwość może mieć własną nazwę wskazującą poprawnie kontekst tego wyjątku.
Potwierdza to również słuszną uwagę: „utrudnij jak najbardziej nieporozumienia” Gishu.
źródło
ReadableMatrix
klasę (z dwóch argumentów tylko do odczytu), mający własności indeksowane podtypyImmutableMatrix
iMutableMatrix
. Kod, który musi tylko czytać matrycę i nie obchodzi go, czy może się zmienić w przyszłości, może akceptować parametr typuReadableMatrix
i być całkowicie zadowolony ze zmiennymi lub niezmiennymi podtypami.List<T>.Count
robi: Nie można go przypisać, ale to nie znaczy, że jest „tylko do odczytu”, ponieważ nie może być modyfikowany przez użytkowników klasy. Można go bardzo dobrze modyfikować, choć pośrednio, przez dodawanie i usuwanie elementów listy.Ponieważ na poziomie IL właściwość odczytu / zapisu przekłada się na dwie metody (pobierającą i ustawiającą).
Podczas zastępowania musisz nadal wspierać podstawowy interfejs. Gdybyś mógł dodać ustawiającego, w rzeczywistości dodawałbyś nową metodę, która pozostałaby niewidoczna dla świata zewnętrznego, jeśli chodzi o interfejs twoich klas.
To prawda, że dodanie nowej metody nie naruszyłoby kompatybilności jako takiej, ale ponieważ pozostałaby ona ukryta, decyzja o jej odrzuceniu ma sens.
źródło
new
zamiastoverride
.new
z twoimi dodatkami.Ponieważ klasa, która ma właściwość tylko do odczytu (bez metody ustawiającej), prawdopodobnie ma ku temu dobry powód. Na przykład może nie być żadnego bazowego magazynu danych. Umożliwienie stworzenia setera łamie kontrakt przedstawiony przez klasę. To po prostu złe OOP.
źródło