P-Programowanie
Tekst
zmniejsz/powiększ
Kolory
jasne/ciemne/kontrast/brak

Właściwości i akcesory get set

Z akcesorów get i set korzysta każdy kto programuje w C#. Stanowią one duże udogodnienie w programowaniu obiektowym. Zapewniają wygodę, bezpieczeństwo i znacząco skracają kod. Akcesory są ściśle związane z właściwościami, dlatego długo zastanawiałem się nad odpowiednim tytułem dla tego artykułu. Są też związane z językiem C# i nie spotkamy ich np. w Javie.

Czym są właściwości

Właściwość to konstrukcja charakterystyczna m.in. dla języka C#. Zapewnia dostęp do pól klasy posługując się przy tym akcesorami get i set. Główną funkcjonalnością właściwości jest możliwość zapisywania i odczytywania prywatnych pól klasy, tak jak by były publiczne.

Kiedy usłyszałem o właściwościach po raz pierwszy, w ogóle ich nie rozumiałem i nie byłem co do nich przekonany. Z biegiem czasu uświadomiłem sobie, że używanie ich w rozbudowanych projektach jest koniecznością, szczególnie jeżeli nad projektem pracuje wiele różnych osób.

Poprawne projektowanie klas

Jednym z podstawowych filarów programowania obiektowego jest hermetyzacja. Mówi ona aby ukrywać składniki klasy, tak aby nie było dostępne z zewnątrz. Nigdy nie wiemy kto będzie pracował na napisanej przez nas klasie. Nie jest dobrym zwyczajem, a wręcz błędem, aby ktoś mógł edytować zmienne naszej klasy z zewnątrz.

Dlatego też wszelkie zmienne dostępne wewnątrz naszej klasy powinny być prywatne. Dostęp do najważniejszych pól, które mogą być dostępne z zewnątrz i świadomie taki dostęp zapewnimy, dostarczamy poprzez napisanie odpowiednich metod:

Jak widzisz, trzymamy się zasad poprawnego programowania obiektowego. Przykład jest dość trywialny. Pole wiek jest zmienną prywatną, aby ją zmodyfikować możemy posłużyć się jedynie metodami, które udostępnia klasa. Metody te są nazywane „setterami” i „getterami”.

Prywatna zmienna oraz przypisane do niej metody do odczytu i zapisu dają nam pewność, że inny programista pracujący na naszej klasie, będzie trzymał się naszych założeń. Przykładowo możemy rozbudować metodę aby akceptowalny wiek był z przedziału 1..99:

Gdyby _wiek był publiczny, nie posiadalibyśmy żadnej kontroli nad tym jaka będzie jego wartość.

Biedni programiści Javy nie mają konstrukcji nazwanej właściwościami. Oznacza to, że każdą zmienną w klasie muszą obudowywać takimi funkcjami jak w przykładzie wyżej. Oczywiście w rozbudowanych przykładach i dużych klasach setterów i getterów są dziesiątki, co tworzy niepotrzebny bałagan.

Ponieważ język C# był lekko wzorowany na Javie, jego projektanci uznali, że warto wprowadzić do języka konstrukcję, która zapewni bardziej praktyczny dostęp do funkcji setterów i getterów.

Używanie właściwości

Właściwość deklarujemy w sposób podobny do zmiennej, jednak w jej wnętrzu należy obsłużyć akcesory get oraz set. Używając właściwości, nie będziemy musieli sami pisać poszczególnych metod do każdego pola.

Przeróbmy przykład znajdujący się wyżej, tak aby zamiast metod setterów i getterów używał właściwości dostępnych w C#:

Obydwa programy zadziałają tak samo. W pierwszym użyliśmy zwykłych metod a w drugim właściwości.

W kodzie pojawiły się akcesory get oraz set. Pole _wiek nadal jest prywatne, natomiast właściwość Wiek jest publiczna. Zapewnia ona dostęp do prywatnego pola _wiek.

