You are on page 1of 52

Jzyk C#. Programowanie.

Wydanie III. Microsoft .NET


Development Series
Autor: Anders Hejlsberg, Mads Torgersen,
Scott Wiltamuth, Peter Golde
Tumaczenie: ukasz Suma
ISBN: 978-83-246-2195-8
Tytu oryginau: The C# Programming
Format: 170x230, stron: 784
Poznaj moliwoci jzyka C# i twrz wysoko wydajne aplikacje
Jak uywa instrukcji wyrae?
Jak korzysta z typw wyliczeniowych?
Jak definiowa i stosowa atrybuty?

Nowoczesny i bezpieczny jzyk programowania C# posiada kilka cech, ktre uatwiaj


opracowywanie solidnych i wydajnych aplikacji na przykad obsug wyjtkw,
wymuszanie bezpieczestwa typw lub mechanizm odzyskiwania pamici, czyli
automatyczne odzyskiwanie pamici operacyjnej zajmowanej przez nieuywane obiekty.
C# 3.0 oferuje moliwo programowania funkcjonalnego oraz technologi LINQ
(zapyta zintegrowanych z jzykiem), co znaczco poprawia wydajno pracy programisty.
Ksika Jzyk C#. Programowanie. Wydanie III. Microsoft .NET Development Series
zawiera pen specyfikacj techniczn jzyka programowania C#, opatrzon najnowszymi
zaktualizowanymi informacjami, m.in. na temat inicjalizatorw obiektw
i kolekcji, typw anonimowych czy wyrae lambda. Dziki licznym komentarzom
i praktycznym poradom, ktre uzupeniaj gwn tre podrcznika, szybko nauczysz si
posugiwa zmiennymi, przeprowadza konwersje funkcji i wyznacza przeadowania.
Dowiesz si, jak optymalnie i z fascynujcym efektem kocowym wykorzystywa ten
nowoczesny jzyk programowania.
Typy i zmienne
Klasy i obiekty
Struktura leksykalna
Deklaracje struktur
Skadowe
Konwersje i wyraenia
Instrukcje i operatory
Tablice
Interfejsy
Kod nienadzorowany
Wskaniki w wyraeniach
Bufory o ustalonym rozmiarze
Dynamiczne alokowanie pamici

Wykorzystaj wiedz i dowiadczenie najlepszych specjalistw,


aby sprawnie posugiwa si jzykiem C#

Spis treci
Sowo wstpne 11
Przedmowa 13
O autorach 15
O komentatorach 17

1. Wprowadzenie 19
1.1.
1.2.
1.3.
1.4.
1.5.
1.6.
1.7.
1.8.
1.9.
1.10.
1.11.
1.12.

Witaj, wiecie 20
Struktura programu 22
Typy i zmienne 24
Wyraenia 29
Instrukcje 32
Klasy i obiekty 36
Struktury 59
Tablice 62
Interfejsy 64
Typy wyliczeniowe 66
Delegacje 68
Atrybuty 72

2. Struktura leksykalna 75
2.1.
2.2.
2.3.
2.4.
2.5.

Programy 75
Gramatyka 75
Analiza leksykalna 77
Tokeny 81
Dyrektywy preprocesora 94

Spis treci

3. Podstawowe pojcia 107


3.1.
3.2.
3.3.
3.4.
3.5.
3.6.
3.7.
3.8.
3.9.
3.10.

Uruchomienie aplikacji 107


Zakoczenie aplikacji 108
Deklaracje 109
Skadowe 113
Dostp do skadowych 115
Sygnatury i przeadowywanie 124
Zakresy 126
Przestrze nazw i nazwy typw 133
Automatyczne zarzdzanie pamici 138
Kolejno wykonania 143

4. Typy 145
4.1.
4.2.
4.3.
4.4.
4.5.
4.6.

Typy wartociowe 146


Typy referencyjne 157
Pakowanie i rozpakowywanie 160
Typy skonstruowane 164
Parametry typu 168
Typy drzew wyrae 169

5. Zmienne 171
5.1.
5.2.
5.3.
5.4.
5.5.

Kategorie zmiennych 171


Wartoci domylne 177
Ustalenie niewtpliwe 177
Referencje zmiennych 194
Niepodzielno referencji zmiennych 194

6. Konwersje 195
6.1.
6.2.
6.3.
6.4.
6.5.
6.6.

Konwersje niejawne 196


Konwersje jawne 202
Konwersje standardowe 210
Konwersje definiowane przez uytkownika 211
Konwersje funkcji anonimowych 216
Konwersje grup metod 223

Spis treci

7. Wyraenia 227
7.1.
7.2.
7.3.
7.4.
7.5.
7.6.
7.7.
7.8.
7.9.
7.10.
7.11.
7.12.
7.13.
7.14.
7.15.
7.16.
7.17.
7.18.
7.19.

Klasyfikacje wyrae 227


Operatory 230
Odnajdywanie skadowych 239
Funkcje skadowe 242
Wyraenia podstawowe 262
Operatory jednoargumentowe 306
Operatory arytmetyczne 311
Operatory przesunicia 320
Operatory relacyjne i testowania typu 322
Operatory logiczne 332
Logiczne operatory warunkowe 334
Operator czenia pustego 337
Operator warunkowy 339
Wyraenia funkcji anonimowych 340
Wyraenia zapyta 350
Operatory przypisa 363
Wyraenia 369
Wyraenia stae 369
Wyraenia booleowskie 371

8. Instrukcje 373
8.1.
8.2.
8.3.
8.4.
8.5.
8.6.
8.7.
8.8.
8.9.
8.10.
8.11.
8.12.
8.13.
8.14.

Punkty kocowe i osigalno 374


Bloki 375
Instrukcja pusta 377
Instrukcje oznaczone 378
Instrukcje deklaracji 379
Instrukcje wyrae 383
Instrukcje wyboru 383
Instrukcje iteracji 390
Instrukcje skoku 398
Instrukcja try 405
Instrukcje checked i unchecked 409
Instrukcja lock 410
Instrukcja using 412
Instrukcja yield 414
7

Spis treci

9. Przestrzenie nazw 419


9.1.
9.2.
9.3.
9.4.
9.5.
9.6.
9.7.

Jednostki kompilacji 419


Deklaracje przestrzeni nazw 420
Synonimy zewntrzne 421
Dyrektywy uywania 422
Skadowe przestrzeni nazw 429
Deklaracje typw 429
Kwalifikatory synonimw przestrzeni nazw 430

10. Klasy 433


10.1.
10.2.
10.3.
10.4.
10.5.
10.6.
10.7.
10.8.
10.9.
10.10.
10.11.
10.12.
10.13.
10.14.

Deklaracje klas 433


Typy czciowe 446
Skadowe klas 455
Stae 469
Pola 471
Metody 481
Waciwoci 503
Zdarzenia 516
Indeksatory 524
Operatory 528
Konstruktory instancji 535
Konstruktory statyczne 543
Destruktory 545
Iteratory 547

11. Struktury 563


11.1.
11.2.
11.3.
11.4.

Deklaracje struktur 563


Skadowe struktury 565
Rnice midzy klas a struktur 565
Przykady struktur 574

12. Tablice 579


12.1. Typy tablicowe 579
12.2. Tworzenie tablic 581
12.3. Dostp do elementw tablic 582

Spis treci

12.4. Skadowe tablic 582


12.5. Kowariancja tablic 582
12.6. Inicjalizatory tablic 583

13. Interfejsy 587


13.1.
13.2.
13.3.
13.4.

Deklaracje interfejsw 587


Skadowe interfejsu 590
W peni kwalifikowane nazwy skadowych interfejsu 595
Implementacje interfejsw 596

14. Typy wyliczeniowe 611


14.1.
14.2.
14.3.
14.4.
14.5.

Deklaracje typw wyliczeniowych 611


Modyfikatory typw wyliczeniowych 612
Skadowe typw wyliczeniowych 612
Typ System.Enum 615
Wartoci typw wyliczeniowych i zwizane z nimi operacje 616

15. Delegacje 617


15.1.
15.2.
15.3.
15.4.

Deklaracje delegacji 618


Zgodno delegacji 621
Realizacja delegacji 621
Wywoanie delegacji 622

16. Wyjtki 625


16.1.
16.2.
16.3.
16.4.

Przyczyny wyjtkw 625


Klasa System.Exception 626
Sposb obsugi wyjtkw 626
Najczciej spotykane klasy wyjtkw 627

17. Atrybuty 629


17.1.
17.2.
17.3.
17.4.
17.5.

Klasy atrybutw 629


Specyfikacja atrybutu 633
Instancje atrybutw 639
Atrybuty zarezerwowane 641
Atrybuty umoliwiajce wspdziaanie 646

Spis treci

18. Kod nienadzorowany 649


18.1.
18.2.
18.3.
18.4.
18.5.
18.6.
18.7.
18.8.
18.9.

Konteksty nienadzorowane 650


Typy wskanikowe 653
Zmienne utrwalone i ruchome 656
Konwersje wskanikw 657
Wskaniki w wyraeniach 660
Instrukcja fixed 667
Bufory o ustalonym rozmiarze 672
Alokacja na stosie 675
Dynamiczne alokowanie pamici 677

A. Komentarze dokumentacji 679


A.1.
A.2.
A.3.
A.4.

Wprowadzenie 679
Zalecane znaczniki 681
Przetwarzanie pliku dokumentacji 691
Przykad 697

B. Gramatyka 703
B.1.
B.2.
B.3.

Gramatyka leksykalna 703


Gramatyka skadniowa 712
Rozszerzenia gramatyczne zwizane z kodem nienadzorowanym 742

C. rda informacji uzupeniajcych 747


Skorowidz 749

10

8. Instrukcje

Jzyk C# oferuje szerok gam instrukcji. Wikszo z nich powinna by dobrze znana programistom, ktrzy mieli okazj korzysta z jzykw C i C++.
instrukcja:
instrukcja-oznaczona
instrukcja-deklaracji
instrukcja-osadzona
instrukcja-osadzona:
blok
instrukcja-pusta
instrukcja-wyraenia
instrukcja-wyboru
instrukcja-iteracji
instrukcja-skoku
instrukcja-try
instrukcja-checked
instrukcja-unchecked
instrukcja-lock
instrukcja-using
instrukcja-yield

Nieterminalny element instrukcja-osadzona jest uywany w przypadku instrukcji, ktre wystpuj w ramach innych instrukcji. Zastosowanie elementu instrukcja-osadzona zamiast elementu
instrukcja wyklucza uycie instrukcji deklaracji i instrukcji oznaczonych w tych kontekstach.
Skompilowanie przedstawionego poniej fragmentu kodu:
void F(bool b) {
if (b)
int i = 44;
}

powoduje zgoszenie bdu, poniewa instrukcja if wymaga zastosowania w swojej gazi elementu
instrukcja-osadzona, nie za elementu instrukcja. Gdyby byo to dopuszczalne, zmienna
i mogaby zosta zadeklarowana, lecz mogaby nie zosta nigdy uyta. Umieszczenie deklaracji
i w bloku powoduje, e przykadowy kod staje si poprawny.

373

8. Instrukcje

8.1. Punkty kocowe i osigalno


Kada instrukcja ma punkt kocowy (ang. end point). W intuicyjnym znaczeniu punktem
kocowym instrukcji jest miejsce, ktre znajduje si bezporednio za instrukcj. Reguy wykonania
instrukcji zoonych (czyli instrukcji zawierajcych instrukcje osadzone) okrelaj dziaanie, ktre
jest przeprowadzane, gdy sterowanie osiga punkt kocowy instrukcji osadzonej. Na przykad
gdy sterowanie osiga punkt kocowy instrukcji w bloku, jest ono przekazywane do kolejnej
instrukcji znajdujcej si w tym bloku.
Jeli instrukcja moe potencjalnie zosta osignita przez sterowanie, wwczas instrukcja nazywana jest osigaln (ang. reachable). Dla odmiany, jeli nie istnieje moliwo, aby instrukcja zostaa
wykonana, nosi ona nazw nieosigalnej (ang. unreachable).
W przedstawionym poniej przykadzie:
void F() {
Console.WriteLine("osigalna");
goto Label;
Console.WriteLine("nieosigalna");
Label:
Console.WriteLine("osigalna");
}

drugie wywoanie funkcji Console.WriteLine jest nieosigalne, poniewa nie ma moliwoci, aby
instrukcja ta zostaa wykonana.
Gdy kompilator stwierdza, e jaka instrukcja jest nieosigalna, generowane jest ostrzeenie. Warto
jednak podkreli, e sytuacja taka nie stanowi bdu.
Aby okreli, czy pewna instrukcja lub punkt kocowy s osigalne, kompilator przeprowadza analiz przepywu sterowania zgodnie z reguami osigalnoci zdefiniowanymi dla kadej instrukcji.
W analizie przepywu sterowania bierze si pod uwag wartoci wyrae staych (opisanych
dokadniej w podrozdziale 7.18), ktre kontroluj zachowanie instrukcji, lecz nie s uwzgldniane
potencjalne wartoci wyrae niestaych. Innymi sowy, dla potrzeb analizy przepywu sterowania
przyjmuje si, e wyraenie niestae danego typu moe mie dowoln warto, jak mona przedstawi za pomoc tego typu.
W przedstawionym poniej przykadzie:
void F() {
const int i = 1;
if (i == 2) Console.WriteLine("nieosigalna");
}

wyraenie booleowskie instrukcji if jest wyraeniem staym, poniewa obydwa operandy operatora == s stae. Jako takie wyraenie to jest przetwarzane na etapie kompilacji, dajc w wyniku
warto false, przez co wywoanie funkcji Console.WriteLine zostaje uznane za nieosigalne.
Jeli jednak i zastpione jest zmienn lokaln, tak jak zostao to pokazane poniej:

374

8.2. Bloki
void F() {
int i = 1;
if (i == 2) Console.WriteLine("osigalna");
}

wywoanie funkcji Console.WriteLine zostaje uznane za instrukcj osigaln, mimo e w rzeczywistoci nigdy nie bdzie wykonywane.
Blok funkcji skadowej jest zawsze uwaany za osigalny. Dziki sukcesywnemu stosowaniu zasad

