Ogólne wskazówki dla C # to zawsze używać właściwości w polu publicznym. Ma to sens - wystawiając pole, ujawniasz wiele szczegółów implementacji. Za pomocą właściwości hermetyzujesz ten szczegół, aby był ukryty przed użyciem kodu, a zmiany implementacyjne są oddzielone od zmian interfejsu.
Zastanawiam się jednak, czy czasami występuje ważny wyjątek od tej reguły w przypadku readonly
słowa kluczowego. Stosując to słowo kluczowe w polu publicznym, dajesz dodatkową gwarancję: niezmienność. Nie jest to tylko szczegół implementacji, niezmienność jest czymś, co może zainteresować konsumenta. Użycie readonly
pola powoduje, że staje się ono częścią kontraktu publicznego i czymś, co nie może zostać zerwane przez przyszłe zmiany lub dziedziczenie bez konieczności modyfikacji interfejsu publicznego. Tego nie może zaoferować nieruchomość.
Czy readonly
w niektórych przypadkach zagwarantowanie niezmienności jest uzasadnionym powodem wyboru pola zamiast nieruchomości?
(Dla wyjaśnienia, z pewnością nie mówię, że zawsze powinieneś dokonywać tego wyboru tylko dlatego, że pole jest w tej chwili niezmienne, tylko wtedy, gdy ma to sens w ramach projektu klasy i zamierzone jest użycie niezmienności w umowie. Najbardziej interesują mnie odpowiedzi skupiające się na tym, czy może to być uzasadnione, a nie na konkretnych przypadkach, w których tak nie jest, na przykład gdy potrzebujesz członka interface
lub chcesz leniwie ładować.)
źródło
Odpowiedzi:
Publiczne statyczne pola tylko do odczytu są z pewnością w porządku. Są zalecane w niektórych sytuacjach, na przykład gdy chcesz mieć nazwany stały obiekt, ale nie możesz użyć
const
słowa kluczowego.Pola członków tylko do odczytu są nieco trudniejsze. Nie ma dużej przewagi uzyskanej nad nieruchomością bez publicznego setera, a jest to poważna wada: pola nie mogą być członkami interfejsów. Jeśli więc zdecydujesz się przefakturować kod, aby działał w oparciu o interfejs zamiast konkretnego typu, będziesz musiał zmienić pola tylko do odczytu na właściwości z publicznymi modułami pobierającymi.
Publiczna nie-wirtualna właściwość bez chronionego obiektu ustawiającego zapewnia ochronę przed dziedziczeniem, chyba że odziedziczone klasy mają dostęp do pola zaplecza. Możesz całkowicie egzekwować ten kontrakt w klasie podstawowej.
Brak możliwości zmiany „niezmienności” członka bez zmiany publicznego interfejsu klasy nie stanowi w tym przypadku dużej bariery. Zwykle, jeśli chcesz zmienić pole publiczne na właściwość, przebiega to płynnie, z tym wyjątkiem, że masz do czynienia z dwoma przypadkami:
object.Location.X = 4
jest możliwe tylko, jeśliLocation
jest polem. Nie dotyczy to jednak pola tylko do odczytu, ponieważ nie można modyfikować struktury tylko do odczytu. (Zakłada się, żeLocation
jest to typ wartości - jeśli nie, to i tak problem nie miałby zastosowania, ponieważreadonly
nie chroni przed tego rodzaju rzeczami.)out
lubref
. Ponownie, nie jest to istotne dla pola tylko do odczytu, boout
iref
parametry mają tendencję do modyfikacji, i to jest błąd kompilatora i tak przekazać wartość readonly jako OUT lub nr ref.Innymi słowy, chociaż konwersja pola tylko do odczytu na właściwość tylko do odczytu narusza zgodność binarną, inny kod źródłowy musiałby zostać ponownie skompilowany bez żadnych zmian w celu obsłużenia tej zmiany. Dzięki temu gwarancja jest naprawdę łatwa do złamania.
Nie widzę żadnych korzyści dla
readonly
pola członka, a niemożność posiadania czegoś takiego w interfejsie jest dla mnie na tyle niekorzystna, że nie używałbym tego.źródło
Z mojego doświadczenia
readonly
wynika , że słowo kluczowe nie jest magią, którą obiecuje.Na przykład masz klasę, w której konstruktor był prosty. Biorąc pod uwagę użycie (oraz fakt, że niektóre właściwości / pola klasy są niezmienne po budowie) możesz pomyśleć, że to nie ma znaczenia, więc użyłeś
readonly
słowa kluczowego.Później klasa staje się bardziej złożona, podobnie jak jej konstruktor. (Powiedzmy, że dzieje się tak w projekcie, który jest albo eksperymentalny, albo ma wystarczająco dużą prędkość zmniejszania zakresu / kontroli, ale musisz jakoś sprawić, żeby działał.) Przekonasz się, że pola, które
readonly
można modyfikować tylko wewnątrz konstruktora - ty nie można go modyfikować metodami, nawet jeśli metoda ta jest wywoływana tylko z konstruktora.To techniczne ograniczenie zostało potwierdzone przez projektantów C # i może zostać naprawione w przyszłej wersji. Ale dopóki nie zostanie naprawiony, użycie
readonly
jest bardziej restrykcyjne i może sprzyjać złemu stylowi kodowania (umieszczenie wszystkiego w metodzie konstruktora).Dla przypomnienia, ani
readonly
własność niepublicznego podmiotu ustalającego nie daje żadnej gwarancji „w pełni zainicjowanego obiektu”. Innymi słowy, to projektant klasy decyduje, co oznacza „w pełni zainicjowany” i jak chronić ją przed przypadkowym użyciem, a taka ochrona na ogół nie jest niezawodna, co oznacza, że ktoś może to obejść.źródło
readonly
pole jako parametrout
lubref
do innych metod, które będą mogły dowolnie modyfikować według własnego uznania.Pola są ZAWSZE szczegółami implementacji. Nie chcesz ujawniać szczegółów implementacji.
Podstawową zasadą inżynierii oprogramowania jest to, że nie wiemy, jakie będą wymagania w przyszłości. Chociaż twoje
readonly
pole może być dziś dobre, nic nie sugeruje, że będzie to część przyszłych wymagań. Co jeśli jutro zaistnieje potrzeba zaimplementowania licznika trafień, aby ustalić, ile razy dostęp do tego pola jest uzyskiwany? Co zrobić, jeśli chcesz zezwolić podklasom na zastąpienie pola?Właściwości są tak łatwe w użyciu i są o wiele bardziej elastyczne niż pola publiczne, że nie mogę myśleć o pojedynczym przypadku użycia, w którym wybrałbym c # jako mój język i używałbym publicznych pól tylko do odczytu w klasie. Gdyby wydajność była tak niska, że nie mogłem skorzystać z dodatkowego wywołania metody, prawdopodobnie użyłbym innego języka.
Tylko dlatego, że można zrobić coś z języka nie oznacza, że należy coś zrobić z językiem.
Zastanawiam się, czy projektanci języków żałują, że można upublicznić pola. Nie pamiętam, kiedy ostatni raz zastanawiałem się nad użyciem niepublicznych pól publicznych w moim kodzie.
źródło
Rational
z publicznością, niezmiennymiNumerator
iDenominator
członkami, mogę być bardzo pewny, że ci członkowie nie będą wymagali leniwego ładowania lub licznika trafień lub podobny?float
typów dlaNumerator
iDenominator
nie będzie wymagać zmianydoubles
?float
anidouble
. Powinny byćint
,long
lubBigInteger
. Być może ci muszą się zmienić ... ale jeśli tak, to musieliby również zmienić interfejs publiczny, więc tak naprawdę używanie właściwości nie dodaje hermetyzacji wokół tego szczegółu.Nie. To nie jest uzasadnienie.
Używanie readonly jako gwarancji niezmienności bez uzasadnienia jest bardziej jak hack.
Tylko do odczytu oznacza, że wartość jest przypisywana przez konstruktor o, gdy zmienna jest zdefiniowana.
Myślę, że to będzie zła praktyka
Jeśli naprawdę chcesz zagwarantować niezmienność, skorzystaj z interfejsu, który ma więcej zalet niż wad.
Przykład prostego interfejsu:
źródło