Akcesor get wywoływany jest w chwili gdy chcemy pobrać wartość właściwości.
Akcesor set wywołany jest w chwili nadania wartości właściwości.
Zwróć uwagę na słowo kluczowe value przy akcesorze set. Ma ono szczególną funkcję jedynie wewnątrz ciała właściwości – reprezentuje wartość przypisywaną do właściwości w akcesorze set.

Od tej chwili programista komunikuje się z polami klasy za pomocą publicznych właściwości. Mimo tego kod nadal jest stosunkowo długi. Na szczęście wprowadzona została deklaracja skrócona.

Skrócona deklaracja właściwości

Bardzo przydatne jest używanie skróconej wersji deklaracji właściwości. Pozwala ona zredukować ilość kodu do absolutnego minimum w prostych programach.

Projektanci języka C# zdali sobie sprawę, że w przeważającej większości właściwość nie będzie miała żadnych dodatkowych funkcji, oprócz wyprowadzania/wprowadzania wartości dla pola prywatnego. Z tego powodu została wprowadzona możliwość skróconej deklaracji właściwości.

Znowu przerobię prosty programik znany z poprzednich przykładów:

Środowisko automatycznie rozwinie skróconą deklarację właściwości do normalnej postaci  podczas kompilacji. Kompilator niejawnie utworzy nawet pole prywatne przypisane do publicznej właściwości. Dowodem tego jest analiza języka CIL po zdebugowaniu aplikacji.

Co sądzisz o tym zapisie? Program działa identycznie tak jak w poprzednich przykładach. Porównując teraz właściwości w języku C# do ich braku w Javie, widać jak bardzo są przydatne i jak bardzo potrafią skrócić kod.

Tworząc klasę Osoba o przykładowych właściwościach Imie, Nazwisko, Wiek w klasie posiadamy 3 linijki kodu. Pisząc tę samą klasę w Javie lub C++ i hermetyzując dane, będziemy mieli około 12 linijek kodu.

Podsumowanie

Zgodnie z informacjami dostępnymi na MSDN, nie powinno się umieszczać zaawansowanych fragmentów kodu we właściwościach. Właściwości powinny w prosty sposób zwracać lub zapisywać wartości określonych pól. Dopuszczalne są jedynie proste operacje w stylu:

Jeżeli pisałeś wcześniej w innym języku nie zniechęcaj się do funkcjonalności jakie zapewnia C#. Używanie właściwości i akcesorów pozwala znacznie zredukować kod a na dodatek narzuca schemat poprawnego programowania obiektowego.

Dodam na koniec, że niektóre źródła podają nazwę własności zamiast właściwości.

Udostępnij ten artykuł na fejsie lub zostaw komentarz!

Komentarze:

Użytkownik Jan napisał/a:

19 stycznia 2014


No właśnie przeczytałem. Dzięki, wszystko jasne wytłumaczone. Lepiej niż w książce „Head First C#”.
Dzięki.

Użytkownik user napisał/a:

26 lutego 2014


Bardzo jasne wyjaśnienie! Nie rozumiałem o co w tym chodzi na wykładach a w internecie znalazłem opisy ale bez przykładów. Bardzo dobry blog!!!

Użytkownik Cezar napisał/a:

04 marca 2014


Po przeczytaniu tego artykułu zrozumiałem po co jest get set.
Supcio.

Użytkownik Rafał napisał/a:

23 maja 2014


Bardzo dobrze wytłumaczone. Jedne z lepszych artykułów w sieci.

Użytkownik Kuba napisał/a:

28 maja 2014


W końcu wytłumaczone jak trzeba. Dla wielu programistów piszących książki te pojęcia są tak oczywiste, że nie zwracają uwagi na fakt, że osoba początkująca może tego nie zrozumieć. Dlatego też wiele osób po rozpoczęciu nauki programowania zaczyna szybko zniechęcać się.
W każdym razie ta stronka wpada do „Ulubionych” :)

Użytkownik Andrzej napisał/a:

19 sierpnia 2014


Fajny artykuł!

Użytkownik Aga napisał/a:

04 września 2014