osigalnoci dla kadej instrukcji nalecej do bloku da si okreli osigalno kadej podanej
instrukcji.
W przedstawionym poniej przykadzie:
void F(int x) {
Console.WriteLine("pocztek");
if (x < 0) Console.WriteLine("ujemna");
}

osigalno drugiego wywoania funkcji Console.WriteLine jest okrelana w nastpujcy sposb:


Pierwsza instrukcja wyraenia Console.WriteLine jest osigalna, poniewa osigalny jest blok
metody F.
Punkt kocowy pierwszej instrukcji wyraenia Console.WriteLine jest osigalny, poniewa
osigalna jest ta instrukcja.
Instrukcja if jest osigalna, poniewa osigalny jest punkt kocowy instrukcji wyraenia
Console.WriteLine.
Druga instrukcja wyraenia Console.WriteLine jest osigalna, poniewa wyraenie booleowskie
instrukcji if nie ma staej wartoci false.
Istniej dwie sytuacje, w ktrych pojawia si bd czasu kompilacji, gdy punkt kocowy instrukcji
jest osigalny:
Poniewa przypadku instrukcji switch nie jest dozwolone, aby sekcja wpadaa do kolejnej
sekcji, za bd czasu kompilacji uwaa si przypadek, gdy osigalny jest punkt kocowy listy
instrukcji sekcji switch. Wystpienie takiego bdu oznacza zwykle brak instrukcji break.
Bd czasu kompilacji pojawia si, gdy osigalny jest punkt kocowy bloku funkcji skadowej,
ktra oblicza warto. Wystpienie takiego bdu oznacza zwykle brak instrukcji return.

8.2. Bloki
Blok umoliwia zapisywanie wielu instrukcji w kontekstach, w ktrych dopuszczalne jest uycie

pojedynczej instrukcji:
blok:
{ lista-instrukcjiopc }

375

8. Instrukcje
Blok skada si z ujtej w nawiasy klamrowe opcjonalnej lista-instrukcji (o ktrej wicej
w punkcie 8.2.1). Jeli lista instrukcji nie wystpuje, blok nazywa si pustym.

Blok moe zawiera instrukcje deklaracji (o ktrych wicej w podrozdziale 8.5). Zakresem lokalnej
zmiennej lub staej zadeklarowanej w bloku jest ten blok.
W ramach bloku znaczenie nazwy uywanej w kontekcie wyraenia zawsze musi by takie samo
(o czym wicej w podpunkcie 7.5.2.1).
Blok jest wykonywany w nastpujcy sposb:
Jeli blok jest pusty, sterowanie jest przekazywane do punktu kocowego bloku.
Jeli blok nie jest pusty, sterowanie jest przekazywane do listy instrukcji. Gdy i jeli sterowanie
osiga punkt kocowy listy instrukcji, sterowanie jest przekazywane do punktu kocowego bloku.
Lista instrukcji bloku jest osigalna, jeli sam blok jest osigalny.
Punkt kocowy bloku jest osigalny, jeli blok ten jest osigalny i jeli jest on pusty lub jeli
osigalny jest punkt kocowy listy wyrae.
Blok zawierajcy jedno lub wiksz liczb instrukcji yield (o ktrych wicej w podrozdziale 8.14)
nosi nazw bloku iteratora. Bloki iteratorw s uywane do implementacji funkcji skadowych jako
iteratorw (o czym wicej w podrozdziale 10.14). Blokw iteratorw dotycz pewne dodatkowe
ograniczenia:

Bdem czasu kompilacji jest, gdy w bloku iteratora pojawia si instrukcja return (dozwolone
s tu jednak instrukcje yield return).
Bdem czasu kompilacji jest, gdy blok iteratora zawiera kontekst nienadzorowany (opisany
w podrozdziale 18.1). Blok iteratora zawsze okrela kontekst nadzorowany, nawet jeli jego
deklaracja jest zagniedona w kontekcie nienadzorowanym.

8.2.1. Lista instrukcji


Lista instrukcji (ang. statement list) zawiera jedn lub wiksz liczb instrukcji zapisanych
w postaci cigu. Listy instrukcji wystpuj w elementach blok (opisanych w podrozdziale 8.2) oraz
w elementach blok-switch (o ktrych wicej w punkcie 8.7.2):
lista-instrukcji:
instrukcja
lista-instrukcji instrukcja

Lista instrukcji jest wykonywana przez przekazanie sterowania do pierwszej instrukcji. Gdy i jeli
sterowanie osiga kocowy punkt instrukcji, jest ono przekazywane do nastpnej. Gdy i jeli sterowanie osiga kocowy punkt ostatniej instrukcji, jest ono przekazywane do kocowego punktu
listy instrukcji.

376

8.3. Instrukcja pusta


Instrukcja znajdujca si na licie instrukcji jest osigalna, jeli speniony jest przynajmniej jeden
z nastpujcych warunkw:
Instrukcja jest pierwsz instrukcj i sama lista instrukcji jest osigalna.
Kocowy punkt poprzedzajcej instrukcji jest osigalny.
Instrukcja jest instrukcj oznaczon, a do jej etykiety odnosi si osigalna instrukcja goto.

VLADIMIR RESHETNIKOV Regua ta nie ma zastosowania, gdy instrukcja goto znajduje


si wewntrz bloku try lub bloku catch instrukcji try, ktra zawiera blok finally, a instrukcja
oznaczona wystpuje poza instrukcj try, za kocowy punkt bloku finally jest nieosigalny.
Punkt kocowy listy instrukcji jest osigalny, jeli osigalny jest kocowy punkt ostatniej instrukcji
znajdujcej si na licie.

8.3. Instrukcja pusta


Instrukcja-pusta nie powoduje wykonania adnych dziaa:
instrukcja-pusta:
;

Instrukcja pusta jest uywana, gdy nie ma adnych operacji do wykonania w kontekcie, w ktrym
wymagana jest instrukcja.
Wykonanie instrukcji pustej powoduje po prostu przekazanie sterowania do kocowego punktu
instrukcji. Z tego powodu kocowy punkt instrukcji pustej jest osigalny, jeli osigalna jest ta
instrukcja pusta.
Instrukcja pusta moe by zastosowana przy pisaniu instrukcji while z pustym ciaem, tak jak
zostao to zaprezentowane poniej:
bool ProcessMessage() {...}
void ProcessMessages() {
while (ProcessMessage())
;
}

Dodatkowo instrukcja pusta moe by wykorzystywana do deklarowania etykiety tu przed zamykajcym nawiasem klamrowym (}) bloku, tak jak zostao to przedstawione poniej:
void F() {

...
if (done) goto exit;

...
exit: ;
}

377

8. Instrukcje

8.4. Instrukcje oznaczone


Dziki elementowi instrukcja-oznaczona moliwe jest poprzedzanie instrukcji za pomoc etykiety.
Instrukcje oznaczone s dopuszczalne w blokach, nie mog jednak wystpowa jako instrukcje
osadzone:
instrukcja-oznaczona:
identyfikator : instrukcja

Za pomoc instrukcji oznaczonej mona deklarowa etykiet o nazwie okrelonej przez identy
fikator. Zakresem etykiety jest cay blok, w ktrym zostaa ona zadeklarowana, w tym rwnie
wszelkie bloki zagniedone. Bdem czasu wykonania jest, gdy dwie etykiety o tej samej nazwie
maj zachodzce na siebie zakresy.
Do etykiety mog odnosi si instrukcje goto (opisane w punkcie 8.9.3) wystpujce w ramach jej
zakresu. W konsekwencji instrukcje goto mog przekazywa sterowanie w obrbie blokw oraz
poza bloki, nigdy jednak do blokw.
Etykiety maj swoj wasn przestrze deklaracji i nie koliduj z innymi identyfikatorami. Przedstawiony poniej przykad:
int F(int x) {
if (x >= 0) goto x;
x = -x;
x: return x;
}

jest prawidowym fragmentem kodu, w ktrym nazwa x jest wykorzystywana zarwno w roli
parametru, jak i etykiety.
Wykonanie instrukcji oznaczonej odpowiada dokadnie wykonaniu instrukcji, ktra znajduje si
bezporednio po niej.
Oprcz osigalnoci zapewnianej przez normalny przepyw sterowania instrukcja oznaczona moe
rwnie zawdzicza osigalno odwoujcej si do niej osigalnej instrukcji goto. Wyjtkiem jest
tu sytuacja, w ktrej instrukcja goto znajduje si wewntrz instrukcji try zawierajcej blok finally,
a instrukcja oznaczona wystpuje poza instrukcj try, za kocowy punkt bloku finally jest nieosigalny; wwczas instrukcja oznaczona nie jest osigalna za pomoc tej instrukcji goto.

ERIC LIPPERT Moe si na przykad zdarzy, e blok finally zawsze bdzie zgasza
wyjtek; w takim przypadku bdzie istniaa osigalna instrukcja goto przekierowujca do
potencjalnie nieosigalnej etykiety.

CHRIS SELLS Prosz nie uywa etykiet ani instrukcji goto. Nigdy nie zdarzyo mi
si ujrze kodu, ktry nie byby bardziej czytelny bez nich.

378

8.5. Instrukcje deklaracji

8.5. Instrukcje deklaracji


Instrukcja-deklaracji umoliwia deklarowanie lokalnej zmiennej lub staej. Instrukcji deklaracji
mona uywa w blokach, ale nie s one dozwolone w roli instrukcji zagniedonych:
instrukcja-deklaracji:
deklaracja-zmiennej-lokalnej ;
deklaracja-staej-lokalnej ;

8.5.1. Deklaracje zmiennych lokalnych


Deklaracja-zmiennej-lokalnej umoliwia deklarowanie jednej lub wikszej liczby zmiennych

lokalnych:
deklaracja-zmiennej-lokalnej:
typ-zmiennej-lokalnej deklaratory-zmiennych-lokalnych
typ-zmiennej-lokalnej:
typ
var
deklaratory-zmiennych-lokalnych:
deklarator-zmiennej-lokalnej
deklaratory-zmiennych-lokalnych , deklarator-zmiennej-lokalnej
deklarator-zmiennej-lokalnej:
identyfikator
identyfikator = inicjalizator-zmiennej-lokalnej
inicjalizator-zmiennej-lokalnej:
wyraenie
inicjalizator-tablicy
Typ-zmiennej-lokalnej elementu deklaracja-zmiennej-lokalnej okrela typ zmiennych wprowadzanych przez deklaracj lub wskazuje za pomoc sowa kluczowego var, e typ ten powinien
zosta wywnioskowany na podstawie inicjalizatora. Po typie wystpuje lista elementw deklaratorzmiennej-lokalnej, z ktrych kady wprowadza now zmienn. Deklarator-zmiennej-lokalnej
skada si z elementu identyfikator okrelajcego nazw zmiennej, po ktrym moe opcjonalnie
wystpowa token = oraz inicjalizator-zmiennej-lokalnej, ktry nadaje zmiennej warto

pocztkow.
Gdy typ-zmiennej-lokalnej jest okrelony jako var, a w zakresie nie ma typu o takiej nazwie,
deklaracja jest deklaracj zmiennej lokalnej niejawnie typowanej (ang. implicitly typed local
variable declaration), ktrej typ jest wnioskowany na podstawie powizanego elementu wyra
enie inicjalizatora. Deklaracje zmiennych lokalnych typowanych niejawnie podlegaj ograniczeniom:

379

8. Instrukcje
Deklaracja-zmiennej-lokalnej nie moe zawiera wielu elementw deklarator-zmiennejlokalnej.

ERIC LIPPERT Na wczesnych etapach opracowywania tej moliwoci dopuszczalne byo


korzystanie w tym miejscu z wielu deklaratorw, tak jak zostao to pokazane poniej:
var a = 1, b = 2.5;

Gdy programici C# ujrzeli ten kod, mniej wicej poowa z nich stwierdzia, e zapis ten
powinien mie t sam semantyk co przedstawiony poniej:
double a = 1, b = 2.5;

Druga poowa powiedziaa jednak, e powinien mie tak semantyk jak poniej:
int a = 1; double b = 2.5;

Obydwie grupy uwaay, e ich interpretacja zagadnienia bya w oczywisty sposb poprawna.
Gdy ma si do czynienia ze skadni, ktra umoliwia dwie niezgodne, w oczywisty sposb
poprawne interpretacje, najlepsz rzecz, jak mona zwykle zrobi, to cakowicie zabroni
korzystania z tej skadni, zamiast wprowadza zbdne zamieszanie.
Deklarator-zmiennej-lokalnej musi zawiera inicjalizator-zmiennej-lokalnej.
Inicjalizator-zmiennej-lokalnej musi by wyraenie.
Wyraenie inicjalizatora musi mie typ czasu kompilacji.
Wyraenie inicjalizatora nie moe odwoywa si do samej deklarowanej zmiennej.

ERIC LIPPERT Podczas gdy w deklaracji zmiennej lokalnej jawnie typowanej moe
wystpowa odwoanie do niej samej z poziomu inicjalizatora, w przypadku deklaracji zmiennej lokalnej niejawnie typowanej jest to niedopuszczalne.
Na przykad instrukcja int j = M(out j); jest dziwna, ale w peni prawidowa. Gdyby jednak
wyraenie miao posta var j = M(out j), wwczas mechanizm wyznaczania przeadowania nie mgby okreli typu zwracanego przez metod M, a wic rwnie typu zmiennej j,
dopki nie byby znany typ argumentu. Typ argumentu jest oczywicie dokadnie tym, co
prbujemy tu okreli.
Zamiast rozwizywa tu problem pierwszestwa kury lub jajka, specyfikacja jzyka po
prostu uniemoliwia stosowanie tego rodzaju konstrukcji.
Poniej zostay przedstawione przykady nieprawidowych deklaracji zmiennych lokalnych typowanych niejawnie:

380

8.5. Instrukcje deklaracji


var
var
var
var
var

x;
y =
z =
u =
v =

{1, 2, 3};
null;
x => x + 1;
v++;

// Bd, brak inicjalizatora, na podstawie ktrego mona wywnioskowa typ


// Bd, nie jest tu dozwolony inicjalizator tablicowy
// Bd, null nie ma typu
// Bd, funkcje anonimowe nie maj typu
// Bd, inicjalizator nie moe odnosi si do samej zmiennej

