Jason. Cała informatyka w jednym miejscu!

Nareszcie. Przed Wami jeden z najważniejszych tematów jakie przyjdzie Wam opanować w "CSharpie" :O. Czy spotkaliście się już z parą słów kluczowych "get" i "set" w języku C#? Jeśli jeszcze nie, to od razu piszę: będziecie to często widzieć. Nawet BARDZO! Pozwólcie mi wejść w szczegóły, abym Wam zobrazował powagę sytuacji, żebyście wzięli to na serio.

PARA SŁÓW "GET" I "SET" W JĘZYKU C# STANOWI JEDEN Z NAJWAŻNIEJSZYCH BUDULCÓW!

Może to brzmieć jak stara śpiewka albo zacięta płyta :D, jednak muszę to znowu podkreślić, że oto czytacie o jednej z najważniejszych podstawowych konstrukcji jakie występują nie tylko w samym języku, ale także w wielu cudzych kodach źródłowych. Jak będziecie mieli okazję się przekonać, będą ku temu wyraźne powody, żeby porzucić stare schematy znane z języka Java na rzecz właściwości "get" i "set" w języku C#, bo właśnie o nie mi się rozchodzi.

MOŻNA JAŚNIEJ?

Kiedy w "CSharpie" mowa o właściwości, to chodzi o dosłowne skróty metod, których jedynym zadaniem jest pobranie wartości danej składowej i przypisanie wartości danej składowej (albo jedno z dwóch, w zależności od pożądanego efektu). Pewnie niektórzy z Was kojarzą te definicje metod w Javie, które powodowały takie poszerzenie kodu źródłowego, że to głowa mała :). Można by rzec, "pain in the ass" :P:

public class MyClass {
	private int x;
	
	public void setX(int x) {
		this.x = x;
	}
	
	public int getX() {
		return x;
	}
}

No, serio! Tak trzeba pisać w Javie. Wyobraźcie sobie definiować każdą taką samą parę metod dla choćby kilku kolejnych danych składowych. Zastrzelić się można :O! Dlatego twórcy języka C# wpadli na pomysł, żeby udostępnić O WIELE prostszą i bardziej zwięzłą metodę definiowania takich "getter'ów" (w przypadku zwracania wartości) i "setter'ów" (w przypadku przypisywania wartości). Oba te pojęcia, "getter" i "setter" są anglojęzycznymi odpowiednikami terminów jakie też możecie czasem usłyszeć. Tak się przyjęło, że "getter" jest polskim "akcesorem", a "setter" jest polskim "mutatorem". Nie gniewajcie się proszę, jeśli ktoś stwierdzi że to nie tak. Te terminy są tak często ze sobą przeplatane, że zrobiła się z tego kwestia gustu :).

PRZYKŁAD KODU ŹRÓDŁOWEGO

Po teoretycznym wstępie, powinno się już co nieco rozjaśnić w łepetynach po co w ogóle ta fatyga, żeby wykorzystywać parę "get" i "set" w języku C# :P. Teraz zaprezentuję bardzo prosty kod źródłowy wykorzystujący właściwość, abyście to sobie porównali do Javy. Przypuśćmy, że mamy taką sytuację jak powyżej. Chcemy w C# umożliwić dostęp do danej składowej, ale tak żeby była cały czas prywatna (a już dobrze wiemy dlaczego POWINNY być prywatne):

class MyClass
{
	private int x;
	
	public int X
	{
		get
		{
			return x;
		}
		
		set
		{
			x = value;
		}
	}
}

To jest najsurowsza postać właściwości wykorzystującej "get" i "set" w języku C# (jak zobaczycie w dalszej części, to można ten zapis wygodnie sprowadzić do mniejszej liczby linijek). Jak możecie zauważyć, dodałem sobie pod daną składową rozległą konstrukcję. To jest ta właściwość wokół której kręci się cały materiał. Wyglądem struktury przypomina definicję metody. Tu także jest modyfikator dostępu, typ zwracanej wartości i klamerki, jednak brak jest nawiasów okrągłych po nazwie. O dziwo, nie wstawiamy tym razem średników ani na końcu, ani pomiędzy akcesorem i mutatorem. Zainteresujmy się teraz tym, co jest w środku właściwości.

W ROLACH GŁÓWNYCH: "GET" I "SET" W JĘZYKU C#

Właściwość zademonstrowana na górze posiada zarówno akcesor, jak i mutator. Co to dla nas za sygnał? Że za jej pomocą będzie można jednocześnie pobrać wartość i przypisać wartość. "get" służy do zwracania wartości i on musi koniecznie zwrócić jakąś wartość przy pomocy "return" (może to być zmienna, może to być stała np. 16), zgodną z określonym typem danych. "set" z kolei to przypisywanie wartości wyznaczonej przez programistę danej składowej. Tu Was zatrzymam na dłużej w dalszej części, bo trzeba wyjaśnić jeden szczegół.

Aby nie popełnić gafy składniowej i nie zamknąć sobie dostępu do kompilacji, zarówno "get" jak i "set", wymagają osobnej pary klamerek celem zapewnienia im osobnego bloku instrukcji. Najprostsze bloki wyglądają tak, jak powyżej. Grunt, żeby "get" coś zwróciło, a "set" przypisało. Nie musicie definiować za każdym razem wejścia i wyjścia. Możecie na przykład zbudować właściwość, która będzie mogła jedynie zwracać wartość:

public int X
{
	get
	{
		return x;
	}
}

tym samym zamykając grodzie do przypisywania :). Manewr "vice-versa" także jest dostępny.