Konkretnie, przejrzyście i zrozumiale. Super. Dzięki wielkie. Karol czekam na podręcznik w Twoim wydaniu ;)

Użytkownik Ila napisał/a:

20 października 2014


Super:) Tak trzymać :)

Użytkownik Tuptuś :-P napisał/a:

27 listopada 2014


Czy mógłbyś prowadzić kurs programowania w C# jak np. Cezary Walenciuk? Masz dar tłumaczenia i mógłbyś pomóc wielu osobom. Jak byś mógł, zrób tak z innymi językami. Moja znajoma uwielbia Jav’ę, inna Android’a a jescze inna języki webmasterskie.

Użytkownik Karol napisał/a:

02 grudnia 2014


Aktualnie brak czasu na pisanie artykułów, a co dopiero całych kursów poukładanych działami. Ciężko trzymać się schematu kursów na blogu, musiałbym całkiem przebudować jego strukturę (linki/menu) lub stworzyć osobny serwis.

Użytkownik return 0; napisał/a:

15 września 2015


Dlaczego twierdzisz, że gdyby zmienna wiek miała ustawiony parametr public:, to nie moglibyśmy mieć żadnego wpływu na jej wartość? Bo z treści artykuły nic takiego nie wynika.

Użytkownik Karol napisał/a:

16 września 2015


Ktoś obcy pracujący na naszej klasie, lub źle napisany algorytm zapisujący wyniki do instancji naszej klasy, mógłby podać dowolną wartość atrybutu wiek. Używając akcesorów można kontrolować dopuszczalny zakres przyjmowanych wartości ograniczając go np. do przedziału 1<x<100.

Użytkownik Daniel napisał/a:

16 grudnia 2015


Bardzo dobra robota!

Użytkownik Tomel napisał/a:

02 lutego 2016


Witam, mam pytanie czy się różnie deklaracja
public int Wiek { get; set; } od public int Wiek;

przecież taka zmienna staję się publiczna i dostęp jest jak to każdej zmiennej

Użytkownik Karol napisał/a:

02 lutego 2016


Druga wersja to deklaracja publicznej zmiennej, a pierwsza to deklaracja publicznej właściwości. Zmienne w klasie powinny być tylko prywatne, jezeli chcemy ją udostępnić na zewnątrz klasy należy to zrobić przez odpowiednią metodę. Zamiast dopisywac do każdej zmiennej prywatnej po 2 metody do zapisywania i odczytywania wartości, lepiej użyć publicznej właściwości. Wskazuje ona na zmienną prywatną (której jawnie w zapisie skróconym nie widać) i odgrywa rolę metod do pobierania i nadpisywania wartości dla tej prywatnej zmiennej.

Użytkownik Krzysiek napisał/a:

22 marca 2016


cytat: „Kompilator niejawnie utworzy nawet pole prywatne przypisane do publicznej właściwości.”
Nie rozumiem jednej rzeczy:
deklaracja pola prywatnego zniknęła, więc nie wiadomo jak się teraz nazywa to pole…
Własność nazywa się Wiek – skąd więc teraz wiadomo do jakiego pola odnosi się ta własność? Do pola o takiej samej nazwie co własność?
Jak korzystać teraz w klasie Osoba z tego prywatnego pola, skoro nie jest ono jawnie zdefiniowane i nie wiadomo jaką ma nazwę?

Użytkownik Konrad napisał/a:

22 marca 2016


Dziękuję kolego, bardzo ładnie wytłumaczone, ja podczas lektury C# rusz głową trochę zgłupiałem, ale nawróciłeś mnie na dobrą drogę.

Użytkownik Karol napisał/a:

22 marca 2016


@Krzysiek
Nie możesz korzystać z utworzonego niejawnie pola prywatnego, ale warto wiedzieć, że tak właśnie to działa. Skrócona wersja właściwości jest rozwijana do pełnej postaci (a więc prywatna zmienna i podpięta do niej właściwość) ale przekonać się o tym można tylko przeglądając kod IL naszego programu. Aby zrzucić plik wykonywalny do kodu IL należy użyć diassemblera np. dołączonego do Visual Studio ildasm.exe.