Warto zmiennej lokalnej jest otrzymywana za pomoc wyraenia, w ktrym uywa si elementu
nazwa-prosta (o ktrym wicej w punkcie 7.5.2). Warto zmiennej lokalnej modyfikuje si za
pomoc elementu przypisanie (o ktrym wicej w podrozdziale 7.16). Zmienna lokalna musi
by niewtpliwie ustalona (o czym wicej w podrozdziale 5.3) w kadym miejscu, w ktrym jej
warto jest uzyskiwana.

CHRIS SELLS Naprawd uwielbiam deklaracje zmiennych lokalnych typowanych niejawnie, gdy typ jest anonimowy (w ktrym to przypadku musisz ich uywa) lub gdy typ
zmiennej jest ujawniony jako cz wyraenia inicjalizujcego, a nie dlatego, e jeste zbyt
leniwy, aby pisa!
Przykadem moe tu by przedstawiony poniej fragment kodu:
var
var
var
var

a
b
v
d

=
=
=
=

new { Name = "Bob", Age = 42 };


1;
new Person();
GetPerson();

// Dobrze
// Dobrze
// Dobrze
// LE!

Kompilator nie ma najmniejszego problemu z tym, e d jest niejawnie typowan zmienn,


za to czowiek, ktry musi czyta taki kod wrcz przeciwnie!
Zakresem zmiennej lokalnej deklarowanej w deklaracja-zmiennej-lokalnej jest blok, w ktrym
nastpuje ta deklaracja. Odwoywanie si do zmiennej lokalnej wystpujce w tekcie programu
przed elementem deklarator-zmiennej-lokalnej jest bdem czasu kompilacji. Bdem czasu
kompilacji jest rwnie zadeklarowanie w ramach zakresu zmiennej lokalnej innej lokalnej
zmiennej lub staej o tej samej nazwie.
Deklaracja zmiennej lokalnej, za pomoc ktrej deklaruje si wiele zmiennych, jest rwnowana
z wieloma deklaracjami pojedynczych zmiennych tego samego typu. Ponadto inicjalizator zmiennej
w deklaracji zmiennej lokalnej dokadnie odpowiada instrukcji przypisania, ktra jest umieszczona
bezporednio za deklaracj.
Przedstawiony poniej przykadowy fragment kodu:
void F() {
int x = 1, y, z = x * 2;
}

dokadnie odpowiada nastpujcemu fragmentowi:

381

8. Instrukcje
void F() {
int x; x = 1;
int y;
int z; z = x * 2;
}

W deklaracji zmiennej lokalnej typowanej niejawnie przyjmuje si, e typ deklarowanej zmiennej
lokalnej jest taki sam jak typ wyraenia uytego do jej zainicjalizowania. Przykadem tego moe
by fragment kodu pokazany poniej:
var
var
var
var
var

i = 5;
s = "Hello";
d = 1.0;
numbers = new int[] {1, 2, 3};
orders = new Dictionary<int,Order>();

Deklaracje zmiennych lokalnych typowanych niejawnie przedstawione w powyszym przykadzie


s dokadnie rwnoznaczne z typowanymi jawnie deklaracjami, ktre zostay zaprezentowane
poniej:
int i = 5;
string s = "Hello";
double d = 1.0;
int[] numbers = new int[] {1, 2, 3};
Dictionary<int,Order> orders = new Dictionary<int,Order>();

8.5.2. Deklaracje staych lokalnych


Deklaracja-staej-lokalnej umoliwia deklarowanie jednej staej lokalnej lub wikszej liczby

staych lokalnych:
deklaracja-staej-lokalnej:
const typ deklaratory-staych
deklaratory-staych:
deklarator-staej
deklaratory-staych , deklarator-staej
deklarator-staej:
identyfikator = wyraenie-stae
Typ elementu deklaracja-staej-lokalnej okrela typ staych wprowadzonych przez deklaracj.
Po typie wystpuje lista elementw deklarator-staej, z ktrych kady wprowadza now sta.
Deklarator-staej skada si z elementu identyfikator okrelajcego nazw staej, po ktrym
nastpuje token =, po nim za wyraenie-stae (opisane w podrozdziale 7.18), ktre okrela
warto staej.
Typ i wyraenie-stae deklaracji staej lokalnej musz stosowa si do tych samych regu, ktre
dziaaj w przypadku odpowiednich elementw deklaracji skadowej staej (o ktrej wicej
w podrozdziale 10.4).

382

8.6. Instrukcje wyrae


Warto staej lokalnej jest uzyskiwana w wyraeniu wykorzystujcym element nazwa-prosta
(opisany w punkcie 7.5.2).
Zakresem staej lokalnej jest blok, w ktrym znajduje si ta deklaracja. Odwoywanie si do staej
lokalnej wystpujce w tekcie programu przed elementem deklarator-staej jest bdem czasu
kompilacji. Bdem czasu kompilacji jest rwnie zadeklarowanie w ramach zakresu staej lokalnej
innej staej lub zmiennej lokalnej o tej samej nazwie.
Deklaracja staej lokalnej, za pomoc ktrej deklaruje si wiele staych, jest rwnowana z wieloma
deklaracjami pojedynczych staych tego samego typu.

8.6. Instrukcje wyrae


Instrukcja-wyraenia powoduje przetworzenie danego wyraenia. Warto obliczona przez

wyraenie, jeli wystpuje, jest odrzucana:


instrukcja-wyraenia:
wyraenie-instrukcji ;
wyraenie-instrukcji:
wyraenie-wywoania
wyraenie-tworzenia-obiektu
przypisanie
wyraenie-poinkrementacyjne
wyraenie-podekrementacyjne
wyraenie-przedinkrementacyjne
wyraenie-przeddekrementacyjne

Nie wszystkie wyraenia mog by stosowane jako instrukcje. W szczeglnoci wyraenia takie
jak x + y czy x == 1 , ktre jedynie obliczaj jak warto (ktra i tak zostaaby nastpnie odrzucona), nie mog wystpowa w roli instrukcji.
Wykonanie elementu instrukcja-wyraenia powoduje przetworzenie zawartego w nim wyraenia,
a nastpnie przekazanie sterowania do kocowego punktu elementu instrukcja-wyraenia.
Kocowy punkt elementu instrukcja-wyraenia jest osigalny, gdy ta instrukcja-wyraenia
jest osigalna.

8.7. Instrukcje wyboru


Instrukcje wyboru powoduj wybranie jednej z wielu moliwych instrukcji do wykonania na
podstawie wartoci pewnego wyraenia:
instrukcja-wyboru:
instrukcja-if
instrukcja-switch

383

8. Instrukcje

8.7.1. Instrukcja if
Instrukcja if powoduje wybranie instrukcji do wykonania na podstawie wartoci wyraenia
booleowskiego:
instrukcja-if:
if ( wyraenie-booleowskie ) instrukcja-osadzona
if ( wyraenie-booleowskie ) instrukcja-osadzona else instrukcja-osadzona

Cz else jest zwizana z najblisz, poprzedzajc j leksykalnie czci if, ktra jest dozwolona
przez skadni. Z tego powodu instrukcja if o przedstawionej poniej postaci:
if (x) if (y) F(); else G();

jest rwnowana z nastpujc:


if (x) {
if (y) {
F();
}
else {
G();
}
}

Instrukcja if jest wykonywana w nastpujcy sposb:


Przetwarzane jest wyraenie-booleowskie (przedstawione w podrozdziale 7.19).
Jeli w wyniku przetwarzania wyraenia booleowskiego otrzymana zostaje warto true, sterowanie jest przekazywane do pierwszej osadzonej instrukcji. Gdy sterowanie osiga jej punkt
kocowy, przekazywane jest do punktu kocowego instrukcji if.
Jeli w wyniku przetwarzania wyraenia booleowskiego otrzymana zostaje warto false i jeli
obecna jest cz else, sterowanie przekazywane jest do drugiej osadzonej instrukcji. Gdy sterowanie osiga jej punkt kocowy, przekazywane jest do punktu kocowego instrukcji if.
Jeli w wyniku przetwarzania wyraenia booleowskiego otrzymana zostaje warto false i jeli
nie jest obecna cz else, sterowanie przekazywane jest do punktu kocowego instrukcji if.
Pierwsza instrukcja osadzona instrukcji if jest osigalna, jeli osigalna jest instrukcja if i wyraenie booleowskie nie ma staej wartoci false.
Druga instrukcja osadzona instrukcji if, jeli obecna, jest osigalna, jeli osigalna jest instrukcja
if i wyraenie booleowskie nie ma staej wartoci true.
Punkt kocowy instrukcji if jest osigalny, jeli osigalny jest punkt kocowy przynajmniej
jednej z jej osadzonych instrukcji. Ponadto punkt kocowy instrukcji if pozbawionej czci
else jest osigalny, jeli osigalna jest instrukcja if i wyraenie booleowskie nie ma staej wartoci true.

384

8.7. Instrukcje wyboru

8.7.2. Instrukcja switch


Instrukcja switch powoduje wybranie do wykonania listy instrukcji zwizanej z etykiet przecznika, ktra odpowiada wartoci wyraenia przeczajcego:
instrukcja-switch:
switch ( wyraenie ) blok-switch
blok-switch:
{ sekcje-switchopc }
sekcje-switch:
sekcja-switch
sekcje-switch sekcja-switch
sekcja-switch:
etykiety-switch lista-instrukcji
etykiety-switch:
etykieta-switch
etykiety-switch etykieta-switch
etykieta-switch:
case wyraenie-stae :
default :
Instrukcja-switch skada si ze sowa kluczowego switch, po ktrym wystpuje ujte w nawiasy
wyraenie (noszce nazw wyraenia przeczajcego), po nim za blok-switch. Blok-switch skada
si z zera lub wikszej liczby ujtych w nawiasy klamrowe elementw sekcja-switch. Kada
sekcja-switch zawiera jedn lub wiksz liczb elementw etykiety-switch, po ktrych wystpuje lista-instrukcji (opisana w punkcie 8.2.1).

Typ rzdzcy (ang. governing type) instrukcji switch jest ustalany na podstawie wyraenia przeczajcego w nastpujcy sposb:
Jeli typem wyraenia przeczajcego jest sbyte, byte, short, ushort, int, uint, long, ulong,
bool, char, string bd typ-wyliczeniowy lub jeli jest to odpowiadajcy jednemu z wymienionych typ dopuszczajcy warto pust, wwczas jest on typem rzdzcym instrukcji switch.
W innym przypadku musi istnie dokadnie jedna zdefiniowana przez uytkownika konwersja
niejawna (o czym wicej w podrozdziale 6.4) z typu wyraenia przeczajcego na jeden
z nastpujcych typw rzdzcych: sbyte, byte, short, ushort, int, uint, long, ulong, bool,
char, string lub typ dopuszczajcy warto pust odpowiadajcy jednemu z wymienionych
typw.
W innym przypadku, jeli nie istnieje tego rodzaju konwersja niejawna lub jeli istnieje wicej
takich konwersji niejawnych, pojawia si bd czasu kompilacji.

385

8. Instrukcje

DON BOX Czsto przyapuj si na marzeniu o moliwoci uywania typu System.Type


jako typu rzdzcego (znanego te jako typeswitch). Kocham funkcje wirtualne rwnie
mocno, jak wszyscy wok, ale niezmiennie pragn pisa kod, ktry interpretuje istniejce
typy i podejmuje rne dziaania na podstawie typu pewnej wartoci. Duo radoci sprawiby
mi rwnie operator dopasowania w rodzaju ML, ktry byby nawet bardziej przydatny.
Wyraenie stae kadej etykiety case musi oznacza warto typu, ktry da si niejawnie konwertowa (o czym wicej w podrozdziale 6.1) na typ rzdzcy instrukcji switch. Jeli dwie lub
wiksza liczba etykiet nalecych do tej samej instrukcji switch okrelaj t sam warto sta,
generowany jest bd czasu kompilacji.
W instrukcji przeczajcej moe znajdowa si co najwyej jedna etykieta default.
Instrukcja switch jest przetwarzana w nastpujcy sposb:
Wyraenie przeczajce jest przetwarzane i konwertowane na typ rzdzcy.
Jeli jedna ze staych okrelonych w etykiecie case nalecej do tej samej instrukcji switch jest
rwna wartoci wyraenia przeczajcego, sterowanie jest przekazywane do listy instrukcji
znajdujcej si po dopasowanej etykiecie case.
Jeli adna ze staych okrelonych w etykiecie case nalecej do tej samej instrukcji switch
nie jest rwna wartoci wyraenia przeczajcego i jeli obecna jest etykieta default, sterowanie
jest przekazywane do listy instrukcji znajdujcej si po etykiecie default.
Jeli adna ze staych okrelonych w etykiecie case nalecej do tej samej instrukcji switch nie
jest rwna wartoci wyraenia przeczajcego i jeli nie jest obecna etykieta default, sterowanie
jest przekazywane do punktu kocowego instrukcji switch.
Jeli osigalny jest kocowy punkt listy instrukcji sekcji switch, generowany jest bd czasu
kompilacji. Zasada ta znana jest jako regua niewpadania (ang. no-fall-through). Przedstawiony
poniej fragment kodu:
switch (i) {
case 0:
CaseZero();
break;
case 1:
CaseOne();
break;
default:
CaseOthers();
break;
}

jest prawidowy, poniewa adna z sekcji instrukcji przeczajcej nie ma osigalnego punktu
kocowego. W przeciwiestwie do jzykw C i C++ wykonanie sekcji switch nie moe tu wpada
do kolejnej sekcji, a prba skompilowania pokazanego poniej przykadu:
386

8.7. Instrukcje wyboru


switch (i) {
case 0:
CaseZero();
case 1:
CaseZeroOrOne();
default:
CaseAny();
}

powoduje wystpienie bdu czasu kompilacji. Gdy po wykonaniu pewnej sekcji instrukcji przeczajcej ma nastpowa wykonanie innej sekcji, zastosowana musi zosta jawna instrukcja goto
lub goto default, tak jak zostao to zaprezentowane poniej:
switch (i) {
case 0:
CaseZero();
goto case 1;
case 1:
CaseZeroOrOne();
goto default;
default:
CaseAny();
break;
}

