Stronę tą wyświetlono już: 2936 razy
Wstęp
Struktury w C# są typami danych, które przechowują wartości pól w nich zawartych. Dla odróżnienia klasy w C# przechowują wskaźnik na miejsce, gdzie dane pól klasy się znajdują. Do typów, które przechowują wartość należą również: char, byte, int, uint, short, ushort, float, double, decimal. Co to w praktyce oznacza pokażę nieco później.
Struktury dziedziczą (jak zresztą wszystko inne) po klasie Object, nie mogą one dziedziczyć po żadnych innych klasach, czy strukturach jak również nie mogą być dziedziczone, mogą one jednak dziedziczyć po dowolnej liczbie interfejsów. Ważną cechą jest również brak destruktora, lecz każda struktura może posiadać konstruktor i metody wewnętrzne.
Deklaracja struktury w C#
Do utworzenia struktury C# służy słowo kluczowe struct. Oto przykładowy ogólny model deklaracji struktury:
Utworzenie instancji struktury odbywa się w następujący sposób:
Powyższe wywołanie spowoduje utworzenie instancji struktury z jednoczesnym wywołaniem jej konstruktora.
Rodzaje dostępu
W C# mamy do dyspozycji pięć różnych typów dostępu:
- public - oznacza, że dana zmienna lub metoda są dostępne z każdego miejsca kodu. Ten typ dostępu jest domyślny dla interfejsów;
- private - oznacza, że dana zmienna lub metoda są dostępne tylko z wnętrza danego typu, w którym została zadeklarowana. Ten typ dostępu jest domyślny dla klas i struktur;
- protected - oznacza, że dana zmienna lub metoda są dostępne z wnętrza klasy do której należy oraz z wnętrza klas pochodnych;
- internal - oznacza, że dana zmienna lub metoda są dostępne jedynie z wnętrza złożenia. C# kompiluje pliki *.cs w moduły a następnie grupuje w złożenia;
- protected internal - oznacza, że dana zmienna lub metoda są dostępne z wnętrza klasy, w której została zadeklarowana lub z wnętrza złożenia.
Przykład praktyczny implementacji struktury
Oto mały programik, w którym zaszyłem implementację struktury rectangle:
Wewnątrz struktury rectangle utworzone zostały cztery pola typu int. Pola te mają domyślnie przypisany dostęp private, co jak już wcześniej wspominałem oznacza, że nie ma do nich dostępu z zewnątrz. Sam konstruktor tejże struktury przyjmuje cztery argumenty, które odpowiadają nazwom pól struktury rectangle. Aby możliwe było ustawienie pól struktury wewnątrz jej konstruktora użyte zostało słowo kluczowe this, oznaczający wskaźnik na instancję struktury rectangle.
Wewnątrz struktury rectangle utworzone zostały również dwie metody publiczne o nazwie PtInRect, których celem jest stwierdzenie, czy podany punkt znajduje się w prostokącie. Istnieje tutaj wersja tej metody ściśle powiązana z instancją struktury rectangle, która przyjmuje jako parametry jedynie współrzędną x i y. Tę metodę można wywoływać bezpośrednio jedynie wewnątrz metod nie statycznych struktury rectangle czy w jej konstruktorze, zaś pośrednio można ją wywołać z poziomu instancji tejże struktury. Druga wersja jest typu statycznego static i nie jest powiązana z żadną instancją struktury w związku z czym można ją wywołać w dowolnym miejscu z poziomu nazwy tejże struktury.
Ostatnia metoda ToString (również publiczna) jest metodą przeciążającą override metodę podstawową klasy Object. Metoda ta zamienia dane zawarte wewnątrz struktury na tekst string.
Przykład działania powyższego kodu:
Podaj współrzędną x: 25 Podaj współrzędną y: 75 Punkt P.x = 25; P.y = 75; znajduje się wewnątrz prostokąta Prostokąt: lewy = 10; prawy = 100; górny = 10; dolny = 200 Punkt P.x = 25; P.y = 75; nie znajduje się wewnątrz prostokąta lewy = 0; prawy = 50; górny = 0; dolny = 50
Właściwości struktury
Struktury mogą być wyposażone w mechanizmy kontrolowanego dostępu do pól. Tymi mechanizmami są właściwości, które można utworzyć w sposób ręczny wpisując kod, lub (w Visual Studio EE) z wykorzystaniem automatycznego mechanizmu generowania właściwości. Ten drugi sposób można wykorzystać klikając prawym przyciskiem myszy na prywatnym lub chronionym polu struktury i wybierając z menu kontekstowego pozycję Refactor→Encapsulate field... tak jak pokazano to na poniższej ilustracji.
Po kliknięciu wspomnianej wcześniej pozycji oczom twym ukazać powinno się okno dialogowe o nazwie Encapsulate Field, w którym to można zmienić nazwę właściwości. Domyślnie nazwa jest taka sama co pole struktury bądź klasy, której dotyczy z tą małą zmianą, że proponowana nazwa zaczyna się z dużej litery. Z tego powodu warto nazwy pól struktury czy klasy zaczynać od małej litery (dla łatwiejszego odróżnienia właściwości od pól). Oczywiście nazwę można zmienić a następnie zatwierdzić klikając przycisk OK.
Po wciśnięciu przycisku OK oczom naszym pokazać powinno się ostatnie okno dialogowe Preview Reference Changes - Encapsulate Field, w którym niezwłocznie należy kliknąć przycisk Apply.
Przykład kodu właściwości wygenerowanego dla pola left dla struktury rectangle wygląda następująco:
W ten sam sposób należy wygenerować kod dla reszty pól klasy z lekką modyfikacją, która będzie wyglądała następująco:
Jak widać w powyższym kodzie wprowadziłem odrobinę kontroli nad tym, jak ustawiane są pola struktury rectangle. Przykład użycia właściwości struktury:
Wynik działania powyższego kodu:
Prostokąt: lewy = 20; prawy = 50; górny = 70; dolny = 100 Lewy: 50 Prawy: 200
Oczywiście możliwe jest utworzenie właściwości, która obsługuje jedynie zwracanie wartości get, lub ustawianie set.
Referencyjne przekazywanie argumentów metody
Typy proste oraz struktury przechowują wartości pól w związku z tym utworzę taką oto metodę wcześniej opisanej już struktury rectangle, która pokaże, co to tak naprawdę oznacza:
A teraz utwórzmy sobie taki oto kod:
Wynik działania:
x: 0; y: 0
Jak widzicie, zmienne x i y nie zostały zmodyfikowane, ponieważ nie są one zmiennymi typu referencyjnego. Co się stało tutaj? Otóż gdy przekazuję jakiś argument metodzie czy to struktury, czy to klasy to ten argument jest kopiowany. W przypadku typu wartościowego kopiowana jest wartość, w przypadku typu referencyjnego kopiowany jest adres pamięci. Na szczęście jest metoda obejścia tego mechanizmu z wykorzystaniem słowa kluczowego ref. Oto poprawiona deklaracja metody GetLeftTop:
Teraz kod wykorzystujący tę metodę będzie wyglądał następująco:
Wynik jego działania:
x: 10; y: 30