Można spojrzeć na to w ten sposób: jeżeli z jakiegoś dziwnego powodu musisz mieć dostęp i do zmiennej prywatnej i do właściwości, wtedy tworzysz pełną postać właściwości ręcznie. Taka sytuacja występuje też w przypadku podpinania się do gotowych klas, w której z jakiegoś powodu chcemy wyprowadzić zmienną prywatną na zewnątrz klasy używając właściwości. Wtedy możemy łatwo się do takiej zmiennej „podpiąć”. W każdym innym wypadku po prostu tworzysz właściwość w postaci skróconej, dla Ciebie jest wygodnie ponieważ wcale nie martwisz się o prywatne zmienne i kod jest krótszy. Jednak mimo tego, kod ten zostanie rozwinięty do pełnej postaci podczas kompilacji i dowodem na to jest właśnie przeglądnięcie kodu IL.

Użytkownik Krzysiek napisał/a:

31 marca 2016


Czyli stosując skróconą wersję deklaracji właściwości w klasie zakładam, że będę korzystał w klasie tylko z tej właściwości (a nie z pola, do którego odnosiłaby się moja deklarowana właściwość) – dobrze rozumiem?
Chciałem tylko zrozumieć zastosowanie właściwości w samej klasie:
1. pierwotnie miałem w klasie deklaracje prywatnego pola – i z tego pola korzystam w klasie
2. dokładam „rozszerzoną” właściwość do pola prywatnego w klasie – mogę korzystać w klasie i z pola i z jego właściwości
2. zamieniam „rozszerzoną” właściwość na skróconą – to teraz mogę korzystać w klasie tylko z właściwości, czy tak?

@Karol
Tak

Użytkownik damian napisał/a:

03 sierpnia 2016


Świetnie wyjaśnione, dzięki!

Użytkownik Marcin napisał/a:

13 października 2016


Wytłumaczone w bardzo przejrzysty sposób.
Dzięki takiemu opisowi udało mi się zrozumieć jakie zalety ma taki sposób zapisu i dlaczego należy tego używać.

Użytkownik Martyna napisał/a:

13 grudnia 2016


Bardzo pomógł mi Pana artykuł. Dzięki wielkie i pozdrawiam. :)

Użytkownik Mateusz napisał/a:

06 stycznia 2017


Witam serdecznie,
to chyba najlepszy kurs C# jaki znalazłem w internecie, fajnie było by żeby powstał artykuł na temat dobrych i złych praktyk programowania w tym języku.

Mam pytanie co końcówki artykułu:
„Zgodnie z informacjami dostępnymi na MSDN, nie powinno się umieszczać zaawansowanych fragmentów kodu we właściwościach.”

Czy poniżej napisany fragment można uznać za zgodny ze sztuką?

abstract public class Element
{
public Point[] Corner { get; set; } = new Point[1];

virtual public int Lenght { get {return 1; } set { Corner[0] = new Point(); } }//<- czy jest to właściwe
}

ewentualnie kiedy fragment kodu można uznać za zaawansowany.

Pozdrawiam.

Użytkownik Karol napisał/a:

07 stycznia 2017


Cześć,
podany przez Ciebie fragment kodu wydaje się być dobry. Pomijam oczywiście jego logiczny sens. Microsoft podał, aby nie umieszczać we właściwościach zaawansowanych fragmentów kodu i właściwie na tym kończy się jakakolwiek informacja z ich strony. Gdy w pracy zdarza nam się umieszczać kod w kwantyfikatorach właściwości, przyjmujemy że nie jest on zbyt zaawansowanych gdy mieści się w jednej linijce kodu.

Użytkownik Marcin napisał/a:

19 lutego 2017


Bardzo pomocna strona. Pozdrawiam!

Zachęcam Cię do zostawienia komentarza!

Ilość znaków: 0

Zachęcam Cię do polubienia bloga na facebooku! Dając lajka wspierasz moją pracę - wszystkie artykuły na blogu są za darmo!