W elemencie sekcja-switch dozwolone jest stosowanie wielu etykiet. Przedstawiony poniej przykad kodu:
switch (i) {
case 0:
CaseZero();
break;
case 1:
CaseOne();
break;
case 2:
default:
CaseTwo();
break;
}

jest prawidowy. Zaprezentowany kod nie amie reguy niewpadania, poniewa etykiety case 2:
oraz default: s czciami tego samego elementu sekcja-switch.
Regua niewpadania zabezpiecza przed wystpowaniem powszechnej klasy bdw, ktre
zdarzaj si w jzykach C i C++, gdy pominite zostaj przypadkiem instrukcje break. Dodatkowo
dziki tej regule sekcje instrukcji przeczajcej mog by dowolnie przestawiane bez wpywu
na zachowanie caej instrukcji. Na przykad kolejno sekcji instrukcji switch przedstawionej
w zaprezentowanym wczeniej fragmencie kodu mona odwrci bez modyfikowania sposobu
dziaania tej instrukcji, tak jak zostao to pokazane poniej:
switch (i) {
default:
CaseAny();

387

8. Instrukcje
break;
case 1:
CaseZeroOrOne();
goto default;
case 0:
CaseZero();
goto case 1;
}

Lista instrukcji sekcji przeczajcej koczy si zwykle instrukcj break, goto case lub goto default,
ale dopuszczalna jest tu kada konstrukcja, ktra sprawia, e kocowy punkt listy instrukcji staje
si nieosigalny. Przykadem moe tu by instrukcja while sterowana przez wyraenie booleowskie
true, o ktrej wiadomo, e nigdy nie osiga swojego punktu kocowego. Podobnie jest z instrukcjami throw i return, ktre zawsze przekazuj sterowanie w inne miejsce i nigdy nie osigaj
swojego punktu kocowego. Z tego powodu prawidowy jest nastpujcy przykadowy fragment kodu:
switch (i) {
case 0:
while (true) F();
case 1:
throw new ArgumentException();
case 2:
return;
}

DON BOX Kocham regu niewpadania, ale a po dzi dzie stale zapominam
o umieszczeniu instrukcji break na kocu klauzuli default i cigle musi mi o tym przypomina kompilator.
Typem rzdzcym instrukcji switch moe by typ string. Przykad zastosowania tej moliwoci
zosta pokazany poniej:
void DoCommand(string command) {
switch (command.ToLower()) {
case "run":
DoRun();
break;
case "save":
DoSave();
break;
case "quit":
DoQuit();
break;
default:
InvalidCommand(command);
break;
}
}

388

8.7. Instrukcje wyboru

Podobnie jak operatory rwnoci acuchw znakowych (opisane w punkcie 7.9.7), instrukcja
switch rozrnia wielkie i mae litery, dlatego dana sekcja zostanie wykonana tylko wwczas, jeli
acuch wyraenia przeczajcego bdzie dokadnie pasowa do staej etykiety case.
Gdy typem rzdzcym instrukcji switch jest string, warto null jest dozwolona jako staa etykiety case.
Lista-instrukcji lub blok-switch moe zawiera instrukcje deklaracji (przedstawione w podrozdziale 8.5). Zakresem zmiennej lub staej lokalnej zadeklarowanej w bloku przeczajcym
jest ten blok przeczajcy.

BILL WAGNER Wynika z tego, e kady blok

switch otaczaj niejawne nawiasy

klamrowe.
W ramach bloku przeczajcego znaczenie nazw uytych w kontekcie wyraenia musi zawsze
by takie samo (o czym wicej w podpunkcie 7.5.2.1).
Lista instrukcji danej sekcji instrukcji przeczajcej jest osigalna, jeli ta instrukcja switch jest
osigalna i speniony jest przynajmniej jeden z przedstawionych poniej warunkw:
Wyraenie przeczajce jest wartoci niesta.
Wyraenie przeczajce jest sta wartoci, ktra pasuje do etykiety case znajdujcej si w tej
sekcji instrukcji przeczajcej.
Wyraenie przeczajce jest wartoci sta, ktra nie pasuje do adnej etykiety case, a ta
sekcja instrukcji przeczajcej zawiera etykiet default.
Do etykiety przeczajcej tej sekcji instrukcji przeczajcej odwouje si osigalna instrukcja
goto case lub goto default.

VLADIMIR RESHETNIKOV Regua ta nie ma zastosowania, gdy instrukcja goto case


lub goto default znajduje si wewntrz bloku try lub bloku catch instrukcji try, ktra
zawiera blok finally, a etykieta instrukcji przeczajcej wystpuje poza instrukcj try, za
kocowy punkt bloku finally jest nieosigalny.
Kocowy punkt instrukcji switch jest osigalny, jeli speniony jest co najmniej jeden z wymienionych poniej warunkw:
Instrukcja switch zawiera osigaln instrukcj break, ktra powoduje opuszczenie instrukcji
switch.

389

8. Instrukcje

VLADIMIR RESHETNIKOV Regua ta nie ma zastosowania, gdy instrukcja break


znajduje si wewntrz bloku try lub bloku catch instrukcji try, ktra zawiera blok finally,
a cel instrukcji break wystpuje poza instrukcj try, za kocowy punkt bloku finally jest
nieosigalny.
Instrukcja switch jest osigalna, wyraenie przeczajce jest wartoci niesta i nie jest obecna
etykieta default.
Instrukcja switch jest osigalna, wyraenie przeczajce jest wartoci sta, ktra nie pasuje
do adnej etykiety case, i nie jest obecna etykieta default.

8.8. Instrukcje iteracji


Instrukcje iteracji powoduj wielokrotne wykonywanie instrukcji osadzonej:
instrukcja-iteracji:
instrukcja-while
instrukcja-do
instrukcja-for
instrukcja-foreach

8.8.1. Instrukcja while


Instrukcja while warunkowo wykonuje osadzon instrukcj zero lub wiksz liczb razy:
instrukcja-while:
while ( wyraenie-booleowskie ) instrukcja-osadzona

Instrukcja while jest wykonywana w nastpujcy sposb:


Przetwarzane jest wyraenie-booleowskie (przedstawione w podrozdziale 7.19).
Jeli w wyniku przetwarzania wyraenia booleowskiego otrzymana zostaje warto true, sterowanie jest przekazywane do instrukcji osadzonej. Gdy sterowanie osiga jej punkt kocowy
(potencjalnie w wyniku wykonania instrukcji continue), przekazywane jest do pocztku instrukcji while.
Jeli w wyniku przetwarzania wyraenia booleowskiego otrzymana zostaje warto false,
sterowanie przekazywane jest do punktu kocowego instrukcji while.
W ramach instrukcji osadzonej instrukcji while moe zosta wykorzystana instrukcja break (opisana w punkcie 8.9.1) w celu przekazania sterowania do kocowego punktu instrukcji while (i tym
samym zakoczenia iteracji instrukcji osadzonej), a instrukcja continue (opisana w punkcie 8.9.2)
moe zosta uyta w celu przekazania sterowania do kocowego punktu instrukcji osadzonej (i tym
samym przeprowadzenia kolejnej iteracji instrukcji while).

390

8.8. Instrukcje iteracji


Instrukcja osadzona instrukcji while jest osigalna, jeli osigalna jest instrukcja while i wyraenie booleowskie nie ma staej wartoci false.
Kocowy punkt instrukcji while jest osigalny, jeli przynajmniej jeden z wymienionych poniej
warunkw jest speniony:
Instrukcja while zawiera osigaln instrukcj break, ktra powoduje opuszczenie instrukcji
while.

VLADIMIR RESHETNIKOV Regua ta nie ma zastosowania, gdy instrukcja break znajduje si wewntrz bloku try lub bloku catch instrukcji try, ktra zawiera blok finally, a cel
instrukcji break wystpuje poza instrukcj try, za kocowy punkt bloku finally jest nieosigalny.
Instrukcja while jest osigalna i wyraenie booleowskie nie ma staej wartoci true.

8.8.2. Instrukcja do
Instrukcja do warunkowo wykonuje osadzon instrukcj jeden raz lub wiksz liczb razy:
instrukcja-do:
do instrukcja-osadzona while ( wyraenie-booleowskie ) ;

Instrukcja do jest wykonywana w nastpujcy sposb:


Sterowanie jest przekazywane do instrukcji osadzonej.
Gdy i jeli sterowanie osiga kocowy punkt instrukcji osadzonej (potencjalnie w wyniku
wykonania instrukcji continue), przetwarzane jest wyraenie-booleowskie (przedstawione
w podrozdziale 7.19). Jeli w wyniku przetwarzania wyraenia booleowskiego otrzymana zostaje
warto true, sterowanie jest przekazywane do pocztku instrukcji do. W innym przypadku
sterowanie przekazywane jest do punktu kocowego instrukcji do.
W ramach instrukcji osadzonej instrukcji do moe zosta wykorzystana instrukcja break (opisana
w punkcie 8.9.1) do przekazania sterowania do kocowego punktu instrukcji do (i tym samym
zakoczenia iteracji instrukcji osadzonej), a instrukcja continue (opisana w punkcie 8.9.2) moe
zosta uyta do przekazania sterowania do kocowego punktu instrukcji osadzonej.
Instrukcja osadzona instrukcji do jest osigalna, jeli osigalna jest instrukcja do.
Kocowy punkt instrukcji do jest osigalny, jeli przynajmniej jeden z wymienionych poniej
warunkw jest speniony:
Instrukcja do zawiera osigaln instrukcj break, ktra powoduje opuszczenie instrukcji do.
Osigalny jest kocowy punkt instrukcji osadzonej i wyraenie booleowskie nie ma staej
wartoci true.
391

8. Instrukcje

VLADIMIR RESHETNIKOV Regua ta nie ma zastosowania, gdy instrukcja break


znajduje si wewntrz bloku try lub bloku catch instrukcji try, ktra zawiera blok finally,
a cel instrukcji break wystpuje poza instrukcj try, za kocowy punkt bloku finally jest
nieosigalny.

8.8.3. Instrukcja for


Instrukcja for powoduje przetworzenie cigu wyrae inicjalizujcych, a nastpnie dopki
speniony jest warunek powtarza wykonanie osadzonej instrukcji i przetwarzanie cigu wyrae iteracji:
instrukcja-for:
for ( inicjalizator-foropc ; warunek-foropc ; iterator-foropc ) instrukcjaosadzona
inicjalizator-for:
deklaracja-zmiennej-lokalnej
lista-wyrae-instrukcji
warunek-for:
wyraenie-booleowskie
iterator-for:
lista-wyrae-instrukcji
lista-wyrae-instrukcji:
wyraenie-instrukcji
lista-wyrae-instrukcji , wyraenie-instrukcji
Inicjalizator-for, jeli jest obecny, skada si z elementu deklaracja-zmiennej-lokalnej (opisanego w punkcie 8.5.1) lub listy rozdzielonych za pomoc przecinkw elementw wyraenieinstrukcji (opisanych w podrozdziale 8.6). Zakres zmiennej lokalnej zadeklarowanej za pomoc
elementu inicjalizator-for zaczyna si od miejsca wystpowania deklaracja-zmiennej-lokalnej
tej zmiennej i rozciga a do koca instrukcji osadzonej. Zakres zawiera element warunek-for
i iterator-for.
Warunek-for, jeli jest obecny, musi by elementem wyraenie-booleowskie (o ktrym wicej

w podrozdziale 7.19).
Iterator-for, jeli jest obecny, skada si z listy elementw wyraenie-instrukcji (o ktrym
wicej w podrozdziale 8.6) rozdzielonych za pomoc przecinkw.

Instrukcja for jest wykonywana w nastpujcy sposb:


Jeli obecny jest inicjalizator-for, w kolejnoci, w jakiej je zapisano, s wykonywane inicjalizatory zmiennych lub wyraenia instrukcji. Krok ten wykonywany jest tylko raz.
392

8.8. Instrukcje iteracji


Jeli obecny jest warunek-for, jest on przetwarzany.
Jeli nie jest obecny warunek-for lub jeli w wyniku przetwarzania go zostaje uzyskana warto
true, sterowanie jest przekazywane do instrukcji osadzonej. Gdy i jeli sterowanie osiga kocowy punkt instrukcji osadzonej (potencjalnie w wyniku wykonania instrukcji continue),
przetwarzane w cigu s wyraenia elementu iterator-for, jeli s one obecne, a nastpnie
przeprowadzana jest kolejna iteracja, poczwszy od przetworzenia elementu warunek-for
w poprzednim kroku.
Jeli obecny jest warunek-for i jeli w wyniku przetwarzania go zostaje uzyskana warto false,
sterowanie jest przekazywane do kocowego punktu instrukcji for.
W ramach instrukcji osadzonej instrukcji for moe zosta uyta instrukcja break (opisana
w punkcie 8.9.1) w celu przekazania sterowania do kocowego punktu instrukcji for (i tym samym
zakoczenia iteracji instrukcji osadzonej), a instrukcja continue (opisana w punkcie 8.9.2) moe
zosta wykorzystana w celu przekazania sterowania do kocowego punktu instrukcji osadzonej
(i tym samym w celu wykonania elementu iterator-for i przeprowadzenia kolejnej iteracji instrukcji for, poczwszy od elementu warunek-for).
Instrukcja osadzona instrukcji for jest osigalna, jeli speniony jest jeden z przedstawionych
poniej warunkw:
Instrukcja for jest osigalna i nie jest obecny warunek-for.
Instrukcja for jest osigalna i obecny jest warunek-for, i nie ma on staej wartoci false.
Kocowy punkt instrukcji for jest osigalny, jeli przynajmniej jeden z wymienionych poniej
warunkw jest speniony:
Instrukcja for zawiera osigaln instrukcj break, ktra powoduje opuszczenie instrukcji for.

VLADIMIR RESHETNIKOV Regua ta nie ma zastosowania, gdy instrukcja break znajduje si wewntrz bloku try lub bloku catch instrukcji try, ktra zawiera blok finally, a cel
instrukcji break wystpuje poza instrukcj try, za kocowy punkt bloku finally jest nieosigalny.
Instrukcja for jest osigalna, obecny jest warunek-for i nie ma on staej wartoci true.

8.8.4. Instrukcja foreach


