Struktury
VarrComodoo
Czym są struktury
Struktury są bardzo podobne do klas chociaż mają zdecydowanie mniejsze możliwości w stosunku do nich. Istotną różnicą pomiędzy strukturami a klasami jest to, że struktury są typu wartościowego, w przeciwieństwie do klas które są typu referencyjnego.
Określając czym są struktury, w różnych źródłach można spotkać stwierdzenia, że są to „uproszczone” klasy, „lekkie” klasy czasami „ograniczone” klasy. Wszystkie porównania stosowane są względem klasy, ponieważ jak pokazano na przykładzie poniżej, struktury do pewnego stopnia można stosować zamiennie zamiast klas.
Definiowanie struktur
Przykład 1:
struct WarunkiPogodowe
{
double temperatura;
double predkoscWiatru;
double wilgotnosc;
KierunekWiatru skadWieje;
//konstruktory, właściwości, metody, zdarzenia
}
enum KierunekWiatru
{
//wszystkie możliwie kierunki
N,
E,
S,
W
}
W powyższej definicji struktury, gdybyśmy zamienili słowo kluczowe struct na class uzyskalibyśmy klasę. Nic by to nie zmieniło w funkcjonalności obiektu WarunkiPogodowe prócz tego, że jako klasa stałby się typem referencyjnym. W drugą stronę nie zawsze możliwa, ani wskazana jest taka zamiana.
Struktur używa się przeważnie dla definicji typów zawierających kilka związanych ze sobą elementów danych, te elementy danych są reprezentowane przez pola takiej struktury. Typowym zastosowaniem struktur jest definiowanie takich typów jak:
• Punkt na powierzchni (pole1 współrzędnaX, pole2 współrzędnaY)
• Punkt w przestrzeni (pole1 współrzędnaX, pole2 współrzędnaY, pole3 współrzędnaZ)
• Ułamek (pole1 licznik, pole2 mianownik)
• Itp.
Struktury możemy definiować z różnym poziomem dostępu do niej, poprzedzając słowo kluczowe struct odpowiednim modyfikatorem dostępu.
Podobieństwa i różnice w stosunku do klas (konstruktory)
Podobnie jak klasy struktury jako składowe mogą zawierać: stałe, pola, metody, właściwości, indeksatory, zdarzenia i konstruktory. Jeżeli chodzi o definiowanie konstruktorów w strukturze to tu pojawia się pierwsza zasadnicza różnica w stosunku do klas. Otóż nie ma możliwości zdefiniowania konstruktora bezparametrowego struktury, kompilator na to nie pozwoli. Struktura ma zdefiniowany niejawnie domyślny konstruktor bezparametrowy który inicjuje wszystkie pola struktury ich wartościami domyślnymi i nie ma możliwości nadpisania tego konstruktora. Jak pokazuje poniższy przykład, taka konstrukcja definicji struktury jest błędna i kompilator nie pozwoli na jej skompilowanie:
struct WarunkiPogodowe
{
double temperatura;
double predkoscWiatru;
double wilgotnosc;
KierunekWiatru skadWieje;
public WarunkiPogodowe() /*błąd, nie można zdefiniować w struktorze konstruktora bezparametrowego*/
{
temperatura = -4;
predkoscWiatru = 60;
wilgotnosc = 40;
skadWieje = KierunekWiatru.N;
}
//konstruktory, właściwości, metody, zdarzenia
}
Aby zainicjować pola struktury innymi wartościami niż domyślne, należy zdefiniować nowy konstruktor z przynajmniej jednym parametrem, ale w takiej sytuacji również mamy ograniczenie. Niezależnie od tego ile parametrów wejściowych będzie miała definicja naszego konstruktora, w jego ciele muszą zostać zainicjowane **WSZYSTKIE ** pola struktury, popatrzmy na przykład dla naszej struktury WarunkiPogodowe:
//prawidłowo zdefiniowany konstruktor
public WarunkiPogodowe(int a)
{
temperatura = a;
predkoscWiatru = 60;
wilgotnosc = 40;
skadWieje = KierunekWiatru.N;
}
//nieprawidłowo zdefiniowany konstruktor
public WarunkiPogodowe(int a)
{
temperatura = a;
}
W drugim przypadku w konstruktorze inicjujemy pole temperatura wartością przekazaną w parametrze konstruktora ale zapomnieliśmy o pozostałych polach tej struktury, w takim przypadku skutecznie przypomni nam o tym kompilator w oknie Errors.
Podobieństwa i różnice w stosunku do klas (inicjowanie pól)
Skoro poruszyliśmy temat inicjowania pól w ciele konstruktora to wyczerpmy temat inicjowania pól w strukturach w ogóle. W klasach bez problemu możemy inicjować pola w chwili ich deklaracji w ciele klasy, w strukturach jest to niedozwolone. W strukturach przypisywać wartość do pól możemy tylko w konstruktorach (w konstruktorze inicjujemy WSZYSTKIE pola struktury) lub po utworzeniu obiektu tej struktury w kodzie klienta (dla przypadku tworzenia struktur, za chwilę temat bardziej się rozwinie). Przykład obrazujący błędne, niedozwolone inicjowanie pola w strukturze poniżej:
//niedozwolone przypisanie wartości do pola w chwili zadeklarowania tego pola
struct WarunkiPogodowe
{
double temperatura = -10;
double predkoscWiatru;
double wilgotnosc;
KierunekWiatru skadWieje;
//konstruktory, właściwości, metody, zdarzenia
}
Podobieństwa i różnice w stosunku do klas (tworzenie obiektu struktury)
Tworzenie obiektu struktury jest możliwe za pomocą operatora new jak i bez niego. Tzn poniższe dwa kody reprezentujące tworzenie dwóch obiektów struktur są prawidłowe i kompilator je przyjmie:
WarunkiPogodowe pogodaNaDzis;
WarunkiPogodowe pogodaNaJutro = new WarunkiPogodowe();
Jednak w przypadku obiektu tworzonego bez operatora new, są kolejne ograniczenia (jak wiadomo operator new używany przy tworzeniu obiektów wywołuje konstruktora tego obiektu). Z powyższego kodu, obiekt pogodNaDzis, w chwili jego utworzenia nia miał wywołanego żadnego konstruktora a w związku z tym ma niezainicjowane pola, a to z koleii znaczy, że będzie mógł być używany dopiero wtedy gdy zainicjujemy mu wszystkie pola z poziomu kodu klienta za pomocą operatora kropki. Takie zainicjowanie jego pól będzie jednak możliwe dopiero wtedy gdy zdefiniujemy jego pola w ciele struktury z modyfikatorem dostępu public inaczej nie będziemy mieli dostępu do jego pól ponieważ, bez zainicjowanych pól w strukturze, nie działają ani właściwości, ani metody które mogłyby realizować dostęp do pól prywatnych. Innymi słowy struktura bez zainicjowanych pól jako obiekt jest „martwa” a „ożywa” i możemy w pełni korzystać z jej wszystkich składowych (pól, metod, właściwości, indeksatorów, stałych i zdarzeń) w chwili gdy zostaną zainicjowane jej wszystkie pola.
Podobieństwa i różnice w stosunku do klas (dziedziczenie)
Struktura w przeciwieństwie do klas nie może dziedziczyć po innej klasie lub innej strukturze ani nie może być elementem bazowym z którego ktoś/coś dziedziczy. Struktury nie obsługują dziedziczenia. Chociaż w niejawny sposób dziedziczą bezpośredno z wbudowanej klasy bazowej System.ValueType.* System.ValueType* dziedziczy po klasie bazowej wszystkich klas System.Object.
Jak już na początku wspomniano struktury są typu wartościowego, jak się teraz okazuje, z tego względu, ponieważ dziedziczą właśnie po System.ValueType.
Pomimo tego że struktury nie obsługują dziedziczenia, struktury mogą implementować interfejsy, i to dowolną ich ilość.
Ładny tekst ;)