JAK KORZYSTAĆ Z TEGO "COSIKA"?

Bardzo prosto. Przypomnijcie sobie odwoływanie się do "zwykłej" danej składowej poprzez instancję klasy. Znajome?

MyClass mc = new MyClass();

mc.X = 30;

Console.WriteLine(mc.X);

Na tym odpowiedź na Wasze pytanie się kończy.

CO TO ZA "VALUE" SIĘ TAM ZNAJDUJE?

Widzicie frazę "value"? To jest słowo kluczowe! W przypadku "settera" oznacza odniesienie do wartości, która będzie występować po prawej stronie operatora przypisania, za każdym razem kiedy będziemy chcieli coś zmodyfikować za pośrednictwem właściwości.

Gdyby nadal to było niezrozumiałe, popatrzcie na to:

MyClass mc = new MyClass();

mc.X = 30;		// value = 30

Tyle! To jest każda wartość podstawiona jako prawostronny operand operatora przypisania, w tym przypadku to jest liczba 30.

WALIDACJA DANYCH DZIĘKI WŁAŚCIWOŚCI

Trzeba napomknąć o tym, że "get" i "set" w języku C#, oprócz widocznego gołym okiem efektywnego redukowania obszerności kodu, dysponuje jeszcze możliwością łatwego zaprogramowania sprawdzania poprawności danych. Dajmy na to, że mamy właściwość kontrolującą numer poziomu gry. Przecież nie może być ujemna, przynajmniej z reguły. To wtedy, kiedy właściwość posiada mutator do edycji wartości, ma prawo wyglądać na przykład w taki sposób:

private int levelNumber;

public int LevelNumber
{
	set
	{
		if(value > 0)
		{
			levelNumber = value;
		}
	}
}

Czytelnicy posiadający wiedzę o działaniu instrukcji warunkowej już powinni "czaić bazę" :D. W sytuacji, kiedy do właściwości zostanie przypisana wartość mniejsza od zera, nie dojdzie do ŻADNEJ modyfikacji! Tym sposobem, zapewniamy podstawową ochronę przed wstawianiem bzdurnych wartości i wątpliwym logicznie oraz potencjalnie wadliwym działaniem programu. "getter" także może mieć tego typu nałożoną instrukcję, jednak o wiele rzadziej potrzebuje się ochrony podczas "wyciągania" wartości.

WŁAŚCIWOŚĆ NIE MUSI BYĆ ZWIĄZANA Z ŻADNYM POLEM

Nagłówek może pobudzić wyobraźnię kierując Was na różne hipotezy, ja jednak oszczędzę Wam umysłowego wysiłku i napiszę, że definiując właściwość może być ona całkowicie niezależna od jakiejkolwiek danej składowej (pola). Spróbujcie sobie wyobrazić, że klasa dysponuje właściwością zaopatrzoną jedynie w "getter". A w nim…

public string MyFavouriteProgrammingLanguage
{
	get
	{
		return "C#";
	}
}

Właśnie. To dokładnie miałem na myśli. Jak wspomniałem wcześniej, blok "get" musi zwrócić COKOLWIEK. Natomiast to nie musi być zmienna! To może być także "sztywna" dana, czyli stała :).

AUTOMATYCZNA IMPLEMENTACJA

Teraz pokażę Wam coś ekstra. Wróćmy na moment do zdania, że można znacznie uprościć najbardziej surowy zapis:

public int X
{
	get
	{
		return x;
	}
	
	set
	{
		x = value;
	}
}

Ano można. Czytelnicy, patrzcie na to :D!

public int X {get; set;}

Zaskoczeni? W terminologii znalazłem frazę "automatyczna implementacja", a jej efekt ukazuje powyższy fragment kodu. Oznacza to wygenerowanie "gettera" i "settera" w identyczny sposób, co ukazano jako pierwszy kod źródłowy w przedstawianym zagadnieniu. Dozwolone jest także wybranie jednego z dwóch słów kluczowych, aby droga do pola była jednokierunkowa:

public int X {get;}

Można z tego skorzystać tylko pod jednym warunkiem: nie będziemy potrzebowali żadnej niestandardowej implementacji wewnątrz bloków "get" i "set" w języku C#. Czyli jeżeli mają być jakieś walidacje danych, to one robią "pa pa!" i trzeba zbudować kod w sposób tradycyjny ;).

AUTOMATYCZNA INICJALIZACJA

Niespodzianek nie ma końca! Od C# 6.0 macie możliwość jednoczesnej implementacji ORAZ inicjalizacji w jednej linijce kodu :D! Żeby już nie przedłużać, chodzi o taki myk:

public int Money {get; set;} = 100;

Posiadacze C# niższych wersji muszą sobie radzić w taki sposób:

public int Money {get; set;}

public MyClass()
{
	Money = 100;
}

KONWENCJE NAZEWNICZE

Wytrzymajcie jeszcze chwilę na przedstawienie powszechnych konwencji. Zasady są takie same jak przy zmiennych "zwykłych". Jedyna maleńka różnica dotyczy rozpoczynania nazwy od wielkiej litery ("PascalCase"), tak jak przy funkcjach. To wszystko.

Właściwości "get" i "set" w języku C#

Właściwości w języku C# znacznie redukują liczbę wierszy kodu zastępując staromodne definicje metod z języka Java.


Postarajcie się nie przeklinać na obszerność artykułu :). Zrobiłem co w mojej mocy, żeby wszystko co najważniejsze, ująć w dosyć prosty język. Mnie zawsze obchodzi to, czy to komuś pomogło. Jeśli tak, misja wykonana :P.

PODOBNE ARTYKUŁY