Instrukcja foreach powoduje wyliczenie elementw kolekcji, ktremu towarzyszy wykonanie
osadzonej instrukcji dla kadego elementu tej kolekcji:
instrukcja-foreach:
foreach ( typ-zmiennej-lokalnej identyfikator in wyraenie ) instrukcjaosadzona

393

8. Instrukcje
Typ i identyfikator instrukcji foreach stanowi deklaracj zmiennej iteracji (ang. iteration variable) instrukcji. Jeli jako typ-zmiennej-lokalnej podane jest sowo kluczowe var, zmienn iteracji

nazywa si niejawnie typowan zmienn iteracji (ang. implicitly typed iteration variable), a za jej
typ przyjmuje si typ elementu instrukcji foreach, tak jak zostao to opisane poniej. Zmienna
iteracji odpowiada zmiennej lokalnej tylko do odczytu o zakresie rozcigajcym si na instrukcj
osadzon. Podczas wykonywania instrukcji foreach zmienna iteracji reprezentuje element kolekcji,
dla ktrego iteracja jest wanie przeprowadzana. Jeli instrukcja osadzona prbuje zmodyfikowa
zmienn iteracji (przez przypisanie lub uycie operatorw ++ oraz--) lub przekaza j jako parametr ref lub out, wwczas generowany jest bd czasu kompilacji.

CHRIS SELLS Ze wzgldu na czytelno kodu powiniene raczej stara si stosowa


instrukcje foreach zamiast for.

VLADIMIR RESHETNIKOV Ze wzgldu na konieczno zapewnienia zgodnoci wstecznej regua ta nie jest stosowana, jeli synonim typu jest wprowadzany za pomoc elementu
dyrektywa-uycia-synonimu lub jeli w zakresie znajduje si nieoglny typ o nazwie var.
Przetwarzanie czasu kompilacji instrukcji foreach zaczyna si od okrelenia typu kolekcji (ang.
collection type), typu enumeratora (ang. enumeraton type) oraz typu elementu (ang. element type)
wyraenie. Okrelanie odbywa si w nastpujcy sposb:
Jeli typem X elementu wyraenie jest typ tablicowy, wwczas istnieje niejawna konwersja
referencyjna z X do interfejsu System.Collections.IEnumerable (poniewa typ System.Array
implementuje ten interfejs). Typem kolekcji jest interfejs System.Collections.IEnumerable,
typem enumeratora jest interfejs System.Collections.IEnumerator, a typem elementu jest typ
elementu tablicy X.
W innym przypadku sprawdzane jest, czy typ X ma odpowiedni metod GetEnumerator:
Przeprowadzane jest odnajdywanie skadowej dla typu X z identyfikatorem GetEnumerator
i bez argumentw typu. Jeli w wyniku tej operacji nie zostaje zwrcone dopasowanie,
pojawia si niejednoznaczno lub zostaje zwrcone dopasowanie niebdce grup metod,
naley sprawdzi interfejs enumerowalny, tak jak zostao to opisane poniej. Zaleca si,
aby zgaszane byo ostrzeenie, jeli odnajdywanie skadowej daje w wyniku co innego ni
grup metod lub brak dopasowania.

ERIC LIPPERT To oparte na wzorcu podejcie zostao opracowane wczeniej, ni


wprowadzono udostpnienie oglnego typu IEnumerable<T>, dlatego te autorzy kolekcji
mogli zapewni mocniejsze komentarze typw w swoich obiektach enumeratorw.

394

8.8. Instrukcje iteracji

Implementacje nieoglnego typu IEnumerable zawsze kocz si pakowaniem kadej skadowej kolekcji liczb cakowitych, poniewa typem zwracanym przez waciwo Current jest
object. Dostawca kolekcji liczb cakowitych mgby zapewni implementacje GetEnumerator,
MoveNext i Current niebazujce na interfejsie, tak aby waciwo Current zwracaa niespakowan warto cakowit.
Oczywicie w wiecie oglnego typu IEnumerable<T> cay ten wysiek staje si niepotrzebny.
Ogromna wikszo iterowanych kolekcji zaimplementuje ten interfejs.
Przeprowadzane jest wyznaczanie przeadowania dla otrzymanej grupy metod i pustej
listy argumentw. Jeli operacja ta nie daje w wyniku adnych moliwych do zastosowania
metod, uzyskany wynik jest niejednoznaczny lub otrzymana zostaje pojedyncza najlepsza
metoda, lecz jest to metoda statyczna lub niepubliczna, wwczas naley sprawdzi interfejs
enumerowalny, tak jak zostao to opisane poniej. Zaleca si, aby zgaszane byo ostrzeenie, jeli wyznaczanie przeadowania daje w wyniku co innego ni jednoznaczn publiczn
metod instancji lub brak moliwych do zastosowania metod.
Jeli typ zwracany E metody GetEnumerator nie jest typem klasy, struktury lub interfejsu,
generowany jest bd i nie s podejmowane adne dalsze kroki.
Przeprowadzane jest odnajdywanie skadowej dla E z identyfikatorem Current i bez argumentw typu. Jeli proces ten nie zwraca dopasowania, wynikiem jest bd lub cokolwiek
innego ni publiczna waciwo instancji umoliwiajca odczyt, generowany jest bd i nie
s podejmowane adne dalsze kroki.
Przeprowadzane jest odnajdywanie skadowej dla E z identyfikatorem MoveNext i bez argumentw typu. Jeli proces ten nie zwraca dopasowania, wynikiem jest bd lub cokolwiek
innego ni publiczna waciwo instancji umoliwiajca odczyt, generowany jest bd i nie
s podejmowane adne dalsze kroki.
Przeprowadzane jest wyznaczanie przeadowania dla grupy metod z pust list argumentw. Jeli w wyniku tej operacji nie uzyskuje si adnych moliwych do zastosowania
metod, wynik operacji jest niejednoznaczny lub otrzymuje si pojedyncz najlepsz metod,
lecz jest to metoda statyczna lub niepubliczna, lub te zwracanym przez ni typem nie
jest bool, generowany jest bd i nie s wykonywane adne dalsze kroki.
Typem kolekcji jest X, typem enumeratora jest E, a typem elementu jest typ waciwoci
Current.
W innym przypadku sprawdzany jest interfejs enumerowalny:
Jeli jest dokadnie jeden typ T, taki e istnieje niejawna konwersja z X na interfejs System.
Collections.Generic.IEnumerable<T>, wwczas typem kolekcji jest ten interfejs, typem
enumeratora jest System.Collections.Generic.IEnumerator<T>, a typem elementu jest T.
W innym przypadku, jeli istnieje wicej ni jeden taki typ T, wwczas generowany jest bd
i nie s podejmowane adne dalsze kroki.

395

8. Instrukcje
W innym przypadku, jeli istnieje niejawna konwersja z X do interfejsu System.Collections.
IEnumerable, wwczas typem kolekcji jest ten interfejs, typem enumeratora jest System.
Collections.IEnumerator, a typem elementu jest object.
W innym przypadku generowany jest bd i nie s podejmowane adne dalsze kroki.
W wyniku podjcia przedstawionych tu krokw, jeli dziaanie zakoczy si sukcesem, otrzymuje
si typ kolekcji C, typ enumeratora E oraz typ elementu T. Instrukcja foreach o postaci:
foreach (V v in x) instrukcja-osadzona

jest rozwijana do postaci:


{
E e = ((C)(x)).GetEnumerator();
try {
V v;
while (e.MoveNext()) {
v = (V)(T)e.Current;

instrukcja-osadzona

}
}
finally {
... // Zwolnienie zmiennej e
}
}

Zmienna e nie jest widoczna ani dostpna dla wyraenia x, instrukcji osadzonej ani jakiegokolwiek innego kodu rdowego programu. Zmienna v jest tylko do odczytu w instrukcji osadzonej.
Jeli nie istnieje jawna konwersja (o ktrej wicej w podrozdziale 6.2) z typu T (typu elementu)
na typ V (typu typ-zmiennej-lokalnej w instrukcji foreach), generowany jest bd i nie s wykonywane adne dalsze kroki. Jeli zmienna x ma warto null, w czasie wykonania zgaszany jest
wyjtek System.NullReferenceException.
Implementacja moe definiowa dany element instrukcja-foreach na rne sposoby na przykad z powodw zwizanych z wydajnoci jeli tylko jego zachowanie jest zgodne z przedstawionym powyej rozwiniciem.
Ciao bloku finally jest konstruowane zgodnie z przedstawionymi poniej krokami:
Jeli istnieje niejawna konwersja z typu E na interfejs System.IDisposable, wwczas
Jeli E jest typem wartociowym niedopuszczajcym wartoci pustej, klauzula finally jest
rozwijana do postaci rwnoznacznej z zapisem:
finally {
((System.IDisposable)e).Dispose();
}

W innym przypadku klauzula finally jest rozwijana do postaci rwnoznacznej z zapisem:


finally {
if (e != null) ((System.IDisposable)e).Dispose();
}

396

8.8. Instrukcje iteracji


z wyjtkiem tego, e jeli E jest typem wartociowym lub parametrem typu zrealizowanym
do typu wartociowego, wwczas rzutowanie na interfejs System.IDisposable nie powoduje
wystpienia operacji pakowania.
W innym przypadku, jeli E jest typem ostatecznym, klauzula finally jest rozwijana do pustego
bloku:
finally {
}

W innym przypadku klauzula finally jest rozwijana do postaci:


finally {
System.IDisposable d = e as System.IDisposable;
if (d != null) d.Dispose();
}

Zmienna lokalna d nie jest widoczna ani dostpna dla jakiegokolwiek fragmentu kodu uytkownika. W szczeglnoci nie powoduje konfliktu z jakkolwiek inn zmienn, ktrej zakres obejmuje
blok finally.
Kolejno, w jakiej instrukcja foreach przechodzi przez elementy tablicy, jest nastpujca:
w przypadku tablic jednowymiarowych elementy s przetwarzane zgodnie z porzdkiem rosncych
indeksw, zaczynajc od indeksu 0, a koczc na indeksie Lenght - 1. W przypadku tablic
wielowymiarowych elementy s przetwarzane w taki sposb, e indeksy wymiaru znajdujcego
si najbardziej na prawo s zwikszane najpierw, a nastpnie zwikszane s indeksy nastpnego
wymiaru na lewo i tak dalej, a do wymiaru znajdujcego si najbardziej na lewo.
Wykonanie przedstawionego poniej przykadowego programu powoduje wywietlenie na ekranie
kadej wartoci dwuwymiarowej tablicy z zachowaniem kolejnoci elementw:
using System;
class Test
{
static void Main() {
double[,] values = {
{1.2, 2.3, 3.4, 4.5},
{5.6, 6.7, 7.8, 8.9}
};
foreach (double elementValue in values)
Console.Write("{0} ", elementValue);
Console.WriteLine();
}
}

W wyniku skompilowania i uruchomienia programu na ekranie komputera pojawiaj si nastpujce dane:


1.2 2.3 3.4 4.5 5.6 6.7 7.8 8.9

W przedstawionym poniej przykadzie:

397

8. Instrukcje
int[] numbers = { 1, 3, 5, 7, 9 };
foreach (var n in numbers) Console.WriteLine(n);

typ n jest wnioskowany jako int, bowiem wanie taki jest typ elementu tablicy liczb.

8.9. Instrukcje skoku


Instrukcje skoku powoduj bezwarunkowe przekazanie sterowania:
instrukcja-skoku:
instrukcja-break
instrukcja-continue
instrukcja-goto
instrukcja-return
instrukcja-throw

Miejsce, do ktrego instrukcja skoku przekazuje sterowanie, nosi nazw celu (ang. target) instrukcji skoku.
Gdy instrukcja skoku wystpuje w bloku, a jej cel znajduje si poza tym blokiem, mwi si, e
instrukcja skoku opuszcza (ang. exit) blok. Mimo e instrukcja skoku moe przekazywa sterowanie poza blok, nigdy nie jest w stanie przekaza go do bloku.
Wykonanie instrukcji skokw jest komplikowane przez obecno ingerujcych instrukcji try.
W przypadku braku takich instrukcji try instrukcja skoku bezwarunkowo przekazuje sterowanie
z instrukcji skoku do jej celu. Jeli s obecne ingerujce instrukcje try, wykonanie jest bardziej
skomplikowane. Jeli instrukcja skoku opuszcza jeden lub wiksz liczb blokw try z powizanymi blokami finally, sterowanie jest pocztkowo przekazywane do bloku finally najbardziej
wewntrznej instrukcji try. Gdy i jeli sterowanie osiga kocowy punkt bloku finally, jest ono
przekazywane do bloku finally kolejnej obejmujcej instrukcji try. Proces ten jest powtarzany
a do momentu wykonania blokw finally wszystkich ingerujcych instrukcji try.
W przedstawionym poniej przykadzie:
using System;
class Test
{
static void Main() {
while (true) {
try {
try {
Console.WriteLine("Przed instrukcj break");
break;
}
finally {
Console.WriteLine("Najbardziej wewntrzny blok finally");
}
}
finally {
Console.WriteLine("Najbardziej zewntrzny blok finally ");

398

8.9. Instrukcje skoku


}
}
Console.WriteLine("Po instrukcji break");
}
}

bloki finally zwizane z dwoma instrukcjami try s wykonywane przed przekazaniem sterowania
do celu instrukcji skoku.
W wyniku skompilowania i uruchomienia przedstawionego powyej programu na ekranie komputera pojawiaj si nastpujce dane:
Przed instrukcj break
Najbardziej wewntrzny blok finally
Najbardziej zewntrzny blok finally
Po instrukcji break

8.9.1. Instrukcja break


Instrukcja break powoduje opuszczenie najbliszej obejmujcej instrukcji switch, while, do, for lub
foreach.
instrukcja-break:
break ;

