[ Pobierz całość w formacie PDF ]
i dostarczane przez wywołujący kod za pośrednictwem argumentów rzeczywistych (dalej nazywanych po prostu argumentami). Metody mają także pojedynczy wyjściowy parametr zwrotny, który jest opcjonalny; jeśli metoda nie zwraca żadnej wartości, określa się to za pomocą typu void. Parametr zwrotny pozwala przekazać informacje z powrotem do wywo- łującego kodu po zakończeniu działania metody. CTS obsługuje też parametry przekazywane przez referencję, które pozwalają wywołującemu i wywoływanemu na współdzielenie tych samych danych. Kilka akapitów niżej wyjaśnię, jak wykorzystać wszystkie te funkcje. Poniższy kod definiuje metodę Add, która przyjmuje dwa parametry i zwraca wartość: class Calculator { public int Add(int x, int y) { return x + y; } } Metodę tę można wywołać z kodu użytkownika, na przykład w następujący sposób: Calculator calc = new Calculator(); int sum = calc.Add(3, 5); Wygenerowany kod IL będzie zawierał wywołanie metody Add obiektu calc. Wartości 3 i 5 są przekazywane jako argumenty odpowiadające parametrom, odpowiednio x i y w pojedynczej ramce aktywacji metody Add. Metoda następnie wykorzystuje dynamiczne wartości 3 i 5 z ramki aktywacji, dodając je do siebie i zwracając wynik. Ramka jest następnie zdejmowana, a do kodu wywołującego przekazywana jest wartość 8; w tym przypadku kod zapisuje ją w lokalnej zmiennej sum. Metody, tak jak wszystkie inne składowe, mogą być instancyjne lub statyczne. Metody in- stancyjne mają dostęp do specjalnego wskaznika this (nazywanego this w C#, a Me w VB) odnoszącego się do instancji, na której wywołano metodę. Za jego pomocą mogą uzyskać dostęp do publicznych, chronionych i prywatnych składowych instancyjnych typu (oraz publicznych i chronionych składowych w hierarchii typu). Rozważmy na przykład klasę z instancyjną składową state: class Example { int state; public void Foo() { state++; this.state++; } } Zauważmy, że C# dedukuje kwalifikację this w odwołaniu do pola state, ponieważ wy- krywa, że w zasięgu znajduje się zmienna instancyjna. C# obsługuje też jawną kwalifikację za pomocą słowa kluczowego this. W obu przypadkach w wygenerowanym IL zostanie to zakodowane przez wczytanie zerowego argumentu ramki aktywacji. Jest to konwencja prze- kazywania wskaznika this od wywołującego do metody instancyjnej. Wszystkie rzeczywiste argumenty zaczynają się od pozycji pierwszej.
54 Część I Podstawowe informacje o CLR Metody statyczne nie mają natomiast wskaznika this. Mają dostęp do prywatnych składo- wych statycznych typu, ale nie otrzymują aktywnej instancji, która pozwoliłaby im na dostęp do składowych instancyjnych. Argumenty metod statycznych zaczynają się od pozycji zerowej. Jest to całkowicie niewidoczne na poziomie języka C#, ale na poziomie IL trzeba uwzględ- niać tę różnicę. Zmienne lokalne Ramka aktywacji metody może zawierać dwa rodzaje danych lokalnych: argumenty i zmienne lokalne. Są one przechowywane na fizycznym stosie; są przydzielane w momencie wywołania metody i usuwane, kiedy metoda kończy działanie (wskutek powrotu albo nieobsłużonego błędu). Omówiliśmy już użycie argumentów. Argumenty to lokacje, do których wywołujący kopiuje dane przekazywane wywoływanemu. Pózniej zbadamy różne style przekazywania argumentów, które w zależności od scenariusza mogą obejmować kopiowanie wartości albo przekazywanie referencji do lokalnych struktur danych wywołującego. Metoda może też używać zmiennych lokalnych. Są one również alokowane na stosie, ale całkowicie niewidoczne dla wywołujących; stanowią szczegół implementacyjny metody. Podczas przydzielania miejsca na stosie zmienne lokalne są inicjalizowane przez zerowanie (co daje 0 dla wartości skalarnych, 0.0 dla wartości zmiennopozycyjnych, false dla wartości logicznych i null dla referencji), a każda z nich otrzymuje koncepcyjny slot. Sloty są typizo- wane, aby CLR mogło przydzielić odpowiednią ilość miejsca, a weryfikator sprawdzić, czy zmienne lokalne są używane w sposób bezpieczny typologicznie. Oto przykład: class Example { public void Foo() { int x = 0; double y = 5.5; string s = "Witaj, świecie"; // & } } Ten kod definiuje trzy zmienne lokalne, x, y i z, które zostają ponumerowane podczas emisji kodu IL. Większość kompilatorów nadałaby im numery odpowiednio: 0, 1 i 2, ale kompilatory mogą przeprowadzać optymalizacje zmieniające tę kolejność. Oczywiście, w języku C# dodatkowe zmienne lokalne mogą być zdefiniowane w dalszej części metody (inaczej niż w C, w którym wszystkie zmienne muszą zostać wprowadzone na początku funkcji), ale jest to cecha specyficzna dla języka; każda taka zmienna zwykle otrzymuje własny slot (choć kompilatory mogą wielokrotnie wykorzystywać te same sloty). Przeciążanie Typ może zawierać wiele metod o takiej samej nazwie. Metody te muszą się jednak różnić w jakiś znaczący sposób; określa się to mianem przeciążania. Metody można przeciążać przez definiowanie wyróżniających je parametrów. Różnice typów zwrotnych nie wystar- czają do przeciążenia metody.
Rozdział 2. Wspólny system typów 55 Przeciążanie pozwala tworzyć metody, które działają podobnie, ale przyjmują różne typy parametrów, co często zwiększa wygodę korzystania z klasy. Ponadto umożliwia określanie domyślnych wartości argumentów, na przykład przez napisanie wersji, które po prostu wy- wołują inne przeciążone wersje z odpowiednimi wartościami parametrów. Rozważmy na przykład przeciążoną metodę Bar w poniższym typie: class Foo { internal void Bar() { Bar(10); /* wartość "domyślna" */ } internal void Bar(int x) { /* & */ } internal void Bar(int x, int y) { /* & */ } internal void Bar(object o) { /* & */ } } Kompilatory (a w przypadku języków dynamicznych również programy do póznego wią- zania) zajmują się rozwikływaniem przeciążeń, procesem, który polega na dopasowywaniu argumentów wywołującego do odpowiedniej wersji metody. Jest to zasadniczo wyszukiwanie wersji najlepiej pasującej do danego zbioru argumentów. Ogólnie rzecz biorąc, kompilatory wybierają najbardziej specyficzne dopasowanie. Jeśli dopasowanie jest niejednoznaczne albo nie uda się znalezć pasującej wersji metody, kompilator zwykle zgłasza ostrzeżenie lub błąd. Jeśli na przykład mamy dwie metody: void Foo(object o); void Foo(string s); to poniższy kod zostanie dowiązany do metody Foo(string), ponieważ jest ona bardziej specyficzna: Foo("Witaj, wiązanie!"); Oczywiście, istnieją bardziej złożone przykłady, które demonstrują szczegóły procesu wią- zania. Wystarczy jednak kilka eksperymentów, aby rozwiać ewentualne wątpliwości dotyczące zasad wiązania w danym języku. CTS nie narzuca żadnych reguł dotyczących rozwikływania przeciążeń, więc mogą one być różne w poszczególnych językach. Styl przekazywania argumentów
[ Pobierz całość w formacie PDF ] zanotowane.pldoc.pisz.plpdf.pisz.plalternate.pev.pl
|
|
|