Professional Documents
Culture Documents
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
4. Typy 145
4.1.
4.2.
4.3.
4.4.
4.5.
4.6.
5. Zmienne 171
5.1.
5.2.
5.3.
5.4.
5.5.
6. Konwersje 195
6.1.
6.2.
6.3.
6.4.
6.5.
6.6.
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.
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.
Spis treci
Spis treci
Spis treci
Wprowadzenie 679
Zalecane znaczniki 681
Przetwarzanie pliku dokumentacji 691
Przykad 697
B. Gramatyka 703
B.1.
B.2.
B.3.
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
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");
}
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.
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
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
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
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.
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
x;
y =
z =
u =
v =
{1, 2, 3};
null;
x => x + 1;
v++;
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
=
=
=
=
// Dobrze
// Dobrze
// Dobrze
// LE!
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>();
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
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.
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();
384
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
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
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
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.
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.
389
8. Instrukcje
390
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 ) ;
8. Instrukcje
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.
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.
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.
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.
394
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
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();
}
396
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();
}
}
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.
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
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
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
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.
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
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);
}
}
}
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.
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
403
8. Instrukcje
// 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
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
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
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.
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.
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) ...
410
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
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.
412
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 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
413
8. Instrukcje
using (ResourceType r1 = e1)
using (ResourceType r2 = e2)
...
using (ResourceType rN = eN)
statement
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.
414
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;
}
416
IEnumerable<int> F() {
yield return 1;
yield return 2; // Moe by wykonane
}
417