Celem instrukcji break jest kocowy punkt najbliszej obejmujcej instrukcji switch, while, do, for
lub foreach. Jeli instrukcji break nie obejmuje instrukcja switch, while, do, for lub foreach,
generowany jest bd czasu kompilacji.
Gdy wiele instrukcji switch, while, do, for lub foreach jest zagniedonych w sobie, instrukcja
break jest stosowana jedynie do najbardziej wewntrznej instrukcji obejmujcej. Aby przekaza
sterowanie przez wiele zagniedonych poziomw, trzeba skorzysta z instrukcji goto (opisanej
w punkcie 8.9.3).
Instrukcja break nie moe spowodowa opuszczenia bloku finally (o czym wicej w podrozdziale 8.10). Gdy instrukcja break wystpuje w ramach bloku finally, cel tej instrukcji musi znajdowa si w tym samym bloku finally. W innym przypadku generowany jest bd czasu kompilacji.
Instrukcja break jest wykonywana w nastpujcy sposb:
Jeli instrukcja break powoduje opuszczenie jednego lub wikszej liczby blokw try z powizanymi blokami finally, sterowanie jest pocztkowo przekazywane do bloku finally najbardziej wewntrznej instrukcji try. Gdy i jeli sterowanie osiga kocowy punkt bloku finally,
jest ono przekazywane do bloku finally kolejnej obejmujcej instrukcji try. Proces ten jest
powtarzany a do momentu wykonania blokw finally wszystkich ingerujcych instrukcji try.
Sterowanie jest przekazywane do celu instrukcji break.
Poniewa instrukcja break bezwarunkowo przekazuje sterowanie w jakie inne miejsce, kocowy
punkt tej instrukcji nigdy nie jest osigalny.
399

8. Instrukcje

8.9.2. Instrukcja continue


Instrukcja continue powoduje rozpoczcie nowej iteracji najbliszej obejmujcej instrukcji while,
do, for lub foreach:
instrukcja-continue:
continue ;

Celem instrukcji continue jest kocowy punkt najbliszej instrukcji osadzonej najbliszej obejmujcej instrukcji while, do, for lub foreach. Jeli instrukcji continue nie obejmuje instrukcja
while, do, for lub foreach, generowany jest bd czasu kompilacji.
Gdy wiele instrukcji while, do, for lub foreach jest zagniedonych w sobie, instrukcja continue
jest stosowana jedynie do najbardziej wewntrznej instrukcji obejmujcej. Aby przekaza sterowanie przez wiele zagniedonych poziomw, trzeba skorzysta z instrukcji goto (opisanej
w punkcie 8.9.3).
Instrukcja continue nie moe spowodowa opuszczenia bloku finally (o ktrym wicej w podrozdziale 8.10). Gdy instrukcja continue wystpuje w ramach bloku finally, cel tej instrukcji musi
znajdowa si w tym samym bloku finally. W innym przypadku generowany jest bd czasu
kompilacji.
Instrukcja continue jest wykonywana w nastpujcy sposb:
Jeli instrukcja continue powoduje opuszczenie jednego lub wikszej liczby blokw try z powizanymi blokami finally, sterowanie jest pocztkowo przekazywane do bloku finally najbardziej wewntrznej instrukcji try. Gdy i jeli sterowanie osiga kocowy punkt bloku finally,
jest ono przekazywane do bloku finally kolejnej obejmujcej instrukcji try. Proces ten jest
powtarzany a do momentu wykonania blokw finally wszystkich ingerujcych instrukcji try.
Sterowanie jest przekazywane do celu instrukcji continue.
Poniewa instrukcja continue bezwarunkowo przekazuje sterowanie w jakie inne miejsce, kocowy punkt tej instrukcji nigdy nie jest osigalny.

8.9.3. Instrukcja goto


Instrukcja goto powoduje przekazanie kontroli do instrukcji oznaczonej za pomoc etykiety:
instrukcja-goto:
goto identyfikator ;
goto case wyraenie-stae ;
goto default ;

Celem instrukcji goto identyfikator instrukcja oznaczona za pomoc danej etykiety. Jeli etykieta
o podanej nazwie nie istnieje w biecej funkcji skadowej lub jeli instrukcja goto nie znajduje si

400

8.9. Instrukcje skoku

w zakresie tej etykiety, generowany jest bd czasu kompilacji. Regua ta dopuszcza uywanie instrukcji goto w celu przekazywania kontroli poza zagniedony zakres, nie da si jednak zrobi
tego do zagniedonego zakresu. W przedstawionym poniej przykadzie:
using System;
class Test
{
static void Main(string[] args) {
string[,] table = {
{"Czerwony", "Niebieski", "Zielony"},
{"Poniedziaek", "roda", "Pitek"}
};
foreach (string str in args) {
int row, colm;
for (row = 0; row <= 1; ++row)
for (colm = 0; colm <= 2; ++colm)
if (str == table[row,colm])
goto done;
Console.WriteLine("{0} nieznaleziony", str);
continue;
done:
Console.WriteLine("Znaleziony {0} na pozycji [{1}][{2}]", str, row, colm);
}
}
}

instrukcja goto jest uywana do przekazywania sterowania poza zagniedony zakres.


Celem instrukcji goto case jest lista instrukcji znajdujca si w bezporednio obejmujcej instrukcji switch (o ktrej wicej w punkcie 8.7.2), ktra zawiera etykiet case z odpowiedni wartoci
sta. Jeli adna instrukcja switch nie obejmuje instrukcji goto case, jeli wyraenie-stae nie
jest niejawnie konwertowalne (o czym wicej w podrozdziale 6.1) na typ rzdzcy najblisz
obejmujc instrukcj switch lub jeli najblisza obejmujca instrukcja switch nie zawiera etykiety
case z odpowiedni wartoci sta, generowany jest bd czasu kompilacji.
Celem instrukcji goto default jest lista instrukcji znajdujca si w bezporednio obejmujcej
instrukcji switch (o ktrej wicej w punkcie 8.7.2), ktra zawiera etykiet default z odpowiedni
wartoci sta. Jeli adna instrukcja switch nie obejmuje instrukcji goto case lub jeli najblisza obejmujca instrukcja switch nie zawiera etykiety case default, generowany jest bd czasu
kompilacji.
Instrukcja goto nie moe spowodowa opuszczenia bloku finally (opisanego w podrozdziale 8.10).
Gdy instrukcja goto wystpuje w ramach bloku finally, cel tej instrukcji musi znajdowa si
w obrbie tego samego bloku finally, a w innym przypadku generowany jest bd czasu kompilacji.

BILL WAGNER Reguy te sprawiaj, e instrukcja goto staje si w C# czym nieco tylko
mniejszym ni czysta zoliwo, ja jednak nadal widz dla niej dobre zastosowanie.

401

8. Instrukcje
Instrukcja goto jest wykonywana w nastpujcy sposb:
Jeli instrukcja goto powoduje opuszczenie jednego bloku lub wikszej liczby blokw try
z powizanymi blokami finally, sterowanie jest pocztkowo przekazywane do bloku finally
najbardziej wewntrznej instrukcji try. Gdy i jeli sterowanie osiga kocowy punkt bloku
finally, jest ono przekazywane do bloku finally kolejnej obejmujcej instrukcji try. Proces
ten jest powtarzany a do momentu wykonania blokw finally wszystkich ingerujcych
instrukcji try.
Sterowanie jest przekazywane do celu instrukcji goto.
Poniewa instrukcja goto bezwarunkowo przekazuje sterowanie w jakie inne miejsce, kocowy
punkt tej instrukcji nigdy nie jest osigalny.

KRZYSZTOF CWALINA Oglnie rzecz biorc, uwielbiam prace Dijkstry, ale nie zgadzam si z opini, e instrukcja goto jest szkodliwa. W rzeczywistoci moe ona bowiem
w wielu przypadkach znacznie uproci kod. Podobnie jak kada inna moliwo, moe ona
oczywicie by naduywana, ale szkodzi tak naprawd zy programista, nie za sama instrukcja jako taka. Mimo to instrukcja goto przydaje mi si tak rzadko, e bez kopotu mgbym
sobie bez niej poradzi.

8.9.4. Instrukcja return


Instrukcja return powoduje zwrcenie sterowania do miejsca wywoania funkcji skadowej,
w ktrej wystpuje ta instrukcja:
instrukcja-return:
return wyraenieopc ;

Instrukcja return bez elementu wyraenie moe by uywana wycznie w funkcji skadowej,
ktra nie oblicza wartoci, a wic w metodzie z typem zwracanym void, akcesorze set waciwoci
lub indeksatora, akcesorach add lub remove zdarzenia, konstruktorze instancji, konstruktorze
statycznym lub destruktorze.
Instrukcja return z elementem wyraenie moe by uywana wycznie w funkcji skadowej,
ktra oblicza warto, a wic w metodzie z typem zwracanym innym ni void, akcesorze get waciwoci lub indeksatora lub operatorze definiowanym przez uytkownika. Musi istnie niejawna
konwersja (opisana w podrozdziale 6.1) z typu elementu wyraenie na typ zwracany przez funkcj
skadow zawierajc t instrukcj.

VLADIMIR RESHETNIKOV Jeli instrukcja return wystpuje w ramach funkcji anonimowej, zamiast przedstawionych tutaj stosowane s reguy zaprezentowane w podrozdziale 6.5.

402

8.9. Instrukcje skoku


Gdy instrukcja return pojawia si w bloku finally (o ktrym wicej w podrozdziale 8.10), generowany jest bd czasu kompilacji.
Instrukcja return jest wykonywana w nastpujcy sposb:
Jeli w instrukcji return podano wyraenie, jest ono przetwarzane i otrzymana w wyniku tego
warto jest konwertowana na zwracany typ zawierajcej funkcji skadowej za pomoc konwersji
niejawnej. Wynik tej konwersji staje si wartoci zwracan do miejsca wywoania.
Jeli instrukcja return jest obejmowana przez jeden blok lub wiksz liczb blokw try
z powizanymi blokami finally, sterowanie jest pocztkowo przekazywane do bloku finally
najbardziej wewntrznej instrukcji try. Gdy i jeli sterowanie osiga kocowy punkt bloku
finally, jest ono przekazywane do bloku finally kolejnej obejmujcej instrukcji try. Proces ten jest powtarzany a do momentu wykonania blokw finally wszystkich ingerujcych
instrukcji try.
Sterowanie jest zwracane do miejsca wywoania zawierajcej je funkcji skadowej.
Poniewa instrukcja return bezwarunkowo przekazuje sterowanie w jakie inne miejsce, kocowy
punkt tej instrukcji nigdy nie jest osigalny.

8.9.5. Instrukcja throw


Instrukcja throw powoduje zgoszenie wyjtku:
instrukcja-throw:
throw wyraenieopc ;

Instrukcja throw z elementem wyraenie powoduje zgoszenie wartoci otrzymanej w wyniku


przetwarzania tego elementu wyraenie. Wyraenie musi oznacza warto typu klasy System.
Exception, typu klasy, ktra dziedziczy po System.Exception, lub typu parametru typu, ktrego
skuteczn klas bazow jest System.Exception (bd te jej podklasa). Jeli w wyniku przetwarzania
elementu wyraenie otrzymana zostaje warto null, zamiast wspomnianego wczeniej zgaszany
jest wyjtek System.NullReferenceException.
Instrukcja throw bez elementu wyraenie moe by uywana jedynie w blokach catch. W tego
rodzaju przypadkach instrukcja throw ponownie zgasza wyjtek, ktry jest w danej chwili obsugiwany przez blok catch.

VLADIMIR RESHETNIKOV Instrukcja throw bez elementu wyraenie nie moe by


stosowana w bloku finally ani w funkcji anonimowej, ktra jest zagniedona wewntrz
najbliszego obejmujcego bloku catch, tak jak zostao to pokazane w przedstawionym poniej fragmencie kodu:

403

8. Instrukcje

delegate void F();


class Program {
static void Main() {
try {
}
catch {
F f = () => { throw; };
try {
}
finally {
throw;
}
}
}
}

// Bd CS0156

// Bd CS0724

Poniewa instrukcja throw bezwarunkowo przekazuje sterowanie w jakie inne miejsce, kocowy
punkt tej instrukcji nigdy nie jest osigalny.
Gdy zgaszany jest wyjtek, sterowanie przekazywane jest do pierwszej klauzuli catch w obejmujcej instrukcji try, ktra moe go obsuy. Proces, ktry si odbywa od momentu zgoszenia
wyjtku do momentu przekazania sterowania do odpowiedniego kodu obsugi, znany jest pod
nazw propagacji wyjtku (ang. exception propagation). Propagacja wyjtku polega na powtarzaniu
przeprowadzania przedstawionych poniej krokw a do momentu znalezienia klauzuli catch,
ktra do niego pasuje. W zaprezentowanym opisie punkt zgoszenia (ang. throw point) jest pocztkowo miejscem, w ktrym zgaszany jest wyjtek.
W biecej funkcji skadowej sprawdzana jest kada instrukcja try obejmujca punkt zgoszenia.
Dla kadej instrukcji S, poczwszy od najbardziej wewntrznej instrukcji try, a skoczywszy
na najbardziej zewntrznej instrukcji try, przeprowadzane s nastpujce dziaania:
Jeli blok try instrukcji S obejmuje punkt zgoszenia i jeli S ma jedn lub wiksz liczb
klauzul catch, klauzule te s sprawdzane w kolejnoci wystpowania w celu odnalezienia
odpowiedniego kodu obsugi wyjtku. Za dopasowanie uwaana jest pierwsza klauzula
catch, w ktrej okrelono typ wyjtku lub typ bazowy typu wyjtku. Oglna klauzula catch
(o ktrej wicej w podrozdziale 8.10) jest uznawana za dopasowanie dla kadego typu
wyjtku. Jeli znaleziona zostaje pasujca klauzula catch, propagacja wyjtku jest koczona
przez przekazanie sterowania do bloku tej klauzuli.
W innym przypadku, jeli blok try lub blok catch instrukcji S obejmuje punkt zgoszenia
i jeli S ma blok finally, sterowanie jest przekazywane do tego bloku finally. Jeli blok
finally zgasza kolejny wyjtek, przetwarzanie biecego wyjtku jest przerywane. W innym
przypadku, gdy sterowanie osiga kocowy punkt bloku finally, przetwarzanie biecego
wyjtku jest kontynuowane.

404

8.10. Instrukcja try


