Właściwości
AdamW
Właściwości to specjalna konstrukcja języka C# która symuluje zachowanie pól klasy lub struktury, a w rzeczywistości jest trochę podobna do funkcji.
Przykład deklaracji właściwości:
class Klasa
{
public int Wlasciwosc
{
get
{
// co ma zostać zwrócone przy odwołaniu do tej właściwości
}
set
{
// co ma stać się z wartością przypisaną do tej właściwości
}
}
}
Taka konstrukcja (czysta właściwość) jest rzadko stosowana, znacznie częściej tworzy się właściwości współpracujące z konkretnym polem klasy.
Przykład:
class Klasa
{
private int pole; // deklaracja właściwego pola klasy. Jest ono - jak nakazuje dobry zwyczaj programowania obiektowego - ukryte przed obiektami z zewnątrz.
public int Wlasciwosc // właściwość
{
get // blok gettera
{
return pole; // nie robi nic oprócz zwrócenia wartości pola.
}
set // blok settera
{
pole=value; // nie robi nic oprócz przypisania do pola.
}
}
}
Od C# 3.0 możliwy jest również zapis skrócony (kod równoważny)
class Klasa
{
public int Property { get; set; }
}
który oznacza właśnie właściwość zachowującą się jak zwykłe pole. Przez kompilator ten kod jest rozwijany do
[CompilerGenerated]
private int <Property>k_BackingField;
public int Property
{
get
{
return this.<Property>k_BackingField;
}
set
{
this.<Property>k_BackingField = value;
}
}
W obydwóch przypadkach jest to maksymalnie okrojona właściwość która w zachowaniu nie różni się niczym od publicznego pola. Jednak bloki get i set wykonują się jak w normalnej metodzie, i nic nie stoi na przeszkodzie żeby je jakoś wzbogacić.
value jest słowem kluczowym mającym specjalne znaczenie jedynie w bloku settera właściwości. Przedstawia on w sposób symboliczny wartość przypisywaną właściwości".
Właściwość która również nie robi niczego pożytecznego, ale demonstruje przykładowe zastosowanie:
class Klasa
{
private int pole; // deklaracja właściwego pola klasy. Jest ono - jak nakazuje dobry zwyczaj programowania obiektowego - ukryte przed obiektami z zewnątrz.
public int Wlasciwosc // właściwość
{
get // blok gettera
{
Console.WriteLine("Ktoś pobiera moją wartość!");
return pole; // zwraca wartość pola.
}
set // blok settera
{
Console.WriteLine("Ktoś zmienia moją wartość!");
pole=value; // przypisuje wartość do pola.
}
}
}
Można też tworzyć pola tylko do odczytu nie definiując metody set.
Różnice między właściwościami i polami, rzeczywista reprezentacja właściwości
Na pierwszy rzut oka zmienna:
int Count;
i właściwość (zapis skrócony):
int Count {get; set;}
wyglądają podobnie. Na drugi zresztą też, bo odwoływanie do obydwóch jest takie same, czyli
int i = Count;
Count = i;
Tak naprawdę jednak property (czyli te gettery i settery) mają za zadanie symulować konstrukcje znane np. z C++
private:
int count;
public:
int getCount() { return count; }
void setCount(int value) { count = value; }
Ok, wracamy do C#. I rzeczywiście - na poziomie kodu "maszynowego" (czyli w przypadku C# oczywiście kodu pośredniego) zwykła zmienna zostanie zapisana (w przenośni, nie na poziomie IL)
int Count;
co innego jeśli użyjemy właściwości. zapis int Count {get; set;} da nam w wyniku skądinąd znajomą konstrukcję...
public int get_Count();
public int set_Count(int value);
I w rzeczywistości odwołanie się do zmiennej również jest intuicyjne - czyli zmienna Count = 10; da nam to samo w kodzie wynikowym, gdy tymczasem i = property Count; da w wyniku i = get_Count(); Nie musisz mi wierzyć na słowo, dodaj do dowolnego swojego kodu deklarację funkcji z przedrostkiem get_ tak żeby kolidowała z nazwą właściwości a otrzymasz błąd kompilacji (Type (typ)' already reserves a member called 'get_(nazwa property)' with the same parameter types).
Konsekwencją powyższego (właściwości są metodami) jest też to że można bezproblemowo zakładać na nie breakpointy - nie jest to możliwe w przypadku pól. Tłumaczy to też dlaczego właściwości można umieszczać w interfejsach, a pola już nie.
Jak wiadomo właściwości (czyli jakiekolwiek reagowanie na zmianę / pobranie zmiennej) są jednak czasami potrzebne. W niewielkim projekcie zmienienie zmiennej w właściwość nie powinno być trudne (po prostu wystarczy dopisać {get;set;}) - ale w sporym projekcie może być gorzej, jeśli np. zmienna w jednej klasie zostanie zmieniona we właściwość to wszystkie pozostałe biblioteki korzystające z tej klasy będą musiały być ponownie skompilowane (bo nie znajdą zmiennej nazwanej - zostawmy już to hipotetyczne - Count, a nic ich nie będzie obchodzić metoda get_Count(). ).
Dlatego właśnie dobrym zwyczajem jest też izolowanie pól klasy od świata zewnętrznego.
"W obydwóch przypadkach jest to maksymalnie okrojona właściwość która w zachowaniu nie różni się niczym od publicznego pola." <- Czy nie powinno być tutaj napisane "prywatnego pola" zamiast "publicznego pola" ?
Samo pole jest jest tutaj prywatne nie ma do niego bezpośredniego dostępu w innej klasie, jednak mamy do niego dostęp przy pomocy publicznej właściwości.
public int get_Count();
public int set_Count(int value);
nie powinno być:
public int get_Count();
public void set_Count(int value);