W swojej znakomitej książce „CLR Via C #” Jeffrey Richter powiedział, że nie lubi właściwości i odradza ich używanie. Podał jakiś powód, ale tak naprawdę nie rozumiem. Czy ktoś może mi wyjaśnić, dlaczego powinienem lub nie powinienem używać właściwości? Czy w języku C # 3.0 z właściwościami automatycznymi to się zmienia?
Jako odniesienie dodałem opinie Jeffreya Richtera:
• Właściwość może być tylko do odczytu lub tylko do zapisu; dostęp do pól jest zawsze czytelny i zapisywalny. Jeśli definiujesz właściwość, najlepiej jest oferować metody dostępu zarówno get, jak i set.
• Metoda właściwości może zgłosić wyjątek; dostęp do pola nigdy nie zgłasza wyjątku.
• Właściwość nie może być przekazana jako parametr out lub ref do metody; pole może. Na przykład następujący kod nie zostanie skompilowany:
using System;
public sealed class SomeType
{
private static String Name
{
get { return null; }
set {}
}
static void MethodWithOutParam(out String n) { n = null; }
public static void Main()
{
// For the line of code below, the C# compiler emits the following:
// error CS0206: A property or indexer may not
// be passed as an out or ref parameter
MethodWithOutParam(out Name);
}
}
• Metoda właściwości może zająć dużo czasu; dostęp do pola zawsze kończy się natychmiast. Częstym powodem używania właściwości jest wykonanie synchronizacji wątków, która może zatrzymać wątek na zawsze, dlatego też właściwość nie powinna być używana, jeśli wymagana jest synchronizacja wątków. W takiej sytuacji preferowana jest metoda. Ponadto, jeśli do Twojej klasy można uzyskać dostęp zdalnie (na przykład Twoja klasa pochodzi z System.MashalByRefObject), wywołanie metody właściwości będzie bardzo powolne, a zatem metoda jest preferowana zamiast właściwości. Moim zdaniem klasy pochodzące od MarshalByRefObject nigdy nie powinny używać właściwości.
• Jeśli metoda właściwości zostanie wywołana wiele razy z rzędu, za każdym razem może zwrócić inną wartość; pole zwraca za każdym razem tę samą wartość. Klasa System.DateTime ma właściwość Readonly Now, która zwraca bieżącą datę i godzinę. Za każdym razem, gdy zapytasz o tę właściwość, zwróci ona inną wartość. To jest błąd i firma Microsoft życzy sobie, aby mogli naprawić klasę, tworząc metodę Now zamiast właściwości.
• Metoda właściwości może powodować obserwowalne skutki uboczne; dostęp do pola nigdy nie ma. Innymi słowy, użytkownik typu powinien mieć możliwość ustawiania różnych właściwości zdefiniowanych przez typ w dowolnej kolejności, bez zauważania innego zachowania w typie.
• Metoda właściwości może wymagać dodatkowej pamięci lub zwrócić odniesienie do czegoś, co w rzeczywistości nie jest częścią stanu obiektu, więc modyfikacja zwróconego obiektu nie ma wpływu na oryginalny obiekt; zapytanie o pole zawsze zwraca odwołanie do obiektu, który na pewno jest częścią stanu oryginalnego obiektu. Praca z właściwością, która zwraca kopię, może być bardzo myląca dla programistów, a ta cecha często nie jest udokumentowana.
źródło
Odpowiedzi:
Jeff nie lubi właściwości, ponieważ wyglądają jak pola - więc programiści, którzy nie rozumieją różnicy, będą traktować je tak, jakby były polami, zakładając, że będą tanie w wykonaniu itp.
Osobiście nie zgadzam się z nim w tym konkretnym punkcie - uważam, że właściwości sprawiają, że kod klienta jest znacznie prostszy do odczytania niż równoważne wywołania metod. Zgadzam się, że programiści muszą wiedzieć, że właściwości są w zasadzie metodami zamaskowanymi - ale myślę, że edukacja programistów na ten temat jest lepsza niż utrudnianie czytania kodu za pomocą metod. (W szczególności, widząc kod Javy z kilkoma metodami pobierającymi i ustawiającymi wywoływanymi w tej samej instrukcji, wiem, że równoważny kod C # byłby o wiele prostszy do odczytania. Prawo Demeter jest bardzo dobre w teorii, ale czasami
foo.Name.Length
tak naprawdę jest właściwa rzecz do użycia ...)(I nie, automatycznie implementowane właściwości tak naprawdę niczego nie zmieniają).
Jest to trochę podobne do argumentów przeciwko stosowaniu metod rozszerzających - rozumiem rozumowanie, ale praktyczna korzyść (gdy jest używana oszczędnie) przeważa w moim przekonaniu.
źródło
Cóż, weźmy jego argumenty jeden po drugim:
Jest to korzystne dla nieruchomości, ponieważ masz bardziej szczegółową kontrolę dostępu.
Chociaż jest to w większości prawda, bardzo dobrze można wywołać metodę na niezainicjowanym polu obiektu i zgłosić wyjątek.
Targi.
Może to również zająć bardzo mało czasu.
Nie prawda. Skąd wiesz, że wartość pola nie zmieniła się (prawdopodobnie przez inny wątek)?
Jeśli to pomyłka, to drobna.
Targi.
Większość protestów można by powiedzieć także o getterach i seterach Javy - i mieliśmy je przez dłuższy czas bez takich problemów w praktyce.
Myślę, że większość problemów można by rozwiązać przez lepsze podświetlanie składni (tj. Różnicowanie właściwości z pól), aby programista wiedział, czego się spodziewać.
źródło
_field
. Lub po prostu małe literyfield
.Nie czytałem tej książki, a ty nie zacytowałeś jej części, której nie rozumiesz, więc muszę zgadywać.
Niektórzy ludzie nie lubią właściwości, ponieważ mogą sprawić, że Twój kod będzie robił zaskakujące rzeczy.
Jeśli piszę
Foo.Bar
, osoby czytające zwykle będą oczekiwać, że jest to po prostu dostęp do pola członkowskiego klasy Foo. To tania, prawie bezpłatna operacja i jest deterministyczna. Mogę to powtarzać w kółko i za każdym razem uzyskuję ten sam wynik.Zamiast tego, z właściwościami, może to być wywołanie funkcji. To może być nieskończona pętla. Może otworzyć połączenie z bazą danych. Może zwracać różne wartości za każdym razem, gdy mam do niego dostęp.
Jest to podobny argument do tego, dlaczego Linus nienawidzi C ++. Twój kod może zaskoczyć czytelnika. Nienawidzi przeciążania operatorów:
a + b
niekoniecznie oznacza proste dodawanie. Może to oznaczać bardzo skomplikowaną operację, podobnie jak właściwości C #. Może mieć skutki uboczne. To może zrobić wszystko.Szczerze mówiąc, uważam, że to słaby argument. Oba języki są pełne takich rzeczy. (Czy powinniśmy unikać przeciążania operatorów również w C #? W końcu można tam użyć tego samego argumentu)
Właściwości pozwalają na abstrakcję. Możemy udawać, że coś jest zwykłym polem i używać go tak, jakby nim było, i nie martwić się o to, co dzieje się za kulisami.
Zwykle uważa się to za dobrą rzecz, ale oczywiście polega na pisaniu przez programistę znaczących abstrakcji. Twoje właściwości powinny zachowywać się jak pola. Nie powinny mieć skutków ubocznych, nie powinny wykonywać drogich lub niebezpiecznych operacji. Chcemy myśleć o nich jak o polach.
Mam jednak inny powód, by uważać je za mniej niż doskonałe. Nie można ich przekazywać przez odwołanie do innych funkcji.
Pola można przekazywać jako
ref
, umożliwiając wywoływanej funkcji bezpośredni do nich dostęp. Funkcje można przekazywać jako delegatów, umożliwiając wywoływanej funkcji bezpośredni do nich dostęp.Właściwości ... nie mogą.
To jest do bani.
Ale to nie znaczy, że właściwości są złe lub nie powinny być używane. Z wielu powodów są świetne.
źródło
ref
; element członkowski (np.Foo
) formularzavoid Foo<T>(ActionByRef<Point,T> proc, ref T param)
ze specjalnym atrybutem, a kompilator przekształci gothing.Foo.X+=someVar;
wFoo((ref Point it, ref int param)=>it.X += param, ref someVar);
. Ponieważ lambda jest delegatem statycznym, nie będzie wymagane żadne zamknięcie, a użytkownik obiektu będzie mógł użyć dowolnej lokalizacji magazynu, dla której właściwość jest obsługiwana jako prawdziwyref
parametr (i może przekazać ją do innych metod jakoref
parametr).W 2009 roku ta rada wydawała się po prostu przekleństwem odmiany Who Moved My Cheese . Dziś jest prawie śmiesznie przestarzały.
Jedną z bardzo ważnych kwestii, na którą wiele odpowiedzi wydaje się chodzić na palcach, ale nie do końca się do nich odnosi, jest to, że te rzekome „niebezpieczeństwa” związane z właściwościami są celową częścią projektu struktury!
Tak, nieruchomości mogą:
Określ różne modyfikatory dostępu dla metody pobierającej i ustawiającej. To przewaga nad polami. Powszechnym wzorcem jest posiadanie publicznego pobierającego i chronionego lub wewnętrznego ustawiacza, bardzo użytecznej techniki dziedziczenia, której nie można osiągnąć za pomocą samych pól.
Podaj wyjątek. Do tej pory jest to jedna z najskuteczniejszych metod walidacji, zwłaszcza podczas pracy ze strukturami interfejsu użytkownika, które obejmują koncepcje powiązań danych. O wiele trudniej jest zapewnić, że obiekt pozostanie w prawidłowym stanie podczas pracy z polami.
Wykonanie zajmie dużo czasu. Prawidłowe porównanie dotyczy metod , które zajmują równie dużo czasu - nie pól . Nie podano żadnej innej podstawy dla stwierdzenia „preferowana jest metoda” niż osobiste preferencje jednego autora.
Zwraca różne wartości z jego metody pobierającej w kolejnych wykonaniach. Wydaje się to prawie żartem w tak bliskiej odległości od punktu wychwalającego zalety
ref
/out
parametry z polami, których wartość pola po aref
/out
wywołaniu jest prawie gwarantowana, że różni się od poprzedniej wartości, i to w sposób nieprzewidywalny.Jeśli mówimy o konkretnym (i praktycznie akademickim) przypadku dostępu jednowątkowego bez połączeń doprowadzających, to jest całkiem dobrze zrozumiałe, że to po prostu zły projekt właściwości, aby mieć efekty uboczne zmieniające stan widzialny, a może moja pamięć jest zanikają, ale nie mogę sobie przypomnieć żadnych przykładów ludzi używających
DateTime.Now
i oczekujących, że za każdym razem wyjdzie ta sama wartość. Przynajmniej nie ma żadnych przypadków, w których nie schrzaniliby tego tak samo źle z hipotetąDateTime.Now()
.Wywołuj obserwowalne skutki uboczne - i to jest właśnie powód, dla którego właściwości zostały wymyślone przede wszystkim jako cecha języka. Wytyczne firmy Microsoft dotyczące projektowania właściwości wskazują, że kolejność ustawiaczy nie powinna mieć znaczenia, ponieważ w przeciwnym razie oznaczałoby to czasowe sprzężenie . Z pewnością nie można osiągnąć czasowego sprzężenia z samymi polami, ale to tylko dlatego, że nie można w ogóle spowodować żadnego znaczącego zachowania z samymi polami, dopóki nie zostanie wykonana jakaś metoda.
Metody dostępu do właściwości mogą w rzeczywistości pomóc w zapobieganiu niektórym typom sprzężeń czasowych przez wymuszenie na obiekcie prawidłowego stanu przed podjęciem jakiejkolwiek akcji - na przykład, jeśli klasa ma a
StartDate
i anEndDate
, wówczas ustawienieEndDate
przedStartDate
może również wymusićStartDate
cofnięcie. Dzieje się tak nawet w środowiskach wielowątkowych lub asynchronicznych, w tym w oczywistym przykładzie interfejsu użytkownika sterowanego zdarzeniami.Inne rzeczy, które mogą robić właściwości, których pola nie mogą obejmować:
Type
lub innychName
klas pochodnych, może dostarczyć interesujących, ale jednak stałych metadanych o sobie.Item(i)
połączeń, uzna za cudowną rzecz.Richter jest wyraźnie płodnym autorem i wie dużo o CLR i C #, ale muszę powiedzieć, że wydaje się, że kiedy pierwotnie napisał tę poradę (nie jestem pewien, czy jest to w jego nowszych wersjach - mam szczerą nadzieję, że nie) że po prostu nie chciał porzucić starych nawyków i miał problemy z zaakceptowaniem konwencji C # (na przykład w porównaniu z C ++).
Rozumiem przez to, że jego argument o „właściwościach uważanych za szkodliwe” sprowadza się zasadniczo do jednego stwierdzenia: właściwości wyglądają jak pola, ale mogą nie działać jak pola. Problem z tym stwierdzeniem polega na tym, że nie jest ono prawdą lub w najlepszym przypadku jest bardzo mylące. Właściwości nie wyglądają jak pola - przynajmniej nie powinny wyglądać jak pola.
Istnieją dwie bardzo mocne konwencje kodowania w C # z podobnymi konwencjami wspólnymi dla innych języków CLR, a FXCop będzie na ciebie krzyczeć, jeśli ich nie będziesz przestrzegać:
W związku z tym nie ma niejednoznaczności co do tego, czy
Foo.Bar = 42
jest to metoda dostępu do właściwości, czy do pola. Jest to metoda dostępu do właściwości i powinna być traktowana jak każda inna metoda - może być powolna, może zgłosić wyjątek, itp. Taka jest natura abstrakcji - to całkowicie zależy od klasy deklarującej, jak zareagować. Projektanci klas powinni stosować zasadę najmniejszego zaskoczenia, ale dzwoniący nie powinni niczego zakładać na temat nieruchomości poza tym, że robi to, co mówi na puszce. To jest celowe.Alternatywą dla właściwości są wszędzie metody pobierające / ustawiające. Takie jest podejście Java i od samego początku było kontrowersyjne . W porządku, jeśli to twoja torba, ale po prostu tak nie jest w obozie .NET. Próbujemy, przynajmniej w ramach systemu statycznego, unikać tego, co Fowler nazywa szumem syntaktycznym . Nie chcemy dodatkowych nawiasów, dodatkowych
get
/set
brodawek ani dodatkowych sygnatur metod - nie, jeśli możemy ich uniknąć bez utraty przejrzystości.Powiedz, co chcesz, ale
foo.Bar.Baz = quux.Answers[42]
zawsze będzie to o wiele łatwiejsze do przeczytania niżfoo.getBar().setBaz(quux.getAnswers().getItem(42))
. A kiedy czytasz tysiące linijek tego dnia, to robi różnicę.(A jeśli twoją naturalną odpowiedzią na powyższy akapit jest powiedzenie „z pewnością trudno go przeczytać, ale byłoby łatwiej, gdybyś podzielił to na wiele wierszy”, to przykro mi, że całkowicie przegapiłeś ten punkt .)
źródło
Nie widzę żadnych powodów, dla których ogólnie nie powinieneś używać Właściwości.
Automatyczne właściwości w C # 3+ tylko trochę upraszczają składnię (a la syntatic sugar).
źródło
To tylko opinia jednej osoby. Przeczytałem sporo książek w języku C # i nie widziałem jeszcze nikogo mówiącego „nie używaj właściwości”.
Osobiście uważam, że właściwości są jedną z najlepszych rzeczy w języku C #. Pozwalają ci ujawnić stan za pomocą dowolnego mechanizmu. Możesz leniwie utworzyć instancję, gdy coś jest używane po raz pierwszy, i możesz przeprowadzić walidację ustawiania wartości itp. Używając ich i pisząc, myślę o właściwościach jako o ustawieniach i pobierających, co jest o wiele ładniejszą składnią.
Jeśli chodzi o zastrzeżenia dotyczące właściwości, jest kilka. Jeden jest prawdopodobnie niewłaściwym wykorzystaniem właściwości, drugi może być subtelny.
Po pierwsze, właściwości to rodzaje metod. Może być zaskakujące, jeśli umieścisz skomplikowaną logikę we właściwości, ponieważ większość użytkowników klasy będzie oczekiwać, że właściwość będzie dość lekka.
Na przykład
Uważam, że stosowanie metod w tych przypadkach pomaga dokonać rozróżnienia.
Po drugie, co ważniejsze, właściwości mogą mieć skutki uboczne, jeśli ocenisz je podczas debugowania.
Załóżmy, że masz taką nieruchomość:
Teraz załóżmy, że masz wyjątek, który występuje, gdy wykonano zbyt wiele zapytań. Zgadnij, co się dzieje, gdy zaczynasz debugowanie i przenoszenie właściwości w debugerze? Złe rzeczy. Unikaj tego! Spojrzenie na nieruchomość zmienia stan programu.
To są jedyne zastrzeżenia, jakie mam. Myślę, że zalety nieruchomości znacznie przewyższają problemy.
źródło
Powód ten musiał zostać podany w bardzo konkretnym kontekście. Zwykle jest na odwrót - zaleca się używanie właściwości, ponieważ zapewniają one poziom abstrakcji umożliwiający zmianę zachowania klasy bez wpływu na jej klientów ...
źródło
Nie mogę się powstrzymać od drążenia szczegółów opinii Jeffreya Richtera:
Źle: Pola mogą być oznaczone jako tylko do odczytu, więc tylko konstruktor obiektu może do nich pisać.
Błąd: implementacja klasy może zmienić modyfikator dostępu pola z publicznego na prywatny. Próby odczytania pól prywatnych w czasie wykonywania zawsze kończą się wyjątkiem.
źródło
Nie zgadzam się z Jeffreyem Richterem, ale domyślam się, dlaczego nie lubi nieruchomości (nie czytałem jego książki).
Chociaż właściwości są jak metody (pod względem implementacji), jako użytkownik klasy spodziewam się, że jej właściwości zachowują się „mniej więcej” jak pole publiczne, np .:
Niestety, widziałem nieruchomości, które nie zachowywały się w ten sposób. Ale problemem nie są same nieruchomości, ale ludzie, którzy je wdrożyli. Więc to wymaga tylko trochę edukacji.
źródło
Jest czas, kiedy rozważam użycie właściwości, a jest to pisanie kodu .Net Compact Framework. Kompilator CF JIT nie wykonuje takiej samej optymalizacji, jak kompilator JIT dla komputerów stacjonarnych i nie optymalizuje prostych metod dostępu do właściwości, więc w tym przypadku dodanie prostej właściwości powoduje niewielkie rozdęcie kodu przy użyciu pola publicznego. Zwykle nie stanowiłoby to problemu, ale prawie zawsze w świecie Compact Framework masz do czynienia z ciasnymi ograniczeniami pamięci, więc nawet małe oszczędności takie jak ta mają znaczenie.
źródło
Nie powinieneś unikać ich używania, ale powinieneś używać ich z kwalifikacjami i ostrożnością, z powodów podanych przez innych współpracowników.
Kiedyś zobaczyłem właściwość o nazwie coś w rodzaju Klienci, która wewnętrznie otworzyła połączenie poza procesem do bazy danych i przeczytała listę klientów. Kod klienta miał „for (int i to Customers.Count)”, co powodowało oddzielne wywołanie bazy danych w każdej iteracji i dla dostępu wybranego Klienta. To skandaliczny przykład, który demonstruje zasadę utrzymywania nieruchomości w bardzo lekkim świetle - rzadko więcej niż dostęp do wewnętrznego pola.
Jednym z argumentów dla używania właściwości jest to, że pozwalają one na walidację ustawianej wartości. Innym jest to, że wartość właściwości może być wartością pochodną, a nie pojedynczym polem, na przykład TotalValue = kwota * ilość.
źródło
Osobiście używam właściwości tylko podczas tworzenia prostych metod get / set. Odchodzę od tego, dochodząc do skomplikowanych struktur danych.
źródło
Argument zakłada, że właściwości są złe, ponieważ wyglądają jak pola, ale mogą wykonywać zaskakujące działania. To założenie jest obalone przez oczekiwania programistów .NET:
Właściwości nie wyglądają jak pola. Pola wyglądają jak właściwości.
Zatem pole jest jak właściwość, która gwarantuje, że nigdy nie zgłosi wyjątku.
Zatem pole jest jak właściwość, ale ma dodatkowe możliwości: przechodzenie do
ref
/out
accept metody.Zatem pole jest jak szybka właściwość.
Zatem pole jest jak właściwość, która gwarantuje zwrócenie tej samej wartości, chyba że pole zostało ustawione na inną wartość.
Ponownie, pole jest właściwością, która na pewno tego nie robi.
To może być zaskakujące, ale nie dlatego, że jest to właściwość, która to robi. Chodzi raczej o to, że prawie nikt nie zwraca mutowalnych kopii we właściwościach, więc wielkość 0,1% jest zaskakująca.
źródło
Wywoływanie metod zamiast właściwości znacznie zmniejsza czytelność wywołującego kodu. Na przykład w J # używanie ADO.NET było koszmarem, ponieważ Java nie obsługuje właściwości i indeksatorów (które są zasadniczo właściwościami z argumentami). Wynikowy kod był wyjątkowo brzydki, z pustymi wywołaniami metody nawiasów w każdym miejscu.
Obsługa właściwości i indeksatorów jest jedną z podstawowych zalet języka C # w porównaniu z Javą.
źródło