Jeli kod obsugi wyjtku nie zosta znaleziony w biecym wywoaniu funkcji skadowej,
wywoanie to jest przerywane. Kroki przedstawione powyej s powtarzane dla miejsca wywoania funkcji skadowej z punktem zgoszenia odpowiadajcym instrukcji, w ktrej wywoana
zostaa ta funkcja skadowa.
Jeli przetwarzanie wyjtku przerywa wszystkie wywoania funkcji skadowych w biecym
wtku, wskazujc tym samym, e w wtku tym nie ma odpowiedniego kodu obsugi dla tego
wyjtku, wwczas przerywany jest sam wtek. Wpyw takiego przerwania na otoczenie zaley
od implementacji.

BILL WAGNER Z zaprezentowanej tu procedury wynika, e powiniene przeprowadza


pewne podstawowe czynnoci czyszczce w znajdujcych si na samym szczycie hierarchii
metodach wszystkich swoich wtkw. W innym przypadku zachowanie Twojej aplikacji
w zderzeniu z bdami bdzie nieprzewidywalne.

8.10. Instrukcja try


Instrukcja try zapewnia mechanizm wychwytywania wyjtkw, ktre zdarzaj si podczas wykonywania bloku. Ponadto instrukcja ta umoliwia okrelenie bloku kodu, ktry jest wykonywany
zawsze, gdy sterowanie opuszcza instrukcj try:
instrukcja-try:
try blok klauzule-catch
try blok klauzula-finally
try blok klauzule-catch klauzula-finally
klauzule-catch:
sprecyzowane-klauzule-catch oglna-klauzula-catchopc
sprecyzowane-klauzule-catchopc oglna-klauzula-catch
sprecyzowane-klauzule-catch:
sprecyzowana-klauzula-catch
sprecyzowane-klauzule-catch sprecyzowana-klauzula-catch
sprecyzowana-klauzula-catch:
catch ( typ-klasy identyfikatoropc ) blok
oglna-klauzula-catch:
catch blok
klauzula-finally:
finally blok

405

8. Instrukcje
Moliwe s trzy postacie instrukcji try:
Blok try, po ktrym wystpuje jeden lub wiksza liczba blokw catch.
Blok try, po ktrym wystpuje blok finally.
Blok try, po ktrym wystpuje jeden lub wiksza liczba blokw catch, po nich za blok finally.
Gdy w klauzuli catch podany jest typ-klasy, musi nim by System.Exception, typ dziedziczcy po
System.Exception lub typ parametru typu, ktrego skuteczn klas bazow jest System.Exception
(bd te jej podklasa).
Gdy w klauzuli catch podane s zarwno typ-klasy, jak i identyfikator, deklarowana jest zmienna
wyjtku (ang. exception variable) o podanej nazwie. Zmienna wyjtku odpowiada zmiennej lokalnej o zakresie rozcigajcym si na blok catch. W czasie wykonywania tego bloku catch zmienna
wyjtku reprezentuje wyjtek, ktry jest wanie obsugiwany. Na potrzeby sprawdzania niewtpliwego ustalenia przyjmuje si, e zmienna wyjtku jest niewtpliwie ustalona w caym swoim
zakresie.
Jeli klauzula catch nie zawiera nazwy zmiennej wyjtku, nie jest moliwe uzyskanie dostpu do
obiektu wyjtku w tym bloku catch.
Klauzula catch, w ktrej nie jest okrelony ani typ wyjtku, ani nazwa zmiennej wyjtku, jest
nazywana ogln klauzul catch. Instrukcja try moe mie tylko jedn ogln klauzul catch; jeli
jest ona obecna, musi by ostatni klauzul catch.
Niektre jzyki programowania mog obsugiwa wyjtki, ktrych nie da si przedstawi jako
obiektw klas pochodnych wobec System.Exception, cho tego rodzaju wyjtki nigdy nie powinny
by generowane przez kod C#. Do wychwytywania takich wyjtkw moe zosta zastosowana
oglna klauzula catch. Z tego powodu rni si ona pod wzgldem skadni od takiej, w ktrej
podano typ System.Exception, tym, e oglna klauzula catch moe wychwytywa wyjtki zgaszane w innych jzykach programowania.

ERIC LIPPERT W biecej implementacji C# i CLR firmy Microsoft domylnie jest tak,
e zgoszony obiekt, ktry nie jest typu pochodnego wobec Exception, jest konwertowany na
obiekt RuntimeWrappedException. Dziki temu instrukcja catch (Exception e) jest w stanie
wychwyci wszystkie wyjtki.
Jeli chcesz zmieni to zachowanie i uywa tradycyjnej semantyki C# 1.0, w ktrej niebdce
klasy Exception obiekty zgaszane przez inne jzyki nie s przechwytywane w ten sposb,
wwczas powiniene zastosowa nastpujcy atrybut podzespou:
[assembly:System.Runtime.CompilerServices.RuntimeCompatibility
(WrapNonExceptionThrows = false)]

406

8.10. Instrukcja try


W celu odnalezienia kodu obsugi wyjtku klauzule catch s sprawdzane w kolejnoci leksykalnej.
Jeli w klauzuli catch podano typ, ktry jest taki sam jak typ podany we wczeniejszej klauzuli
catch tej samej instrukcji try lub jest wobec niego pochodny, generowany jest bd czasu kompilacji. Gdyby nie istniao to ograniczenie, moliwe byoby tworzenie nieosigalnych klauzul catch.
W ramach bloku catch mona korzysta z instrukcji throw (o ktrej wicej w punkcie 8.9.5) bez
elementu wyraenie w celu ponownego zgoszenia wyjtku, ktry zosta przechwycony przez ten
blok catch. Przypisania do zmiennej wyjtku nie zmieniaj wyjtku, ktry jest ponownie zgaszany.
W przedstawionym poniej przykadzie:
using System;
class Test
{
static void F() {
try {
G();
}
catch (Exception e) {
Console.WriteLine("Wyjtek w F: " + e.Message);
e = new Exception("F");
throw; // Ponowne zgoszenie
}
}
static void G() {
throw new Exception("G");
}
static void Main() {
try {
F();
}
catch (Exception e) {
Console.WriteLine("Wyjtek w Main: " + e.Message);
}
}
}

metoda F przechwytuje wyjtek, wywietla na ekranie pewien komunikat diagnostyczny, modyfikuje zmienn wyjtku, a nastpnie zgasza go ponownie. Zgoszony ponownie wyjtek jest oryginalnym wyjtkiem, dlatego na ekranie komputera pojawiaj si nastpujce dane:
Wyjtek w F: G
Wyjtek w Main: G

Gdyby w pierwszym bloku catch zgosi wyjtek e, zamiast ponownie zgasza biecy wyjtek, na
ekranie komputera pojawiyby si nastpujce dane:
Wyjtek w F: G
Wyjtek w Main: F

Prba przekazania sterowania poza blok finally za pomoc instrukcji break, continue lub goto
stanowi bd czasu kompilacji. Gdy w bloku finally wystpuje instrukcja break, continue lub goto,
jej cel musi znajdowa si w obrbie tego samego bloku finally; w innym przypadku generowany
jest bd czasu kompilacji.

407

8. Instrukcje
Wystpienie w bloku finally instrukcji return jest bdem czasu kompilacji.
Instrukcja try jest wykonywana w nastpujcy sposb:
Sterowanie jest przekazywane do bloku try.
Gdy i jeli sterowanie osignie kocowy punkt bloku try:
Jeli instrukcja try ma blok finally, wykonywany jest ten blok finally.
Sterowanie jest przekazywane do kocowego punktu instrukcji try.
Jeli wyjtek jest propagowany do instrukcji try w czasie wykonywania bloku try:
Klauzule catch, jeli jakie wystpuj, s sprawdzane w kolejnoci wystpowania w celu
odnalezienia odpowiedniego kodu obsugi wyjtku. Pierwsza klauzula catch, w ktrej
okrelono typ wyjtku lub typ bazowy typu wyjtku jest uwaana za dopasowanie. Oglna
klauzula catch jest uznawana za dopasowanie dla kadego typu wyjtku. Jeli znaleziona
zostaje pasujca klauzula catch:
Jeli w pasujcej klauzuli catch zadeklarowano zmienn wyjtku, przypisywany jest do
niej obiekt wyjtku.
Sterowanie jest przekazywane do pasujcego bloku catch.
Gdy i jeli sterowanie osiga kocowy punkt bloku catch:
Jeli instrukcja try ma blok finally, wykonywany jest ten blok finally.
Sterowanie jest przekazywane do kocowego punktu instrukcji try.
Jeli wyjtek jest propagowany do instrukcji try podczas wykonywania bloku catch:
Jeli instrukcja try ma blok finally, wykonywany jest ten blok finally.
Sterowanie jest przekazywane do kolejnej obejmujcej instrukcji try.
Jeli instrukcja try nie ma klauzul catch lub jeli adna z klauzul catch nie pasuje do
wyjtku:
Jeli instrukcja try ma blok finally, wykonywany jest ten blok finally.
Sterowanie jest przekazywane do kolejnej obejmujcej instrukcji try.

ERIC LIPPERT Jeli stos wywoania zawiera kod chroniony za pomoc blokw try-catch
napisanych w innych jzykach programowania (takich jak Visual Basic), rodowisko uruchomieniowe moe zastosowa filtr wyjtkw, aby sprawdzi, czy dany blok catch jest
odpowiedni dla zgaszanego wyjtku. W wyniku tego kod uytkownika moe zosta wykonany po zgoszeniu wyjtku, lecz przed wykonaniem powizanego bloku finally. Jeli Twj

408

8.11. Instrukcje checked i unchecked

kod obsugi wyjtkw zaley od stanu globalnego, ktry jest spjny dziki temu, e blok
finally jest uruchamiany przed jakimkolwiek innym kodem uytkownika, wwczas powiniene w odpowiedni sposb sprawdzi, czy Twj kod wychwytuje wyjtek, zanim rodowisko uruchomieniowe stosuje definiowane przez uytkownika filtry wyjtkw, ktre mog
znajdowa si na stosie.
Instrukcje bloku finally s wykonywane zawsze, gdy sterowanie opuszcza instrukcj try. Jest
tak niezalenie od tego, czy przepyw sterowania jest wynikiem normalnego wykonania, wynikiem
wykonania instrukcji break, continue, goto lub return, czy te wynikiem propagacji wyjtku poza
t instrukcj try.
Jeli wyjtek jest zgaszany podczas wykonywania bloku finally i nie jest przechwytywany w obrbie tego bloku, jest on propagowany do nastpnej obejmujcej instrukcji try. Jeli kolejny
wyjtek wystpuje w czasie propagacji, wyjtek ten zostaje utracony. Proces propagacji wyjtku
zosta dokadniej przedstawiony w opisie instrukcji throw (znajdujcym si w punkcie 8.9.5).

BILL WAGNER Zachowanie to sprawia, e bardzo wane jest pisanie klauzul finally
w sposb bezpieczny w celu uniknicia ryzyka zgoszenia drugiego wyjtku.
Blok try instrukcji try jest osigalny, jeli osigalna jest ta instrukcja try.
Blok catch instrukcji try jest osigalny, jeli osigalna jest ta instrukcja try.
Blok finally instrukcji try jest osigalny, jeli osigalna jest ta instrukcja try.
Kocowy punkt instrukcji try jest osigalny, jeli spenione s obydwa podane poniej warunki:
Osigalny jest punkt kocowy bloku try lub osigalny jest punkt kocowy przynajmniej
jednego bloku catch.
Jeli obecny jest blok finally, osigalny jest punkt kocowy tego bloku finally.

8.11. Instrukcje checked i unchecked


Instrukcje checked i unchecked s uywane do sterowania kontekstem kontroli przekroczenia
zakresu (ang. overflow checking context) w przypadku konwersji i operacji arytmetycznych na
typach cakowitych:
instrukcja-checked:
checked blok
instrukcja-unchecked:
unchecked blok

409

8. Instrukcje
Instrukcja checked powoduje, e wszystkie wyraenia znajdujce si w elemencie blok s przetwarzane w kontekcie kontrolowanym, za instrukcja unchecked powoduje, e wszystkie wyraenia znajdujce si w elemencie blok s przetwarzane w kontekcie niekontrolowanym.
Instrukcje checked i unchecked dokadnie odpowiadaj operatorom checked i unchecked (opisanym
w punkcie 7.5.12), z wyjtkiem tego, e operuj one na blokach wyrae.

8.12. Instrukcja lock


Instrukcja lock umoliwia naoenie blokady wzajemnie wykluczajcej na dany obiekt, wykonanie
instrukcji, a nastpnie zdjcie tej blokady:
instrukcja-lock:
lock ( wyraenie ) instrukcja-osadzona
Wyraenie instrukcji lock musi oznacza warto typu, o ktrym wiadomo, e jest to typ-refe
rencyjny. Dla elementu wyraenie instrukcji lock nigdy nie jest przeprowadzana niejawna

konwersja pakowania (o ktrej wicej w punkcie 6.1.7). Z tego powodu pojawia si bd czasu
kompilacji, jeli wyraenie oznacza warto typ-wartociowy.
Instrukcja lock o postaci:
lock (x) ...

gdzie x jest wyraeniem typ-referencyjny, jest dokadnie rwnowana z kodem:


System.Threading.Monitor.Enter(x);
try {
...
}
finally {
System.Threading.Monitor.Exit(x);
}

z wyjtkiem tego, e x jest przetwarzane tylko raz.

ERIC LIPPERT Nieszczliw konsekwencj wyboru tego sposobu generowania kodu


jest to, e w przypadku kompilacji przeznaczonych do debugowania pomidzy operacj Enter
i blokiem try moe wystpowa pusta instrukcja IL (te bezuyteczne w innych sytuacjach
instrukcje s czsto generowane po to, aby narzdzie diagnostyczne miao do dyspozycji
wyranie okrelone miejsce, w ktrym mona w razie potrzeby umieci punkt przerwania
podczas procesu diagnozowania).
Jeli uruchamiasz kompilacj przeznaczon do diagnozowania i nastpi przeczenie wtku
podczas wykonywania tej operacji pustej, moe si zdarzy, e wyjtek przerwania wtku
zostaje zgoszony w innym wtku. Przerwanie wtku moe wystpi przed wejciem do

410

8.12. Instrukcja lock

