Właściwości
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:
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:
Od C# 3.0 możliwy jest również zapis skrócony (kod równoważny)
który oznacza właśnie właściwość zachowującą się jak zwykłe pole. Przez kompilator ten kod jest rozwijany do
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:
Można też tworzyć pola tylko do odczytu nie definiując metody set.
Na pierwszy rzut oka zmienna:
i właściwość (zapis skrócony):
wyglądają podobnie. Na drugi zresztą też, bo odwoływanie do obydwóch jest takie same, czyli
Tak naprawdę jednak property (czyli te gettery i settery) mają za zadanie symulować konstrukcje znane np. z C++
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)
co innego jeśli użyjemy właściwości. zapis int Count {get; set;} da nam w wyniku skądinąd znajomą konstrukcję...
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.
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.
Kategoria: C#