bloku try, dlatego blok finally nigdy nie jest wykonywany i blokada nigdy nie zostaje
zwolniona. Zachowanie takie moe prowadzi do bardzo trudnych do zdiagnozowania sytuacji zakleszczenia.
Problem ten zostanie by moe wyeliminowany w przyszych wersjach C# za pomoc rnych
postaci operacji Enter, ktre bdzie si dao bezpiecznie wywoa w ramach bloku try.

BILL WAGNER Uycie instrukcji lock() wie si rwnie z dodatkowymi przeprowadzanymi przez kompilator kontrolami tego, czy blokowanie nie dotyczy elementu typu wartociowego.
Podczas utrzymywania blokady wzajemnie wykluczajcej moliwe jest rwnie naoenie i zwolnienie blokady przez kod przetwarzany w ramach tego samego wtku wykonania. W przeciwiestwie do niego kod wykonywany w innych wtkach nie moe naoy blokady, zanim bieca
blokada nie zostanie zwolniona.
Nie zaleca si blokowania obiektw System.Type w celu zsynchronizowania dostpu do danych
statycznych. Inny fragment kodu moe zablokowa ten sam typ, co moe spowodowa zakleszczenie. Lepszym sposobem jest synchronizowanie dostpu do danych statycznych przez blokowanie
prywatnego obiektu statycznego. Przykad takiej operacji zosta przedstawiony poniej:
class Cache
{
private static object synchronizationObject = new object();
public static void Add(object x) {
lock (Cache.synchronizationObject) {

...

}
}
public static void Remove(object x) {
lock (Cache.synchronizationObject) {
...
}
}
}

JOSEPH ALBAHARI Dobr taktyk przy pisaniu bibliotek przeznaczonych do publicznego wykorzystania jest zabezpieczanie funkcji statycznych przed ubocznymi efektami dziaania wielu rnych wtkw (co mona zwykle osign przez implementowanie blokady
w obrbie funkcji, tak jak zostao to przedstawione na powyszym przykadzie). Znacznie
trudniej jest uytkownikom kodu stosowa blokad wok wywoania Twoich statycznych
metod lub waciwoci (a czsto jest to wrcz niemoliwe), poniewa nie bd oni wiedzieli,
z jakich innych miejsc wywoywane s Twoje funkcje.
411

8. Instrukcje

8.13. Instrukcja using


Instrukcja using umoliwia uzyskanie jednego lub wikszej liczby zasobw, wykonanie instrukcji,
a nastpnie zwolnienie tych zasobw:
instrukcja-using:
using ( pozyskanie-zasobu ) instrukcja-osadzona
pozyskanie-zasobu:
deklaracja-zmiennej-lokalnej
wyraenie

Zasb (ang. resource) jest klas lub struktur implementujc interfejs System.IDisposable, ktry
zawiera pojedyncz bezparametrow metod o nazwie Dispose. Kod, w ktrym uywany jest
zasb, moe wywoa metod Dispose, aby wskaza, e zasb nie jest ju potrzebny. Jeli metoda
Dispose nie zostaje wywoana, wwczas automatyczne zwolnienie zasobu nastpuje w kocu jako
efekt odzyskiwania pamici.

JOSEPH ALBAHARI Wywoanie metody Dispose nie wpywa w aden sposb na


dziaanie mechanizmu odzyskiwania pamici: obiekt zaczyna nadawa si do odzyskania,
gdy (i tylko gdy) nie odwouj si do niego adne inne obiekty. Rwnie odzyskiwanie pamici
nie wpywa na zwalnianie zasobu: mechanizm odzyskiwania nie wywoa metody Dispose,
dopki nie napiszesz finalizatora (destruktora), w ktrym jawnie znajdzie si to wywoanie.
Dwie czynnoci przeprowadzane najczciej w ramach metody Dispose to zwalnianie niezarzdzanych zasobw i wywoanie metod Dispose na rzecz innych obiektw, do ktrych
odwouje si obiekt biecy, lub takich, w ktrych jest posiadaniu. Zwolnienie niezarzdzanych zasobw moliwe jest rwnie z poziomu samego finalizatora, ale operacja taka
oznacza oczekiwanie nieokrelon ilo czasu na uruchomienie mechanizmu odzyskiwania
pamici. I wanie dlatego istnieje IDisposable.
Jeli postaci elementu pozyskanie-zasobu jest deklaracja-zmiennej-lokalnej, wwczas typem
elementu deklaracja-zmiennej-lokalnej musi by interfejs System.IDisposable lub typ, ktry
moe by jawnie konwertowany do System.IDisposable. Jeli pozyskanie-zasobu ma posta
wyraenie, wwczas wyraenie to musi by typu System.IDisposable lub typu, ktry mona niejawnie konwertowa do System.IDisposable.
Zmienne lokalne zadeklarowane w elemencie pozyskanie-zasobu s tylko do odczytu i musz
zawiera inicjalizator. Jeli instrukcja osadzona prbuje zmodyfikowa te zmienne lokalne
(za pomoc operatorw ++ oraz --), pobra ich adresy lub przekaza je jako parametry ref lub
out, generowany jest bd czasu kompilacji.

412

8.13. Instrukcja using


Instrukcja using jest przeksztacana przez podzia na trzy czci: pozyskanie, uycie i zwolnienie.
Uycie zasobu jest niejawnie ujte w instrukcj try zawierajc klauzul finally. Ta klauzula
finally zwalnia uywany zasb. Jeli pozyskany zostaje zasb null, wwczas nie jest przeprowadzane wywoanie Dispose i nie jest zgaszany wyjtek.
Instrukcja using o postaci:
using (ResourceType resource = expression) statement

odpowiada jednemu z dwch moliwych rozwini. Gdy ResourceType jest typem wartociowym,
rozwinicie instrukcji jest nastpujce:
{
ResourceType resource = expression;
try {
statement;
}
finally {
((IDisposable)resource).Dispose();
}
}

W innym przypadku, gdy ResourceType jest typem referencyjnym, rozwinicie ma posta:


{
ResourceType resource = expression;
try {
statement;
}
finally {
if (resource != null) ((IDisposable)resource).Dispose();
}
}

W kadym z tych rozwini zmienna resource w instrukcji osadzonej jest tylko do odczytu.
Implementacja moe definiowa dany element instrukcja-using na rne sposoby co moe
by na przykad spowodowane wzgldami wydajnociowymi jeli tylko jego zachowanie jest
zgodne z przedstawionymi powyej rozwiniciami.
Instrukcja using o postaci:
using (expression) statement

ma te same dwa rozwinicia, ale w tym przypadku ResourceType jest niejawnie typem czasu
kompilacji elementu expression, a zmienna resource jest niedostpna w instrukcji osadzonej i jest
dla niej niewidoczna.
Gdy pozyskanie-zasobu przyjmuje posta deklaracja-zmiennej-lokalnej, moliwe jest pozyskanie wielu zasobw danego typu. Instrukcja using o postaci:
using (ResourceType r1 = e1, r2 = e2, ..., rN = eN) statement

jest dokadnie rwnowana z nastpujc sekwencj zagniedonych instrukcji using:

413

8. Instrukcje
using (ResourceType r1 = e1)
using (ResourceType r2 = e2)
...
using (ResourceType rN = eN)
statement

Wykonanie przedstawionego poniej przykadowego kodu powoduje utworzenie pliku o nazwie


log.txt i zapisanie w nim dwch linii tekstu. Program otwiera nastpnie ten sam plik do odczytu
i kopiuje znajdujce si w nim linie tekstu na ekran:
using System;
using System.IO;
class Test
{
static void Main() {
using (TextWriter w = File.CreateText("log.txt")) {
w.WriteLine("To jest pierwsza linia");
w.WriteLine("To jest druga linia ");
}
using (TextReader r = File.OpenText("log.txt")) {
string s;
while ((s = r.ReadLine()) != null) {
Console.WriteLine(s);
}
}
}
}

Dziki temu, e klasy TextWriter i TextReader implementuj interfejs IDisposable, w przykadzie


mona zastosowa instrukcje using w celu zapewnienia, e uywany plik jest prawidowo zamykany po wykonaniu operacji zapisu i odczytu.

CHRIS SELLS Niemal zawsze powiniene owija za pomoc blokw using wszelkie
pozyskiwane zasoby, ktre implementuj interfejs IDisposable (chyba e przetrzymujesz
ten obiekt pomidzy wywoaniami metod). Mimo e mechanizm odzyskiwania pamici platformy .NET wietnie radzi sobie ze zwalnianiem zasobw pamiciowych, wszystkimi innymi
zasobami powiniene zarzdza samodzielnie. Rwnie kompilator doskonale radzi sobie
z generowaniem dla Ciebie odpowiedniego kodu, ale tylko wwczas, gdy umieszczasz alokacje
zasobw w blokach using.

8.14. Instrukcja yield


Instrukcja yield jest uywana w bloku iteratora (o ktrym wicej w podrozdziale 8.2) w celu
zwrcenia wartoci do obiektu enumeratora (o ktrym wicej w punkcie 10.4.4) lub obiektu
enumerowalnego (o ktrym wicej w punkcie 10.14.5) iteratora lub w celu zasygnalizowania
koca iteracji:

414

8.14. Instrukcja yield


instrukcja-yield:
yield return wyraenie ;
yield break ;

Zwr uwag na to, e yield nie jest sowem zarezerwowanym; ma ono specjalnie znaczenie
wycznie wtedy, gdy jest uywane bezporednio przed sowem kluczowym return lub break.
W innych kontekstach yield moe by wykorzystywane jako identyfikator.
Miejsce, w ktrym moe wystpi instrukcja yield, wyznaczane jest przez kilka wymienionych
poniej ogranicze:
Gdy instrukcja yield (w ktrejkolwiek z postaci) wystpuje poza elementem ciao-metody,
ciao-operatora lub ciao-akcesora, pojawia si bd czasu kompilacji.
Gdy instrukcja yield (w ktrejkolwiek z postaci) wystpuje wewntrz funkcji anonimowej,
pojawia si bd czasu kompilacji.
Gdy instrukcja yield (w ktrejkolwiek z postaci) wystpuje w klauzuli finally instrukcji try,
pojawia si bd czasu kompilacji.
Gdy instrukcja yield return wystpuje gdziekolwiek w instrukcji try, ktra zawiera klauzule
catch, pojawia si bd czasu kompilacji.
Przedstawiony poniej fragment kodu zawiera kilka przykadw prawidowych i nieprawidowych
zastosowa instrukcji yield:
delegate IEnumerable<int> D();
IEnumerator<int> GetEnumerator() {
try {
yield return 1; // W porzdku
yield break;
// W porzdku
}
finally {
yield return 2; // Bd, instrukcja yield w klauzuli finally
yield break;
// Bd, instrukcja yield w klauzuli finally
}
try {
yield return 3; // Bd, instrukcja yield return w bloku try...catch
yield break;
// W porzdku
}
catch {
yield return 4; // Bd, instrukcja yield return w bloku try...catch
yield break;
// W porzdku
}
D d = delegate {
yield return 5; // Bd, instrukcja yield w funkcji anonimowej
};
}
int MyMethod() {
yield return 1;
// Bd, niewaciwy typ zwracany dla bloku iteratora
}

415

8. Instrukcje

Musi istnie niejawna konwersja (opisana w podrozdziale 6.1) z typu wyraenia znajdujcego si
w instrukcji yield return na typ zwracany (o czym wicej w punkcie 10.14.3) przez iterator.
Instrukcja yield return jest wykonywana w nastpujcy sposb:
Wyraenie podane w instrukcji jest przetwarzane, niejawnie konwertowane na typ zwracany
i przypisywane do waciwoci Current obiektu enumeratora.
Zawieszane jest wykonanie bloku iteratora. Jeli instrukcja yield return znajduje si w obrbie
jednego bloku lub wikszej liczby blokw try, powizane bloki finally nie s wykonywane
w tym momencie.
Metoda MoveNext obiektu enumeratora zwraca do miejsca wywoania warto true, wskazujc,
e obiektowi enumeratora udao si przej do kolejnego elementu.
Nastpne wywoanie metody MoveNext obiektu enumeratora powoduje ponowne podjcie wykonania bloku iteratora w tym miejscu, w ktrym zostao ono ostatnio zawieszone.
Instrukcja yield break jest wykonywana w nastpujcy sposb:
Jeli instrukcja yield break powoduje opuszczenie jednego lub wikszej liczby blokw try
z powizanymi blokami finally, sterowanie jest pocztkowo przekazywane do bloku finally
najbardziej wewntrznej instrukcji try. Gdy i jeli sterowanie osiga kocowy punkt bloku
finally, jest ono przekazywane do bloku finally kolejnej obejmujcej instrukcji try.
Proces ten jest powtarzany a do momentu wykonania blokw finally wszystkich ingerujcych instrukcji try.
Sterowanie jest przekazywane do miejsca wywoania bloku iteratora. Jest nim metoda MoveNext lub metoda Dispose obiektu enumeratora.
Poniewa instrukcja yield break bezwarunkowo przekazuje sterowanie w jakie inne miejsce,
kocowy punkt tej instrukcji nigdy nie jest osigalny.

CHRIS SELLS Czasami zdarza mi si zapomnie, e instrukcja yield return nie dziaa
jak instrukcja return pod tym wzgldem, e kod znajdujcy si po yield return moe zosta
wykonany. Na przykad w przedstawionym poniej fragmencie programu kod wystpujcy
po pierwszym return nigdy nie moe zosta wykonany:
int F() {
return 1;
return 2;
}

// Nigdy nie moe by wykonane

W przeciwiestwie do tego kod po pierwszej instrukcji yield return widocznej w poniszym


fragmencie moe by wykonany:

416

8.14. Instrukcja yield

IEnumerable<int> F() {
yield return 1;
yield return 2; // Moe by wykonane
}

Czsto bolenie odczuwam to w przypadku instrukcji if:


IEnumerable<int> F() {
if(...) { yield return 1; }
yield return 2;
}

// Chc, eby byo to jedyn wartoci zwracan


// Ups!

W takich przypadkach pomocne moe si okaza zapamitanie, e instrukcja yield return


nie jest kocowa, tak jak instrukcja return.

BILL WAGNER W podrozdziale tym przedstawione zostao dziaanie instrukcji yield


w teorii. W praktyce instrukcje yield bd powodoway tworzenie zagniedonych klas, ktre
implementuj wzorzec iteratora.

417

You might also like