You are on page 1of 286

Elsz

ltalban ez az a rsz, ahol a szerz bemutatkozik, kifejti a motivciit illetve


ksznetet mond a krnyezete segtsgrt. Nem fogok nagy meglepetst okozni, ez
most is gy lesz.
Az elmlt kt vben, mita a jegyzet ltezik rengeteg levelet kaptam, klnfle
tmban. Egy kzs pont viszont mindegyikben volt: a levlrk egy idsebb emberre
szmtottak, szmos esetben tanrnak gondoltak. Ez alapveten nem zavar, st jl
esik, hiszen ez is bizonytja, hogy sikerlt egy rett, mindenki szmra emszthet
knyvet ksztenem. Most viszont - abbl az alkalombl, hogy a jegyzet letben
ekkora esemny trtnt, gy rzem ideje hivatalosan bemutatkoznom:
Reiter Istvn vagyok, 24 ves programoz. B tz ve foglalkozom informatikval, az
utbbi hatot pedig mr a stt oldalon tltttem. Elsdlegesen (vastag)kliens oldalra
specializldtam ez ebben a pillanatban a WPF/Silverlight kettst jelenti - br elbbi
kzelebb ll a szvemhez. Jelenleg - munka mellett - az ELTE Programtervez
Informatikus szakn folytatok tanulmnyokat.
2008-ban elindtottam szakmai blogomat a rgi msPortal-on - ez ma a devPortal
akadmiai szekcijaknt szolgl - s ekkor szletett meg bennem egy kisebb
dokumentci terve, amely sszefoglaln, hogy mit kell a C# nyelvrl tudni.
Elkezdtem rni, de az anyag egyre csak ntt, tereblyesedett s vgl megszletett a
jegyzet els szzegynhny oldalas vltozata. A pozitv fogadtats miatt folytattam
az rst s nhny hnap utn az eredeti, viszonylag sszeszedett jegyzetbl egy
risi, kaotikus, ppenhogy hasznlhat massza keletkezett.
Ez volt az a pont, ahol llekben feladtam az egszet, nem volt kedvem, motivcim
rendberakni.
Eltelt tbb mint fl v, megrkezett 2010 s elhatroztam, hogy - jvi fogadalom
gyannt - feltmasztom a szrnyeteget. Az eredeti jegyzet tl sokat akart, ezrt gy
dntttem, hogy kiemelem az alapokat - ez gyakorlatilag a legels vltozat - s azt
bvtem ki. Ez olyannyira jl sikerlt, hogy kzel hromszoros terjedelmet sikerlt
elrnem a kiindulshoz kpest.
Mr csak egy dologgal tartozom, ksznetet kell mondjak a kvetkezknek:
-

Mindenkinek aki az elmlt kt vben tancsokkal, kiegsztsekkel,


javtsokkal ltott el.
Mindenkinek aki elolvasta vagy el fogja olvasni ezt a knyvet, remlem
tetszeni fog.
A devPortal kzssgnek
A Microsoft Magyarorszgnak

A jegyzet ingyenesen letlthet a devPortal-rl:


http://devportal.hu/content/CSharpjegyzet.aspx

-2-

Tartalomjegyzk
1

Bevezet .........................................................................................................................9
1.1 A jegyzet jellsei ....................................................................................................9
1.2

Microsoft .NET Framework .......................................................................................... 10


2.1 A .NET platform .................................................................................................... 10
2.1.1

MSIL/CIL ....................................................................................................... 10

2.1.2

Fordts s futtats........................................................................................... 11

2.1.3

BCL ................................................................................................................ 11

2.2

A C# programozsi nyelv ....................................................................................... 11

2.3

Alternatv megoldsok ............................................................................................ 12

2.3.1

SSCLI ............................................................................................................. 12

2.3.2

Mono............................................................................................................... 12

2.3.3

DotGNU.......................................................................................................... 13

Hello C#! .................................................................................................................. 14


3.1 A C# szintaktikja .................................................................................................. 15
3.1.1

Kulcsszavak .................................................................................................... 15

3.1.2

Megjegyzsek ................................................................................................. 16

3.2
4

Nvterek ................................................................................................................. 17

Vltozk ....................................................................................................................... 18
4.1 Deklarci s definci ........................................................................................... 18
4.2

Tpusok .................................................................................................................. 18

4.3

Loklis s globlis vltozk .................................................................................... 20

4.4

Referencia- s rtktpusok ..................................................................................... 20

4.5

Referencik ............................................................................................................ 22

4.6

Boxing s unboxing ................................................................................................ 23

4.7

Konstansok............................................................................................................. 25

4.8

A felsorolt tpus ...................................................................................................... 25

4.9

Null tpusok............................................................................................................ 27

4.10
5

Jogi felttelek ...........................................................................................................9

A dinamikus tpus ............................................................................................... 28

Opertorok.................................................................................................................... 30
5.1 Opertor precedencia .............................................................................................. 30
5.2

rtkad opertor ................................................................................................... 31

5.3

Matematikai opertorok .......................................................................................... 32

5.4

Relcis opertorok ................................................................................................ 32

5.5

Logikai s feltteles opertorok .............................................................................. 33

5.6

Bit opertorok ........................................................................................................ 36

5.7

Rvid forma ........................................................................................................... 39


-3-

5.8
6

Egyb opertorok ................................................................................................... 40

Vezrlsi szerkezetek .................................................................................................... 42


6.1 Szekvencia ............................................................................................................. 42
Elgazs ................................................................................................................. 42

6.3

Ciklus ..................................................................................................................... 45

6.2

6.3.1

Yield ............................................................................................................... 50

6.3.2

Prhuzamos ciklusok ....................................................................................... 50

Gyakorl feladatok ....................................................................................................... 52


7.1 Szorztbla ............................................................................................................. 52
Szmolgp ............................................................................................................ 55

7.3

K Papr Oll ................................................................................................... 57

7.4

Szmkitall jtk .................................................................................................. 59

7.2

Tpuskonverzik ........................................................................................................... 64
8.1 Ellenrztt konverzik ........................................................................................... 64
Is s as .................................................................................................................... 65

8.3

Karakterkonverzik ................................................................................................ 66

8.2

Tmbk ........................................................................................................................ 67
9.1 Tbbdimenzis tmbk .......................................................................................... 68

10

Stringek ........................................................................................................................ 71
10.1
Metdusok .......................................................................................................... 72
10.2

StringBuilder ...................................................................................................... 73

10.3

Regulris kifejezsek .......................................................................................... 74

11

Gyakorl feladatok II. ................................................................................................... 77


11.1
Minimum- s maximumkeress........................................................................... 77
11.2

Szigetek .............................................................................................................. 77

11.3

tlaghmrsklet ................................................................................................ 79

11.4

Buborkrendezs................................................................................................. 79

12

Objektum-orientlt programozs - elmlet .................................................................... 81


12.1
UML ................................................................................................................... 81
12.2

Osztly ............................................................................................................... 81

12.3

Adattag s metdus ............................................................................................. 82

12.4

Lthatsg .......................................................................................................... 82

12.5

Egysgbezrs .................................................................................................... 83

12.6

rklds ........................................................................................................... 83

13

Osztlyok...................................................................................................................... 85
13.1
Konstruktorok ..................................................................................................... 86
13.2

Adattagok ........................................................................................................... 89

13.3

Lthatsgi mdostk ........................................................................................ 90

13.4

Parcilis osztlyok .............................................................................................. 90


-4-

13.5

Begyazott osztlyok .......................................................................................... 92

13.6

Objektum inicializlk ........................................................................................ 93

13.7

Destruktorok ....................................................................................................... 93

13.7.1 IDisposable ................................................................................................... 100


14

Metdusok .................................................................................................................. 102


14.1
Paramterek ...................................................................................................... 104
14.1.1 Alaprtelmezett paramterek ......................................................................... 109
14.1.2 Nevestett paramterek .................................................................................. 110
14.2

Visszatrsi rtk .............................................................................................. 110

14.3

Kiterjesztett metdusok..................................................................................... 111

15
16
17

Tulajdonsgok ............................................................................................................ 113


Indexelk .................................................................................................................... 115
Statikus tagok ............................................................................................................. 117
17.1
Statikus adattag ................................................................................................. 117
17.2

Statikus konstruktor .......................................................................................... 118

17.3

Statikus metdus ............................................................................................... 120

17.4

Statikus tulajdonsg .......................................................................................... 120

17.5

Statikus osztly ................................................................................................. 120

18

Struktrk ................................................................................................................... 122


18.1
Konstruktor....................................................................................................... 122
18.2

Destruktor ......................................................................................................... 123

18.3

Adattagok ......................................................................................................... 124

18.4

Hozzrendels ................................................................................................... 124

18.5

rklds ......................................................................................................... 126

19

Gyakorl feladatok III................................................................................................. 127


19.1
Faktorilis s hatvny ....................................................................................... 127
19.2

Gyorsrendezs .................................................................................................. 128

19.3

Lncolt lista ...................................................................................................... 130

19.4

Binris keresfa ................................................................................................ 131

rklds ................................................................................................................... 136


20.1
Virtulis metdusok .......................................................................................... 138

20

20.2

Polimorfizmus .................................................................................................. 140

20.3

Lezrt osztlyok s metdusok .......................................................................... 141

20.4

Absztrakt osztlyok ........................................................................................... 141

21

Interfszek .................................................................................................................. 144


21.1
Explicit interfszimplementci ........................................................................ 146
21.2

Virtulis tagok .................................................................................................. 147

22

Opertor kiterjeszts ................................................................................................... 149


22.1
Egyenlsg opertorok ...................................................................................... 150
-5-

22.2

A ++/-- opertorok ............................................................................................ 151

22.3

Relcis opertorok .......................................................................................... 152

22.4

Konverzis opertorok ...................................................................................... 152

22.5

Kompatibilits ms nyelvekkel ......................................................................... 153

23

Kivtelkezels............................................................................................................. 154
23.1
Kivtel hierarchia .............................................................................................. 156
23.2

Kivtel ksztse ............................................................................................... 156

23.3

Kivtelek tovbbadsa ...................................................................................... 157

23.4

Finally blokk ..................................................................................................... 158

24

Gyakorl feladatok IV. ............................................................................................... 159


24.1
IEnumerator s IEnumerable ............................................................................. 159
24.2

IComparable s IComparer ............................................................................... 161

24.3

Mtrix tpus ...................................................................................................... 162

25

Delegateek ................................................................................................................ 164


25.1
Paramter s visszatrsi rtk .......................................................................... 167
25.2

Nvtelen metdusok.......................................................................................... 168

25. Esemnyek ................................................................................................................... 169


26
Generikusok............................................................................................................. 173
26.1
Generikus metdusok........................................................................................ 173
26.2

Generikus osztlyok .......................................................................................... 174

26.3

Generikus megszortsok .................................................................................. 176

26.4

rklds ......................................................................................................... 178

26.5

Statikus tagok ................................................................................................... 178

26.6

Generikus gyjtemnyek ................................................................................... 178

26.6.1 List<T> ......................................................................................................... 179


26.6.2 SortedList<T, U> s SortedDictionary<T, U> ............................................... 181
26.6.3 Dictionary<T, U> .......................................................................................... 182
26.6.4 LinkedList<T> .............................................................................................. 182
26.6.5 ReadOnlyCollection<T> ............................................................................... 183
26.7

Generikus interfszek, delegate ek s esemnyek ............................................ 183

26.8

Kovariancia s kontravariancia ......................................................................... 184

27

Lambda kifejezsek .................................................................................................... 186


27.1
Generikus kifejezsek ....................................................................................... 186
27.2

Kifejezsfk ...................................................................................................... 188

27.3

Lambda kifejezsek vltozinak hatkre ......................................................... 188

27.4

Nvtelen metdusok kivltsa lambda kifejezsekkel ........................................ 189

28
29

Attribtumok .............................................................................................................. 191


Unsafe kd.................................................................................................................. 194
29.1
Fix objektumok ................................................................................................. 196
-6-

29.2

Natv DLL kezels ............................................................................................ 197

30

Tbbszl alkalmazsok ............................................................................................. 199


30.1
Application Domain -ek .................................................................................... 201
30.2

Szlak ............................................................................................................... 201

30.3

Aszinkron delegate-ek....................................................................................... 202

30.3.1 Prhuzamos delegate hvs ............................................................................ 206


30.4

Szlak ltrehozsa ............................................................................................. 207

30.5

Foreground s background szlak ..................................................................... 208

30.6

Szinkronizci .................................................................................................. 209

30.7

ThreadPool ....................................................................................................... 213

31
32

Reflection ................................................................................................................... 216


llomnykezels ........................................................................................................ 218
32.1
Olvass/rs filebl/fileba .................................................................................. 218
32.2

Knyvtrstruktra kezelse ............................................................................... 221

32.3

Inmemory streamek ........................................................................................ 223

32.4

XML ................................................................................................................. 224

32.5

XML DOM ....................................................................................................... 227

32.6

XML szerializci............................................................................................. 229

33

Konfigurcis file hasznlata ...................................................................................... 231


33.1
Konfigurci-szekci ksztse ......................................................................... 232

34

Hlzati programozs ................................................................................................. 235


34.1
Socket ............................................................................................................... 235
34.2

Blokk elkerlse ............................................................................................... 241

34.3

Tbb kliens kezelse ......................................................................................... 243

34.3.1 Select ............................................................................................................ 243


34.3.2 Aszinkron socketek ....................................................................................... 245
34.3.3 Szlakkal megvalstott szerver ..................................................................... 246
34.4

TCP s UDP ..................................................................................................... 249

35

LINQ To Objects ........................................................................................................ 250


35.1
Nyelvi eszkzk ............................................................................................... 250
35.2

Kivlaszts........................................................................................................ 251

35.2.1 Projekci ....................................................................................................... 254


35.2.2 Let................................................................................................................. 255
35.3

Szrs ............................................................................................................... 255

35.4

Rendezs .......................................................................................................... 257

35.5

Csoportosts .................................................................................................... 258

35.5.1 Null rtkek kezelse ..................................................................................... 260


35.5.2 sszetett kulcsok ........................................................................................... 260
35.6

Listk sszekapcsolsa ...................................................................................... 262


-7-

35.7

Outer join.......................................................................................................... 263

35.8

Konverzis opertorok ...................................................................................... 264

35.9

Element opertorok ....................................................................................... 266

35.10

Halmaz opertorok............................................................................................ 267

35.11

Aggregt opertorok ......................................................................................... 268

35.12

PLINQ Prhuzamos vgrehajts ..................................................................... 269

35.12.1

Tbbszlsg vs. Prhuzamossg ............................................................... 269

35.12.2

Teljestmny .............................................................................................. 269

35.12.3

PLINQ a gyakorlatban ............................................................................... 270

35.12.4

Rendezs ................................................................................................... 273

35.12.5

AsSequential.............................................................................................. 274

36

Visual Studio .............................................................................................................. 275


36.1
Az els lpsek ................................................................................................. 275
Fellet............................................................................................................... 278

36.3

Debug ............................................................................................................... 280

36.4

Debug s Release .............................................................................................. 282

37

36.2

Osztlyknyvtr .......................................................................................................... 283

-8-

Bevezet

Napjainkban egyre nagyobb teret nyer a .NET Framework s egyik f nyelve a C#.
Ez a jegyzet abbl a clbl szletett, hogy megismertesse az olvasval ezt a
nagyszer technolgit.
A jegyzet a C# 2.0, 3.0 s 4.0 verzijval foglalkozik, az utbbi kett ltal bevezetett
j eszkzket az adott rsz kln jelli. Nhny fejezet felttelez olyan tudst, amely
alapjt egy ksbbi rsz kpezi, ezrt ne essen ktsgbe a kedves olvas, ha
valamit nem rt, egyszeren olvasson tovbb s trjen vissza a krdses anyaghoz,
ha rtallt a vlaszra. A jegyzet megrtshez nem szksges programozni tudni,
viszont alapvet informatikai ismeretek (pl. szmrendszerek) jl jnnek.
A jegyzethez tartoz forrskdok letlthetek a kvetkez webhelyrl:
http://cid-283edaac5ecc7e07.skydrive.live.com/browse.aspx/Nyilv%C3%A1nos/Jegyzet

Brmilyen krst, javaslatot s hibajavtst szvesen vrok a reiteristvan@gmail.com


e-mail cmre.

1.1 A jegyzet jellsei

Forrskd: szrke alapon, bekeretezve


Megjegyzs: fehr alapon, bekeretezve
Parancssor: fekete alapon, keret nlkl

1.2 Jogi felttelek

A jegyzet teljes tartalma a Creative Commons Nevezd meg!-Ne add el! 2.5
Magyarorszg liszensze al tartozik. Szabadon mdosthat s terjeszthet a forrs
feltntetsvel.
A jegyzet ingyenes, mindennem rtkestsi ksrlet tiltott s a szerz
beleegyezse nlkl trtnik!

-9-

Microsoft .NET Framework

A kilencvenes vek kzepn a Sun MicroSystems kiadta a Java platform els


nyilvnos vltozatt. Az addigi programnyelvek/platformok klnbz okokbl nem
tudtk felvenni a Java val a versenyt, gy szmtalan fejleszt dnttt gy, hogy a
knyelmesebb s sokoldalbb Java t vlasztja.
Rszben a piac visszaszerzsnek rdekben a Microsoft a kilencvenes vek vgn
elindtotta a Next Generation Windows Services fednev projektet, amelybl aztn
megszletett a .NET, amely a kiss elavult s nehzkesen programozhat COM
platformot hvatott levltani (ettl fggetlenl a COM ma is ltez viszonylag
npszer eszkz ez fleg a hatalmas szoftverbzisnak ksznhet, minden
Windows rendszer rszt kpezi s szmos .NET knyvtr is pt r).

2.1 A .NET platform


Maga a .NET platform a Microsoft, a Hewlett Packard, az Intel s msok
kzremkdsvel megfogalmazott CLI (Common Language Infrastructure) egy
implementcija. A CLI egy szablyrendszer, amely maga is tbb rszre oszlik:
A CTS (Common Type System) az adatok kezelst, a memriban val
megjelenst, az egymssal val interakcit, stb. rja le.
A CLS (Common Language Specification) a CLI kompatibilis nyelvekkel
kapcsolatos elvrsokat tartalmazza.
A VES (Virtual Execution System) a futsi krnyezetet specifiklja, nevezik CLR nek (Common Language Runtime) is.
ltalnos tvhit, hogy a VES/CLR t virtulis gpknt azonostjk. Ez abbl a szintn
tves elkpzelsbl alakult ki, hogy a .NET ugyanaz, mint a Java, csak Microsoft
kntsben. A valsgban nincs .NET virtulis gp, helyette n. felgyelt (vagy
managed) kdot hasznl, vagyis a program teljes mrtkben natv mdon,
kzvetlenl a processzoron fut, mellette pedig ott a keretrendszer, amely felels pl. a
memriafoglalsrt vagy a kivtelek kezelsrt.
A .NET nem egy programozsi nyelv, hanem egy krnyezet. Gyakorlatilag brmelyik
programozsi nyelvnek lehet .NET implementcija. Jelenleg kb. 50 nyelvnek ltezik
hivatalosan .NET megfelelje, nem beszlve a szmtalan hobbifejlesztsrl.

2.1.1 MSIL/CIL
A hagyomnyos programnyelveken mint pl. a C++ megrt programok n. natv
kdra fordulnak le, vagyis a processzor szmra kis tlzssal azonnal
rtelmezhetek.
A .NET (akrcsak a Java) ms ton jr, a fordt elszr egy kztes nyelvre
(Intermediate Language) fordtja le a forrskdot. Ez a nyelv a .NET vilgban az
- 10 -

MSIL, illetve a szabvnyosts utn a CIL (MICROSOFT/CommonIL) klnbsg


csak az elnevezsben van.
Jogos a krds, hogy a kt mdszer kzl melyik a jobb? Ha nagy ltalnossgban
beszlnk, akkor a vlasz az, hogy nincs kztk klnbsg. Igaz, hogy a natv
nyelvek hardver-kzelibbek s emiatt gyorsabbak tudnak lenni, viszont ez tbb
hibalehetsggel is jr, amelyek elkerlse a felgyelt krnyezetben kiegyenlti az
eslyeket.
Bizonyos terleteken viszont egyik vagy msik megkzelts jelents eltrst
eredmnyezhet. J plda a szmtgpes grafika ahol a natv nyelvek vannak
elnyben pont azrt, mert az ilyen szmtsignyes feladathoz minden csepp
erforrst ki kell prselni a hardverbl. Msfell a felgyelt krnyezet a hatkonyabb
memriakezels miatt jobban teljest olyan helyzetekben ahol nagy mennyisg
adatot mozgatunk a memrin bell (pl. szmos rendez algoritmus ilyen).

2.1.2 Fordts s futtats


A natv programok n. gpi kdra fordulnak le, mg a .NET forrskdokbl egy CIL
nyelv futtathat llomny keletkezik. Ez a kd a felteleptett .NET Framework nek
szl utastsokat tartalmaz. Amikor futtatjuk ezeket az llomnyokat, elszr az n.
JIT (JustInTime) fordt veszi kezelsbe, lefordtja ket gpi kdra, amit a
processzor mr kpes kezelni.
Amikor elszr fordtjuk le a programunkat, akkor egy n. Assembly (vagy
szerelvny) keletkezik. Ez tartalmazza a felhasznlt, illetve megvalstott tpusok
adatait (ez az n. Metadata) amelyek a futtat krnyezetnek szolglnak informcival
(pl. osztlyok szerkezete, metdusai, stb.). Egy Assembly egy vagy tbb filebl is
llhat, tipikusan .exe (futtathat llomny) vagy .dll (osztlyknyvtr) kiterjesztssel.

2.1.3 BCL
A .NET Framework teleptsvel a szmtgpre kerl tbbek kztt a BCL
(Base Class Library), ami az alapvet feladatok (file olvass/ rs, adatbzis kezels,
adatszerkezetek, stb) elvgzshez szksges eszkzket tartalmazza. Az sszes
tbbi knyvtr (ADO.NET, WCF, stb) ezekre pl.

2.2 A C# programozsi nyelv


A C# (ejtsd: sz-srp) a Visual Basic mellett a .NET f programozsi nyelve. 1999
ben Anders Hejlsberg vezetsvel kezdtk meg a fejlesztst.
A C# tisztn objektumorientlt, tpus biztos, ltalnos felhasznls nyelv. A
tervezsnl a lehet legnagyobb produktivits elrst tartottk szem eltt. A nyelv
elmletileg platform fggetlen (ltezik Linux s Mac fordt is), de napjainkban a
legnagyobb hatkonysgot a Microsoft implementcija biztostja.
- 11 -

2.3 Alternatv megoldsok


A Microsoft .NET Framework jelen pillanatban csak s kizrlag Microsoft Windows
opercis rendszerek alatt elrhet. Ugyanakkor a szabvnyosts utn a CLI
specifikci nyilvnos s brki szmra elrhet lett, ezen ismeretek birtokban
pedig tbb fggetlen csapat vagy cg is ltrehozta a sajt CLI implementcijt, br
eddig mg nem sikerlt teljes mrtkben reproduklni az eredetit. Ezen cljukat
nehezti, hogy a Microsoft idkzben szmos, a specifikciban nem szerepl
vltoztatst vgzett a keretrendszeren.
A hivatalosnak tekinthet ECMA szabvny nem felttlenl tekinthet tkletes
tmutatnak a keretrendszer megrtshez, nhol jelents eltrsek vannak a
valsghoz kpest. Ehelyett ajnlott a C# nyelv fejleszti ltal ksztett C# referencia,
amely br nem elssorban a .NET hez kszlt rtkes informcikat tartalmaz.

2.3.1 SSCLI
Az SSCLI (Shared Source Common Language Infrastructure) vagy korbbi nevn
Rotor a Microsoft ltal fejlesztett nylt forrs, keresztplatformos vltozata a .NET
Frameworknek (teht nem az eredeti lebuttott vltozata). Az SSCLI Windows,
FreeBSD s Mac OSX rendszereken fut.
Az SSCLI t kimondottan tanulsi clra ksztette a Microsoft, ezrt a liszensze
engedlyez mindenfajta mdostst, egyedl a piaci rtkestst tiltja meg. Ez a
rendszer nem szolgltatja az eredeti keretrendszer teljes funkcionalitst, jelen
pillanatban valamivel a .NET 2.0 mgtt jr.
Az SSCLI projekt jelen pillanatban lellni ltszik. Ettl fggetlenl a forrskd s a
hozz tartoz dokumentcik rendelkezsre llnak, letlthetek a kvetkez
webhelyrl:
http://www.microsoft.com/downloads/details.aspx?FamilyId=8C09FD61-3F26-4555-AE173121B4F51D4D&displaylang=en

2.3.2 Mono
A Mono projekt szlatyja Miguel de Icaza, 2000 ben kezdte meg a fejlesztst s
egy vvel ksbb mutatta be ez els kezdetleges C# fordtt. A Ximian (amelyet
Icaza s Nat Friedman alaptott) felkarolta az tletet s 2001 jliusban hivatalosan
is elkezddtt a Mono fejlesztse. 2003 ban a Novell felvsrolta a Ximian t, az
1.0 verzi mr Novell termkknt kszlt el egy vvel ksbb.
A Mono elrhet Windows, Linux, UNIX, BSD, Mac OSX s Solaris rendszereken is.
Napjainkban a Mono mutatja a leggretesebb fejldst, mint a Microsoft .NET
- 12 -

jvbeli ellenfele, illetve keresztplatformos trsa. A Mono emblmja egy majmot


brzol, a sz ugyanis spanyolul majmot jelent.
A Mono hivatalos oldala:http://www.mono-project.com/Main_Page

2.3.3 DotGNU
A DotGNU a GNU projekt rsze, amelynek clja egy ingyenes s nylt alternatvt
nyjtani a Microsoft implementci helyett. Ez a projekt szemben a Mono val
nem a Microsoft BCL lel val kompatibilitst helyezi eltrbe, hanem az eredeti
szabvny pontos s tkletes implementcijnak a ltrehozst. A DotGNU sajt
CLI megvalstsnak a Portable .NET nevet adta. A jegyzet rsnak idejn a
projekt lellni ltszik.
A DotGNU hivatalos oldala: http://www.gnu.org/software/dotgnu/

- 13 -

Hello C#! Ismerkednk a nyelvvel

A hres Hello World! program elsknt Dennis Ritchie s Brian Kernighan A C


programozsi nyelv cm knyvben jelent meg s azta szinte hagyomny, hogy
egy programozsi nyelv bevezetjeknt ezt a programot mutatjk be.
Mi itt most nem a vilgot, hanem a C# nyelvet dvzljk, ezrt ennek megfelelen
mdostsuk a forrskdot:
using System;
class HelloWorld
{
static public void Main()
{
Console.WriteLine("Hello C#!");
Console.ReadKey();
}
}

Mieltt lefordtjuk, tegynk pr lpst a parancssorbl val fordts elsegtsre.


Ahhoz, hogy gy le tudjunk fordtani egy forrsfilet, vagy meg kell adnunk a
fordtprogram teljes elrsi tjt (ez a mi esetnkben elg hossz) vagy a
fordtprogram knyvtrt fel kell venni a PATH krnyezeti vltozba.
Utbbi lelhelye: Vezrlpult/Rendszer -> Specilis fl/Krnyezeti vltozk. A
rendszervltozk listjbl keressk ki a Path t s kattintsunk a Szerkeszts
gombra. Most nyissuk meg a Sajtgpet, C: meghajt, Windows mappa, azon bell
Microsoft.NET/Framework. Nyissuk meg vagy a v2.0, a v3.5... stb. kezdet mappt
(attl fggen, hogy a C# fordt melyik verzijra van szksgnk). Msoljuk ki a
cmsorbl ezt a szp hossz elrst, majd menjnk vissza a Path hoz. A vltoz
rtknek sorban navigljunk el a vgre, rjunk egy pontosvesszt (;) s illesszk
be az elrsi utat. Nyomjuk meg az OK gombot s ksz is vagyunk. Ha van
megnyitva konzol vagy PowerShell, azt indtsuk jra s rjuk be, hogy csc. Azt kell
ltnunk,hogy:
Microsoft Visual C# 2008 Compiler Version 3.5
(Az vszm s verzi vltozhat, ez itt most a C# 3.0 zenete.)
Most mr fordthatunk a
csc filenv.cs
paranccsal. Termszetesen a szveges file kiterjesztse .txt, ezrt nevezzk is t,
mivel a C# forrskdot tartalmaz fileok kiterjesztse: .cs
Nzzk, hogy mit is tettnk: az els sor megmondja a fordtnak, hogy hasznlja a
System nvteret. Ezutn ltrehozunk egy osztlyt mivel a C# teljesen objektumorientlt , ezrt utastst csak osztlyon bell adhatunk meg. A HelloWorld
osztlyon bell definilunk egy Main nev statikus fggvnyt, ami a programunk
- 14 -

belpsi pontja lesz. Minden egyes C# program a Main fggvnnyel kezddik, ezt
mindenkppen ltre kell hoznunk. Vgl meghvjuk a Console osztlyban lv
WriteLine s ReadKey fggvnyeket. Elbbi kirja a kpernyre a paramtert, utbbi
vr egy billenty letsre.
Ebben a bekezdsben szerepel nhny (sok) kifejezs, amik ismeretlenek lehetnek,
de a jegyzet ksbbi fejezeteiben mindenre fny derl majd.

3.1 A C# szintaktikja
Amikor egy programozsi nyelv szintaktikjrl beszlnk, akkor azokra a
szablyokra gondolunk, amelyek megszabjk a forrskd felptst. Ez azrt fontos,
mert az egyes fordtprogramok csak ezekkel a szablyokkal ltrehozott kdot tudjk
rtelmezni.
A C# gynevezett C-stlus szintaxissal rendelkezik (azaz a C programozsi nyelv
szintaxist veszi alapul), ez hrom fontos szablyt von maga utn:
Az egyes utastsok vgn pontosvessz (;) ll
A kis- s nagybetk klnbz jelentsggel brnak, azaz a program s
Program azonostk klnbznek. Ha a fenti kdban Console.WriteLine
helyett console.writeline t rtunk volna, akkor a program nem fordulna le.
A program egysgeit (osztlyok, metdusok, stb.) n. blokkokkal jelljk ki,
kapcsos zrjelek ({ s }) segtsgvel.

3.1.1 Kulcsszavak
Szinte minden programnyelv definil kulcsszavakat, amelyek specilis jelentsggel
brnak a fordt szmra. Ezeket az azonostkat a sajt meghatrozott jelentskn
kvl nem lehet msra hasznlni, ellenkez esetben a fordt hibt jelez. Vegynk
pldul egy vltozt, aminek az int nevet akarjuk adni. Az int egy beptett tpus a
neve is, azaz kulcssz, teht nem fog lefordulni a program.
int int;//hiba

A legtbb fejleszteszkz besznezi a kulcsszavakat (is), ezrt knny elkerlni a


fenti hibt.

- 15 -

A C# 77 kulcsszt ismer:
abstract
as
base
bool
break
byte
case
catch
char
checked
class
const
continue
decimal

default
delegate
do
double
else
enum
event
explicit
extern
false
finally
fixed
float
for

foreach
goto
If
implicit
In
int
interface
internal
Is
lock
long
namespace
new
null

object
operator
out
override
params
private
protected
public
readonly
ref
return
sbyte
sealed
short

Sizeof
stackalloc
Static
String
Struct
Switch
This
Throw
True
Try
Typeof
Uint
Ulong
unchecked

unsafe
ushort
using
virtual
volatile
void
while

Ezeken kvl ltezik mg 23 azonost, amelyeket a nyelv nem tart fenn specilis
hasznlatra, de klnleges jelentssel brnak. Amennyiben lehetsges, kerljk a
hasznlatukat hagyomnyos vltozk, metdusok, osztlyok ltrehozsnl:
add
ascending
by
descending

equals
from
get
global

group
in
into
join

let
on
orderby
partial

Remove var
Select where
Set
yield
Value

Nhnyuk a krnyezettl fggen ms-ms jelentssel is brhat, a megfelel fejezet


bvebb informcit ad majd ezekrl az esetekrl.

3.1.2 Megjegyzsek
A forrskdba megjegyzseket tehetnk. Ezzel egyrszt zeneteket hagyhatunk (pl.
egy metdus lersa) magunknak vagy a tbbi fejlesztnek, msrszt a kommentek
segtsgvel dokumentcit tudunk generlni, ami szintn az els clt szolglja, csak
ppen lvezhetbb formban.
Megjegyzseket a kvetkezkppen hagyhatunk:
using System;
class HelloWorld
{
static public void Main()
{
Console.WriteLine("Hello C#"); // Ez egy egysoros komment
Console.ReadKey();
/* Ez
egy
tbbsoros komment */
}
}

- 16 -

Az egysoros komment a sajt sora legvgig tart, mg a tbbsoros a /* s */


prokon bell rvnyes. Utbbiakat nem lehet egymsba gyazni:
/*
*/

/* */

Ez a kd nem fordul le.


A kommenteket a fordt nem veszi figyelembe, tulajdonkppen a fordtprogram
els lpse, hogy a forrskdbl eltvolt minden megjegyzst.

3.2 Nvterek
A .NET Framework osztlyknyvtrai szerny becsls szerint is legalbb tzezer
nevet, azonostt tartalmaznak. Ilyen nagysgrenddel elkerlhetetlen, hogy a nevek
ne ismtldjenek. Ekkor egyrszt nehz eligazodni kzttk, msrszt a fordt sem
tudn, mikor mire gondolunk. Ennek a problmnak a kikszblsre hoztk ltre a
nvterek fogalmt. Egy nvtr tulajdonkppen egy virtulis doboz, amelyben a
logikailag sszefgg osztlyok, metdusok, stb. vannak. Nyilvn knnyebb
megtallni az adatbzis-kezelshez szksges osztlyokat, ha valamilyen kifejez
nev nvtrben vannak (pl.System.Data).
Nvteret magunk is definilhatunk, a namespace kulcsszval:
namespace MyNameSpace
{
}

Ezutn a nvtrre vagy a program elejn a using kulcsszval, vagy az azonost el


rt teljes elrssel hivatkozhatunk:
using MyNameSpace;
//vagy
MyNameSpace.Valami

A jegyzet els felben fleg a System nvteret fogjuk hasznlni.

- 17 -

Vltozk

Amikor programot runk, akkor szksg lehet trolkra, ahov az adatainkat


ideiglenesen eltroljuk. Ezeket a trolkat vltozknak nevezzk.
A vltozk a memria egy (vagy tbb) celljra hivatkoz lerk. Egy vltozt a
kvetkez mdon hozhatunk ltre C# nyelven:
Tpus vltoznv;
A vltoznv els karaktere csak bet vagy alulvons jel (_) lehet, a tbbi karakter
szm is. Lehetleg kerljk az kezetes karakterek hasznlatt.
Konvenci szerint a vltoznevek kisbetvel kezddnek. Amennyiben a vltoznv
tbb szbl ll, akkor clszer azokat a szhatrnl nagybetvel elvlasztani (pl.
pirosAlma, vanSapkaRajta, stb.).

4.1 Deklarci s definci


Egy vltoz (illetve lnyegben minden objektum) letciklusban megklnbztetnk
deklarcit s defincit. A deklarcinak tartalmaznia kell a tpust s azonostt, a
definciban pedig megadjuk az objektum rtkt. rtelemszeren a deklarci s a
definci egyszerre is megtrtnhet.
int x; // deklarci
x = 10; // definci
int y = 11; // delarci s definci

4.2 Tpusok
A C# ersen (statikusan) tpusos nyelv, ami azt jelenti, hogy minden egyes vltoz
tpusnak ismertnek kell lennie fordtsi idben, ezzel biztostva azt, hogy a program
pontosan csak olyan mveletet hajthat vgre amire valban kpes. A tpus hatrozza
meg, hogy egy vltoz milyen rtkeket tartalmazhat, illetve mekkora helyet foglal a
memriban.
A kvetkez tblzat a C# beptett tpusait tartalmazza, mellettk ott a .NET
megfeleljk, a mretk s egy rvid lers:

- 18 -

C# tpus

.NET tpus

Mret (byte)

byte

System.Byte

char
bool

System.Char
System.Boolean

2
1

sbyte

System.SByte

short

System.Int16

ushort

System.Uint16

int

System.Int32

uint

System.Uint32

float

System.Single

double

System.Double

decimal
long
ulong
string
object

System.Decimal
System.Int64
System.Uint64
System.String
System.Object

16
8
8
N/A
N/A

Lers

Eljel nlkli 8 bites egsz szm


(0..255)
Egy Unicode karakter
Logikai tpus, rtke igaz(1 vagy true)
vagy hamis(0 vagy false)
Eljeles, 8 bites egsz szm (128..127)
Eljeles, 16 bites egsz szm (32768..32767
Eljel nlkli, 16 bites egsz szm
(0..65535)
Eljeles, 32 bites egsz szm (
2147483648.. 2147483647).
Eljel nlkli, 32 bites egsz szm
(0..4294967295)
Egyszeres pontossg lebegpontos
szm
Ktszeres pontossg lebegpontos
szm
Fix pontossg 28+1 jegy szm
Eljeles, 64 bites egsz szm
Eljel nlkli, 64 bites egsz szm
Unicode karakterek szekvencija
Minden ms tpus se

A forrskdban teljesen mindegy, hogy a rendes vagy a .NET nven hivatkozunk


egy tpusra.
Alaktsuk t a Hello C# programot gy, hogy a kirand szveget egy vltozba
tesszk:
using System;
class HelloWorld
{
static public void Main()
{
//string tpus vltoz, benne a kirand szveg
string message="Hello C#";
Console.WriteLine(message);
Console.ReadKey();
}
}

A C# 3.0 mr lehetv teszi, hogy egy metdus hatkrben deklarlt vltoz


tpusnak meghatrozst a fordtra bzzuk. ltalban olyankor tesszk ezt, amikor
hossz tpusnvrl van sz, vagy nehz meghatrozni a tpust. Ezt az akcit a var
kulcsszval kivitelezhetjk.
Ez termszetesen nem jelenti azt, hogy gy hasznlhatjuk a nyelvet, mint egy
tpustalan krnyezetet! Abban a pillanatban, amikor rtket rendeltnk a vltozhoz
- 19 -

(radsul ezt azonnal meg is kell tennnk!), az gy fog viselkedni, mint az ekvivalens
tpus. Az ilyen vltozk tpusa nem vltoztathat meg, de a megfelel
tpuskonverzik vgrehajthatak.
int
var
z =
var

x = 10; // int tpus vltoz


z = 10; // int tpus vltoz
"string"; // fordtsi hiba
w; //fordtsi hiba

4.3 Loklis s globlis vltozk


Egy blokkon bell deklarlt vltoz loklis lesz a blokkjra nzve, vagyis a program
tbbi rszbl nem lthat (gy is mondhatjuk, hogy a vltoz hatkre a blokkjra
terjed ki). A fenti pldban a message egy loklis vltoz, ha egy msik fggvnybl
vagy osztlybl prblnnk meg elrni, akkor a program nem fordulna le.
Globlis vltoznak azokat az objektumokat nevezzk, amelyek a program brmely
rszbl elrhetek. A C# nem rendelkezik a ms nyelvekbl ismers globlis
vltozval, mivel deklarcit csak osztlyon bell vgezhetnk. thidalhatjuk a
helyzetet statikus vltozk hasznlatval, errl ksbb sz lesz.

4.4 Referencia- s rtktpusok


A .NET minden tpus direkt vagy indirekt mdon a System.Object nev tpusbl
szrmazik, s ezen bell sztoszlik rtk- s referencia-tpusokra (egyetlen kivtel a
pointer tpus, amelynek semmifle kze sincs a System.Object-hez). A kett kztti
klnbsg leginkbb a memriban val elhelyezkedsben jelenik meg.
A CLR kt helyre tud adatokat pakolni, az egyik a verem (stack), a msik a halom
(heap). A stack egy n. LIFO (last-in-first-out) adattr, vagyis a legutoljra berakott
elem lesz a tetejn, kivenni pedig csak a mindenkori legfels elemet tudjuk. A heap
nem adatszerkezet, hanem a program ltal lefoglalt nyers memria, amit a CLR
tetszs szerint hasznlhat. Minden mvelet a stack-et hasznlja, pl. ha ssze
akarunk adni kt szmot akkor a CLR lerakja mindkettt a stack-be s meghvja a
megfelel utastst. Ezutn kiveszi a verem legfels kt elemt, sszeadja ket, majd
a vgeredmnyt visszateszi a stack-be:
int x=10;
int y=11;
x + y
A verem:
|11|
|10| --sszeads mvelet--|21|

A referencia-tpusok minden esetben a halomban jnnek ltre, mert ezek sszetett


adatszerkezetek s gy hatkony a kezelsk. Az rtktpusok vagy a stack-ben vagy
a heap-ben vannak attl fggen, hogy hol deklarltuk ket.
- 20 -

Metduson bell, loklisan deklarlt rtktpusok a stack-be kerlnek, a referenciatpuson bell adattagknt deklarlt rtktpusok pedig a heap-ben foglalnak helyet.
Nzznk nhny pldt!
using System;
class Program
{
static public void Main()
{
int x = 10;
}
}

Ebben a programban xet loklisan deklarltuk egy metduson bell, ezrt biztosak
lehetnk benne, hogy a verembe fog kerlni.
class MyClass
{
private int x = 10;
}

Most x egy referencia-tpuson (esetnkben egy osztlyon) belli adattag, ezrt a


halomban foglal majd helyet.
class MyClass
{
private int x = 10;
public void MyMethod()
{
int y = 10;
}

Most egy kicsit bonyolultabb a helyzet. Az y nev vltozt egy referencia-tpuson


bell, de egy metdusban, loklisan deklarltuk, gy a veremben fog troldni, x
pedig mg mindig adattag, ezrt marad a halomban.
Vgl nzzk meg, hogy mi lesz rtk- s mi referencia-tpus: rtktpus lesz az
sszes olyan objektum, amelyeket a kvetkez tpusokkal deklarlunk:

Az sszes beptett numerikus tpus (int, byte, double, stb.)


A felsorolt tpus (enum)
Logikai tpus (bool)
Karakter tpus (char)
Struktrk (struct)

Referencia-tpusok lesznek a kvetkezk:

Osztlyok (class)
Interfsz tpusok (interface)
Delegate tpusok (delegate)
Stringek
- 21 -

Minden olyan tpus, amely kzvetlen mdon szrmazik a System.Objectbl


vagy brmely class kulcsszval bevezetett szerkezetbl.

4.5 Referencik
Az rtk- illetve referencia-tpusok kztti klnbsg egy msik aspektusa az,
ahogyan a forrskdban hivatkozunk rjuk. Vegyk a kvetkez kdot:
int x = 10;
int y = x;

Az els sorban ltrehoztuk az x nev vltozt, a msodikban pedig egy j vltoznak


adtuk rtkl xet. A krds az, hogy y hova mutat a memriban: oda ahol x van,
vagy egy teljesen ms terletre?
Amikor egy rtktpusra hivatkozunk, akkor tnylegesen az rtkt hasznljuk fel,
vagyis a krdsnkre a vlasz az, hogy a kt vltoz rtke egyenl lesz, de nem
ugyanazon a memriaterleten helyezkednek el, teht y mshova mutat, teljesen
nll vltoz.
A helyzet ms lesz referencia-tpusok esetben. Mivel k sszetett tpusok, ezrt
fizikailag lehetetlen lenne az rtkeikkel dolgozni, ezrt egy referencia-tpusknt
ltrehozott vltoz tulajdonkppen a memrinak arra a szeletre mutat, ahol az
objektum tnylegesen helyet foglal. Nzzk meg ezt kzelebbrl:
using System;
class MyClass
{
public int x;
}
class Program
{
static public void Main()
{
MyClass s = new MyClass();
s.x = 10;
MyClass p = s;
p.x = 14;
Console.WriteLine(s.x);
}
}

Vajon mit fog kirni a program?


Kezdjk az elejrl! Hasonl a fellls, mint az elz forrskdnl, viszont amikor a
msodik vltoznak rtkl adjuk az elst, akkor az trtnik, hogy a p nev
referencia ugyanarra a memriaterletre hivatkozik majd, mint az s, vagyis
tulajdonkppen s-nek egy lneve (alias) lesz. rtelemszeren, ha p mdosul, akkor s
is gy tesz, ezrt a fenti program kimenete 14 lesz.
- 22 -

4.6 Boxing s unboxing


Boxingnak (bedobozols) azt a folyamatot nevezzk, amely megengedi egy
rtktpusnak, hogy gy viselkedjen, mint egy referencia-tpus. Korbban azt
mondtuk, hogy minden tpus kzvetlenl vagy indirekt mdon a System.Object bl
szrmazik. Az rtktpusok esetben az utbbi teljesl, ami egy igen specilis
helyzetet jelent. Az rtktpusok alapveten nem szrmaznak az Objectbl, mivel
gy hatkony a kezelsk, nem tartozik hozzjuk semmifle tlsly (elmletileg akr
az is elfordulhatna ilyenkor, hogy a referencia-tpusokhoz adott extrk (sync blokk,
metdustbla, stb...) tbb helyet foglalnnak, mint a tnyleges adat).
Hogy mirt van ez gy, azt nagyon egyszer kitallni: az rtktpusok egyszer
tpusok amelyek kis mennyisg adatot tartalmaznak, ezenkvl ezeket a tpusokat
klnsen gyakran fogjuk hasznlni, ezrt elengedhetetlen, hogy a lehet
leggyorsabban kezelhessk ket.
A problma az, hogy az rtktpusoknak a fentiektl fggetlenl illeszkednik kell a
tpusrendszerbe, vagyis tudnunk kell gy kezelni ket, mint egy referenciatpust s itt
jn kpbe a boxing mvelet. Nzznk egy pldt: az eddig hasznlt
Console.WriteLine metdus deklarcija gy nz ki:
public static void WriteLine(
Object value
)

Lthat, hogy a paramter tpusa object, lenykori nevn System.Object ms szval


egy referencia-tpus. Mi trtnik vajon, ha egy int tpus vltozt (egy rtktpust)
akarunk gy kirni? A WriteLine metdus minden tpust gy r ki, hogy meghvja rajtuk
a ToString metdust, amely visszaadja az adott tpus string-alakjt. A baj az, hogy a
ToStringet a System.Object deklarlja, ilyen mdon a referencia-tpusok mind
rendelkeznek vele, de az rtktpusok mr nem. Mg nagyobb baj, hogy a ToString
hvshoz a sima objectknt meghatrozott vltozknak el kell kertenik a trolt
objektum valdi tpust, ami a GetType metdussal trtnik amelyet szintn a
System.Object deklarl ami nem is lenne nmagban problma, de az rtktpusok
nem trolnak magukrl tpusinformcit pp a kis mret miatt.
A megoldst a boxing mvelet jelenti, ami a kvetkezkppen mkdik: a rendszer
elkszt a halmon egy az rtktpus valdi tpusnak megfelel keretet (dobozt)
amely tartalmazza az eredeti vltoz adatait, illetve az sszes szksges informcit
ahhoz, hogy referencia-tpusknt tudjon mkdni lnyegben az is.
Els rnzsre azt gondoln az ember, hogy a dobozols rendkvl drga mulatsg,
de ez nem felttlenl van gy. A valsgban a fordt kpes gy optimalizlni a
vgeredmnyt, hogy nagyon kevs htrnyunk szrmazzon ebbl a mveletbl,
nhny esetben pedig nagyjbl ugyanazt a teljestmnyt rjk el, mint referenciatpusok esetben.
Vegyk szre, hogy az eddigi WriteLine hvsoknl a konverzi krs nlkl azaz
implicit mdon mkdtt annak ellenre, hogy rtk- s referencia-tpusok kztt
nincs szoros relci. Az ilyen kapcsolatot implicit konverzbilis kapcsolatnak
- 23 -

nevezzk s nem tvesztend ssze a polimorfizmussal (hamarosan), br nagyon


hasonlnak ltszanak.
A kvetkez forrskd azt mutatja, hogy miknt tudunk kzzel dobozolni:
int x = 10;
object boxObject = x; // bedobozolva
Console.WriteLine("X rtke: {0}", boxObject);

Itt ugyanaz trtnik, mintha rgtn az x vltozt adnnk t a metdusnak csak ppen
egy lpssel hamarabb elksztettk x referencia-tpus klnjt.
Az unboxing (vagy kidobozols) a boxing ellentte, vagyis a bedobozolt
rtktpusunkbl kinyerjk az eredeti rtkt:
int x = 0;
object obj = x; // bedobozolva
int y = (int)obj; // kidobozolva

Az object tpus vltozn explicit tpuskonverzit hajtottunk vgre (errl hamarosan),


gy visszakaptuk az eredeti rtket.
A kidobozols szintn rdekes folyamat: logikusan gondolkodva azt hinnnk, hogy
most minden fordtva trtnik, mint a bedobozolsnl, vagyis a vermen elksztnk
egy j rtktpust s tmsoljuk az rtkeket. Ezt majdnem teljesen igaz egyetlen
apr kivtellel: amikor vissza akarjuk kapni a bedobozolt rtktpusunkat az unbox IL
utastst hvjuk meg, amely egy n. value-type-pointert ad vissza, amely a halomra
msolt s bedobozolt rtktpusra mutat. Ezt a cmet azonban nem hasznlhatjuk
kzvetlenl a verembe msolshoz, ehelyett az adatok egy ideiglenes vermen
ltrehozott objektumba msoldnak majd onnan egy jabb msols mvelettel a
szmra kijellt helyre vndorolnak.
A ketts msols pazarlsnak tnhet, de ez egyrszt megkerlhetetlen szably
msrszt a JIT ezt is kpes gy optimalizlni, hogy ne legyen nagy
teljestmnyvesztesg.
Fontos mg megjegyezni, hogy a bedobozols utn teljesen j objektum keletkezik,
amelynek semmi kze az eredetihez:
using System;
class Program
{
static public void Main()
{
int x = 10;
object z = x;
z = (int)z + 10;
Console.WriteLine(x);
Console.WriteLine(z);
}

- 24 -

A kimenet 10 illetve 20 lesz. Vegyk szre azt is, hogy z n konverzit kellett
vgrehajtanunk az sszeadshoz, de az rtkadshoz nem (elszr kidobozoltuk,
sszeadtuk a kt szmot, majd az eredmnyt visszadobozoltuk).

4.7 Konstansok
A const tpusmdost kulcssz segtsgvel egy objektumot konstanss,
megvltoztathatatlann tehetnk. A konstansoknak egyetlen egyszer adhatunk (s
ekkor ktelez is adnunk) rtket, mgpedig a deklarcinl. Brmely ksbbi
prblkozs fordtsi hibt okoz.
const int x; // Hiba
const int x = 10; // Ez j
x = 11; // Hiba

A konstans vltozknak adott rtket/kifejezst fordtsi idben ki kell tudnia rtkelni


a fordtnak. A kvetkez forrskd ppen ezrt nem is fog lefordulni:
using System;
class Program
{
static public void Main()
{
Console.WriteLine("Adjon meg egy szmot: ");
const int x = int.Parse(Console.ReadLine());
}
}

A Console.ReadLine metdus egy sort olvas be a standard bemenetrl (ez


alaprtelmezs szerint a konzol lesz, de megvltoztathat), amelyet terminl
karakterrel (Carriage Return, Line Feed, stb.), pl. az Enter-rel zrunk.
A metdus egy string tpus rtkkel tr vissza, amelybl ki kell nyernnk a
felhasznl ltal megadott szmot. Erre fogjuk hasznlni az int.Parse metdust, ami
paramterknt egy stringet vr, s egsz szmot ad vissza. A paramterknt
megadott karaktersor nem tartalmazhat numerikus karakteren kvl mst, ellenkez
esetben a program kivtelt dob.

4.8 A felsorolt tpus


A felsorolt tpus olyan adatszerkezet, amely meghatrozott rtkek nvvel elltott
halmazt kpviseli. Felsorolt tpust az enum kulcssz segtsgvel deklarlunk:
enum Animal { Cat, Dog, Tiger, Wolf };

Ezutn gy hasznlhatjuk:

- 25 -

Animal b = Animal.Tiger;
if(b == Animal.Tiger) // Ha b egy tigris
{
Console.WriteLine("b egy tigris...");
}

Enum tpust csakis metduson kvl (osztlyon bell, vagy nll tpusknt)
deklarlhatunk, ellenkez esetben a program nem fordul le:
using System;
class Program
{
static public void Main()
{
enum Animal { Cat = 1, Dog = 3, Tiger, Wolf }
}
}

Ez a kd hibs! Nzzk a javtott vltozatot:


using System;
class Program
{
enum Animal { Cat = 1, Dog = 3, Tiger, Wolf }
static public void Main()
{
}
}

Most mr j lesz (s akkor is lefordulna, ha a Program osztlyon kvl deklarlnnk).


A felsorols minden tagjnak megfeleltethetnk egy egsz (numerikus) rtket. Ha
mst nem adunk meg, akkor alaprtelmezs szerint a szmozs nulltl kezddik s
deklarci szerinti sorrendben (rtsd: balrl jobbra) eggyel nvekszik. Ezen a mdon
az enum objektumokon explicit konverzit hajthatunk vgre a megfelel numerikus
rtkre:
enum Animal { Cat, Dog, Tiger, Wolf }
Animal a = Animal.Cat;
int x = (int)a; // x == 0
a = Animal.Wolf;
x = (int)a; // x == 3

A tagok rtkei alaprtelmezetten int tpusak, ezen vltoztathatunk:


enum Animal : byte { Cat, Dog, Tiger, Wolf };

Termszetesen ez egytt jr azzal, hogy a tagok rtknek az adott tpus


rtkhatrai kztt kell maradniuk, vagyis a pldban egy tag rtke nem lehet tbb
mint 255.
- 26 -

Ilyen mdon csakis a beptett egsz numerikus tpusokat hasznlhatjuk (pl. byte,
long, uint, stb...)
Azok az nevek amelyekhez nem rendeltnk rtket implicit mdon, az ket
megelz nv rtktl szmtva kapjk meg azt, nvekv sorrendben. gy a lenti
pldban Tiger rtke ngy lesz:
using System;
class Program
{
enum Animal { Cat = 1, Dog = 3, Tiger, Wolf }
static public void Main()
{
Animal a = Animal.Tiger;
Console.WriteLine((int)a);
}
}

Az Enum.TryParse metdussal string rtkekbl gyrthatunk enum rtkeket:


using System;
class Program
{
enum Animal { Cat = 1, Dog = 3, Tiger, Wolf }
static public void Main()
{
string s1 = "1";
string s2 = "Dog";
Animal a1, a2;
Enum.TryParse(s1, true, out a1);
Enum.TryParse(s2, true, out a2);
}
}

4.9 Null tpusok


A referencia-tpusok az inicializls eltt automatikusan nullrtket vesznek fel,
illetve mi magunk is megjellhetjk ket belltatlannak:
class RefType{ }
RefType rt = null;

Ugyanez az rtktpusoknl mr nem mkdik:


int x = null; // ez le sem fordul

Azt mr tudjuk, hogy a referencia-tpusokra referencikkal, azaz a nekik megfelel


memriacmmel mutatunk, ezrt lehetsges null rtket megadni nekik. Az
- 27 -

rtktpusok pedig az ltaluk trolt adatot reprezentljk, ezrt k nem vehetnek fel
null rtket.
Ahhoz, hogy meg tudjuk llaptani, hogy egy rtktpus mg nem inicializlt, egy
specilis tpust, a nullable tpust kell hasznlnunk, amit a rendes tpus utn rt
krdjellel jelznk:
int? i = null; // ez mr mkdik

Egy nullable tpusra val konverzi implicit mdon (kln krs nlkl) megy vgbe,
mg az ellenkez irnyban explicit konverzira lesz szksgnk (vagyis ezt tudatnunk
kell a fordtval):
int y = 10;
int? x = y; // implicit konverzi
y = (int)x; // explicit konverzi

4.10 A dinamikus tpus


Ennek a fejezetnek a teljes megrtshez szksg van az osztlyok, illetve
metdusok fogalmra, ezeket egy ksbbi fejezetben tallja meg az olvas.
A C# 3.0ig bezrlag minden vltoz s objektum statikusan tpusos volt, vagyis
egyrszt a tpust fordtskor meg kellett tudnia hatrozni a fordtnak, msrszt ez
futsi id alatt nem vltozhatott meg.
A C# 4.0 bevezeti a dynamic kulcsszt, amely hasznlatval dinamikusan tpusoss
tehetnk objektumokat. Mit is jelent ez a gyakorlatban? Lnyegben azt, hogy
minden dynamiccal jellt objektum brmit megtehet fordtsi idben, mg olyan
dolgokat is, amelyek futsidej hibt okozhatnnak. Ezenkvl az sszes ilyen
objektum futsidben megvltoztathatja a tpust is:
using System;
class Program
{
static public void Main()
{
dynamic x = 10;
Console.WriteLine(x); // x most 10
x = "szalmi";
Console.WriteLine(x); // x most szalmi
}

Vegyk a kvetkez osztlyt:

- 28 -

class Test
{
public void Method(string s)
{
}
}

Ha a fenti metdust meg akarjuk hvni, akkor meg kell adnunk szmra egy string
tpus paramtert is. Kivve, ha a dynamicot hasznljuk:
static public void Main()
{
dynamic t = new Test();
t.Method(); // ez lefordul
}

A fenti forrskd minden tovbbi nlkl lefordul, viszont futni nem fog.
A konstruktorok nem tartoznak az tverhet metdusok kz, akr hasznltuk a
deklarcinl a dynamicot, akr nem. A paramtereket minden esetben ktelez
megadnunk, ellenkez esetben a program nem fordul le.
Br a fenti tesztek szrakoztatak, valjban nem tl hasznosak. A dynamic
hagyomnyos objektumokon val hasznlata lnyegben nemcsak tlthatatlann
teszi a kdot, de komoly teljestmnyproblmkat is okozhat, ezrt mindenkppen
kerljk el az ilyen szitucikat!
A dinamikus tpusok igazi haszna a ms programnyelvekkel klnsen a script
alap nyelvekkel val egyttmkdsben rejlik. A dynamic kulcssz mgtt egy
komoly platform, a Dynamic Language Runtime (DLR) ll (termszetesen a dynamic
mellett j nhny osztly is helyett kapott a csomagban). A DLR olyan tpustalan,
azaz gyengn tpusos nyelvekkel tud egyttmkdni, mint a Lua, JavaScript, PHP,
Python vagy Ruby.

- 29 -

Opertorok

Amikor programozunk, utastsokat adunk a szmtgpnek. Ezek az utastsok


kifejezsekbl llnak, a kifejezsek pedig opertorokbl s operandusokbl, illetve
ezek kombincijbl jnnek ltre:
i = x + y;
Ebben az utastsban inek rtkl adjuk x s y sszegt. Kt kifejezs is van az
utastsban:
1 lps: x + y > ezt az rtket jelljk * -al
2 lps: i = * > i nek rtkl adjuk a * -ot
Az els esetben x s y operandusok, a + jel pedig az sszeads mvelet opertora.
Ugyangy a msodik pontban i s * (vagyis x + y) az operandusok, az rtkads
mvelet (=) pedig az opertor.
Egy opertornak nem csak kt operandusa lehet. A C# nyelv egy- (unris) s hromoperandus (ternris) opertorokkal is rendelkezik.
A kvetkez nhny fejezetben tvesznk nhny opertort, de nem az sszeset.
Ennek oka az, hogy bizonyos opertorok nmagukban nem hordoznak jelentst, egy
- egy specilis rszterlet kapcsoldik hozzjuk, ezrt ezeket az opertorokat majd a
megfelel helyen ismerjk meg (pl. az indexel opertor most kimarad, elsknt a
tmbknl tallkozhat majd vele a kedves olvas).

5.1 Opertor precedencia


Amikor tbb opertor is szerepel egy kifejezsben, a fordtnak muszj valamilyen
sorrendet (precedencit) fellltani kzttk, hiszen az eredmny ettl is fgghet.
Pldul:
10 * 5 + 1
Ennl a kifejezsnl, sorrendtl fggen az eredmny lehet 51 vagy 60. A j
megolds az elbbi, az opertorok vgrehajtsnak sorrendjben a szorzs s az
oszts elnyt lvez (termszetesen rvnyeslnek a matematikai szablyok). A
legels sorrendi helyen szerepelnek pl. a zrjeles kifejezsek, utolsn pedig az
rtkad opertor. Ha bizonytalanok vagyunk a vgrehajts sorrendjben, akkor
mindig hasznljunk zrjeleket, ez a vgleges programra nzve semmilyen hatssal
nincs (s a forrskd olvashatsgt is javtja).
A fenti kifejezs teht gy nzne ki:

- 30 -

(10 * 5) + 1
A C# nyelv precedencia szerint 14 kategriba sorolja az opertorokat (a kisebb
sorszmt fogja a fordt hamarabb kirtkelni):
1. Zrjel, adattag hozzfrs (pont (.) opertor), metdushvs, postfix
inkrementl s dekrementl opertorok, a new opertor, typeof, sizeof,
checked s unchecked
2. Pozitv s negatv opertorok (x = -5), logika- s binris tagads, prefix
inkrementl s dekrementl opertorok, explicit tpuskonverzi
3. Szorzs, maradkos s maradk nlkli oszts
4. sszeads, kivons
5. Bit-eltol (>> s <<) opertorok
6. Kisebb (vagy egyenl), nagyobb (vagy egyenl), as, is
7. Egyenl s nem egyenl opertorok
8. Logikai S
9. Logikai XOR
10. Logikai VAGY
11. Feltteles S
12. Feltteles VAGY
13. Feltteles opertor ( ? : )
14. rtkad opertor, illetve a rvid formban hasznlt opertorok (pl: x +=y)

5.2 rtkad opertor


Az egyik legltalnosabb mvelet, amit elvgezhetnk az az, hogy egy vltoznak
rtket adunk. A C# nyelvben ezt az egyenlsgjel segtsgvel tehetjk meg:
int x = 10;

Ltrehoztunk egy int tpus vltozt, elneveztk x nek, majd kezdrtknek 10et
adtunk. Termszetesen nem ktelez a deklarcinl megadni a defincit (amikor
meghatrozzuk, hogy a vltoz milyen rtket kapjon), ezt el lehet halasztani:
int x;
x = 10;

Ettl fggetlenl a legtbb esetben ajnlott akkor rtket adni egy vltoznak, amikor
deklarljuk (persze ez inkbb csak eszttikai krds, a fordt lesz annyira okos,
hogy ugyanazt generlja le a fenti kt kdrszletbl).
Egy vltoznak nem csak konstans rtket, de egy msik vltozt is rtkl
adhatunk, de csakis abban az esetben, ha a kt vltoz azonos tpus, illetve ha
ltezik megfelel konverzi (a tpuskonverzikkal egy ksbbi fejezet foglalkozik).

- 31 -

int x = 10;
int y = x; // y rtke most 10

5.3 Matematikai opertorok


A kvetkez pldban a matematikai opertorok hasznlatt vizsgljuk meg:
using System;
public class Operators
{
static public void Main()
{
int x = 10;
int y = 3;
int z = x + y; // sszeads: z = 10 + 3
Console.WriteLine(z); // Kirja az eredmnyt: 13
z = x - y; // Kivons: z = 10 - 3
Console.WriteLine(z); // 7
z = x * y; //Szorzs: z = 10 * 3
Console.WriteLine(z);//30
z = x / y; // Maradk nlkli oszts: z = 10 / 3;
Console.WriteLine(z); // 3
z = x % y; // Maradkos oszts: z = 10 % 3
Console.WriteLine(z); // Az oszts maradkt rja ki: 1
Console.ReadKey(); //Vr egy billenty letsre
}
}

5.4 Relcis opertorok


A relcis opertorok segtsgvel egy adott rtkkszlet elemei kztti viszonyt
tudjuk lekrdezni. Relcis opertort hasznl mveletek eredmnye vagy igaz (true)
vagy hamis (false) lesz. A numerikus tpusokon rtelmezve van egy rendezs relci:
using System;
public class RelOp
{
static public void Main()
{
int x = 10;
int y = 23;
Console.WriteLine(x
Console.WriteLine(x
Console.WriteLine(x
Console.WriteLine(x
}

> y); // Kirja az eredmnyt: false


== y); // false
!= y); // x nem egyenl y al: true
<= y); // x kisebb-egyenl mint y: true

Az els sor egyrtelm, a msodikban az egyenlsget vizsgljuk a ketts


egyenlsgjellel. Ilyen esetekben figyelni kell, mert egy elts is nehezen kiderthet
- 32 -

hibt okoz, amikor egyenlsg helyett az rtkad opertort hasznljuk. Az esetek


tbbsgben ugyanis gy is le fog fordulni a program, mkdni viszont valsznleg
rosszul fog.
A relcis opertorok sszefoglalsa:
x>y
x >= y
x<y
x <= y
x == y
x != y

x nagyobb, mint y
x nagyobb vagy egyenl, mint y
x kisebb, mint y
x kisebb vagy egyenl, mint y
x egyenl y-nal
x nem egyenl y-nal

5.5 Logikai s feltteles opertorok


Akrcsak a C++, a C# sem rendelkezik igazi logikai tpussal, helyette 1 s 0 jelzi az
igaz s hamis rtkeket:
using System;
public class RelOp
{
static public void Main()
{
bool l = true;
bool k = false;
if(l == true && k == false)
{
Console.WriteLine("Igaz");
}
}

Elszr felvettnk kt logikai (bool) vltozt, az elsnek igaz a msodiknak hamis


rtket adtunk. Ezutn egy elgazs kvetkezik, errl bvebben egy ksbbi
fejezetben lehet olvasni, a lnyege az, hogy ha a felttel igaz, akkor vgrehajt egy
utastst (vagy utastsokat). A fenti pldban az S (&&) opertort hasznltuk, ez
kt operandust vr s akkor ad vissza igaz rtket, ha mindkt operandusa igaz
vagy nullnl nagyobb rtket kpvisel. Ebbl kvetkezik az is, hogy akr az elz
fejezetben megismert relcis opertorokbl felptett kifejezsek, vagy matematikai
formulk is lehetnek operandusok. A program nem sok mindent tesz, csak kirja,
hogy Igaz.
Nzzk az S igazsgtblzatt:
A
B
Eredmny
hamis hamis
hamis
hamis igaz
hamis
igaz hamis
hamis
igaz
igaz
igaz

- 33 -

A fenti forrskd j gyakorls az opertor-precedencihoz, az elgazs felttelben


elszr az egyenlsget fogjuk vizsglni (a hetes szm kategria) s csak utna a
feltteles S t (tizenegyes kategria).
A msodik opertor a VAGY:
using System;
public class RelOp
{
static public void Main()
{
bool l = true;
bool k = false;

if(l == true || k == true)


{
Console.WriteLine("Igaz");
}

A vagy (||) opertor akkor trt vissza igaz rtket, ha az operandusai kzl
valamelyik igaz vagy nagyobb, mint nulla. Ez a program is ugyanazt csinlja, mint
az elz, a klnbsg a felttelben van. Lthat, hogy k biztosan nem igaz (hiszen
ppen eltte kapott hamis rtket).
A VAGY igazsgtblzata:
A
B
Eredmny
hamis hamis
hamis
hamis igaz
igaz
igaz hamis
igaz
igaz
igaz
igaz
Az eredmny kirtkelse az n. lusta kirtkels (vagy rvidzr) mdszervel
trtnik, azaz a program csak addig vizsglja a felttelt, amg muszj. Tudni kell azt
is, hogy a kirtkels mindig balrl jobbra halad, ezrt pl. a fenti pldban k soha
nem fog kirtkeldni, mert l van az els helyen, s mivel igaz rtket kpvisel,
ezrt a felttel is biztosan teljesl.
A harmadik a tagads (!):
using System;
public class RelOp
{
static public void Main()
{
int x = 10;

if(!(x == 11)) // x nem 11, ezrt false, de ezt tagadjuk: true


{
Console.WriteLine("X nem egyenl 11 -gyel!");
}

- 34 -

Ennek az opertornak egy operandusa van s akkor ad vissza igaz rtket, ha az


operandusban megfogalmazott felttel hamis, vagy ha numerikus kifejezsrl
beszlnk - egyenl nullval.
A tagads (negci) igazsgtblja:
A
Eredmny
hamis
igaz
igaz
hamis
Ez a hrom opertor n. feltteles opertor, kzlk pedig az S s a VAGY
opertoroknak ltezik csonkolt logikai prja is. A klnbsg annyi, hogy a logikai
opertorok az eredmnytl fggetlenl kirtkelik a teljes kifejezst, nem lnek a
lusta kirtkelssel.
A logikai VAGY mvelet:
if(l == true | k == true)
{
Console.WriteLine("Igaz");
}

A logikai S:
if(l == true & k == true)
{
Console.WriteLine("Igaz");
}

A logikai opertorok csaldjhoz tartozik (ha nem is szorosan) a feltteles opertor.


Ez az egyetlen hromoperandus opertor s a kvetkezkppen mkdik:
felttel ? igaz-g : hamis-g;
using System;
public class RelOp
{
static public void Main()
{
int x = 10;
int y = 10;
Console.WriteLine((x == y) ? "Egyenl" : "Nem egyenl");
}
}

Az opertor gy mkdik, hogy a krdjel eltti kifejezst kirtkeli, majd megnzi,


hogy a kifejezs igaz vagy hamis. Ha igaz, akkor a krdjel utni rtk lesz a teljes
kifejezsnk rtke, ha pedig hamis, akkor pedig a kettspont utni. Egyszer if-else
(errl ksbb) gakat lehet ezzel a mdszerrel kivltani, sok gpelst
megsprolhatunk vele s a kdunk is kompaktabb, ttekinthetbb lesz tle.

- 35 -

5.6 Bit opertorok


Az elz fejezetben emltett logikai opertorok bitenknti mveletek elvgzsre is
alkalmasak. A szmtgp az adatokat kettes szmrendszerbeli alakban trolja, gy
pldul, ha van egy byte tpus vltoznk (ami egy byte, azaz 8 bit hosszsg),
aminek a 2 rtket adjuk, akkor az a kvetkezkppen jelenik meg a memriban:
2 00000010
A bit opertorok ezzel a formval dolgoznak. Az eddig megismert kett mell mg jn
ngy msik opertor is. A mveletek:
Bitenknti S: veszi a kt operandus binris alakjt s a megfelel bitprokon
elvgzi az S mveletet, azaz ha mindkt bit 1 llsban van, akkor az adott helyen
az eredmnyben is az lesz, egybknt pedig 0:
01101101
00010001 AND
00000001
Elg egyrtelm,
kiszmolshoz.

hogy

az

igazsgtblt

hasznltuk

az

eredmny

Plda:
using System;
public class Program
{
static public void Main()
{
Console.WriteLine(10 & 2);
//1010 & 0010 = 0010 = 2
}
}

A programot futtatva ltni fogjuk, hogy a Console.WriteLine a mvelet eredmnyt


tzes szmrendszerben rja majd ki.
A bitenknti VAGY hasonlan mkdik, mint az S, de a vgeredmnyben egy bit
rtke akkor lesz 1, ha a kt operandus adott bitje kzl valamelyik legalbb az:
01101101
00010001 OR
01111101

- 36 -

Plda a VAGY-ra:
using System;
public class Program
{
static public void Main()
{
Console.WriteLine(10 | 2);
// 1010 | 0010 = 1010 = 10
}
}

Biteltols balra: a kettes szmrendszerbeli alak fels bitjt eltoljuk, majd a jobb
oldalon keletkez res bitet nullra lltjuk. Az opertor jele: <<.
10001111 LEFT SHIFT
100011110
Plda:
using System;
public class Program
{
static public void Main()
{
int x = 143;
Console.WriteLine(x << 1);
// 10001111 (=143) << 1 = 100011110 = 286
}
}

Amikor biteltolst vgznk, figyelnnk kell arra, hogy a mvelet vgeredmnye


minden esetben 32 bites, eljeles szm (int) lesz. Ennek nagyon egyszer oka az,
hogy gy biztostja a .NET, hogy az eredmny elfrjen (a fenti pldban hasznlt 143
pl. pontosan 8 biten felrhat szm, azaz egy byte tpusban mr nem frne el az
eltols utn, hiszen akkor 9 bitre lenne szksgnk).
Biteltols jobbra: most az als bitet toljuk el, s fell ptoljuk a hinyt. Az opertor:
>>.
Plda:
using System;
public class Program
{
static public void Main()
{
byte x = 143;
Console.WriteLine(x >> 1);
// 10001111 (=143) >> 1 = 01000111 = 71
}
}

- 37 -

A legtbb beptett tpust knnyen konvertlhatjuk t klnbz szmrendszerekre a


Convert.ToString(x, y) metdussal, ahol x az az objektum, amit konvertlunk, y pedig
a cl-szmrendszer:
using System;
public class Program
{
static public void Main()
{
byte x = 10;
Console.WriteLine(Convert.ToString(x, 2)); // 1010
int y = 10;
Console.WriteLine(Convert.ToString(y, 2)); // 1010
char z = 'a';
Console.WriteLine(Convert.ToString(z, 2)); // 1100001
Console.WriteLine(Convert.ToString(z, 16)); // 61
Console.WriteLine(Convert.ToString(z, 10)); // 97
}
}

Ez a metdus a konvertls utn csak a hasznos rszt fogja visszaadni, ezrt fog
az int tpus - egybknt 32 bites - vltoz csak 4 biten megjelenni (hiszen a 10 egy
pontosan 4 biten felrhat szm).
A char tpus numerikus rtkre konvertlsakor az Unicode tblban elfoglalt helyt
adja vissza. Ez az a karakter esetben 97 (tzes szmrendszer), 1100001 (kettes
szr.) vagy 0061 (tizenhatos szr.) lesz. Ez utbbinl is csak a hasznos rszt kapjuk
vissza, hiszen a fels nyolc bit itt nullkbl ll.
Vegyk szre, hogy amg a balra val eltols tnylegesen fizikailag hozztett az
eredeti szmunkhoz, addig a jobbra tols elvesz belle, hiszen a fellre rkez
nulla bitek nem hasznosulnak az eredmny szempontjbl. rtelemszeren a balra
tols ezrt mindig nvelni, a jobbra tols pedig mindig cskkenteni fogja az
eredmnyt.
Ennl is tovbb mehetnk, felfedezve a biteltolsok valdi hasznt: egy n bittel balra
tols megfelel az alapszm 2 az nedik hatvnyval val szorzsnak:
143 << 1 = 143 * (2^1) = 286
143 << 2 = 143 * (2^2) = 572
Ugyangy a jobbra tols ugyanazzal a hatvnnyal oszt (nullra kerektssel):
143 >> 1 = 143 / (2^1) =71
143 >> 2 = 143 / (2^2) = 35
Amikor olyan programot ksztnk, amely ersen pt kettvel vagy hatvnyaival val
szorzsra/osztsra, akkor ajnlott bitmveleteket hasznlni, mivel ezeket a
processzor sokkal gyorsabban vgzi el, mint a hagyomnyos szorzst
(tulajdonkppen a szorzs a processzor egyik leglassabb mvelete).

- 38 -

5.7 Rvid forma


Vegyk a kvetkez pldt:
x = x + 10;

Az x nev vltozt megnveltk tzzel. Csakhogy van egy kis baj: ez a megolds
nem tl hatkony. Mi trtnik valjban? Elsknt rtelmezni kell a jobb oldalt, azaz
ki kell rtkelni xet, hozz kell adni tzet s eltrolni a veremben. Ezutn ismt
kirtkeljk xet, ezttal a bal oldalon.
Szerencsre van megolds, mgpedig az n. rvid forma. A fenti sorbl ez lesz:
x + = 10;

Rvidebb, szebb s hatkonyabb. Az sszes aritmetikai opertornak ltezik rvid


formja.
Az igazsghoz azrt az is hozztartozik, hogy a fordtprogram elvileg felismeri a fent
felvzolt szitucit s a rvid formval egyenrtk IL t kszt belle (ms krds,
hogy a forrskd gy viszont szebb s olvashatbb).
A problma ugyanaz, de a megolds ms a kvetkez esetben:
x = x + 1;

Szemmel lthatan ugyanaz a baj, azonban az eggyel val nvelsre/cskkentsre


van nll opertorunk:
++x s --x
x++ s x--

Ebbl az opertorbl rgtn kt verzit is kapunk, prefixes (++/-- ell) s postfixes


(++/-- htul) formt. A prefixes alak pontosan azt teszi, amit elvrunk tle, azaz
megnveli(vagy rtelemszeren cskkenti) az operandust eggyel.
A postfixes forma egy kicsit bonyolultabb, elsknt ltrehoz egy tmeneti vltozt,
amiben eltrolja az operandusa rtkt, majd megnveli eggyel az operandust, vgl
visszaadja az tmeneti vltozt. Ez elsre taln nem tnik hasznosnak, de vannak
helyzetek, amikor lnyegesen megknnyti az letnket a hasznlata.
Attl fggen, hogy nveljk vagy cskkentjk az operandust, inkrementl illetve
dekrementl opertorrl beszlnk. Ez az opertor hasznlhat az sszes beptett
numerikus tpuson, valamint a char illetve enum tpusokon is.

- 39 -

5.8 Egyb opertorok


Unris (+ s -): az adott szm pozitv illetve negatv rtkt jelezzk vele:
using System;
public class Program
{
static public void Main()
{
int x = 10;
int y = 10 + (-x);
Console.WriteLine(y);
}
}

Ez a program nullt fog kirni (termszetesen rvnyeslnek a matematikai


szablyok). Ezeket az opertorokat csakis eljeles tpusokon hasznlhatjuk, mivel az
opertor int tpussal tr vissza (akkor is, ha pl. byte tpusra alkalmaztuk). A kvetkez
program le sem fordul:
using System;
public class Program
{
static public void Main()
{
byte x = 10;
byte y = -x;
}
}

A typeof az operandusa tpust adja vissza:


usingSystem;
classProgram
{
Static public void Main()
{
int x = 143;
if(typeof(int) == x.GetType())
{
Console.WriteLine("x tpusa int");
}
}
}

A vltozn meghvott GetType metdus a vltoz tpust adja vissza (ez egy
System.Objecthez tartoz metdus, gy a hasznlathoz dobozolni kell az
objektumot).

- 40 -

A sizeof opertor a paramtereknt megadott rtktpus mrett adja vissza byte


ban. Ez az opertor kizrlag unsafe mdban hasznlhat s csakis rtktpusokon
(illetve pointer tpusokon):
using System;
public classProgram
{
static public void Main()
{
unsafe
{
Console.WriteLine(sizeof(int));
}
}
}

Ez a program ngyet fog kirni, hiszen az int tpus 32 bites, azaz 4 byte mret tpus.
A programot az unsafe kapcsolval kell lefordtanunk:
csc /unsafe main.cs

- 41 -

Vezrlsi szerkezetek

Vezrlsi szerkezetnek
konstrukcikat nevezzk.

program

utastsainak

sorrendisgt

szablyoz

6.1 Szekvencia
A legegyszerbb vezrlsi szerkezet a szekvencia. Ez tulajdonkppen egyms utn
megszabott sorrendben vgrehajtott utastsokbl ll.

6.2 Elgazs
Gyakran elfordul, hogy meg kell vizsglnunk egy lltst, s attl fggen, hogy igaz
vagy hamis, a programnak ms-ms utastst kell vgrehajtania. Ilyen esetekben
elgazst hasznlunk:
using System;
public class Program
{
static public void Main()
{
int x = 10;
if(x == 10) // Ha x egyenl 10 -zel
{
Console.WriteLine("x rtke 10");
}
}
}

Termszetes az igny arra is, hogy azt a helyzetet is kezelni tudjuk, amikor x rtke
nem tz. Ilyenkor hasznljuk az else gat:
using System;
public class Program
{
static public void Main()
{
int x = 11;
if(x == 10) // Ha x egyenl 10 -zel
{
Console.WriteLine("x rtke 10");
}
else // Ha pedig nem
{
Console.WriteLine("x rtke nem 10");
}
}
}

- 42 -

Az else szerkezet akkor lp letbe, ha a hozz kapcsold felttel(ek) nem igaz(ak).


nmagban else g nem llhat (nem is lenne sok rtelme).
A fenti helyzetben rhattuk volna ezt is:
using System;
public class Program
{
static public void Main()
{
int x = 11;
if(x == 10) // Ha x egyenl 10 -zel
{
Console.WriteLine("x rtke 10");
}
if(x != 10)//Ha pedig x nem 10
{
Console.WriteLine("x rtke nem 10");
}
}

Ez a program pontosan ugyanazt csinlja, mint az elz, de van egy nagy klnbsg
a kett kztt: mindkt felttelt ki kell rtkelnie a programnak, hiszen kt klnbz
szerkezetrl beszlnk (ez egyttal azzal is jr, hogy a feltteltl fggen mindkt
llts lehet igaz).
Arra is van lehetsgnk, hogy tbb felttelt is megvizsgljunk, ekkor else-if et
hasznlunk:
using System;
public class Program
{
static public void Main()
{
int x = 13;
if(x == 10) // Ha x == 10 -zel
{
Console.WriteLine("x rtke 10");
}
elseif(x == 12) // Vagy 12 -vel
{
Console.WriteLine("x rtke 12");
}
else // De ha egyik sem
{
Console.WriteLine("x rtke nem 10 vagy 12");
}
}
}

A program az els olyan gat fogja vgrehajtani, amelynek a felttele teljesl (vagy
ha egyik felttel sem bizonyult igaznak, akkor az else gat ha adtunk meg ilyet).

- 43 -

Egy elgazsban pontosan egy darab if, brmennyi else-if s pontosan egy else g
lehet. Egy elgazson bell is rhatunk elgazst.
Az utols pldban olyan vltozt vizsgltunk, amely nagyon sokfle rtket vehet
fel. Nylvn ilyenkor nem tudunk minden egyes llapothoz felttelt rni (pontosabban
tudunk, csak az nem lesz szp). Ilyen esetekben azonban van egy egyszerbb s
elegnsabb megolds, mgpedig a switch-case szerkezet. Ezt akkor hasznljuk, ha
egy vltoz tbb lehetsges llapott akarjuk vizsglni:
using System;
public class Program
{
static public void Main()
{
int x = 11;
switch(x)
{
case 10:
Console.WriteLine("x rtke 10");
break;
case 11:
Console.WriteLine("x rtke 11");
break;
}
}
}

A switch szerkezeten bell megadhatjuk azokat az llapotokat, amelyekre reaglni


szeretnnk. Az egyes esetek utastsai utn meg kell adnunk, hogy mi trtnjen
ezutn. Az egyes gak a kijellt feladatuk vgrehajtsa utn a break utastssal
kilpnek a szerkezetbl:
using System;
public class Program
{
enum Animal { TIGER, WOLF, CAT, DOG};
static public void Main()
{
Animal animal = Animal.DOG;

switch(animal)
{
case Animal.TIGER:
Console.WriteLine("Tigris");
break;
default:
Console.WriteLine("Nem ismerem ezt az llatot!");
break;
}

jdonsgknt megjelenik a default llapot, ez lnyegben az else g testvre lesz,


akkor kerl ide a vezrls, ha a switch nem tartalmazza a vizsglt vltoz llapott
(vagyis a default biztostja, hogy a switch egy ga mindenkppen lefusson).
- 44 -

A C++ nyelvtl eltren a C# nem engedlyezi, hogy break utasts hinyban egyik
llapotbl tcssszunk egy msikba. Ez all a szably all egyetlen kivtel, ha az
adott g nem tartalmaz semmilyen utastst:
using System;
public class Program
{
enum Animal { TIGER, WOLF, CAT, DOG };
static public void Main()
{
Animal animal = Animal.DOG;
switch(animal)
{
case Animal.TIGER:
case Animal.DOG:
default:
Console.WriteLine("Ez egy llat!");
break;
}
}

A break utastson kvl hasznlhatjuk a goto t is, ekkor tugrunk a megadott gra:
using System;
public class Program
{
enum Animal { TIGER, WOLF, CAT, DOG};
static public void Main()
{
Animal animal = Animal.DOG;

switch(animal)
{
case Animal.TIGER:
goto default;
case Animal.DOG:
goto default;
default:
Console.WriteLine("Ez egy llat!");
break;
}

6.3 Ciklus
Amikor egy adott utastssorozatot egyms utn tbbszr kell vgrehajtanunk, akkor
ciklust hasznlunk. A C# ngyfle ciklust biztost szmunkra.
Az els az n. szmlls ciklus (nevezzk for-ciklusnak). Nzzk a kvetkez
programot:
- 45 -

using System;
public class Program
{
static public void Main()
{
for(int i = 0;i < 10;++i)
{
Console.WriteLine(i);
}
}
}

Vajon mit r ki a program? Mieltt ezt megmondanm, elszr inkbb nzzk meg
azt, hogy mit csinl: a for utni zrjelben talljuk az n. ciklusfelttelt, ez minden
ciklus rsze lesz, s azt adjuk meg benne, hogy hnyszor fusson le a ciklus. A
szmlls ciklus felttele els rnzsre elgg sszetett, de ez ne tvesszen meg
minket, valjban nem az. Mindssze hrom krdsre kell vlaszt adnunk: Honnan?
Meddig? s Hogyan?
Menjnk sorjban: a honnanra adott vlaszban megmondjuk azt, hogy milyen tpust
hasznlunk a szmolshoz s azt, hogy honnan kezdjk a szmolst.
Tulajdonkppen ebben a lpsben adjuk meg az n. Ciklusvltozt, amelyre a
ciklusfelttel pl. A fenti pldban egy int tpus ciklusvltozt hoztunk ltre a
ciklusfelttelen bell s nulla kezdrtket adtunk neki.
A ciklusvltoz neve konvenci szerint i lesz az angol iterate ismtel szbl. Tbb
ciklusvltoz hasznlatakor ltalban i, j, k ... sorrendet kvetnk.
Mivel a ciklusfelttel utn blokkot nyitunk, azt hinn az ember, hogy a ciklusvltoz a
loklis lesz a ciklus blokkjra (a for utn kvetkez kapcsos zrjelekkel hatrolt
rszre) nzve, de ez nem fedi a valsgot. A ciklusfelttelen bell deklarlt
ciklusvltoz loklis lesz a ciklust tartalmaz blokkra (vagyis ebben az esetben a
teljes Main fggvnyre) nzve. pp ezrt a kvetkez forrskd nem fordulna le:
using System;
public class Program
{
static public void Main()
{
for(int i = 0;i < 10;++i)
{
Console.WriteLine(i);
}
int i = 10; // itt a hiba
}

Kvetkezzen a Meddig?! Most azt kell megvlaszolnunk, hogy a ciklusvltoz


milyen rtket vehet fel, ami kielgti a ciklusfelttelt. Most azt adtuk meg, hogy i-nek
kisebbnek kell lennie tznl, vagyis kilenc mg j, de ha i ennl nagyobb, akkor a
ciklust be kell fejezni.

- 46 -

Termszetesen bonyolultabb kifejezst is megadhatunk:


using System;
public class Program
{
static public void Main()
{
for(int i = 1;i < 10 && i != 4;++i)
{
Console.WriteLine(i);
}
}
}

Persze ennek a programnak klnsebb rtelme nincs, de a ciklusfelttel


rdekesebb. Addig megy a ciklus, amg i kisebb tznl s nem egyenl nggyel.
rtelemszeren csak hromig fogja kirni a szmokat, hiszen mire a ngyhez r, a
ciklusfelttel mr nem lesz igaz.
Utoljra a Hogyan? krdsre adjuk meg a vlaszt, vagyis azt, hogy milyen mdon
vltoztatjuk a ciklusvltoz rtkt. A leggyakoribb mdszer a pldban is lthat
inkrementl (dekrementl) opertor hasznlata, de itt is megadhatunk sszetett
kifejezst:
using System;
public class Program
{
static public void Main()
{
for(int i = 0;i < 10;i += 2)
{
Console.WriteLine(i);
}
}
}

Ebben a kdban kettesvel nveljk a ciklusvltozt, vagyis a pros szmokat ratjuk


ki a kpernyre.
Most mr meg tudjuk vlaszolni, hogy az els programunk mit csinl: nulltl kilencig
kirja a szmokat.
A for ciklusbl tetszs szerint elhagyhatjuk a ciklusfej brmely rszt akr az
egszet is, ekkor vgtelen ciklust kszthetnk.
Vgtelen ciklusnak nevezzk azt a ciklust, amely soha nem r vget. Ilyen ciklus
szlethet programozsi hibbl, de szndkosan is, mivel nha erre is szksgnk
lesz.

- 47 -

using System;
public class Program
{
static public void Main()
{
for(;;)
{
Console.WriteLine("Vgtelen ciklus");
}
}
}

Ez a forrskd lefordul, de a fordttl figyelmeztetst kapunk (warning), hogy


gyans kdot szlelt.
A program futst a Ctrl+C billentykombincival llthatjuk le, ha parancssorbl
futtattuk.
Msodik kliensnk az ell-tesztels ciklus (mostantl hvjuk while-ciklusnak), amely
onnan kapta a nevt, hogy a ciklusmag vgrehajtsa eltt ellenrzi a ciklusfelttelt,
ezrt elfordulhat az is, hogy a ciklus-trzs egyszer sem fut le:
using System;
public class Program
{
static public void Main()
{
int i = 0; // ciklusvltoz deklarci
while(i < 10) // ciklusfelttel: fuss amg i kisebb, mint 10
{
Console.WriteLine("i rtke: {0}", i);
++i; // ciklusvltoz nvelse
}
}
}

A program ugyanazt csinlja mint az elz, viszont itt jl lthatan elklnlnek a


ciklusfelttelrt felels utastsok (kezdrtk, ciklusfelttel, nvel/cskkent).
Mkdst tekintve az ell-tesztels ciklus hasonlt a szmllsra (mindkett elszr
a ciklusfelttelt ellenrzi), de az elbbi sokkal rugalmasabb, mivel tbb lehetsgnk
van a ciklusfelttel megvlasztsra.
A vltoz rtknek kiratsnl a Console.WriteLine egy msik verzijt hasznltuk,
amely n. formtum-sztringet kap paramterl. Az els paramterben a kapcsos
zrjelek kztt megadhatjuk, hogy a tovbbi paramterek kzl melyiket
helyettestse be a helyre (nulltl szmozva).
A harmadik versenyz kvetkezik, t htul-tesztels ciklusnak hvjk (legyen dowhile), nem nehz kitallni, hogy azrt kapta ezt a nevet, mert a ciklusmag
vgrehajtsa utn ellenrzi a ciklusfelttelt, gy legalbb egyszer biztosan lefut:

- 48 -

using System;
public class Program
{
static public void Main()
{
int i = 0;
do
{
Console.WriteLine("i rtke: {0}", i);
++i;
}while(i < 10);
}
}

Vgl de nem utolssorban a foreach (neki nincs kln neve) ciklus kvetkezik.
Ezzel a ciklussal vgigiterlhatunk egy tmbn vagy gyjtemnyen, illetve minden
olyan objektumon, ami megvalstja az IEnumerable s IEnumerator interfszeket
(interfszekrl egy ksbbi fejezet fog beszmolni, ott lesz sz errl a kettrl is).
A pldnk most nem a mr megszokott szmoljunk el kilencig lesz, helyette
vgigmegynk egy stringen:
using System;
public class Program
{
static public void Main()
{
string str = "abcdefghijklmnopqrstuvwxyz";
foreach(char ch in str)
{
Console.Write(ch);
}
}

A ciklusfejben felvesznk egy char tpus vltozt (egy string karakterekbl ll),
utna az in kulcssz kvetkezik, amivel kijelljk, hogy min megynk t. A pldban
hasznlt ch vltoz nem ciklusvltoz, hanem n. itercis vltoz, amely felveszi az
iterlt gyjtemny aktulis elemnek rtkt. ppen ezrt egy foreach ciklus nem
mdosthatja egy gyjtemny elemeit (le sem fordulna ebben az esetben a program).
A foreach ciklus ktfle mdban kpes mkdni: ha a lista, amin alkalmazzuk,
megvalstja az IEnumerable s IEnumerator interfszeket, akkor azokat fogja
hasznlni, de ha nem, akkor hasonl lesz a vgeredmny, mint egy szmlls ciklus
esetben (leszmtva az itercis vltozt, az mindenkppen megmarad).
A foreach pontos mkdsvel az interfszekrl szl fejezet foglalkozik majd, ahol
tbbek kztt megvalstunk egy osztlyt, amelyen a foreach kpes vgigiterlni
(azaz megvalstjuk az IEnumerable s IEnumerator interfszeket).

- 49 -

6.3.1 Yield
A yield kifejezs lehetv teszi, hogy egy ciklusbl olyan osztlyt generljon a fordt,
amely megvalstja az IEnumerable interfszt s ezltal hasznlhat legyen pl. a
foreach ciklussal:
using System;
using System.Collections;
public class Program
{
static public IEnumerable EnumerableMethod(int max)
{
for(int i = 0;i < max;++i)
{
yield return i;
}
}
static public void Main()
{
foreach(int i in EnumerableMethod(10))
{
Console.Write(i);
}
}
}

A yield mkdsi elve a kvetkez: a legels metdushvsnl a ciklus megtesz egy


lpst, ezutn kilpnk a metdusbl de annak llapott megrizzk, azaz a
kvetkez hvsnl nem jraindul a ciklus, hanem onnan folytatja ahol legutbb
abbahagytuk.

6.3.2 Prhuzamos ciklusok


Ennek a fejezetnek a megrtshez szksg van a generikus listk s a lambda
kifejezsek ismeretre, ezekrl egy ksbbi fejezet szl.
A tbb processzormaggal rendelkez szmtgpek teljestmnynek kihasznlsa
cljbl a Microsoft elksztette a Task Parallel Library t (illetve a PLINQ t, errl
egy ksbbi fejezetben), amely a .NET 4.0 verzijban kapott helyet, ezrt ehhez a
fejezethez a C# 4.0 hoz kszlt fordt szksges.
A TPL szmunkra rdekes rsze a prhuzamos ciklusok megjelense. A NET 4.0 a
for s a foreach ciklusok prhuzamostst tmogatja a kvetkez mdon:

- 50 -

using System;
using System.Collections.Generic;
using System.Threading.Tasks; // ez kell
class Program
{
static public void Main()
{
List<int> list = new List<int>()
{
1, 2, 4, 56, 78, 3, 67
};
Parallel.For(0, list.Count, (index) =>
{
Console.Write("{0}, ", list[index]);
});
Console.WriteLine();
Parallel.ForEach(list, (item) => Console.Write("{0}, ", item));
}

A For els paramtere a ciklusvltoz kezdrtke, msodik a maximumrtk, mg a


harmadik helyen a ciklusmagot jelent Action<int> generikus delegate ll, amely
egyetlen bemen paramtere a ciklusvltoz aktulis rtke.
A ForEach kt paramtere kzl az els az adatforrs, mg a msodik a ciklusmag.
Mindkt ciklus szmos vltozattal rendelkezik, ezek megtallhatak a kvetkez
MSDN oldalon:
http://msdn.microsoft.com/en-us/library/system.threading.tasks.parallel_members.aspx

- 51 -

Gyakorl feladatok

7.1 Szorztbla
Ksztsnk szorztblt! A program vagy a parancssori paramterknt kapott szmot
hasznlja, vagy ha ilyet nem adtunk meg, akkor generljon egy vletlen szmot.
Megolds (7/Mtable.cs)
Elsknt ksztsk el a program vzt:
using System;
class Program
{
static public void Main(string[] args)
{
}
}

Vegyk szre, hogy a Main metdus kapott egy paramtert, mgpedig egy string
tpus elemekbl ll tmbt (tmbkrl a kvetkez fejezetek adnak tbb
tjkoztatst, most ez nem annyira lesz fontos). Ebben a tmbben lesznek az n.
parancssori paramtereink.
De mi is az a parancssori paramter? Egy nagyon egyszer pldt nzznk meg,
azt, amikor lefordtunk egy C# forrskdot:
csc main.cs
Ebben az esetben a csc a fordtprogram neve, mg a forrskdot tartalmaz file
neve pedig a paramter. Ha ezt vesszk alapul, akkor az args tmb egyetlen elemet
tartalmaz a csc-re nzve, mgpedig a main.cs t. Lssuk, hogy hogyan fog ez
kinzni a mi programunkban (feltesszk, hogy mul.exe lesz a neve):
mul.exe 12
Vagyis a 12 es szorztblt szeretnnk ltni.
A kvetkez lpsben fejlesszk tovbb a programot, hogy rja ki a paramterknt
megadott szm ktszerest. Ehhez mg szksgnk van arra is, hogy szm tpuss
alaktsuk a paramtert, hiszen azt stringknt kapjuk meg. Erre a feladatra az
int.Parse metdust hasznljuk majd, amely szmm konvertlja a paramtereknt
kapott szveget (persze csak akkor, ha ez lehetsges, egybknt kivtelt dob).

- 52 -

A forrskd most gy alakul:


using System;
class Program
{
static public void Main(string[] args)
{
int number = int.Parse(args[0]);
Console.WriteLine(number * 2);
}
}

Mivel a tmbket mindig nulltl kezdve indexeljk, ezrt az els parancssori


paramter a megadott szm a nulladik helyen lesz.
A programot most gy tudjuk futtatni:
mul.exe 10
Erre az eredmny 20 lesz. Egyetlen problma van, a program sszeomlik, ha nem
adunk meg paramtert.
Most mdostsuk gy, hogy figyelmeztesse a felhasznlt, hogy meg kell adnia egy
szmot is!
Ezt gy fogjuk megoldani, hogy lekrdezzk a paramtereket tartalmaz tmb
hosszt, s ha ez az rtk nulla, akkor kirjuk az utastst:
using System;
class Program
{
static public void Main(string[] args)
{
if(args.Length == 0)
{
Console.WriteLine("Adjon meg egy paramtert!");
}
else
{
int number = int.Parse(args[0]);
Console.WriteLine(number * 2);
}
}
}

Egy kicsit szebb lesz a forrskd, ha az else g hasznlata helyett az if gba tesznk
egy return utastst, amely visszaadja a vezrlst annak a rendszernek amely az t
tartalmaz metdust hvta (ez a metdus jelen esetben a Main, t pedig mi vagyis
inkbb az opercis rendszer hvta, azaz a program befejezi a futst):

- 53 -

using System;
class Program
{
static public void Main(string[] args)
{
if(args.Length == 0)
{
Console.WriteLine("Adj meg egy paramtert!");
return;
}
int number = int.Parse(args[0]);
Console.WriteLine(number * 2);
}

A kvetkez lpsben ahelyett, hogy kilpnk, ha nincs paramter, inkbb


generlunk egy vletlen szmot. Ehhez szksgnk lesz egy Random tpus
objektumra.
A forrskd most ilyen lesz:
using System;
class Program
{
static public void Main(string[] args)
{
int number;
if(args.Length
{
Random r
number =
}
else
{
number =
}

== 0)
= new Random();
r.Next(100);

int.Parse(args[0]);

Console.WriteLine(number * 2);
}
}

Vletlenszmot a Next metdussal generltunk, a fenti formjban 0 s 100 kztt


generl egy szmot, de hasznlhatjuk gy is:
number = r.Next(10, 100);

Ekkor 10 s 100 kztti lesz a szm.


Mr nincs ms dolgunk, mint megrni a feladat lnyegt, a szorztblt:

- 54 -

using System;
class Program
{
static public void Main(string[] args)
{
int number;
if(args.Length == 0)
{
Randomr = new Random();
number = r.Next(100);
}
else
{
number = int.Parse(args[0]);
}
for(int i = 1;i <= 10;++i)
{
Console.WriteLine("{0} x {1} = {2}",
i, number, i * number);
}
}

7.2 Szmolgp
Ksztsnk egy egyszer szmolgpet! A program indtsakor krjen be kt szmot
s egy mveleti jelet, majd rja ki az eredmnyt.
Ezutn bvtsk ki a programot, hogy a kt szmot illetve a mveleti jelet parancssori
paramterknt is megadhassuk (ekkor nincs kln mveletvlaszt men, hanem
rjuk ki rgtn az eredmnyt):
main.exe 12 23 +
(az eredmny pedig 35 lesz).

Megolds (7/Calculator.cs)
Most is kt rszbl ll a programunk, elszr hozz kell jutnunk a szmokhoz s az
opertorhoz, majd elvgezzk a megfelel mveletet s kirjuk az eredmnyt.
Ezttal is a szksges vltozk deklarcijval kezdjk a programrst, hrom darab
kell, kt numerikus (legyen most int) s egy karaktertpus. Ezutn bekrjk a
felhasznltl a szksges adatokat, vagy pedig felhasznljuk a paramtereket. Ez
utbbi esetben vgezznk egy kis hibaellenrzst, vizsgljuk meg, hogy pontosan
hrom paramtert kaptunke!

- 55 -

A forrskd eddig gy nz ki:


using System;
class Program
{
static public void Main(string[] args)
{
int x, y;
char op;
if(args.Length == 0)
{
Console.WriteLine("Az els szm: ");
x = int.Parse(Console.ReadLine());
Console.WriteLine("A msodik szm: ");
y = int.Parse(Console.ReadLine());
Console.WriteLine("A mvelet(+, -, *, /): ");
op = Convert.ToChar(Console.Read());
}
else
{
if(args.Length != 3)
{
Console.WriteLine("Tl sok/kevs paramter!");
return;
}
else
{
x = int.Parse(args[0]);
y = int.Parse(args[1]);
op = Convert.ToChar(args[2]);
}
}

Az opertor megszerzshez egyrszt a Console.Read metdust hasznltuk (mivel


csak egyetlen karakterre van szksg), msrszt ennek a metdusnak a visszatrsi
rtkt amely egy egsz szm t kellett konvertlnunk karaktertpuss, ehhez a
Convert.ToChar metdus nyjtott segtsget.
Most mr nagyon egyszer dolgunk van, mindssze ki kell szmolnunk az
eredmnyt. Ezt nylvn a beolvasott opertor alapjn fogjuk megtenni, ebben a
helyzetben pedig a legkzenfekvbb, ha a switch szerkezetet hasznljuk:

- 56 -

int result=0;
switch(op)
{
case '+':
result
break;
case '-':
result
break;
case '*':
result
break;
case '/':
result
break;
}

= x + y;
= x - y;
= x * y;
= x / y;

Console.WriteLine("A mvelet eredmnye: {0}", result);

Amire figyelni kell az az, hogy a result vltoz kapjon kezdrtket, ellenkez
esetben ugyanis nem fordul le a program (uninitialized variable kezdet hibazenetet
kapunk). Ez azrt van gy, mert a vltoz-deklarci s az utols sorban lev kirats
kztt nem biztos, hogy megtrtnik a vltoz-definci.
Ugyan az rtktpusok bizonyos helyzetekben automatikusan nulla rtket kapnak,
de ez nem minden esetben igaz s loklis vltozk esetn pp ez a helyzet. Ezrt
minden olyan esetben, amikor egy loklis vltoz deklarcija s defincija kztt
hasznlni akarjuk a vltozt, akkor hibazenetet fogunk kapni.
A fenti esetben megoldst jelenthet az is, ha bevezetnk a switch szerkezetben egy
default cmkt, amivel minden esetben megtrtnik - valamilyen formban az
rtkads.

7.3 K Papr Oll


Ksztsnk k-papr-oll jtkot! Ebben az esetben is hasznljuk a vletlenszmgenertort. A jtk folyamatos legyen, vagyis addig tart, amg a felhasznl kilp
(neheztskppen lehet egy karakter, amelyre a jtk vgetr). Tartsuk nylvn a
jtk llst s minden fordul utn rjuk ki az aktulis eredmnyt.
Megolds (7/SPS.cs)
A programunk lnyegben hrom rszbl fog llni: elsknt megkrdezzk a
felhasznltl, hogy mit vlasztott, majd sorsolunk a gpnek is valamit, vgl pedig
kirtkeljk az eredmnyt.
Els dolgunk legyen, hogy deklarlunk t darab vltozt, egy Random objektumot,
kt stringet (ezekben troljuk, hogy mit vlasztottak a versenyzk), s kt byte
tpust (ezekben pedig az eredmnyeket tartjuk szmon, itt elg lesz a byte is, mert
feltesszk, hogy senki nem fog tbb szzszor jtszani egyms utn).
Szksgnk lesz mg egy logikai tpusra is, ezzel fogjuk jelezni a programnak, hogy
akarunk e mg jtszani.

- 57 -

Ksztsk el a program vzt a vltoz-deklarcikkal, illetve a fciklussal (a


ciklustrzsben krdezznk r, hogy akarunk e mg jtszani):
using System;
class Program
{
static public void Main()
{
Random r = new Random();
string compChoice = "";
string playerChoice = "";
int compScore = 0;
int playerScore = 0;
bool l = true;
do
{
Console.WriteLine("Akarsz mg jtszani? i/n");
if(Console.ReadKey(true).KeyChar == 'n'){ l = false; }
}while(l);
}

A Console.ReadKey metdussal egyetlen karaktert olvasunk be a standard


bemenetrl, de ezt az adatot nem hasznlhatjuk fel azonnal, mivel nem char tpust
hanem ConsoleKeyInfo objektumot ad vissza. Ez utbbi KeyChar tulajdonsgval
kapjuk vissza a bert karaktert. A ReadKey most kapott egy paramtert is, amellyel
azt jeleztk, hogy az adott karakter ne jelenjen meg a konzolon.
Most krjk be az adatokat:
Console.WriteLine("Mit vlasztasz? (k/p/o)");
switch(Console.ReadKey(true).KeyChar)
{
case 'k':
playerChoice = "k";
break;
case 'p':
playerChoice = "papr";
break;
case 'o':
playerChoice = "oll";
break;
}
switch(r.Next(0,3))
{
case 0:
compChoice = "k";
break;
case 1:
compChoice = "papr";
break;
case 2:
compChoice = "oll";
break;
}

- 58 -

Ez a kdrszlet termszetesen a ciklustrzsben a krds el kerl. Az egyes


lehetsgeket a k/p/o billentykre lltottuk.
Mr csak az eredmny kirtkelse van htra:
if((playerChoice == "k" && compChoice == "papr")
||
(playerChoice == "papr" && compChoice == "oll")
||
(playerChoice == "oll" && compChoice == "k"))
{
Console.WriteLine("Vesztettl! Az lls:\nSzmtgp:
{0}\nJtkos:{1}",
++compScore, playerScore);
}
elseif(playerChoice == compChoice)
{
Console.WriteLine("Dntetlen! Az lls:\nSzmtgp:
{0}\nJtkos:{1}",
compScore, playerScore);
}
else
{
Console.WriteLine("Nyertl! Az lls:\nSzmtgp: {0}\nJtkos:{1}",
compScore, ++playerScore);
}

rdekesebb megolds bitmveletekkel elkszteni a kirtkelst, ehhez egy kis


segtsg: jelljk a hrom lehetsget (k/p/o) a kvetkezkppen: 100, 010, 001.
Nyilvnval, hogy egy bitenknti VAGY mvelettel ellenrizni tudjuk a dntetlen
eredmnyt mivel ekkor 100 | 100 = 100, vagyis ugyanazt kapjuk vissza. Ha az
eredmny nem dntetlen, akkor a VAGY hromfle eredmnyt adhat vissza (KP, KO,
OP): 110, 101 s 011, amelyeket pl. egy switch szerkezettel dolgozhatunk fel.

7.4 Szmkitall jtk


Ksztsnk szmkitall jtkot, amely lehetsget ad kivlasztani, hogy a
felhasznl prblja kitallni a program ltal kisorsolt szmot, vagy fordtva. A kitallt
szm legyen 1 s 100 kztt. t prblkozsa lehet a jtkosnak, minden tipp utn
rjuk ki, hogy a tippelt szm nagyobb, vagy kisebb-e, mint a kitallt szm.
Ha a gpen van a sor, akkor hasznljunk vletlenszm-genertort a szm
ltrehozsra. A gpi jtkos gy tallja ki a szmot, hogy mindig felezi az
intervallumot (pl.elszr 50et tippel, ha a kitallt szm nagyobb, akkor 75 jn, s gy
tovbb).
A felhasznltl a jtk vgn krdezzk meg, hogy akare ismt jtszani.
Megolds (7/Number.cs)
Ennek a feladatnak a legnagyobb kihvsa, hogy olyan programszerkezetet rakjunk
ssze, amely tlthat s knnyen mdosthat. Legyen az alaptlet az, hogy
elgazsokat hasznlunk. Nzzk meg gy a program vzt:

- 59 -

static public void Main()


{
// Itt kivlasztjuk, hogy ki vlaszt szmot
if(/* A jtkos vlaszt */)
{
// A szmtgp megprblja kitallni a szmot
}
else// A szmtgp vlaszt
{
// A jtkos megprblja kitallni a szmot
}
}

// Megkrdezzk a jtkost, hogy akar-e mg jtszani

Nem nz ki rosszul, de a problma az, hogy a kt felttel blokkja nagyon el fog


hzni, emiatt pedig kevsb lesz olvashat a forrskd. Ez persze nem olyan nagy
baj, de ahhoz elg, hogy valami mst prbljunk ki.
A procedurlis s alacsonyszint nyelvek az ilyen feladatokat ugr utastsokkal
oldjk meg, vagyis a forrskdban elhelyezett cmkk kztt ugrlnak
(tulajdonkppen a magas szint nyelveknl is ez trtnik, csak ezt mi nem ltjuk
mivel az if/switch/stb. elfedi ellnk). Ez a mdszer a magas szint nyelvek esetn
nem igazn ajnlott (fleg, mert megvannak az eszkzk az ugrls kikerlsre), de
jelenleg nem is hasznljuk ki teljesen a nyelv adta lehetsgeket, ezrt most szabad
rosszalkodnunk. rjuk t a fenti vzat egy kicsit:
using System;
class Program
{
static public void Main()
{
START:
Console.WriteLine("Vlassz jtkmdot!");
Console.WriteLine("1 - Te gondolsz egy szmra");
Console.WriteLine("2 - A szmtgp gondol egy szmra");
switch(Console.ReadKey(true).KeyChar)
{
case '1': goto PLAYER;
case '2': goto COMPUTER;
}
PLAYER: goto END;
COMPUTER: goto END;
END:
Console.WriteLine("\nAkarsz mg jtszani? i/n");

switch(Console.ReadKey(true).KeyChar)
{
case 'i': goto START;
case 'n': break;
}

- 60 -

Kzben ki is egsztettk a kdot egy kicsit, lehet vele ksrletezni, amg el nem
ksztjk a lnyeget. Jl lthat, hogy a cmkkkel kellemesen olvashatv s
rthetv vlt a kd (persze ennl nagyobb terjedelm forrsnl mr problmsabb
lehet).
Mr elksztettk a programrszt, amely megkrdezi a jtkost, hogy szeretne-e mg
jtszani. Egy apr hibja van, mgpedig az, hogy ha i vagy n helyett ms billentyt
nyomunk le, akkor a program vgetr. Ezt knnyen kijavthatjuk, ha egy kicsit
gondolkodunk. Nylvn a default cmkt kell hasznlni s ott egy ugr utastssal a
vezrlst a megfelel helyre tenni:
END:
Console.WriteLine("\nAkarsz mg jtszani? i/n");
switch(Console.ReadKey(true).KeyChar)
{
case 'i': goto START;
case 'n': break;
default: goto END;
}

Most kvetkezik a program lnyege: a jtk elksztse. Elsknt azt a szitucit


implementljuk, amikor a jtkos prblja kitallni a szmot, mivel ez az egyszerbb.
Szksgnk lesz termszetesen vltozkra is, de rdemes tgondolni, hogy gy
vegyk fel ket, hogy mindkt programrsz hasznlhassa ket. Ami biztosan mindkt
esetben kell az a vletlenszmgenertor, valamint kt int tpus vltoz az egyikben
a jtkos s a szmtgp tippjeit troljuk, a msik pedig a ciklusvltoz lesz, neki
adjunk azonnal nulla rtket. Ezt a kettt deklarljuk egyelre, rgtn a START
cmke eltt. Ksztsk is el a programot, nem lesz nehz dolgunk, mindssze egy
ciklusra lesz szksgnk:
COMPUTER:
int number = r.Next(100);
i = 0;
while(i < 5)
{
Console.WriteLine("\nA tipped: ");
x = int.Parse(Console.ReadLine());
if(x < number)
{
Console.WriteLine("A szm ennl nagyobb!");
}
elseif(x > number)
{
Console.WriteLine("A szm ennl kisebb!");
}
else
{
Console.WriteLine("Nyertl!");
goto END;
}
++i;
}
Console.WriteLine("\nVesztettl, a szm {0} volt.", number);
goto END;

- 61 -

Ennek megrtse nem okozhat gondot, lphetnk a kvetkez llomsra, ami


viszont kicsit nehezebb lesz. Ahhoz, hogy megfelel stratgit ksztsnk a
szmtgp szmra, magunknak is tisztban kell lennnk azzal, hogy hogyan lehet
megnyerni ezt a jtkot. A legltalnosabb mdszer, hogy mindig felezzk az
intervallumot, gy az utols tippre mr elg szk lesz az a szmhalmaz, amibl
vlaszthatunk (persze gy egy kicsit szerencsejtk is lesz).
Nzznk meg egy pldt: a gondolt szm legyen a 87 s tudjuk, hogy a szm egy s
szz kztt van. Az els tippnk 50 lesz, amire termszetesen azt a vlaszt kapjuk,
hogy a szm ennl nagyobb. Mr csak 50 lehetsges szm maradt, ismt feleznk, a
kvetkez tippnk gy a 75 lesz. Ismt azt kapjuk vissza, hogy ez nem elg. Ismt
feleznk, mghozz maradk nlkl, vagyis tizenkettt adunk hozz a hetventhz
s gy ki is talltuk a gondolt szmot.
Most mr knnyen fel tudjuk rni, hogy mit kell tennie a szmtgpnek: az els ngy
ksrletnl felezzk az intervallumot, az utols krben pedig tippelnk. Nzzk a ksz
kdot:
PLAYER:
Console.WriteLine("Gondolj egy szmra! (1 - 100)");
Console.ReadLine();
x = 50;
int min = 0;
int max = 100;
while(i < 5)
{
Console.WriteLine("A szmtgp szerint a szm {0}",x);
Console.WriteLine("Szerinted? (k/n/e)");
switch(Console.ReadKey(true).KeyChar)
{
case 'k':
if(i == 3){ x = r.Next(min, x); }
else
{
max = x;
x -= (max - min) / 2;
}
break;
case 'n':
if(i == 3){ x = r.Next(x + 1,max); }
else
{
min = x;
x += (max - min) / 2;
}
break;
case 'e':
Console.WriteLine("A szmtgp nyert!");
goto END;
}
++i;
}
Console.WriteLine("A szmtgp nem tudta kitallni a szmot. ");
goto END;

- 62 -

A min illetve max vltozkkal tartjuk szmon az intervallum als, illetve fels hatrt.
Az x vltozban troljuk az aktulis tippet, neki meg is adtuk a kezdrtket.
Egy pldn keresztl nzzk meg, hogy hogyan mkdik a kdunk. Legyen a
gondolt szm ismt 87. A szmtgp els tippje 50, mi erre azt mondjuk, hogy a
szm ennl nagyobb, ezrt a switch n ga fog beindulni. Az intervallum als hatra
ezutn x (vagyis 50) lesz, mivel tudjuk, hogy a szm ennl biztosan nagyobb. A fels
hatr nylvn nem vltozik, mr csak az j xet kell kiszmolni, vagyis xhez hozz
kell adni a fels s als hatrok klnbsgnek a felt: (100 50) / 2 = 25 (ellenkez
esetben pedig nylvn le kellene vonni ugyanezt).
Amirl mg nem beszltnk az az a felttel, amelyben x egyenlsgt vizsgljuk
hrommal (vagyis azt akarjuk tudni, hogy eljtt e az utols tipp ideje). Ez az
elgazs fogja visszaadni az utols tippet a vletlenszm-genertorral a megfelelen
leszktett intervallumban.

- 63 -

Tpuskonverzik

Azt mr tudjuk, hogy az egyes tpusok msknt jelennek meg a memriban,


azonban gyakran kerlnk olyan helyzetbe, hogy egy adott tpusnak gy kellene
viselkednie, mint egy msiknak. Ilyen helyzetekben tpuskonverzit (vagy castolst)
kell elvgeznnk.
Ktflekppen konvertlhatunk: implicit s explicit mdon. Az elbbi esetben nem
kell semmit tennnk, a fordt krs nlkl elvgzi helyettnk. Implicit konverzi
ltalban hasonl tpusokon mkdik, szinte minden esetben a szkebb tpusrl a
tgabbra:
int x = 10;
long y = x; // y == 10, implicit konverzi

Ebben az esetben a long s int mindketten egsz numerikus tpusok, s mivel a long
tgabb (az int 32 bites mg a long 64 bites egsz szm), ezrt a konverzi gond
nlkl vgbe megy. Egy implicit konverzi minden esetben sikeres s nem jr
adatvesztssel.
Egy explicit konverzi nem felttlenl fog mkdni, s ha mgis akkor adatveszts is
fellphet. Vegyk a kvetkez pldt:
int x = 300;
byte y = (byte)x; // explicit konverzi, y == ??

A byte szkebb tpus mint az int (8 illetve 32 bitesek), ezrt explicit konverzit
hajtottunk vgre, ezt a vltoz eltti zrjelbe rt tpussal jelltk. Ha lehetsges a
konverzi, akkor vgbemegy, egybknt a fordt figyelmeztetni fog.
Vajon mennyi most az y vltoz rtke? A vlasz elsre meglep lehet: 44. A
magyarzat: a 300 egy kilenc biten felrhat szm (100101100), persze az int
kapacitsa ennl nagyobb, de most csak a hasznos rszre van szksg. A byte
viszont (ahogy a nevben is benne van) egy nyolcbites rtket trolhat (vagyis a
maximum rtke 255 lehet), ezrt a 300nak csak az els 8 bitjt (00101100)
adhatjuk t ynak, ami pont 44.

8.1 Ellenrztt konverzik


A programfejleszts alatt hasznos lehet tudnunk, hogy minden konverzi gond nlkl
lezajlott-e vagy sem. Ennek ellenrzsre n. ellenrztt konverzit fogunk
hasznlni, amely kivtelt dob (errl hamarosan), ha a forrs nem fr el a
clvltozban:
checked
{
int x = 300;
byte y = (byte)x;
}

- 64 -

Ez a kifejezs kivtelt (System.OverflowException) fog dobni (ha elindtjuk a


lefordtott programot hibazenet fogad majd). Figyeljnk arra, hogy ilyen esetekben
csak a blokkon bell deklarlt, statikus s tagvltozkat vizsglhatjuk.
Elfordul, hogy csak egy-egy konverzit szeretnnk vizsglni, amihez nincs szksg
egy egsz blokkra:
int x = 300;
byte y = checked((byte)x);

Az ellenrzs kikapcsolst is megtehetjk az unchecked hasznlatval:


int x = 300;
byte y = unchecked((byte)x);

Az ajnls szerint ellenrztt konverzikat csak a fejleszts ideje alatt (tesztelsre)


hasznljunk, mivel nmi teljestmnyvesztssel jr.

8.2 Is s as
Az is opertort futsidej tpus-lekrdezsre hasznljuk:
using System;
public class Program
{
static public void Main()
{
int x = 10;

if(x is int) // ha x egy int


{
Console.WriteLine("x tpusa int");
}

Ez a program lefordul, de figyelmeztetst kapunk, mivel a fordt felismeri, hogy a


felttel mindig igaz lesz.
Ennek az opertornak a leggyakoribb felhasznlsi
megvalsts lekrdezse (errl ksbb).

terlete

az

interfsz-

Prja az as az ellenrzs mellett egy explicit tpuskonverzit is vgrehajt. Ezzel az


opertorral csakis referencia-tpusra konvertlhatunk, rtktpusra nem (ekkor le sem
fordul a program).

- 65 -

Nzznk egy pldt:


using System;
public class Program
{
static public void Main()
{
object a = "123";
object b = "Hello";
object c = 10;
string aa = a as string;
Console.WriteLine(aa == null ? "NULL" : aa); // 123
string bb = b as string;
Console.WriteLine(bb == null ? "NULL" : bb); // Hello
string cc = c as string;
Console.WriteLine(cc == null ? "NULL" : cc); // NULL
}
}

Amennyiben ez a konverzi nem hajthat vgre, a clvltozhoz null rtk rendeldik


(ezrt is van a referencia-tpusokhoz korltozva ez az opertor).

8.3 Karakterkonverzik
A char tpust implicit mdon tudjuk numerikus tpusra konvertlni, ekkor a karakter
unicode rtkt kapjuk vissza:
using System;
class Program
{
static public void Main()
{
for(char ch = 'a';ch <= 'z';++ch)
{
Console.WriteLine((int)ch);
}
}
}

Erre a kimeneten a kvetkez szmok jelennek meg: 97, 98, 99, 100, ...
A kis a bet hexadecimlis unicode szma 0061h, ami a 97 decimlis szmnak felel
meg, teht a konverzi a tzes szmrendszerbeli rtket adja vissza.

- 66 -

Tmbk

Gyakran van szksgnk arra, hogy tbb azonos tpus objektumot troljunk el,
ilyenkor knyelmetlen lenne mindegyiknek kln vltozt foglalnunk (kpzeljnk el 30
darab int tpus vltozt, mg lerni is egy rkkvalsg lenne), de ezt nem is kell
megtennnk, hiszen rendelkezsnkre ll a tmb adatszerkezet.
A tmb meghatrozott szm, azonos tpus elemek halmaza. Minden elemre
egyrtelmen mutat egy index (egsz szm). A tmbk referencia-tpusok. A C#
mindig folytonos memriablokkokban helyezi el egy tmb elemeit.
Tmbt a kvetkezkppen deklarlhatunk:
int[] array = new int[10];

Ez a tmb tz darab int tpus elem trolsra alkalmas. A tmb deklarcija utn az
egyes indexeken lv elemek automatikusan a megfelel nullrtkre inicializldnak
(ebben az esetben 10 darab nullt fog tartalmazni a tmbnk). Ez a szably
referencia-tpusoknl kiss mshogy mkdik, mivel ekkor a tmbelemek null -ra
inicializldnak. Ez nagy klnbsg, mivel rtktpusok esetben szimpla nullt
kapnnk vissza az ltalunk nem belltott indexre hivatkozva (vagyis ez egy teljesen
szablyos mvelet), mg referencia-tpusoknl ugyanez NullReferenceException
tpus kivtelt fog generlni.
Az egyes elemekre az indexel opertorral (szgletes zrjelek: [ ]) s az elem
indexvel (sorszmval) hivatkozunk. A szmozs mindig nulltl kezddik, gy a
legutols elem indexe: az elemek szma mnusz egy. A kvetkez pldban
feltltnk egy tmbt vletlen szmokkal s kiratjuk a tartalmt:
using System;
class Program
{
static public void Main()
{
int[] array = new int[10];
Random r = new Random();
for(int i = 0;i < array.Length;++i)
{
array[i] = r.Next();
}

foreach(int item in array)


{
Console.WriteLine(item);
}

A pldban a ciklusfelttel megadsakor a tmb Length nev tulajdonsgt


hasznltuk, amely visszaadja a tmb hosszt. Lthat, az indexel-opertor
hasznlata is, az array[i ]a tmb iedik elemt jelenti. Az indexelssel vigyzni kell,
ugyanis a fordt nem ellenrzi fordtsi idben az indexek helyessgt, viszont
- 67 -

helytelen indexels esetn futs idben IndexOutOfRangeException kivtelt fog


dobni a program.
Egy tmbt akr a deklarci pillanatban is feltlthetnk a neknk megfelel
rtkekkel:
char[] charArray = new char[]{ 'b', 'd', 'a', 'c' };

Ekkor az elemek szmt a fordt fogja meghatrozni. Az elemek szmt a


deklarcival azonnal meghatrozzuk, ezen a ksbbiekben nem lehet vltoztatni.
Dinamikusan bvthet adatszerkezetekrl a Gyjtemnyek cm fejezet szl.
Minden tmb a System.Array osztlybl szrmazik, ezrt nhny hasznos mvelet
azonnal rendelkezsnkre ll (pl. rendezhetnk egy tmbt a Sort metdussal):
chararray.Sort(); // tmb rendezse

9.1 Tbbdimenzis tmbk


Eddig az n. egydimenzis tmbt (vektort) hasznltuk. Lehetsgnk van azonban
tbbdimenzis tmbk ltrehozsra is, ekkor nem egy indexszel hivatkozunk egy
elemre, hanem annyival, ahny dimenzis a tmb. Vegyk pldul a matematikbl
mr ismert mtrixot:
12, 23, 2
A = [ 13, 67, 52 ]
45, 55, 1
Ez egy ktdimenzis tmbnek (mtrix a neve ki hinn?) felel meg, az egyes
elemekre kt indexxel hivatkozunk, els helyen a sor ll s utna az oszlop. gy a 45
indexe: [2, 0] (ne feledjk, mg mindig nulltl indexelnk).
Multidimenzis tmbt a kvetkez mdon hozunk ltre C# nyelven:
int[,] matrix = new int[3, 3];

Ez itt egy 3x3as mtrix, olyan mint a fent lthat. Itt is sszekthetjk az elemek
megadst a deklarcival, br egy kicsit trkksebb a dolog:
int[,] matrix = new int[,]
{
{12, 23, 2},
{13, 67, 52},
{45, 55, 1}
};

Ez a mtrix pontosan olyan, mint amit fnt lertunk. Az elemszm most is


meghatrozott, nem vltoztathat.

- 68 -

Nylvn nem akarjuk mindig kzzel feltlteni a tmbket, viszont ezttal nem olyan
egyszer a dolgunk, hiszen egy ciklus biztosan nem lesz elg ehhez, vagyis
gondolkodnunk kell: az index els tagja a sort, a msodik az oszlopot adja meg,
pontosabban az adott sorban elfoglalt indext. Ez alapjn pedig j tletnek tnik, ha
egyszerre csak egy dologgal foglalkozunk, azaz szpen vgig kell valahogyan
mennnk minden soron egyesvel. Erre a megoldst az n. egymsba gyazott
ciklusok jelentik: a kls ciklus a sorokon megy t, a bels pedig a sorok elemein:
using System;
class Program
{
static public void Main()
{
int[,] matrix = new int[3, 3];
Random r = new Random();
// sorok
for(int i = 0;i < matrix.GetLength(0);++i)
{
// oszlopok
for(int j = 0;j < matrix.GetLength(1);++j)
{
matrix[i, j] = r.Next();
}
}
}

Most nem rjuk ki a szmokat, ez nem okozhat gondot. A tmbk GetLength


metdusa a paramterknt megadott dimenzi hosszt adja vissza (nulltl
szmozva), teht a pldban az els esetben a sor, a msodikban az oszlop hosszt
adjuk meg a ciklusfelttelben.
A tbbdimenzis tmbk egy varinsa az n. egyenetlen (jagged) tmb. Ekkor
legalbb egy dimenzi hosszt meg kell adnunk, ez konstans marad viszont a bels
tmbk hossza tetszs szerint megadhat:
int[][] jarray = new int[3][];

Ksztettnk egy hrom sorral rendelkez tmbt, azonban a sorok hosszt (az
egyes sorok nylvn nll vektorok) rrnk ksbb megadni, s nem kell
ugyanolyan hossznak lennik.

- 69 -

using System;
class Program
{
static public void Main()
{
int[][] jarray = new int[3][];
Random r = new Random();
for(int i = 0;i < 3;++i)
{
jarray[i] = new int[r.Next(1, 5)];
for(int j = 0;j < jarray[i].Length;++j)
{
jarray[i][j] = i + j;
}
}
}

Vletlenszm-genertorral adjuk meg a bels tmbk hosszt, persze rtelmes


kereteken bell. A bels ciklusban jl lthat, hogy a tmb elemei valban tmbk,
hiszen hasznltuk a Length tulajdonsgot (persze a hagyomnyos tbbdimenzis
tmbk esetben is ez a helyzet, de ott nem lenne rtelme kln elrhetv tenni az
egyes sorokat).
Az inicializls a kvetkezkppen alakul ebben az esetben:
int[][] jarray = new int[][]
{
newint[]{ 1, 2, 3, 4, 5 },
newint[]{ 1, 2, 3 },
newint[]{ 1 }
};

- 70 -

10

Stringek

A C# beptett karaktertpusa (char) egy unicode karaktert kpes trolni kt byteon. A


szintn beptett string tpus ilyen karakterekbl ll (teht az egyes betket charknt
kezelhetjk).
using System;
class Program
{
static public void Main()
{
string s = "ezegystring";
Console.WriteLine(s);
}
}

A ltszat ellenre a string referencia-tpus, viszont nem ktelez hasznlnunk a new


opertort.
Egy string egyes betire az indexel opertorral hivatkozhatunk (vagyis minden
stringet kezelhetnk tmbknt is):
using System;
class Program
{
static public void Main()
{
string s = "ezegystring";
Console.WriteLine(s[0]); // e
}
}

Ekkor a visszaadott objektum tpusa char lesz. A foreach ciklussal indexel opertor
nlkl is vgigiterlhatunk a karaktersorozaton:
foreach(char ch in s)
{
Console.WriteLine(ch);
}

Az indexel opertort nem csak vltozkon, de nyers szvegen is alkalmazhatjuk:


Console.WriteLine("ezegystring"[4]); // y

Ilyenkor egy nvtelen vltozt kszt a fordt s azt hasznlja.


Nagyon fontos tudni, hogy mikor egy ltez string objektumnak j rtket adunk,
akkor nem az eredeti pldny mdosul, hanem egy teljesen j objektum keletkezik a
memriban (vagyis a string n. immutable megvltoztathatatlan tpus). Ez a
viselkeds fleg akkor okozhat (teljestmny)problmt, ha sokszor van szksgnk
erre a mveletre.
- 71 -

10.1 Metdusok
A .NET szmos hasznos metdust biztost a stringek hatkony kezelshez. Most
megvizsglunk nhnyat, de tudni kell, hogy a metdusoknak szmos vltozata
lehet, itt most a leggyakrabban hasznltakat nzzk meg:
sszehasonlts:
using System;
class Program
{
static public void Main()
{
string a = "egyik";
string b = "msik";
int x = String.Compare(a, b);
if(x == 0)
{
Console.WriteLine("A kt string egyenl");
}
else if(x < 0)
{
Console.WriteLine("Az 'a' a kisebb");
}
else
{
Console.WriteLine("A 'b' a kisebb");
}
}

A String.Compare metdus nullt ad vissza, ha a kt string egyenl, s nullnl


kisebbet/nagyobbat, ha nem (pontosabban, ha lexikografikusan lnyegben
bcsorrend szerint kisebb/nagyobb).
Keress:
using System;
class Program
{
static public void Main()
{
string s = "verylonglongstring";
char[] chs = new char[]{ 'y', 'z', 'o' };
Console.WriteLine(s.IndexOf('r')); // 2
Console.WriteLine(s.IndexOfAny(chs)); // 3
Console.WriteLine(s.LastIndexOf('n')); // 16
Console.WriteLine(s.LastIndexOfAny(chs)); // 9
Console.WriteLine(s.Contains("long")); // true
}

- 72 -

Az IndexOf s LastIndexOf metdusok egy string vagy karakter els illetve utols
elfordulsi indext (stringek esetn a kezdindexet) adjk vissza. Ha nincs tallat,
akkor a visszaadott rtk -1 lesz. A kt metdus Anyre vgzd vltozata egy
karaktertmbt fogad paramtereknt s az abban tallhat sszes karaktert
prblja megtallni. A Contains igaz rtkkel tr vissza, ha a paramtereknt
megadott karakter(sorozat) benne van a stringben.
Mdosts:
using System;
class Program
{
static public void Main()
{
string s = "smallstring";
char[] chs = new char[]{ 's', 'g' };
Console.WriteLine(s.Replace('s', 'l')); // lmallltring
Console.WriteLine(s.Trim(chs)); // mallstrin
Console.WriteLine(s.Insert(0, "one")); // onesmallstring
Console.WriteLine(s.Remove(0, 2)); // allstring
Console.WriteLine(s.Substring(0, 3)); // sma
Console.WriteLine(s.ToUpper()); // SMALLSTRING
Console.WriteLine(s.ToLower()); // smallstring
}
}

A Replace metdus az els paramternek megfelel karaktereket lecserli a


msodik paramterre. A Trim string elejn s vgn lv karaktereket vgja le, a
Substring pedig kivg egy karaktersorozatot, paramterei a kezd s vgindexek
(van egyparamteres vltozata is, ekkor a csak a kezdindexet adjuk meg s a
vgig megy). Az Insert/Remove metdusok hozzadnak, illetve elvesznek a
stringbl (a megadott indexeken). Vgl a ToLower s ToUpper metdusok kisilletve nagybetss alaktjk az eredeti stringet.
Fontos megjegyezni, hogy ezek a metdusok soha nem az eredeti stringen vgzik a
mdostsokat, hanem egy j pldnyt hoznak ltre, s azt adjk vissza.

10.2 StringBuilder
Azt mr tudjuk, hogy amikor mdostunk egy stringet, akkor automatikusan egy j
pldny jn ltre a memriban, ez pedig nem felttlenl olcs mvelet.
Ha sokszor (legalbb 10+) van szksgnk erre, akkor hasznljuk inkbb a
StringBuilder tpust, ez automatikusan lefoglal egy nagyobb darab memrit, s ha
ez sem elg, akkor allokl egy megfelel mret terletet s tmsolja magt oda. A
StringBuilder a System.Text nvtrben tallhat.

- 73 -

Plda a StringBuilder hasznlatra:


using System;
using System.Text;
class Program
{
static public void Main(string[] args)
{
StringBuilder sb = new StringBuilder(50);
for(char ch = 'a';ch <= 'z';++ch)
{
sb.Append(ch);
}
Console.WriteLine(sb);
}

A StringBuilder fenti konstruktora (van tbb is) helyet foglal tven karakter szmra
(ltezik alaprtelmezett konstruktora is, ekkor tizenhat karakternek foglal helyet). Az
Append metdussal tudunk karaktereket (vagy egsz stringeket) hozzfzni.

10.3 Regulris kifejezsek


Regulris kifejezsek segtsgvel vizsglhatjuk, hogy egy karaktersorozat megfelele egy adott mintnak. Nzznk is egy pldt: ksztsnk olyan regulris kifejezst,
amely termszetes szmokra illik! A pldban a nullt nem soroljuk a termszetes
szmok kz, vagyis a minta a kvetkez lesz: az els szmjegy egy s kilenc
kztti lesz, ezutn pedig brmennyi nulla s kilenc kztti szmjegy jhet. A
forrskd:
using System;
using System.Text.RegularExpressions; // ez kell
class Program
{
static public void Main()
{
Regex pattern = new Regex("^[1-9][0-9]*");
string s1 = "012345";
string s2 = "112345";
string s3 = "2";
Console.WriteLine("{0} : {1}", s1, (pattern.IsMatch(s1) ?
"termszetes szm" : "nem termszetes szm"));
Console.WriteLine("{0} : {1}", s2, (pattern.IsMatch(s2) ?
"termszetes szm" : "nem termszetes szm"));
Console.WriteLine("{0} : {1}", s3, (pattern.IsMatch(s3) ?
"termszetes szm" : "nem termszetes szm"));
}

- 74 -

A regulris kifejezst egy Regex pldnynak adjuk t, lssuk, hogy hogyan pl fel:
^: ezzel a karakterrel (Alt Gr + 3) megmondjuk, hogy a mintaillesztst a string elejtl
kell kezdeni.
[1-9]: a string elejn egy darab numerikus karakter ll kivve a nullt.
[0-9]*: a csillag(*) nulla vagy tbb elfordulst jelent, vagyis az els szmjegy utn
nulla vagy tbb nulla s kilenc kztti szm llhat.
A programunk a kvetkez kimenetet produklja:
012345 : nem termszetes szm
112345 : termszetes szm
2 : termszetes szm
A kvetkez pldnk egy kicsit bonyolultabb lesz a regulris kifejezs pedig mr-mr
ijeszt: kpzeljk el, hogy a vilgon uniformizljk a telefonszmokat, a kvetkez
mdon: minden szm az orszg elhvjval kezddik amelyet egy + jel elz meg,
az elhvszm kt vagy hrom szmjegybl ll, az els szm nem lehet nulla.
Ezutn egy szkz jn, amelyet a krzetszm kvet, ami szintn kt vagy hrom
szmjegy az els helyen itt sem llhat nulla. Vgl kvetkezik a telefonszm, ami a
krzetszmot szkzzel elvlasztva kveti. Minden telefonszm hrom darab hrom
szmjegybl ll blokkbl ll ezeket ktjel vlasztja el. Egy plda:
+36 30 123-456-789
Lssuk a forrskdot:
string s = @"^(\+)[1-9][0-9]{1,2}\s[1-9][0-9]{1,2}\s(\d{3}(-)){2}\d{3}$";
Regex pattern = new Regex(s);
string s1 = "+36 30 661-345-612";
string s2 = "+3630 661-567-233";
string s3 = "+56 30 667-876-987-456";
Console.WriteLine(pattern.IsMatch(s1)); // true
Console.WriteLine(pattern.IsMatch(s2)); // false
Console.WriteLine(pattern.IsMatch(s3)); // false

A regulris kifejezs el ktelezen oda kell tennnk a @ jelet, mivel specilis


karaktereket is tartalmaz (erre a fordt is figyelmeztet).
A kifejezsnk kt rszbl ll:
^(\+)[1-9][0-9]{1,2}\s: ez a minta lesz az els kt helyen s hvatott az
elhvt illetve a krzetszmot ellenrizni: a mr ismert ^-vel az illesztst a
- 75 -

karaktersor elejtl kezdjk, az ezt kvet (\+) pedig megmondja, hogy az els
helyen egy + jel van (ez persze nem lesz ott a krzetszm eltt). A + el \ t kell
tennnk, mivel ennek a jelnek nll jelentse is van regulris kifejezsben. A
zrjelek kz nyers karaktereket (is) rhatunk. Ezutn ismers kvetkezik: az [19][0-9] et mr nem okozhat gondot megrteni az utna kvetkez {1,2} t mr
inkbb. Ezzel azt kzltk, hogy az eltte lv meghatrozs ([0-9]) legalbb egy
legfeljebb kt alkalommal szerepel egyms utn. Vgl a \s egy szkzt jell.
(\d{3}(-)){2}\d{3}$: ez a kifejezs a ktjellel elvlasztott szmblokkokat jelli. A
vgn lv $ jel jelzi, hogy a karaktersorozat vgig kell illesztenie, vagyis az
egszet ellenrizze (msklnben a 345-345-345-456 sorozat rvnyes lenne,
hiszen benne van az amit kerestnk).
Haladjunk tovbbra is a vgrl a {3}rl mr tudjuk, hogy azt jelenti: az eltte lv
kifejezsnek pontosan hromszor kell szerepelnie, ez jelen esetben azt jelenti, hogy
a minta vgn hrom szmjegy ll, amelyet a \dvel (d mint digit) jellnk.
A \d eltt szerepl {2} az egsz zrjelben lv kifejezsre vonatkozik, ahol
megmondtuk, hogy hrom szmjegy utn egy ktjel kvetkezik.
A .NET regulris kifejezsei meglepen sokrtek, rengeteg lehetsgnk van, a
fenti kt plda csak zelt volt. Tovbbi informcit tallhatunk az MSDN megfelel
oldaln:
http://msdn.microsoft.com/en-us/library/az24scfc(v=VS.90).aspx

- 76 -

11

Gyakorl feladatok II.

11.1 Minimum- s maximumkeress


Keressk ki egy tmb legnagyobb, illetve legkisebb elemt s ezek indexeit (ha tbb
ilyen elem van, akkor elg az els elforduls).
Megolds (11/MinMax.cs)
A mininum/maximum-keress a legalapvetbb algoritmusok egyike. Az alapelv
rendkvl egyszer, vgigmegynk a tmb elemein, s minden elemet
sszehasonltunk az aktulis legkisebbel/legnagyobbal. Nzzk is meg a forrskdot
(csak a lnyeg szerepel itt, a tmb feltltse nem okozhat gondot):
int
int
int
int

min = 1000;
max = -1;
minIdx = 0;
maxIdx = 0;

for(int i = 0;i < 30;++i)


{
if(array[i] < min)
{
min = array[i];
minIdx = i;
}
if(array[i] > max)
{
max = array[i];
maxIdx = i;
}
}

A min s max vltozknak kezdrtket is adtunk, ezeket gy kell megvlasztani,


hogy ezek biztosan kisebbek illetve nagyobbak legyenek, mint a tmb elemei
(feltesszk, hogy nullnl kisebb s ezernl nagyobb szm nincs a tmbben).
rtelemszeren, mivel minden elemet ktelezen meg kell vizsglnunk, ez az
algoritmus nem tl gyors nagy elemszm esetn, viszont nagyon jl mkdik
prhuzamos krnyezetben a tmb egyes rszeit kln feldolgozegysg kapja
meg.

11.2 Szigetek
Egy szigetcsoport fltt elreplve bizonyos idkznknt megnztk, hogy pp hol
vagyunk. Ha sziget (szrazfld) fltt, akkor lertunk egy egyest, ha tenger fltt,
akkor nullt.
A programunk ezt az adatot dolgozza fl, amelyet vagy a billentyzetrl vagy ha
van a parancssori paramterbl kap meg. A feladatok:
- 77 -

Adjuk meg a leghosszabb egybefgg szrazfld hosszt.


Adjuk meg, hogy hny szigetet talltunk.
Megolds (11/Islands.cs)

A megoldsban csak az adatok feldolgozst nzzk meg, a beolvassuk nem


okozhat mostanra gondot. A szigeteken vgzett mrseket a data nev, string tpus
vltozban troltuk el.
A kt feladatot egyszerre fogjuk megoldani, mivel a programunk a kvetkez elven
alapul: a data stringen fogunk keresztlmenni egy ciklus segtsgvel. Ha a string
adott indexn egyest tallunk, akkor elindtunk egy msik ciklust, amely attl az
indextl megy egszen addig, amg nullhoz nem r. Ekkor egyrszt megnveljk
eggyel a szigetek szmt trol vltozt, msrszt tudni fogjuk, hogy milyen hossz
volt a sziget s sszehasonlthatjuk az eddigi eredmnyekkel.
Nzzk is meg a forrskdot:
int islandCount = 0;
int maxIslandLength = 0;
int i = 0;
while(i < data.Length)
{
if(data[i] == '1')
{
++islandCount;
int j = i;
int tmp = 0;
while(j < data.Length && data[j] == '1')
{
++j;
++tmp;
}
i = j;
if(tmp > maxIslandLength){ maxIslandLength = tmp; }
}
else
{
++i;
}
}

A kdban kt rdekes dolog is van, az els a bels ciklus felttele: ellenrizzk, hogy
mg a szigetet mrjk s azt is, hogy nem-e rtnk a string vgre. Ami fontos, az a
felttelek sorrendje: mivel tudjuk, hogy a felttelek kirtkelse balrl jobbra halad,
ezrt elszr azt kell vizsglnunk, hogy helyes indexet hasznlunk-e, ellenkez
esetben ugyanis kivtelt kapnnk (hiszen ha a string vge utn vagyunk ott mr
nincs semmi).
A msik rdekessg a kt ciklusvltoz. Amikor befejezzk a bels ciklust, akkor a
kls ciklusvltozt j pozciba kell helyeznnk, mghozz oda, ahol a bels ciklus
abbahagyta a vizsglatot.
- 78 -

11.3 tlaghmrsklet
Az v minden napjn megmrtk az tlaghmrskletet, az eredmnyeket pedig egy
mtrixban troltuk (az egyszersg kedvrt tegyk fel, hogy minden hnap harminc
napos, az eredmnyeket pedig vletlenszm-genertorral (sszer kereteken bell)
sorsoljuk ki).
Keressk meg az v legmelegebb s leghidegebb napjt!
Adjuk meg az v legmelegebb s leghidegebb hnapjt!
Volt e egymst kvet t nap (egy hnapon bell), amikor mnusz fokot
mrtnk?
Megolds (11/Temperature.cs)

Ehhez a feladathoz nem tartozik rsos megolds, tulajdonkppen egy minimum- s


maximum-kivlasztsrl van sz, csak ppen ktdimenzis tmbben (viszont a
jegyzethez csatolva van egy lehetsges megolds).

11.4 Buborkrendezs
Valstsuk meg egy tmbn a buborkrendezst.

Megolds (11/BubbleSort.cs)
A buborkos rendezs egy alapvet rendezsi algoritmus, amelynek alapelve, hogy
a kisebb elemek bubork mdjra felszivrognak, mg a nagyobb elemek
lesllyednek. Ennek az algoritmusnak tbbfle implementcija is ltezik, mi most
kt vltozatt is megvizsgljuk. Az els gy nz ki:
for(int i = 0;i < array.Length - 1;++i)
{
for(int j = array.Length - 1;j > i;--j)
{
if(array[j - 1] > array[j])
{
int tmp = array[j];
array[j] = array[j - 1];
array[j - 1] = tmp;
}
}
}

Kezdjk a bels ciklussal. Ez a tmb vgrl fog visszafel menni s cserlgeti az


elemeket, hogy a legkisebbet vigye tovbb magval. Legyen pl. a tmb utols
nhny eleme:
- 79 -

10 34 5
Fogjuk az 5 t (array[j]) s sszehasonltjuk az eltte lev elemmel, ami a 34
(array[j-1]). Mivel nagyobb nla, ezrt megcserljk a kettt:
10 5 34
Ezutn cskkentjk a ciklusvltozt, ami most megint az eddigi legkisebb elemre az
5 re fog mutatni s cserlhetjk tovbb. Termszetesen, ha kisebb elemet tallunk,
akkor ezutn t fogjuk tovbb vinni, egszen addig, amg a legkisebb elem elfoglalja
a tmb els indext. Itt jn kpbe a kls ciklus, ami azt biztostja, hogy a rendezett
elemeket mr ne vizsgljuk, hiszen a bels ciklus minden futsakor a tmb elejre
tesszk az aktulis legkisebb elemet.
Nzznk meg egy msik megoldst is:
for(int i = 1;i < array.Length;++i)
{
int y = array[i];
int j = i - 1;
while(j > -1 && y < array[j])
{
array[j + 1] = array[j];
--j;
}
array[j + 1] = y;
}

Itt lnyegben ugyanarrl van sz, csak most ellrl vizsgljuk az elemeket.
Nem rt tudni, hogy a buborkos rendezs csak kis elemszm esetben hatkony,
nagyjbl O(n^2) nagysgrend.
Az O() (n. nagy ord) jellst hasznljuk egy algoritmus futsidejnek
megbecslsre (illetve hasznljk a matematika ms terletein is).

- 80 -

12

Objektum-orientlt programozs - elmlet

A korai programozsi nyelvek nem az adatokra, hanem a mveletekre helyeztk a


hangslyt, mert akkoriban mg fleg matematikai szmtsokat vgeztek a
szmtgpekkel. Ahogy aztn a szmtgpek szles krben elterjedtek,
megvltoztak az ignyek, az adatok pedig tl komplexekk vltak ahhoz, hogy a
procedurlis mdszerrel knyelmesen s hatkonyan kezelni lehessen ket.
Az els objektum-orientlt programozsi nyelv a Simula 67 volt. Tervezi (Ole-Johan
Dahl s Kristen Nygaard) hajk viselkedst szimulltk s ekkor jtt az tlet, hogy a
klnbz hajtpusok adatait egy egysgknt kezeljk, gy egyszerstve a munkt.
Az OOP mr nem a mveleteket helyezi a kzppontba, hanem az egyes adatokat
(adatszerkezeteket) s a kzttk lev kapcsolatokat (hierarchit).
Ebben a fejezetben az OOP elmleti oldalval foglalkozunk, a cl a paradigma
megrtse, gyakorlati pldkkal a kvetkez rszekben tallkozhatunk (szintn a
kvetkez rszekben tallhat meg nhny elmleti fogalom is, amelyek gyakorlati
pldkon keresztl rthetbben megfogalmazhatk, ezrt ezek csak ksbb lesznek
trgyalva,pl. polimorfizmus).

12.1 UML
Az OOP tervezs elsegtsre hoztk ltre az UML t (Unified Modelling
Language). Ez egy ltalnos tervezeszkz, a clja, hogy egy minden fejleszt ltal
ismert kzs jelrendszert valstson meg. A kvetkezkben az UML eszkzeit fogjuk
felhasznlni az adatok kztti relcik grafikus brzolshoz.

12.2 Osztly
Az OOP vilgban egy osztly olyan adatok s mveletek sszessge, amellyel
lerhatjuk egy modell (vagy entits) tulajdonsgait s mkdst. Legyen pldul a
modellnk a kutya llatfaj. Egy kutynak vannak tulajdonsgai (pl. letkor, sly, stb.)
s van meghatrozott viselkedse (pl. csvlja a farkt, jtszik, stb.)
Az UML a kvetkezkppen jell egy osztlyt:

Kutya
Amikor programot runk, akkor az adott osztlybl (osztlyokbl) ltre kell hoznunk
egy (vagy tbb) pldnyt, ezt pdnyostsnak nevezzk. Az osztly s pldny
kztti klnbsgre j plda a recept (osztly) s a stemny (pldny).

- 81 -

12.3 Adattag s metdus


Egy objektumnak az letciklusa sorn megvltozhat az llapota, tulajdonsgai. Ezt
az llapotot valahogy el kell tudnunk trolni, illetve biztostani kell a szksges
mveleteket a tulajdonsgok megvltoztatshoz (pl. a kutya eszik (ez egy mvelet),
ekkor megvltozik a jllakottsg tulajdonsga/llapota).
A tulajdonsgokat trol vltozkat adattagoknak (vagy meznek), a mveleteket
metdusoknak nevezzk. A mveletek sszessgt felletnek is hvjuk.
Mdostsuk a diagramunkat:
Kutya
jollak : int
eszik() : void
Az adattagokat nv: tpus alakban brzoljuk, a metdusokat pedig
nv(paramterlista): visszatrsi_rtk formban. Ezekkel a fogalmakkal egy
ksbbi fejezet foglalkozik majd.

12.4 Lthatsg
Az egyes tulajdonsgokat s metdusokat nem felttlenl kell kzszemlre
bocstani. Az OOP egyik alapelve, hogy a felhasznl csak annyi adatot kapjon
meg, amennyi felttlenl szksges. A kutys pldban az eszik() mvelet magba
foglalja a rgst, nyelst, emsztst is, de errl nem fontos tudniuk, csak az evs
tnye szmt.
Ugyangy egy tulajdonsg (adattag) esetben sem j, ha mindenki hozzjuk fr (az
elfogadhat, ha a kzvetlen csald hozzfr a szmlmhoz, de idegenekkel nem
akarom megosztani).
Az s-OOP szablyai hromfle lthatsgot fogalmaznak meg (ez nyelvtl fggen
bvlhet), a C# lthatsgairl a kvetkez rszekben lesz sz.
A hromfle lthatsg:
Public: mindenki lthatja (UML jells: +).
Private: csakis az osztlyon bell elrhet, a leszrmazott osztlyok nem
lthatjk s nem is mdosthatjk (a szrmaztats s rklds hamarosan
jn) (UML jells: -).
Protected: ugyanaz, mint a private, de a leszrmazott osztlyok
mdosthatjk is (UML jells: #).

- 82 -

A Kutya osztly most gy nz ki:


Kutya
-jollak : int
+eszik() : void

12.5 Egysgbezrs
A hagyomnyos, nem OO programnyelvek (pl. a C) az adatokat s a rajtuk
vgezhet mveleteket a program kln rszeiknt kezelik. Bevett szoks ezeket
elklnteni egy nll forrsfileba, de ez mg mindig nem elg biztonsgos. A kett
kztt nincs sszerendels, ezrt ms programozk gond nlkl trhatjk egyiket
vagy msikat, illetve hozzfrnek a struktrkhoz s nem megfelelen hasznlhatjk
fel azokat.
Az OO paradigma egysgbe zrja az adatokat s a hozzjuk tartoz felletet, ez az
n. egysgbezrs (encapsulation vagy information hiding). Ennek egyik nagy
elnye, hogy egy adott osztly bels szerkezett gond nlkl megvltoztathatjuk,
mindssze arra kell figyelni, hogy a fellet ne vltozzon (pl. egy autt biztosan tudunk
kormnyozni, attl fggetlenl, hogy az egy szemlyaut, traktor vagy Forma-1es
gp).

12.6 rklds
Az rklds vagy szrmaztats az j osztlyok ltrehozsnak egy mdja. Egy
(vagy tbb) mr ltez osztlybl hozunk ltre egy jat gy, hogy az minden
szljnek tulajdonsgt rkli vagy tfogalmazza azokat. A legegyszerbben egy
pldn keresztl rthet meg. Legyen egy llat osztlyunk. Ez egy elgg tg
fogalom, ezrt szkthetjk a krt, mondjuk a Gerinces llatokra. Ezen bell
megklnbztethetnk Emls t vagy Hll t. Az Emls osztly egy
leszrmazottja lehet a Kutya s gy tovbb. Az rkldst specializlsnak is
nevezik. A specializls sorn az osztlyok kztt n. az-egy (is-a) relci ll fenn.
gy amikor azt mondjuk, hogy a Kutya az egy llat akkor arra gondolunk, hogy a
Kutya egy specializltabb forma, amelynek megvan a sajt karakterisztikja, de
vgeredmnyben egy llat. Ennek a gondolatmenetnek a gyakorlati felhasznls
sorn lesz jelentsge.

- 83 -

A diagram a fenti pldt brzolja. UML-l az rkldst res nyllal jelljk, amely
a specializlt osztly fell mutat az ltalnosabbra. Az osztlyok kztt fennll
kapcsolatok sszessgt hierarchinak nevezzk.
Elfordul, hogy nem fontos szmunkra a bels szerkezet, csak a felletet szeretnnk
trkteni, hogy az osztlyunkat fel tudja hasznlni a programunk egy msik rsze
(ilyen pldul a mr emltett foreach ciklus). Ilyenkor nem egy igazi osztlyrl,
hanem egy interfszrl felletrl beszlnk, amelynek nincsenek adattagjai,
csakis a mveleteket deklarlja.
A C# rendelkezik nll interfszekkel, de ez nem minden programnyelvre igaz, ezrt
k egy hasonl szerkezetet, n. absztrakt osztlyokat hasznlnak. Ezekben
elfordulnak adattagok is, de leginkbb a fellet definilsra koncentrlnak. A C#
nyelvi szinten tmogat absztrakt osztlyokat is, a kett kztt ugyanis lnyegi
klnbsg van. Az interfszek az osztlytl fggetlenek, csakis felletet biztostanak
(pldul az IEnumerable s IEnumerator a foreachnek (is) biztostanak felletet, az
nem lnyeges, hogy milyen osztlyrl van sz). Az absztrakt osztlyok viszont egy
shz ktik az utdokat (erre az esetre plda az llat osztly, amelyben mondjuk
megadunk egy absztrakt evs metdust, amit az utdok megvalstanak - egy
krokodil nylvn mshogy eszik mint egy hangya, de az evs az llatokhoz kthet
valami, ezrt kzs).

- 84 -

13

Osztlyok

Osztlyt a class kulcssz segtsgvel deklarlhatunk:


using System;
class Dog
{
}
class Program
{
static public void Main(string[] args)
{
}
}

Lthat, hogy a fprogram s a sajt osztlyunk elklnl.


Konvenci szerint az osztlynv mindig nagybetvel kezddik.
Felmerlhet a krds, hogy honnan tudja a fordt, hogy melyik a f osztly? A
helyzet az, hogy a Main egy specilis metdus, ezt a fordt automatikusan felismeri
s megjelli, mint a program belpsi pontjt. Igazbl lehetsges tbb Main nev
metdust is ltrehozni, ekkor a fordtprogramnak meg kell adni (a /main kapcsol
segtsgvel), hogy melyik az igazi belpsi pont. Ettl fggetlenl ezt a megoldst,
ha csak lehet, kerljk el.
Nzznk egy pldt:
using System;
class Program1
{
static public void Main()
{
Console.WriteLine("Program1");
}
}
class Program2
{
static public void Main()
{
Console.WriteLine("Program2");
}
}

Ezt a forrskdot gy fordthajuk le:


csc /main:Program1 main.cs
Futtatskor a Program1 szveget fogja kirni a program.

- 85 -

A fordt tovbbi kapcsolirl tudhatunk meg tbbet, ha parancssorba berjuk, hogy:


csc -help
A fenti pldban a lehet legegyszerbb osztlyt hoztuk ltre. A C++ nyelvet ismerk
figyeljenek arra, hogy az osztly-deklarci vgn nincs pontosvessz.
Az osztlyunkbl a new opertor segtsgvel tudunk kszteni egy pldnyt.
Dog d = new Dog();

A new hvsakor lefut a konstruktor, megfelel nagysg hely foglaldik a


memriban, ezutn pedig megtrtnik az adattagok inicializlsa is.

13.1 Konstruktorok
Minden esetben amikor pldnyostunk egy specilis metdus a konstruktor fut le,
melynek feladata, hogy belltsa az osztly rtkeit. Br a fenti osztlyunkban nem
definiltunk semmi ilyesmit ettl fggetlenl rendelkezik alaprtelmezett (azaz
paramter nlkli) konstruktorral. Ez igaz minden olyan osztlyra, amelynek nincs
konstruktora (amennyiben brmilyen konstruktort ltrehoztunk, akkor ez a lehetsg
megsznik).
Az alaprtelmezett konstruktor legelszr meghvja a sajt sosztlya
alaprtelmezett konstruktort. Ha nem szrmaztattunk direkt mdon (mint a fenti
programban), akkor ez a System.Object konstruktora lesz (tulajdonkppen ez elbb
vagy utbb mindenkppen meghvdik, hiszen az sosztly konstruktora is meghvja
a sajt st s gy tovbb).
Abban az esetben, ha az sosztly nem tartalmaz alaprtelmezett konstruktort (mert
van neki paramteres), akkor valamely msik konstruktort explicit mdon hvni kell a
leszrmazott osztly konstruktorbl a base metdussal, minden ms esetben
fordtsi hiba az eredmny.
class Base
{
public Base(string s){ }
}
class Derived : Base
{
}

Ez a forrskd(rszlet) nem fordul le, mivel a Base osztly csakis paramteres


konstruktorral rendelkezik.

- 86 -

class Base
{
public Base(string s){ }
}
class Derived : Base
{
public Derived() : base("abc"){ }
}

gy mr viszont mkdni fog.


A base nem sszekeverend a Base nev osztllyal, ez egy nll metdus, amely
minden esetben az sosztly valamely konstruktort hvja.
Az alaprtelmezett konstruktor valamilyen formban minden esetben lefut, akkor is,
ha az osztlyban deklarltunk paramterest, hiszen tovbbra is ez felel a
memriafoglalsrt.
Egy osztly pldnyostshoz a pldnyostst vgz programrsz szmra lthat
kell legyen a pldnyostand osztly konstruktora.
Az adattagok ha vannak automatikusan a nekik megfelel nullrtkre
inicializldnak(pl: int-> 0, bool ->false, referencia- s nullabletpusok ->null).
A C++ nyelvet ismerk vigyzzanak, mivel itt csak alaprtelmezett konstruktort
kapunk automatikusan, rtkad opertort illetve msol konstruktort nem.
Ugyanakkor minden osztly a System.Object bl szrmazik (mg akkor is ha erre
nem utal semmi), ezrt nhny metdust (pldul a tpus lekrdezshez) a
konstruktorhoz hasonlan azonnal hasznlhatunk.
Jelen pillanatban az osztlyunkat semmire nem tudjuk hasznlni, ezrt ksztsnk
hozz nhny adattagot s egy konstruktort:
using System;
class Dog
{
private string _name;
private int _age;
public Dog(string name, int age)
{
this._name = name;
this._age = age;
}
}
class Program
{
static public void Main()
{
Dog d = new Dog("Rex", 2);
}
}

- 87 -

A konstruktor neve meg kell egyezzen az osztly nevvel s semmilyen visszatrsi


rtke nem lehet. A mi konstruktorunk kt paramtert vr, a nevet s a kort
(metdusokkal s paramtereikkel a kvetkez rsz foglalkozik bvebben).
Egy osztlynak paramterlisttl fggen brmennyi konstruktora lehet s egy
konstruktorbl hvhatunk egy msikat a thisszel:
class Test
{
public Test() : this(10){ }
public Test(int x){ }
}

Ha tbb konstruktor is van, akkor a paramter tpushoz leginkbb illeszked fut le.
A pldban a konstruktor trzsben rtket adtunk a mezknek a this hivatkozssal,
amely mindig arra a pldnyra mutat, amelyen meghvtk (a this kifejezs gy minden
olyan helyen hasznlhat, ahol az osztlypldnyra van szksg). Nem ktelez
hasznlni, ugyanakkor hasznos lehet, hogy ha sok adattag/metdus van, illetve ha a
paramterek neve megegyezik az adattagokval. A fordtprogram automatikusan
odakpzeli magnak a fordts sorn, gy mindig tudja mivel dolgozik.
Az adattagok private elrsek (ld. elmleti rsz), azaz most csakis az osztlyon
bell hasznlhatjuk s mdosthatjuk ket, pldul a konstruktorban, ami viszont
publikus.
Nem csak a konstruktorban adhatunk rtket a mezknek, hanem hasznlhatunk n.
inicializlkat is:
class Dog
{
private string _name = "Rex";
private int _age = 5;
public Dog(string name, int age)
{
this._name = name;
this._age = age;
}
}

Az inicializls mindig a konstruktor eltt fut le, ez egyben azt is jelenti, hogy az
utbbi fellbrlhatja. Ha a Dog osztlynak ezt a mdostott vltozatt hasznltuk
volna fentebb, akkor a pldnyosts sorn minden esetben fellrnnk az
alaprtelmezettnek megadott kort.
Az inicializls sorrendje megegyezik a deklarls sorrendjvel (fellrl lefel halad).
A konstruktorok egy specilis vltozata az n. msol- vagy copy-konstruktor. Ez
paramtereknt egy sajt magval megegyez tpus objektumot kap s annak
rtkeivel inicializlja magt. Msol konstruktort ltalban az rtkad opertorral
szoktak implementlni, de az opertor-kiterjeszts egy msik fejezet tmja, gy most
egyszerbben oldjuk meg:
- 88 -

class Dog
{
private string _name = "Rex";
private int _age = 5;
public Dog(string name, int age)
{
this._name = name;
this._age = age;
}
public Dog(Dog otherDog)
: this(otherDog._name, otherDog._age)
{ }
}

A program els rnzsre furcsa lehet, mivel privt elrhetsg tagokat hasznlunk,
de ezt minden gond nlkl megtehetjk, mivel a C# a privt elrhetsget csak
osztlyon kvl rvnyesti, ugyanolyan tpus objektumok ltjk egymst.
Most mr hasznlhatjuk is az j konstruktort:
Dog d = new Dog("Rex", 2);
Dog e = new Dog(d);

13.2 Adattagok
Az adattagok vagy mezk olyan vltozk, amelyeket egy osztlyon (vagy struktrn)
bell deklarltunk. Az adattagok az osztlypldnyhoz tartoznak (azaz minden egyes
pldny klnll adattagokkal rendelkezik) vele szletnek s halnak is meg. Az
eddigi pldinkban is hasznltunk mr adattagokat, ilyenek voltak a Dog osztlyon
belli _name s _age vltozk.
Az adattagokon hasznlhatjuk a const tpusmdostt is, ekkor a deklarcinl
rtket kell adnunk a meznek, hasonlan az elz fejezetben emltett
inicializlshoz. Ezek a mezk pontosan ugyangy viselkednek mint a hagyomnyos
konstansok.
Egy konstans mezt nem lehet statikusnak (statikus tagokrl hamarosan) jellni,
mivel a fordt egybknt is gy fogja kezelni (ha egy adat minden objektumban
vltozatlan, felesleges minden alkalommal kln pldnyt kszteni belle), vagyis
minden konstans adattagbl globlisan minden pldnyra vonatkozan egy darab
van.
A mezkn alkalmazhat a readonly mdost is, ez kt dologban klnbzik a
konstansoktl: az rtkads elhalaszthat a konstruktorig s az rtkl adott
kifejezs eredmnynek nem szksges ismertnek lennie fordtsi idben.

- 89 -

13.3 Lthatsgi mdostk


A C# tfle mdostt ismer:
public: az osztlyon/struktrn kvl s bell teljes mrtkben hozzfrhet.
private: csakis a tartalmaz osztlyon bell lthat, a leszrmazottak sem
lthatjk, osztlyok/struktrk esetben az alaprtelmezs.
protected: csakis a tartalmaz osztlyon s leszrmazottain bell lthat.
internal: csakis a tartalmaz (s a bart) assembly(ke)n bell lthat.
protected internal: a protected s internal keverke.
Ezek kzl leggyakrabban az els hrmat fogjuk hasznlni.

13.4 Parcilis osztlyok


C# nyelvben ltrehozhatunk n. parcilis (darab, tredk) osztlyokat (partial class),
ha egy osztly-deklarciban hasznljuk a partial kulcsszt (ezt minden darabnl
meg kell tennnk). Egy parcilis osztly defincija tbb rszbl (tipikusan tbb
forrsfile-bl) llhat. Egy parcilis osztly minden tredknek ugyanazzal a
lthatsgi mdostval kell rendelkeznie, valamint az egyik rsznl alkalmazott
egyb mdostk (pl. abstract), illetve az sosztly deklarci a teljes osztlyra
(rtsd: minden tredkre) rvnyes lesz (ebbl kvetkezik, hogy ezeket nem ktelez
feltntetni minden darabnl). Ugyanakkor ennl az utols felttelnl figyelni kell arra,
hogy ne adjunk meg egymsnak ellentmond mdostkat (pl. egy osztly nem
kaphat abstract s sealed mdostkat egyidben).
Nzznk egy pldt:
// main.cs
using System;
partial class PClass
{
}
class Program
{
static public void Main()
{
PClass p = new PClass();
p.Do();
}
}

Lthat, hogy egy olyan metdust hvtunk, amelynek hinyzik a deklarcija.

- 90 -

Ksztsnk egy msik forrsfilet is:


// partial.cs
using System;
partial class PClass
{
public void Do()
{
Console.WriteLine("Hello!");
}
}

A kt filet gy tudjuk fordtani:


csc main.cs partial.cs
A .NET a parcilis osztlyokat fknt olyan esetekben hasznlja, amikor az osztly
egy rszt a fordt generlja (pl. a grafikus fellet alkalmazsoknl a kezdeti
belltsokat az InitializeComponent metdus vgzi, ezt teljes egszben a fordt
kszti el). Ennek a megoldsnak az a nagy elnye, hogy knnyen ki tudjuk
egszteni ezeket a generlt osztlyokat.
Brmelyik osztly (teht a nem-parcilis is) tartalmazhat begyazott parcilis
osztlyt, ekkor rtelemszeren a tredkek a tartalmaz osztlyon bell kell legyenek
(ugyanez nem vonatkozik a parcilis osztlyon bell lv parcilis osztlyokra, ott a
begyazott osztlyok tredkei sztoszolhatnak a tartalmaz osztly darabjai kztt).
Egy parcilis osztly darabjainak ugyanabban az assemblyben kell lennik.
A C# 3.0 mr engedlyezi parcilis metdusok hasznlatt is, ekkor a metdus
deklarcija s defincija sztoszlik:
partial class PClass
{
partial void Do();
}
partial class PClass
{
partial void Do()
{
Console.WriteLine("Hello!");
}
}

Parcilis metdusnak nem lehet elrhetsgi mdostja (pp ezrt minden esetben
private elrs lesz) valamint void-dal kell visszatrnie.
A partial kulcsszt ilyenkor is ki kell tenni minden elfordulsnl. Csakis parcilis
osztly tartalmazhat parcilis metdust.

- 91 -

13.5 Begyazott osztlyok


Egy osztly tartalmazhat metdusokat, adattagokat s ms osztlyokat is. Ezeket a
bels osztlyokat begyazott (nested) osztlynak nevezzk. Egy ilyen osztlyt
ltalban elrejtnk, de ha mgis publikus elrsnek deklarljuk, akkor a kls
osztlyon keresztl rhetjk el. A begyazott osztlyok alaprtelmezs szerint privt
elrsek.
class Outer
{
class Inner
{
// a begyazott osztly nem lthat
}
}
class Outer
{
public class Inner
{
// gy mr ltszik
}
}
Outer.Inner x = new Outer.Inner(); // pldnyosts

Egy begyazott osztly hozzfr az t tartalmaz osztlypldny minden tagjhoz


(belertve a private elrs tagokat s ms begyazott osztlyokat is), de csakis
akkor, ha a begyazott osztly trol egy, a kls osztlyra hivatkoz referencit:
class Outer
{
private int value = 11;
private Inner child;
public Outer()
{
child = new Inner(this);
}
public void Do()
{
child.Do();
}
class Inner
{
Outer parent;
public Inner(Outer o)
{
parent = o;
}
public void Do()
{
Console.WriteLine(parent.value);
}
}
}

- 92 -

13.6 Objektum inicializlk


A C# 3.0 objektumok pldnyostsnak egy rdekesebb formjt is tartalmazza:
using System;
class Person
{
public Person(){ }
private string _name;
public string Name
{
get{ return _name; }
set{ _name = value;}
}
}
class Program
{
static public void Main()
{
Person p = new Person()
{
Name = "Istvn"
};
Console.WriteLine(p.Name);
}

Ilyen esetekben vagy egy nyilvnos tagra, vagy egy tulajdonsgra hivatkozunk (ez
utbbit hasznltuk). Termszetesen, ha ltezik paramteres konstruktor, akkor is
hasznlhatjuk ezt a belltsi mdot.

13.7 Destruktorok
A destruktorok a konstruktorokhoz hasonl specilis metdusok, amelyek az osztly
ltal hasznlt erforrsok felszabadtsrt felelsek.
A .NET n. automatikus szemtgyjt (garbage collector) rendszert hasznl,
amelynek lnyege, hogy a hivatkozs nlkli objektumokat (nincs rjuk mutat
rvnyes referencia) a keretrendszer automatikusan felszabadtja.
MyClass mc = new MyClass(); // mc egy MyClass objektumra mutat
mc = null; // az objektumra mr nem mutat semmi, felszabadthat

Objektumok alatt ebben a fejezetben csak s kizrlag referencia-tpusokat rtnk,


az rtktpusokat nem a GC kezeli.
A szemtgyjt mkdse nem determinisztikus, azaz elre nem tudjuk
megmondani, hogy mikor fut le, ugyanakkor kzzel is meghvhat, de ez nem
ajnlott. A kvetkez pldban foglalunk nmi memrit, majd megvizsgljuk, hogy
mi trtnik felszabadts eltt s utn:
- 93 -

using System;
class Program
{
static public void Main()
{
Console.WriteLine("Foglalt memria: {0}",
GC.GetTotalMemory(false));
for(int i = 0;i < 10;++i)
{
int[] x = new int[1000];
}
Console.WriteLine("Foglalt memria: {0}",
GC.GetTotalMemory(false));
GC.Collect(); // meghvjuk a szemtgyjtt
Console.WriteLine("Foglalt memria: {0}",
GC.GetTotalMemory(false));
}
}

A GC osztly GetTotalMemory metdusa a program ltal lefoglalt byte -ok szmt


adja vissza, paramterknt megadhatjuk, hogy meg szeretnnk e hvni a
szemtgyjtt.
A fenti program kimenete valami ilyesmi kell legyen:
Foglalt memria: 21060
Foglalt memria: 70212
Foglalt memria: 27144
Vegyk szre, hogy a ciklusban ltrehozott tmbk minden ciklus vgn
eltntethetek, mivel nincs tbb rjuk hivatkoz referencia (hiszen a loklis
objektumok hatkre ott vget r).
De hogyan is mkdik a szemtgyjt? A .NET n. genercis garbage collector-t
hasznl, amely abbl a feltevsbl indul ki, hogy a legfrissebben ltrehozott
objektumok lesznek leghamarabb felszabadthatak (ez az n. genercis hipotzis)
(gondoljunk csak a loklis vltozkra, amelyeket viszonylag sokat hasznlunk).
Ez alapjn a kvetkez trtnik: minden friss objektum a nulladik legfiatalabb
generciba kerl. Amikor eljn a szemtgyjts ideje, a GC elszr ezt a genercit
vizsglja meg, s ha tall hivatkozs nlkli objektumot azt trli (pontosabban az
elfoglalt memriaterletet szabadnak jelli), a maradkot pedig trakja az els
generciba. Ezutn sorban tvizsglja a tbbi genercit (mg kett van) s elvgzi
a megfelel mdostsokat. rtelemszeren a msodik genercis objektumok akkor
trldnek, ha a program megll (illetve elfogyhat a memria is, ekkor
OutOfMemoryException kivtel keletkezik). Az egyes genercik sszefgg
memriaterleten vannak, gy kevesebbet kell dolgoznia a gyjtnek.

- 94 -

Az is rdekes krds, hogy honnan tudja a GC, hogy melyik objektumok


feleslegesek. Ehhez be kell vezetnnk kt fogalmat, a gyenge- illetve ers
referencik (weak- s strong-reference) intzmnyt:
Minden olyan objektumra, amelyet a new opertorral hozunk ltre, ers
referencival hivatkozunk, ezek normlis objektumok, amelyek akkor s
csakis akkor kerlnek hatkrn kvlre (s takarthatak el a GC ltal), ha
nincs rjuk hivatkoz rvnyes referencia.
A gyenge referencik ennek pp ellenkezjt nyjtjk, brmikor trlhet az
ltaluk mutatott objektum ha nincs elg memria akkor is ha ltezik r
mutat rvnyes gyenge hivatkozs.
A .NET nyelvi szinten tmogatja a gyenge referencikat:
int[][] array = new int[10][];
for(int i = 0;i < 10;++i)
{
array[i] = new int[1000];
}
WeakReference wr = new WeakReference(array);
array = null;

Ebben a pldban miutn az eredeti tmbhivatkozst nullra lltottuk, a tmb


objektumokra mr csak gyenge referencia mutat, azaz brmikor eltakarthatak.
Egy WeakReference objektumbl visszanyerhet az eredeti ers referencia a
Target tulajdonsg segtsgvel, viszont ne felejtsk el ellenrizni ennek nullrtkt,
mert lehet, hogy mr tment rajta a GC (s konvertlnunk is kell, mivel object
tpussal tr vissza):
WeakReference wr = new WeakReference(array);
array = null;
if(wr.Target != null)
{
int[][]array = (int[][])wr.Target;
}

A msik fogalom, amelyet ismernnk kell, az az n. application root objektumok.


Ezek olyan objektumok, amelyekrl felttelezhetjk, hogy elrhetek (ilyen
objektumok lesznek pl. az sszes loklis s globlis vltoz). A GC mindig a root
objektumokat vizsglja meg elszr, s rajtuk keresztl pti fel a memriatrkpet.
Most mr tisztban vagyunk az alapokkal, vizsgljuk meg, hogy mi trtnik
valjban. Azt mondtuk, hogy a GC tvizsglja a genercikat, ennek azonban van
egy kis htrnya, mgpedig az, hogy lass. Ahhoz, hogy a takarts valban
hatkony legyen, fel kell fggeszteni a program futst, vagy azzal prhuzamosan
dolgozni. Mindkt esetben rosszul jrunk, hiszen vagy lefagy a program egy idre,
vagy kevesebb erforrshoz jut (ugyanakkor azt is szmtsba kell venni, hogy a GC
a lehet legjobb idben rtsd: akkor amikor a program a legkevesebb erforrst
hasznlja fog beindulni, teht nem felttlenl fog igazn nagy gondot jelenteni az
- 95 -

alkalmazs szempontjbl). Nylvn tbbmagos processzorral szerelt PC knl jobb


a helyzet, de attl mg fennll a hatkonysg problmja.
pp ezrt, ahelyett, hogy minden alkalommal teljes vizsglatot vgezne a GC,
bevezettk a rszleges takarts fogalmt, amely a kvetkez feltevsre pl: az
egyes illetve kettes genercikban lv objektumok nagy valsznsggel nem
mdosultak, vagyis feltehetjk, hogy van rjuk hivatkoz referencia (gondoljunk arra,
hogy pl. a loklis vltozk szinte soha nem fognak tkerlni mg az egyes
generciba sem, vagyis az egyes s kettes generci tagjai tipikusan hossz let
objektumok lesznek). Termszetesen ezt nem tudhatjuk biztosan, ezrt minden .NET
alkalmazshoz automatikusan ltrejn egy adatszerkezet (kpzeljk el tmbknt),
amelynek egyes indexei a memria egy bizonyos nagysg terletnek llapott
mutatjk (nem az objektumokt!).
Teht eljn a GC ideje, tvlogatja a nulladik genercit, majd fogja a fenti
adatszerkezetet (n. card table) s megvizsgl minden olyan objektumot, amely
olyan memriaterleten fekszik amelyet a card table mdostottnak jellt. Ez
drmaian megnveli a GC hatkonysgt, hiszen a teljes felhasznlt memrinak
csak kis rszt kell megvizsglnia.
A GC a nevvel ellenttben nem csak ennyit tesz, valjban az dolga az
objektumok teljes letciklusnak a kezelse s a memria megfelel szervezse is.
Amikor elindtunk egy .NET programot, akkor a GC elsknt szabad memrit kr az
opercis rendszertl (a .NET n. szegmensekre osztja a memrit, minden
szegmens 16 MB mret), mgpedig ktszegmensnyit: egyet a hagyomnyos
objektumoknak (GC Heap) s egyet a nagymret (100+ kilobyte) objektumoknak
(LOH Large Object Heap) (ez utbbit csakis teljes takartsnl vizsglja a GC).
Ezutn nyugodtan kszthetnk objektumokat, mert ha elfogy a hely, a GC
automatikusan j szegmenseket fog ignyelni.
Azt gondoln az ember, hogy ennyi az egsz, de minden objektum letben eljn a
pillanat, amikor visszaadja a lelkt a teremtjnek, nevezetesen a GCnek. Ilyenkor
r hrul az a rendkvl fontos feladat is, hogy rendbe rakja a memrit. Mit is rtnk
ez alatt? Hatkonysg szempontjbl az a legjobb, ha az osztlypldnyok
egymshoz kzel lehetleg egyms mellett vannak a memriban. pp ezrt a
GC minden (f)gyjtciklus (teht teljes takarts) alkalmval tmozgatja az
objektumokat, hogy a lehet leghatkonyabban kezelhessk ket
Ennek a megoldsnak egy htultje, hogy ilyen mdon nem hasznlhatunk
unmanaged kdot, mivel ez teljes mrtkben megakadlyozza a pointermveleteket.
A megoldst az objektumok rgztse (n. pinning) jelenti, errl egy ksbbi fejezet
szmol be.
A managelt kd ppen a fenti tnyek miatt tudja felvenni a versenyt a natv
programokkal. St, olyan alkalmazsok esetben, ahol sokszor foglalunk s
szabadtunk fel memrit, a natv kd htrnyba is kerl(het).
sszessgben azt mondhatjuk, hogy natv s managelt program kztt nincs nagy
klnbg sebessg tekintetben.

- 96 -

A GC hromfle mdban tud mkdni:


A GC prhuzamosan fut az alkalmazssal
A GC felfggesztheti az alkalmazst
Szerver md
Nzzk az elst: az objektumok alloklst egy klnll szl vgzi, ha szksg van
tiszttsra, akkor a program tbbi szlt csak nagyon rvid ideig fggeszti fel s a
takartssal egyidejleg a program tovbbra is helyet foglalhat a memriban, kivve
ha az tlpte a maximlisan kiszabott keretet. Ez a limitls a nulladik genercira
vonatkozik, teht azt szabja meg, hogy mennyi memrit hasznlhat fel egyszerre a
G0 (amennyiben ezt az rtket elrjk, a GC beindul).Ezt a mdszert olyan
alkalmazsoknl hasznljuk, amikor fontos, hogy felhasznli fellet reszponzv
maradjon. Ez az alaprtelmezett md.
Hasonlan mkdik a msodik is, viszont teljes mrtkben lelltja az alkalmazst
(n. Stop-The-World mdszer) a tisztts idejre. Ez a md sokkal kisebb G0 kerettel
rendelkezik.
Szerver mdban minden egyes processzor kln heappel s GCvel rendelkezik.
Ha egy alkalmazs kifut a memribl, szl a GCnek, amely felfggeszti a program
futst a tisztts idejre.
Ha meg akarjuk vltoztatni egy program GC mdjt, szksgnk lesz egy
konfigurcis filera (errl egy ksbbi fejezetben), amely a kvetkezket tartalmazza
( a pldban kikapcsoljuk a prhuzamos futst):
<configuration>
<runtime>
<gcConcurrent enabled="false"/>
</runtime>
</configuration>

Vagy:
<configuration>
<runtime>
<gcServer enabled="true"/>
</runtime>
</configuration>

Ezzel pedig a szervermdot lltottuk be.


Most pedig megnzzk, hogy hogyan hasznlhatjuk a GC t a gyakorlatban: A
konstruktor(ok) mellett egy msik specilis metdus is jr minden referenciatpushoz, ez pedig a destruktor.
A GC megsemmists eltt az objektumokon meghvja a hozzjuk tartoz destruktort,
msnven Finalizer-t. Ennek a metdusnak a feladata, hogy felszabadtsa az osztly
- 97 -

ltal hasznlt erforrsokat (pl., hogy lezrja a hlzati kapcsolatokat, bezrjon


minden egyes megnyitott file-t, stb.). Vegyk a kvetkez kdot:
using System;
class DestructableClass
{
public DestructableClass()
{
Console.WriteLine("Konstruktor");
}
~DestructableClass()
{
Console.WriteLine("Destruktor");
}
}
class Program
{
static public void Main()
{
DestructableClass dc = newDestructableClass();
Console.ReadKey();
}
}

A destruktor neve tilde jellel (~) kezddik, neve megegyezik az osztlyval, s nem
lehet semmilyen mdostja vagy paramtere (rtelemszeren egy destruktor mindig
privt elrhetsg lesz, vagyis kzvetlenl soha nem hvhatjuk, ez csakis a GC
eljoga).
Soha ne ksztsnk res destruktort, mivel a GC minden destruktorrl bejegyzst
kszt, s mindenkppen meghvja mindegyiket akkor is, ha res, vagyis ez egy
felesleges metdushvs lenne.
Ha lefordtjuk ezt a kdot s elindtjuk a programot, elszr a Konstruktor szt
fogjuk ltni, majd egy gomb lenyomsa utn megjeleni a prja is (ha ltni is akarjuk,
nem rt parancssorbl futtatni). A fenti kd valjban a kvetkez formban ltezik:
class DestructableClass
{
public DestructableClass()
{
Console.WriteLine("Konstruktor");
}
protected override void Finalize()
{
try
{
Console.WriteLine("Destruktor");
}
finally
{
base.Finalize();
}
}
}

- 98 -

Ez a forrskd csak plda, nem fordul le, mivel a Finalize metdust nem
definilhatjuk fell, erre val a destruktor.
A Finalize-t minden referencia-tpus rkli a System.Object tl. Elszr felszabadtja
az osztly erforrsait (a destruktorban ltalunk megszabott mdon), azutn
meghvja az sosztly Finalize metdust (ez legalbb a System.Object destruktora
lesz) s gy tovbb, amg a lnc vgre nem r:
using System;
class Base
{
~Base()
{
Console.WriteLine("Base");
}
}
class Derived : Base
{
~Derived()
{
Console.WriteLine("Derived");
}
}
class Program
{
static public void Main()
{
Derived d = new Derived();
}
}

A destruktorokra vonatkozik nhny szably, ezek a kvetkezek:

Egy osztlynak csak egy destruktora lehet


A destruktor nem rklhet
A destruktort nem lehet direkt hvni, a hvs mindig automatikusan trtnik
Destruktora csakis osztlynak lehet, struktrnak nem

Legtbbszr felesleges destruktort kszteni, ez csak nhny specilis esetben


szksges, pl. amikor valamilyen unmanaged erforrst (memria, file, stb...)
hasznlunk.

- 99 -

13.7.1 IDisposable
Az IDisposable interfsz segtsgvel egy osztly ltal hasznlt erforrsok
felszabadtsa kzzel elre meghatrozott idpontban is megtrtnhet, teht
nem kell a GC re vrni.
using System;
class DisposableClass : IDisposable
{
public void Dispose()
{
// Takartunk
GC.SuppressFinalize(this);
}
}
class Program
{
static public void Main()
{
DisposableClass dc = new DisposableClass();
}
}

Az interfsz ltal deklarlt Dispose metdusban meg kell hvnunk a


GC.SuppressFinalize metdust, hogy jelezzk a GC nek, hogy ez az osztly mr
felszabadtotta az erforrsait, s nem kell destruktort hvnia.
A fenti kdban egy kicsit csaltunk: a SuppressFinalize csak akkor kell, ha valban
definiltunk destruktort, egybknt felesleges.
Destruktorok hasznlata helyett ltalban az n. Dispose tervezsi mintt
alkalmazzuk, amely megvalstshoz nylvn az IDisposable interfsz lesz
segtsgnkre.
class DisposableClass : IDisposable
{
private bool disposed = false;
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
{
if(!disposed) {
if(disposing) { // managed erforrsok felszabadtsa }
// unmanaged erforrsok felszabadtsa
disposed = true;
}
}
~DisposableClass() { Dispose(false); }
}

- 100 -

Ebben a forrskdban kt Dispose metdust ksztettnk, az els paramter nlkli


az, amit az interfsztl kaptunk, mg a msik arra szolgl, hogy sszehangoljuk a GC
munkjt a kzi erforrs-felszabadtssal.
Ha a msodik metdus paramtere true rtk a hvskor, akkor tudjuk, hogy kzzel
hvtuk a Dispose metdust, vagyis mind a menedzselt, mind a natv erforrsok
felszabadthatak. Ha a paramter rtke false, akkor pedig a hvs a destruktorbl
szrmazik, vagyis csak az unmanaged erforrsokkal kell trdnnk.
Az IDisposable interfszt megvalst osztlyok hasznlhatak n. using blokk-ban,
ami azt jelenti, hogy a blokk hatkrn kvlre rve a Dispose metdus
automatikusan meghvdik:
using System;
class DisposableClass : IDisposable
{
public void Dispose()
{
Console.WriteLine("Takartunk...");
GC.SuppressFinalize(this);
}
}
class Program
{
static public void Main()
{
using(DisposableClass dc = new DisposableClass())
{
Console.WriteLine("Using blokk...");
}
}
}

A legtbb I/O mvelettel (file- s hlzatkezels) kapcsolatos osztly megvalstja az


IDisposable t, ezrt ezeket ajnlott mindig using blokkban hasznlni.

- 101 -

14

Metdusok

Az objektum-orientlt programozsban egy metdus olyan programrsz, amely vagy


egy objektumhoz, vagy egy osztlyhoz kthet. Elbbi az n. osztly metdus, utbbi
pedig a statikus metdus. Ebben a fejezetben az osztly (instance) metdusokrl
lesz sz.
Egy metdussal megvltoztathatjuk egy objektum llapott, vagy informcit
kaphatunk annak adatairl. Optimlis esetben egy adattaghoz csakis metdusokon
keresztl frhetnk hozz (ez akkor is igaz, ha ltszlag nem gy trtnik, pl. minden
opertor valjban metdus formjban ltezik, ezt majd ltni fogjuk).
Bizonyra szeretnnk, ha a korbban elksztett kutya osztlyunk nem csak lgna a
semmiben, hanem tenne is valamit. Ksztsnk nhny metdust a legfontosabb
mveletekhez: az evshez s az alvshoz:
using System;
class Dog
{
string name;
int age;
public Dog(string name, int age)
{
this.name = name;
this.age = age;
}
public void Eat()
{
Console.WriteLine("A kutya eszik...");
}

public void Sleep()


{
Console.WriteLine("A kutya alszik...");
}

class Program
{
static public void Main()
{
Dog d = new Dog("Rex", 2);
d.Eat();
d.Sleep();
}
}

Nzzk meg, hogyan pl fel egy metdus: elsknt megadjuk a lthatsgot s itt is
rvnyes a szably, hogy ennek hinyban az alaprtelmezett privt elrs lesz
rvnyben. Ezutn a visszatrsi rtk tpusa ll, jelen esetben a voiddal jeleztk,
hogy nem vrunk ilyesmit. Kvetkezik a metdus neve, ez konvenci szerint
nagybetvel kezddik, vgl a sort a paramterlista zrja.

- 102 -

Egy metdust az objektum neve utn rt pont opertorral hvhatunk meg (ugyanez
rvnyes a publikus adattagokra, tulajdonsgokra, stb. is).
A hagyomnyos procedurlis programozs (pl. a C vagy Pascal nyelv) a
metdusokhoz hasonl, de filozfijban ms eszkzket hasznl, ezek a fggvny
(function) s az eljrs (procedure). Mi a klnbsg? Azt mondtuk, hogy a
metdusok egy osztlyhoz kthetek, annak letciklusban jtszanak szerepet.
Nzznk egy pldt:
using System;
class NewString1
{
private string aString;
public NewString1(string s)
{
this.aString = s;
}
public void PrintUpper()
{
Console.WriteLine(this.aString.ToUpper());
}
}
class NewString2
{
public void PrintUpper(string s)
{
Console.WriteLine(s.ToUpper());
}
}
class Program
{
static public void Main()
{
NewString1 ns1 = new NewString1("baba");
NewString2 ns2 = new NewString2();
ns1.PrintUpper();
ns2.PrintUpper("baba");
}

Pontosan ugyanaz trtnik mindkt esetben, de van egy nagy klnbsg. Az els
osztly valdi osztly: adattaggal, konstruktorral, stb. Van llapota, vgezhetnk
rajta mveleteket. A msodik osztly nem igazi osztly, csak egy doboz, amelyben
egy teljesen nll, egyedl is letkpes szerkezet van, mindssze azrt kell az
osztly-definci, mert egybknt nem fordulna le a program (ebben az esetben egy
statikus metdust kellett volna ksztennk, errl hamarosan).
Az els osztlyban metdust definiltunk, a msodikban eljrst (eljrs s fggvny
kztt a lnyegi klnbsg, hogy utbbinak van visszatrsi rtke).

- 103 -

14.1 Paramterek
Az objektummal val kommunikci rdekben kpesnek kell lennnk kvlrl
megadni adatokat, vagyis paramtereket. A paramterek szmt s tpusait a
metdus deklarcijban, vesszvel elvlasztva adjuk meg. Egy metdusnak
gyakorlatilag brmennyi paramtere lehet.
A metdus nevt s paramterlistjt alrsnak, szignatrnak vagy prototpusnak
nevezzk. Egy osztly brmennyi azonos nev metdust tartalmazhat, ameddig a
paramterlistjuk klnbzik. A paramterek a metduson bell loklis vltozkknt
viselkednek, s a paramter nevvel hivatkozunk rjuk.
using System;
class Test
{
public void Method(string param)
{
Console.WriteLine("A paramter: {0}", param);
}
}
class Program
{
static public void Main()
{
Test t = new Test();
t.Method("Paramter");
}
}

A C# nyelvben paramterek tadhatunk rtk s cm szerint is. Elbbi esetben egy


teljesen j pldny jn ltre az adott osztlybl, amelynek rtkei megegyeznek az
eredetivel. A msik esetben egy az objektumra mutat referencia addik t, teht az
eredeti objektummal dolgozunk.
Az rtk- s referencia-tpusok klnbzen viselkednek az tads szempontjbl.
Az rtktpusok alaprtelmezetten rtk szerint addnak t, mg a referenciatpusoknl a cm szerinti tads az elre meghatrozott viselkeds. Utbbi esetben
van azonban egy kivtel, mgpedig az, hogy mg a referencia-tpus rtkeit
megvltoztathatjuk (s ez az eredeti objektumra is hat) addig magt a referencit
mr nem (teht nem kszthetnk j pldnyt, amelyre az tadott referencia mutat).
Ha ezt mgis megtesszk, az nem eredmnyez fordtsi hibt, de a vltozs csakis a
metduson bell lesz szlelhet.
Erre a magyarzat nagyon egyszer: mr emltettk, hogy egy metdusparamter
loklis vltozknt viselkedik, vagyis ebben az esetben egyszeren egy loklis
referencival dolgoznnk.

- 104 -

using System;
class Test
{
public int x = 10;
public void TestMethod(Test t)
{
t = new Test();
t.x = 11;
}
}
class Program
{
static public void Main()
{
Test t = new Test();
Console.WriteLine(t.x); // 10;
t.TestMethod(t);
Console.WriteLine(t.x); // 10
}
}

Ha mgis mdostani akarjuk egy referencia-tpus referencijt, akkor kln


jeleznnk kell azt, hogy valdi referenciaknt akarjuk tadni.
Ktflekppen adhatunk t paramtert referencia szerint. Az els esetben az tadott
objektumnak inicializltnak kell lennie (teht mindenkppen mutatnia kell valahov,
hasznlnunk kellett a new opertort). Ha ezt nem tettk meg, attl a program mg
lefordul, de a metdus hvsakor kivtelt fogunk kapni (NullReferenceException).A
referencia szerinti tadst a forrskdban is jellni kell, mind a metdus
prototpusnl, mind a hvs helyn a ref mdostval.
Referencia-tpust gyakorlatilag soha nem kell ilyen mdon tadnunk (persze nincs
megtiltva, de gondos tervezssel elkerlhet), kivtelt kpez, ha ezt valamilyen
.NETen kvli eszkz megkveteli (a lnyeg, hogy mr alloklt objektumra mutat
referencit optimlis esetben nem lltunk mshov).
A ref rtktpusok esetben mr sokkal hasznosabb, nzzk a kvetkez
forrskdot:

- 105 -

using System;
class Test
{
public void Swap(int x, int y)
{
int tmp = x;
x = y;
y = tmp;
}
}
class Program
{
static public void Main()
{
int x = 10;
int y = 20;
Test t = new Test();
t.Swap(x, y);
Console.WriteLine("x = {0}, y = {1}", x, y);
}

A Swap eljrssal megprbljuk felcserlni x s y rtkeit. Azrt csak prbljuk, mert


int tpusok (mivel rtktpusrl van sz) rtk szerint addnak t, vagyis a metdus
belsejben teljesen j vltozkkal dolgozunk.
rjuk t egy kicsit a forrst:
using System;
class Test
{
public void Swap(ref int x, ref int y)
{
int tmp = x;
x = y;
y = tmp;
}
}
class Program
{
static public void Main()
{
int x = 10;
int y = 20;
Test t = new Test();
t.Swap(ref x, ref y);
Console.WriteLine("x = {0}, y = {1}", x, y);
}
}

Most mr az trtnik, amit szeretnnk: x s y rtke megcserldtt.

- 106 -

Egy rdekesebb mdszer kt szm megcserlsre: hasznljuk a kizr vagy


opertort, ami akkor ad vissza igaz rtket, ha a kt operandusa kzl pontosan az
egyik igaz. Nzzk elszr a kdot:
public void Swap(ref int x, ref int y)
{
if(x != y)
{
x ^= y;
y ^= x;
x ^= y;
}
}

A kt szmot rjuk fel kettes szmrendszerben: x (= 10) = 01010 s y (= 20) =10100.


Most lssuk, hogy mi trtnik! Az els sor:
01010
10100 XOR
--------11110 (ez lesz most x)
A msodik sor:
10100
11110 XOR
-------01010 (ez most y, ez az rtk a helyn van)
Vgl a harmadik sor:
11110
01010 XOR
--------10100 (ksz vagyunk)
Hogy ez a mdszer mirt mkdik, azt mindenki gondolja t maga, egy kis segtsg
azrt jr: felhasznljuk a XOR kvetkez tulajdonsgait:
Kommutatv: A XOR B = B XOR A
Asszociatv: (A XOR B) XOR C = A XOR (B XOR C)
Ltezik neutrlis elem (jelljk NE vel): A XOR NE = A
Minden elem sajt maga inverze: A XOR A = 0 (ez az llts az oka annak, hogy
ellenriznnk kell, hogy x s y ne legyen egyenl)
Br ez az eljrs hatkonyabbnak tnik, igazbl ez egy hagyomnyos PC n mg
lassbb is lehet, mint az tmeneti vltozt hasznl trsa. A XOR Swap olyan
limitlt helyzetekben hasznos, ahol nincs elg memria/regiszter manverezni
tipikusan mikrokontrollerek esetben.
A cm szerinti tads msik formjban nem inicializlt paramtert is tadhatunk, de
ekkor felttel, hogy a metduson bell lltsuk be (tadhatunk gy mr inicializlt
- 107 -

paramtert is, de ekkor is felttel, hogy j objektumot ksztsnk). A hasznlata


megegyezik a reffel, azaz a szignatrban s a hvsnl is jelezni kell a
szndkunkat. A hasznland kulcssz az out (Nomen est omen A nv ktelez):
using System;
class Init
{
public void TestInit(out Test t)
{
t = new Test() { s = "Hello!"};
}
}
class Test
{
public string s = null;
}
class Program
{
static public void Main()
{
Test t = null;
Init i = new Init();
i.TestInit(out t);
Console.WriteLine(t.s); // Hello!
}
}

A fenti programokban pontosan tudtuk, hogy hny paramtere van egy metdusnak.
Elfordul viszont, hogy ezt nem tudjuk egyrtelmen megmondani, ekkor n.
paramter-tmbket kell hasznlnunk. Ha ezt tesszk, akkor az adott metdus
paramter-listjban a paramter-tmbnek kell az utols helyen llnia, illetve egy
paramter-listban csak egyszer hasznlhat ez a szerkezet.
using System;
class Test
{
public void PrintElements(params object[] list)
{
foreach(var item in list)
{
Console.WriteLine(item);
}
}
}
class Program
{
static public void Main()
{
Test t = new Test();
t.PrintElements("alma", "krte", 4, 1, "di");
t.PrintElements(); // ez is mkdik
}
}

- 108 -

A paramter-tmbt a params kulcsszval vezetjk be, ezutn a metdus belsejben


pontosan gy viselkedik, mint egy normlis tmb. Paramter-tmbknt tadhatunk
megfelel tpus tmbket is.

14.1.1 Alaprtelmezett paramterek


A C# 4.0 bevezeti az alaprtelmezett paramtereket, amelyek lehetv teszik, hogy
egy paramtereknek alaprtelmezett rtkeket adjunk, ezltal nem kell ktelezen
megadnunk minden paramtert a metdus hvsakor. Nzzk a kvetkez pldt:
class Person
{
public Person(string firstName, string lastName)
{
FirstName = firstName;
LastName = lastName;
}
public string Person(string firstName, string lastName, string job)
: this(firstName, lastName)
{
Job = job;
}
public string FirstName {get; private set; }
public string LastName {get; private set; }
public string Job {get; private set; }
}

Mivel nem tudunk biztosan minden emberhez munkahelyet rendelni, ezrt kt


konstruktort kellett ksztennk. Ez alapveten nem nagy problma, viszont az
gondot okozhat, ha valaki csak az els konstruktort hasznlja, majd megprbl
hozzfrni a munka tulajdonsghoz. Nylvn ezt sem nagy gond megoldani, de mirt
fradnnk, ha rendelkezsnkre llnak az alaprtelmezett paramterek? rjuk t a
forrskdot:
class Person
{
public Person(string firstName, string lastName, string job = "N/A")
{
FirstName = firstName;
LastName = lastName;
Job = job;
}
public string FirstName { get; private set; }
public string LastName { get; private set; }
public string Job { get; private set; }
}

A job paramterhez most alaprtelmezett rtket rendeltnk, gy biztosak lehetnk


benne, hogy minden adattag megfelelen inicializlt. Az osztlyt most gy tudjuk
hasznlni:

- 109 -

Personp1=newPerson("Istvn","Reiter");
Personp2=newPerson("Istvn","Reiter","brtnr");

14.1.2 Nevestett paramterek


A C# 4.0 az alaprtelmezett paramterek mellett bevezeti a nevestett paramter
(named parameter) fogalmt, amely segtsgvel explicit megadhatjuk, hogy melyik
paramternek adunk rtket. Nzzk az elz fejezet Person osztlynak
konstruktort:
Person p = new Person(firstName:"Istvn", lastName:"Reiter");

Mivel tudatjuk a fordtval, hogy pontosan melyik paramterre gondolunk, ezrt nem
kell betartanunk az eredeti metdus-deklarciban elrt sorrendet:
Person p = new Person(lastName:"Reiter", firstName:"Istvn");

14.2 Visszatrsi rtk


Az objektumainkon nem csak mveleteket vgznk, de szeretnnk lekrdezni az
llapotukat is s felhasznlni ezeket az rtkeket. Ezenkvl szeretnnk olyan
fggvnyeket is kszteni, amelyek nem kapcsoldnak kzvetlenl egy osztlyhoz,
de hasznosak lehetnek (pl. az Int.Parse fggvny ilyen).
Ksztsnk egy egyszer fggvnyt, amely sszead kt szmot, az eredmnyt pedig
visszatrsi rtkknt kapjuk meg:
using System;
class Test
{
public int Add(int x, int y)
{
return x + y;
}
}
class Program
{
static public void Main()
{
Test t = new Test();
int result = t.Add(10, 11);
}
}

A vgeredmnyt a return utastssal adhatjuk vissza. A metdus-deklarcinl meg


kell adnunk a visszatrsi rtk tpust is. Amennyiben ezt megtettk, a metdusnak
mindenkppen tartalmaznia kell egy return utastst a megfelel tpus elemmel (ez

- 110 -

lehet null is referencia- s nullable tpusok esetben) s ennek az utastsnak


mindenkppen le kell futnia:
public int Add(int x, int y)
{
if(x != 0 && y != 0)
{
return x + y;
}
}

Ez a metdus nem fordul le, mivel nem lesz minden krlmnyek kztt visszatrsi
rtk. A visszatrtett rtk tpusnak vagy egyeznie kell a visszatrsi rtk
tpusval, vagy a kett kztt lteznie kell implicit tpuskonverzinak:
public int Add(int x, int y)
{
return (byte)(x + y); // ez mkdik, br nincs sok rtelme
}
public int Add(int x, int y)
{
return (long)(x + y);// ez le sem fordul
}

Visszatrsi rtkkel rendelkez metdust hasznlhatunk minden olyan helyen, ahol


a program valamilyen tpust vr (rtkads, logikai kifejezsek, metdus
paramterei, ciklusfelttel, stb.).
Valjban a Main metdus kt formban ltezik: visszatrsi rtkkel s a nlkl. A
Main esetben a visszatrsi rtk azt jelli, hogy a program futsa sikeres volte (1)
vagy sem (0). Ezt a gyakorlatban akkor tudjuk hasznlni, ha egy kls program/scipt
hvta meg a programunkat s kvncsiak vagyunk, hogy sikeres volte a futsa.
static public int Main()
{
return 0;
}

14.3 Kiterjesztett metdusok


A C# 3.0 lehetsget ad arra, hogy egy mr ltez tpushoz j metdusokat adjunk,
anlkl, hogy azt kzvetlenl mdostannk, vagy szrmaztatnnk belle. Egy
kiterjesztett metdus (extension method) minden esetben egy statikus osztly
statikus metdusa kell, hogy legyen (errl a kvetkez fejezetben).
Egsztsk ki a string tpust egy metdussal, ami kirja a kpernyre az adott
karaktersorozatot:

- 111 -

using System;
static public class StringHelper
{
static public void Print(this string s)
{
Console.WriteLine(s);
}
}
class Program
{
static public void Main()
{
string s = "ezegystring";
s.Print();
StringHelper.Print(s); // gy is hasznlhatjuk
}
}

A this mdost utn a paramter tpusa kvetkezik, amely meghatrozza a


kiterjesztett osztly tpust. A fenti pldban lthat, hogy rendes statikus
metdusknt is hasznlhat egy extension method.
Ha kt kiterjesztett metdus ugyanazzal a szignatrval rendelkezik, akkor a
hagyomnyos, statikus ton kell hvnunk ket. Ha nem gy tesznk, akkor a
specilisabb (szkebb tpus) paramter metdus fog meghvdni.
Kiterjesztett metdust nem definilhatunk begyazott osztlyban.

- 112 -

15

Tulajdonsgok

A tulajdonsgokat (property) a mezk kzvetlen mdostsra hasznljuk, anlkl,


hogy megsrtennk az egysgbezrs elvt. A tulajdonsgok kvlrl nzve
pontosan ugyanolyanok, mint a hagyomnyos vltozk, de valjban ezek specilis
metdusok. Minden tulajdonsg rendelkezhet n. getter s setter blokkal, elbbi a
property mgtt lv mez rtkt adja vissza, utbbi pedig rtket ad neki:
using System;
class Person
{
public Person(string name)
{
this.name = name;
}

string name;
public string Name
{
get { return this.name; }
set { this.name = value; }
}

class Program
{
static public void Main()
{
Person p = new Person("Istvn");
Console.WriteLine(p.Name);
}
}

Lthatjuk, hogy egy property deklarci hasonlan pl fel mint a metdusok, azzal
a kivtellel, hogy nincs paramterlista. Vegyk szre, hogy a setterben egy
ismeretlen, value nev vltozt hasznltunk. Ez egy specilis elem, azt az rtket
tartalmazza amelyet hozzrendeltnk a setter-hez:
Person p = new Person("Istvn");
p.Name = "Bla"; // value == "Bla"

A getter s setter elrhetsgnek nem muszj megegyeznie, de a getternek minden


esetben publikusnak kell lennie:
public string Name
{
private get { return this._name; } // ez nem mkdik
set { this.name = value; }
}
public string Name
{
get { return this.name; }
private set { this.name = value; }//ez viszont j
}

- 113 -

Arra is van lehetsg, hogy csak az egyiket hasznljuk, ekkor csak rhat/olvashat
tulajdonsgokrl beszlnk:
public string Name
{
get { return this._name; }
}

Egyik esetben sem vagyunk rknyszertve, hogy azonnal visszaadjuk/beolvassuk az


adattag rtkt, tetszs szerint vgezhetnk mveleteket is rajtuk:
public string Name
{
get { return "Mr. " + this._name; }
}

A C# 3.0 rendelkezik egy nagyon rdekes jtssal, az n. automatikus


tulajdonsgokkal. Nem kell ltrehoznunk sem az adattagot, sem a teljes
tulajdonsgot, a fordt mindkettt legenerlja neknk:
public string Name
{
get; set;
}

A fordt automatikusan ltrehoz egy private elrs, string tpus name nev
adattagot s elkszti hozz a getter-t/setter-t is. Van azonban egy problma,
mghozz az, hogy a fordts pillanatban ez a vltoz mg nem ltezik, vagyis
kzvetlenl nem hivatkozhatunk r pl. a konstruktorban. Ilyenkor a setter-en keresztl
kell rtket adnunk.
class Person
{
public Person(string name)
{
this.Name = name;
}
public string Name
{
get; set;
}
}

- 114 -

16

Indexelk

Az indexelk hasonlak a tulajdonsgokhoz, azzal a klnbsggel, hogy nem nvvel,


hanem egy indexxel frnk hozz az adott informcihoz.
ltalban olyan
esetekben hasznljk, amikor az osztly/struktra tartalmaz egy tmbt vagy
valamilyen gyjtemnyt (vagy olyan objektumot, amely maga is megvalst egy
indexelt).
Egy indexelt gy implementlhatunk:
using System;
using System.Collections;
class Names
{
private ArrayList nameList;
public Names()
{
nameList = new ArrayList();
nameList.Add("Istvn");
nameList.Add("Judit");
nameList.Add("Bla");
nameList.Add("Eszter");
}
public int Count
{
get
{
return nameList.Count;
}
}
public string this[int idx]
{
get
{
if(idx >= 0 && idx < nameList.Count)
{
return nameList[idx].ToString();
}
return null;
}

}
class Program
{
static public void Main()
{
Names n = new Names();
for(int i = 0;i < n.Count;++i)
{
Console.WriteLine(n[i]);
}
}

- 115 -

Ez gyakorlatilag egy nvtelen tulajdonsg, a this mutat az aktulis objektumra, amin


az indexelt definiltuk. Tbb indexet is megadhatunk, amelyek klnbz tpus
indexxel vagy visszatrsi rtkkel rendelkezhetnek.

- 116 -

17

Statikus tagok

A hagyomnyos adattagok s metdusok objektumszinten lteznek, azaz minden


objektum minden adattagjbl sajt pldnnyal rendelkezik. Gyakran van azonban
szksgnk objektumtl fggetlen mezkre/metdusokra, pl., ha meg szeretnnk
szmolni, hogy hny objektumot hoztunk ltre. Erre a clra szolglnak az n. statikus
tagok, amelyekbl osztlyszinten sszesen egy darab ltezik. Statikus tagokat akkor
is hasznlhatunk, ha az osztlybl nem kszlt pldny.
A statikus tagok jelentsge a C# nyelv tisztn objektum orientlt mivoltban rejlik,
ugyanis nem definilhatunk globlis (mindenki szmra egyformn elrhet) tagokat.
Ezt (is) vltjk ki a statikus adattagok s metdusok.

17.1 Statikus adattag


Statikus tagot a static kulcssz segtsgvel hozhatunk ltre:
using System;
class Animal
{
static public int AnimalCounter = 0;
public Animal()
{
++Animal.AnimalCounter;
}
~Animal()
{
--Animal.AnimalCounter;
}
}
class Program
{
static public void Main()
{
Animal a = new Animal();
Console.WriteLine(Animal.AnimalCounter);
}
}

A pldban a statikus adattag rtkt minden alkalommal megnveljk eggyel,


amikor meghvjuk a konstruktort s cskkentk, amikor az objektum elpusztul, vagyis
az aktv pldnyok szmt tartjuk szmon vele.
A statikus tagokhoz az osztly nevn (s nem egy pldnyn) keresztl frnk hozz
(a statikus tag gazdaosztlybl az osztly neve nlkl is hivatkozhatunk rjuk, de
ez nem ajnlott, mivel rontja az olvashatsgot).
A statikus tagok ha az osztlynak nincs statikus konstruktora rgtn a program
elejn inicializldnak. Az olyan osztlyok statikus tagjai, amelyek rendelkeznek

- 117 -

statikus konstruktorral, az inicializlst elhalasztjk addig a pontig, amikor elszr


hasznljuk az adott osztly egy pldnyt.
Konvenci szerint minden statikus tag (adattagok is) neve nagybetvel kezddik. A
statikus s lthatsgi mdost megadsnak sorrendje mindegy.

17.2 Statikus konstruktor


A statikus konstruktor a statikus tagok belltsrt felel. A statikus konstruktor
kzvetlenl azeltt fut le, hogy egy pldny keletkezik az adott osztlybl vagy
hozzfrtek valamely tagjhoz.
A statikus konstruktornak nem lehet lthatsgot adni, illetve nincsenek paramterei
sem. Nem frhet hozz pldnytagokhoz sem.
using System;
class Test
{
static public int Var = Test.Init();
static public int Init()
{
Console.WriteLine("Var = 10");
return 10;
}
static Test()
{
Console.WriteLine("Statikus konstruktor");
}
public Test()
{
Console.WriteLine("Konstruktor");
}
}
class Program
{
static public void Main()
{
Console.WriteLine("Start...");
Test t = new Test();
}

Ha elindtjuk a programot, a kvetkez kimenetet kapjuk:


Start...
Var = 10
Statikus konstruktor
Konstruktor

- 118 -

A statikus konstruktoroknak van azonban egy hatalmas htultjk, amelyet a


kvetkez pldban lthatunk:
static class A1
{
static public int x = 10;
}
static class A2
{
static public int x;
static A2()
{
x = 10;
}
}

A kt osztly ltszlag ugyanazt teszi, mgis risi teljestmnyklnbsg van


kztk:
class Program
{
static public void Main()
{
Stopwatch sw = Stopwatch.StartNew();
for(int i = 0;i < 10000000;++i)
{
int x = A1.x;
}
Console.WriteLine("Eltelt id: {0}ms", sw.ElapsedMilliseconds);
sw = Stopwatch.StartNew();
for(int i = 0;i < 10000000;++i)
{
int x = A2.x;
}
Console.WriteLine("Eltelt id: {0}ms", sw.ElapsedMilliseconds);
}
}

A Stopwatch osztly a System.Diagnostics nvtrben van s ahogyan ltszik is


idt tudunk mrni vele.
Mindktszer tzmilli alkalommal krtk el a statikus tag rtkt, lssuk az
eredmnyt:
Eltelt id: 12ms
Eltelt id: 88ms
A klnbsg elkpeszten nagy, az ok pedig a kvetkez: ha definiltunk statikus
konstruktort akkor a rendszer minden egyes alkalommal, amikor statikus taghoz
prblunk hozzfrni, ellenrzi, hogy meghvdott-e mr a statikus konstruktor, ez
pedig a fenti teljestmnyvesztesget eredmnyezi.
- 119 -

17.3 Statikus metdus


Statikus metdust a hagyomnyos metdusokhoz hasonlan ksztnk, mindssze a
static kulcsszra van szksgnk. Ilyen metdus volt az elz pldban az Init
metdus is. A statikus konstruktortl eltren r nem vonatkozik, hogy nem lehetnek
paramterei. Statikus metdusok nem frnek hozz az osztly normlis tagjaihoz,
legalbbis direkt mdon nem (az minden tovbbi nlkl mkdik, ha egy pldny
referencijt adjuk t neki).
Statikus metdust ltalban akkor hasznlunk, ha nem egy pldny llapotnak a
megvltoztatsa a cl, hanem egy osztlyhoz kapcsold mvelet elvgzse. Ilyen
metdus pldul az Int32 osztlyhoz tartoz Parse statikus metdus is.
A leghresebb statikus metdus a Main.

17.4 Statikus tulajdonsg


A statikus tulajdonsgok a C# egy viszonylag ritkn hasznlt lehetsge. ltalban
osztlyokhoz kapcsold konstans rtkek lekrdezsre hasznljuk (lnyegben a
statikus metdusok egy olvashatbb verzija).
Plda:
class Math
{
static public double PI
{
get { return 3.14; }
}
}

17.5 Statikus osztly


Egy osztlyt statikusnak jellhetnk, ha csak s kizrlag statikus tagjai vannak. Egy
statikus osztlybl nem hozhat ltre pldny, nem lehet pldny-konstruktora (de
statikus igen) s mindig lezrt (ld. rklds). A fordt minden esetben ellenrzi
ezeknek a feltteleknek a teljeslst.
Plda:
using System;
static class MathHelper
{
static public double PI
{
get { return 3.14; }
}
static public double Cos(double x)

- 120 -

{
return Math.Cos(x);
}
}
class Program
{
static public void Main()
{
Console.WriteLine(MathHelper.Cos(1));
}
}

- 121 -

18

Struktrk

A struktrk szerkezetket tekintve hasonlak az osztlyokhoz, viszont azoktl


eltren nem referencia-, hanem rtktpusok.
Minden struktra indirekt mdon a System.ValueType osztlybl szrmazik. Ez egy
specilis tpus, amely lehetsget biztost rtktpusok szmra, hogy referenciatpusknt viselkedjenek (lsd: Boxing).
A struktrk kzvetlenl tartalmazzk a sajt rtkeiket, mg az osztlyok csak
referencikat trolnak. pp ezrt struktrt ltalban akkor hasznlunk, ha egyszer
adatokkal kell dolgoznunk, de nincs szksgnk egy osztly minden szolgltatsra.

18.1 Konstruktor
Minden struktra alaprtelmezetten rendelkezik egy konstruktor-szersggel
(vigyzat, nem igazi konstruktor), amely elvgzi a tagok nullra inicializlst
(lnyegben nullkkal tlti fel az adott memriaterletet). Ez a lehetsg mindig l,
nem rejthet el.
using System;
struct Test
{
public int x;
}
class Program
{
static public void Main()
{
Test t = new Test();
Console.WriteLine(t.x); // x == 0
}
}

Nem ktelez hasznlni a new opertort, de ha gy tesznk, akkor a struktra


tagjainak hasznlata eltt definilni kell az rtkket, ellenkez esetben a program
nem fordul le:
using System;
struct Test
{
public int x;
}
class Program
{
static public void Main()
{
Test t;
Console.WriteLine(t.x); // nem j, x inicializlatlan
}
}

- 122 -

Kszthetnk sajt konstruktort, de ekkor minden mez


gondoskodnunk kell. Egy struktra mezit nem inicializlhatjuk:

rtkadsrl

structTest
{
int _x = 10; // ez nem j
int _y;
public Test(int x, int y)
{
_y = y;// ez sem j, x nem kap rtket
}
}

Struktrnak csakis paramteres konstruktort definilhatunk, pramter nlkli


alaprtelmezettet nem. Viszont ha ezt megtettk, attl az alaprtelmezett konstruktor
mg hasznlhat marad:
using System;
struct Test
{
int _x;
int _y;
public Test(int x, int y)
{
_y = y;
_x = x;
}
}
class Program
{
static public void Main()
{
Test t1 = new Test(10, 11);
Test t2 = new Test(); //ez is mkdik
}
}

18.2 Destruktor
Struktrk nem rendelkezhetnek destruktorral. Egy struktra kt helyen lehet a
memriban: a stack-ben s a heap-ben (ha egy referencia-tpus tagja).
Ahhoz, hogy megrtsk, hogy mirt nincs destruktor, szksgnk van a kvetkezre:
egy struktrban lv referenciatpusnak csak a referencijt troljuk. Ha a
veremben van a struktra, akkor elbb vagy utbb kikerl onnan, s mivel gy a
benne lv referenciatpusra mr nem mutat referencia (legalbbis a struktrbl
nem) ezrt eltakarthat. Ugyanez a trtnet akkor is, ha a struktra pldny egy
referenciatpusban foglal helyet.

- 123 -

18.3 Adattagok
A struktrk az adattagokat kzvetlenl troljk (mg osztlyok esetben mindig
referencikat tartunk szmon). Egy struktra minden adattagja amennyiben a
konstruktorban nem adunk meg mst automatikusan a megfelel nulla rtkre
inicializldik.
Struktra nem tartalmazhat sajt magval megegyez tpus adattagot. Ugyangy,
egy struktra nem tartalmazhat olyan tpus tagot, amely tpus hivatkozik az eredeti
struktrra:
struct Test
{
Test t;
}
struct Test1
{
Test2 t;
}
struct Test2
{
Test1 t;
}

Mindhrom struktra hibs. Az ok nagyon egyszer: mivel a struktrk direkt mdon


nem referencikon keresztl troljk az adattagjaikat, valamint mivel a struktrk
nem vehetnek fel null rtket a fenti szerkezetek mind vgtelen hurkot (s vgtelen
memriafoglalst) okoznnak (Test1 struktra amiben Test2 amiben Test1 s gy
tovbb) (lsd: tranzitv lezrt).

18.4 Hozzrendels
Amikor egy struktra pldnynak egy msik pldnyt adunk rtkl akkor egy
teljesen j objektum keletkezik:
using System;
struct Test
{
public int x;
}
class Program
{
static public void Main()
{
Test t1 = new Test();
Test t2 = t1;
t2.x = 10;
Console.WriteLine("t1.x = {0}, t2.x = {1}", t1.x, t2.x);
}
}

- 124 -

Ha lefordtjuk ezt a kdot, azt fogjuk ltni, hogy t1.x rtke nem vltozott, teht nem
referencit adtunk t t2nek.
Most nzznk meg egy nagyon gyakori hibt, amibe belefuthatunk. Adott a
kvetkez programkd:
using System;
struct Point
{
int _x;
public int X
{
get { return_x; }
set { _x = value; }
}
int _y;
public int Y
{
get { return_y; }
set { _y = value; }
}
public Point(int x, int y)
{
_x = x;
_y = y;
}
}
struct Line
{
Point a;
public Point A
{
get { return a; }
set { a = value; }
}
Point b;
public Point B
{
get { return b; }
set { b = value; }
}
}
class Program
{
static public void Main()
{
Line l = new Line();
l.A = new Point(10, 10);
l.B = new Point(20, 20);
}
}

Teljesen szablyos forrs, le is fordul. Lthat, hogy a Point struktra publikus


tulajdonsgokkal br, vagyis jogosnak tnik, hogy a Line struktrn keresztl
mdostani tudjuk a koordintkat. Egsztsk ki a kdot:
- 125 -

static public void Main()


{
Line l = new Line();
l.A = new Point(10, 10);
l.B = new Point(20, 20);
l.A.X = 5;
}

Ez a forrskd nem fog lefordulni, mivel nem vltoznak akarunk rtket adni. Mi
lehet a hiba oka? A problma ott van, hogy rosszul rtelmeztk ezt a kifejezst. Az
l.A valjban a getter-t hvja meg, ami az eredeti struktra egy msolatval tr
vissza, amelynek a tagjait mdostani viszont nincs rtelme. Ilyen esetekben mindig
j struktrt kell ksztennk:
static public void Main()
{
Line l = new Line();
l.A = new Point(10, 10);
l.B = new Point(20, 20);
l.A = new Point(5, 10);
}

Ez a hiba viszonylag gyakran fordul el grafikus fellet alkalmazsok ksztse


kzben, mivel a .NET beptett Point tpusa szintn struktra.

18.5 rklds
Struktrk szmra az rklds tiltott, minden struktra automatikusan sealed
mdostt kap. Ilyen mdon egy struktra nem lehet absztrakt, tagjainak
elrhetsge nem lehet protected/protected internal, metdusai nem lehetnek
virtulisak illetve csak a System.ValueType) metdusait definilhatja t. Ez utbbi
esetben a metdushvsok nem jrnak bedobozolssal:
using System;
class Test
{
public int x;
public override string ToString()
{
return "X == " + x.ToString();
}
}
class Program
{
static public void Main()
{
Test t = new Test();
t.x = 10;
Console.WriteLine(t.ToString());
}
}

- 126 -

19

Gyakorl feladatok III.

19.1 Faktorilis s hatvny


Ksztsnk rekurzv faktorilis s hatvnyt szmt fggvnyeket!
Megolds (19/RecFact.cs s 19/RecPow.cs)
Mi is az a rekurzv fggvny? Egy fggvny, amely nmagt hvja. Rengeteg olyan
problma van, amelyeket tbb, lnyegben azonos feladatot vgrehajt rszre lehet
osztani. Vegyk pl. a hatvnyozst: semmi mst nem tesznk, mint meghatrozott
szm szorzst vgznk, mghozz ugyanazzal a szmmal. rhatunk persze egy
egyszer ciklust is, de ez egy kicsit atombombval egrre tpus megolds lenne.
Nzzk meg a hatvnyozs rekurzv megfeleljt:
using System;
class Program
{
static public double Pow(double x, int y)
{
if(y == 0){ return 1.0; }
else return x * Pow(x, y - 1);
}
static public void Main()
{
double result = Pow(2, 10);
Console.WriteLine(result); // 1024
}
}

Lthat, hogy xet (az alapot) rintetlenl hagyjuk, mg a kitevt (y) a fggvny
minden hvsakor eggyel cskkentjk, egszen addig, amg rtke nulla nem lesz,
ekkor befejezzk az rdgi krt s visszaadjuk az eredmnyt.
Hasonlkppen kszthetjk el a faktorilist szmol programot is:
using System;
class Program
{
static public int Fact(int x)
{
if(x == 0){ return 1; }
else return x * Fact(x - 1);
}

static public void Main()


{
int result = Fact(10);
Console.WriteLine(result);
}

- 127 -

19.2 Gyorsrendezs
Valstsuk meg a gyorsrendezst!
Megolds (19/QuickSort.cs)
A gyorsrendezs a leggyorsabb rendez algoritmus, nagy elemszm esetn
O(n*logn) nagysgrend tlaggal. Az algoritmus lnyege, hogy a rendezend
elemek kzl kivlaszt egy n. pivot elemet, amely el a nla nagyobb, mg pedig a
nla kisebb elemeket teszi, majd az gy kapott kt csoportra ismt meghvja a
gyorsrendezst (teht egy rekurzv algoritmusrl beszlnk). Lssuk, hogy hogyan is
nz ez ki a gyakorlatban!
Nagy elemszmnl ugyan jl teljest ez az algoritmus, de kevs elem esetn fordul a
kocka. ppen ezrt amikor a rendezsre tadott tmbrszlet kellen kicsi akkor egy
ltalnosabb rendezst, pl. buborkrendezst rdemes hasznlni.
A legtbb programozsi nyelv beptett rendezsei ltalban a gyorsrendezs egy
varicijt hasznljk.
class Array
{
private int[] array;
public Array(int length)
{
array = new int[length];
}
public int this[int idx]
{
get { return array[idx]; }
set { array[idx] = value; }
}
public int Length
{
get { return array.Length; }
}
public void Sort()
{
QuickSort(0, array.Length - 1);
}
private void QuickSort(int left, int right)
{
// rendezs...
}
}

Ksztettnk egy osztlyt, amely reprezentlja a rendezend tmbt. A Sort


metdussal fogjuk meghvni a tnyleges rendez metdust amely kt paramtert
kap, a rendezsre kivlasztott tmbrszlet als s fels indext. A metdus
implementcija a kvetkezkppen nz ki:
- 128 -

private void QuickSort(int left, int right)


{
int pivot = array[left];
int lhold = left;
int rhold = right;
while(left < right)
{
while(array[right] >= pivot && left < right)
{
--right;
}
if(left != right)
{
array[left] = array[right];
++left;
}
while(array[left] <= pivot && left < right)
{
++left;
}
if(left != right)
{
array[right] = array[left];
--right;
}
}
array[left] = pivot;
pivot = left;
left = lhold;
right = rhold;
if(left < pivot)
{
QuickSort(left, pivot - 1);
}
if(right > pivot)
{
QuickSort(pivot + 1, right);
}
}

A tmb mindkt oldalrl behatroljuk a kisebb/nagyobb elemeket majd tovbbhvjuk


a rendezst. Nzzk meg az algoritmus mkdst egy pldn keresztl! A
rendezend szmsorozat legyen:
3, 9, 4, 6, 8, 11
Left s right 0 s 5 (ugye hat elem van a tmbben, s nulltl indexelnk), ezeket az
rtkeket eltroljuk, mivel az rtkk mdosulni fog, de a metdus vgn szksg
van az eredetiekre.
Az els ciklus mivel nem fog a left indexen lv szmnl (3) kissebbet tallni gy
vgzdik, hogy right rtke 0 lesz. Az elgazsba mivel right s left egyenl nem
megynk bele, ugyangy ahogyan a msodik ciklusba sem, mivel a hrmasnl
kisebb elem nincs a nla nagyobbak pedig utna helyezkednek el. A kvetkez
- 129 -

elgazs szintn kimarad s nekillhatunk kiszmolni, hogy miknt hvjuk meg jra a
metdust. A tmb vltozatlan marad, a pivot vltoz nulla rtket kap, right s left
pedig visszakapjk az eredeti rtkket.
Ezutn a msodik elgazst fogjuk hasznlni (ne feledjk, hogy right rtke ismt 5)
s meghvjuk a QuickSort metdust 1 illetve 5 paramterekkel, vagyis az els elemet
(3) mivel mr a helyn van tugorjuk.
Kvetkezik a msodik fordul, a pivot vltoz rtke most kilenc lesz, mg left s right
rtke 1 s 5. Az els ciklus egyszer fog lefutni hiszen a tmb negyedik indexn l
nyolcas szm mr kissebb mint a pivot elem. Left nem egyenl right tal ezrt a
kvetkez elgazsba is bemegynk s a left indexre helyezzk a right indexen lv
nyolcast (a pivot elem pedig pont az itt mr nem lv kilencest trolja). Left elrelp
eggyel, hogy a tmb msodik indexre (4) mutasson.
A msodik ciklus is lefut, egszen addig fogjuk nvelni left rtkt, amg elri a right
ltal mutatott nyolcast, hiszen ott a ciklus felttel msodik fele srl. Most left s right
rtke egyenl: 4. ppen ezrt a msodik elgazst kihagyjuk s tovbb lpnk. A
left ltal mutatott indexre behelyezzk a pivotban lv kilencest, amivel helyre is ll a
rend. Pivot rtke ezutn ngy lesz s a kt msik vltoz is visszakapja az rtkt.
Left kissebb most, mint a pivot s right pedig nagyobb nla gy mindkt elgazs
felttele teljesl, vagyis most mindkt oldalra hvjuk a metdust.
Az ez utni esemnyek tgondolsa pedig az olvas feladata.

19.3 Lncolt lista


Valstsuk meg a lncolt lista adatszerkezetet!
Megolds (19/LinkedList.cs)
Amikor tmbkkel dolgozunk akkor a tmb elemeit indexekkel rjk el. A lncolt lista
olyan adatszerkezet, amelynek elemei a soron kvetkez elemre hivatkoz
referencit tartalmaznak. A lncolt listt az els fej- vagy gykrelemen keresztl
rjk el. Ha az elemek csak a kvetkez tagra mutatnak, akkor egyszeresen-, ha a
megelz elemre is, akkor ktszeresen lncolt listrl beszlnk:
vagy
Elsknt valstsuk meg az elemeket jelkpez osztlyt:
class Node
{
public Node(int value)
{
this.Value = value;
}
public int Value{ get; set; }
public Node Next{ get; set; }
public NodePrevious { get; set; }
}

- 130 -

Most pedig jjjn a lncolt lista osztly:


class LinkedList
{
public LinkedList(){ }
public LinkedList(int[] values)
{
foreach(int value in values)
{
this.Add(value);
}
}
public void Add(int value)
{
if(Root == null)
{
Root = new Node(value);
}
else
{
Node current = Root;
while(current.Next != null)
{
current = current.Next;
}
current.Next = new Node(value);
current.Next.Previous = current;
}
}
public NodeRoot { get; private set; }
}

Az Add metdus az, amelyik szmunkra rdekes. Elsknt megvizsgljuk, hogy


ltezik-e gykrelem, ha nem akkor ltrehozzuk s nincs is ms dolgunk (ugye
ilyenkor mg nincs se elz, se rkvetkez elem). Ms a helyzet, ha van mr
nhny elem a listban, ekkor meg kell keresnnk a legutols elemet s utna fzni
az jat (megvalsthattuk volna gy is a listt, hogy trolunk egy referencit az utols
elemre, ez lnyegesen gyorsabb lenne de kevsb rdekes).
Ahhoz, hogy megkeressk az utols elemet szksgnk lesz egy tmeneti
referencira, amely mindig az aktulis elemet mutatja majd. A ciklust addig kell
futtatni, ameddig az aktulis elem rkvetkezje null rtkre nem mutat, ekkor
belltjuk a Next s Previous rtkeket is.

19.4 Binris keresfa


Ksztsnk binris keresft!
Megolds (19/BinaryTree.cs)
A fa tpus olyan adatszerkezet, amelynek elemei nulla vagy tbb gyermekelemmel s
maximum egy szlelemmel rendelkeznek:
- 131 -

A
B

A kpen az A elem gyermekei B illetve C akiknek termszetesen A lesz a kzs


szlelemk.
A fa tpus egy specilis esete a binris fa, amely minden elemnek pontosan egy
szl s maximum kett gyermek eleme lehet. A binris fa specilis esete pedig a
binris keresfa, amelynek jellemzje, hogy egy szl elem bal oldali rszfjban a
szlelemnl kisebb jobboldali rszfjban pedig a szlelemnl nagyobb elemek
vannak, ezltal egyrtelmen meghatrozhat, hogy egy elem benne van-e a fban
vagy nincs (rtelemszeren a rszfk is keresfk, vagyis rjuk is ugyanez
vonatkozik). A binris keresfa minden elemnek egyedi kulcssal kell rendelkeznie,
vagyis ugyanaz az elem ktszer nem szerepelhet a fban. A keress mvelet
O(logn) nagysgrend.
Ahogyan az elz feladatban, most is ksztsk el a fa cscsait jelkpez osztlyt:
class TreeNode
{
public TreeNode(int value)
{
this.Value = value;
}
public int Value { get; set; }
public TreeNode Parent { get; set; }
public TreeNode Left { get; set; }
public TreeNode Right { get; set; }
}

Most pedig ksztsk el a fa osztlyt!


class BinaryTree
{
public BinaryTree() { }
public BinaryTree(int[] values)
{
foreach(int value in values)
{
this.Insert(value);
}
}
public void Insert(int value) { }
public TreeNode Root { get; private set; }
}

Az osztly vza hasonlt a lncolt listhoz, itt is szksgnk van egy gykrelemre,
ez tulajdonkppen a legels beszrt cscs lesz.
- 132 -

rjuk meg a hinyz Insert metdust:


public void Insert(int value)
{
if(Root == null)
{
Root = new TreeNode(value);
}
else
{
TreeNode current = Root;
while(current != null)
{
if(current.Value > value)
{
if(current.Left == null)
{
current.Left = new TreeNode(value);
current.Left.Parent = current;
return;
}
else
{
current = current.Left;
}
}
else if(current.Value < value)
{
if(current.Right == null)
{
current.Right = new TreeNode(value);
current.Right.Parent = current;
return;
}
else
{
current = current.Right;
}
}
else return;
}
}

Ha a gykrelem null rtken ll, akkor ksztnk egy j TreeNode objektumot,


egybknt megkeressk az j elem helyt oly mdon, hogy minden cscsnl a
megfelel irnyba fordulunk. Amennyiben az adott rtk mr szerepel a fban
egyszeren elhagyjuk a ciklust. Tegyk fel, hogy az elemek a kvetkez sorrendben
rkeznek:
10, 1, 4, 6, 6, 3, 9, 12
Ekkor a binris keresfa gy fog kinzni:

- 133 -

10

12

A kvetkez feladatunk, hogy kirjuk a fa elemeit a konzolra. Persze ez nem is olyan


egyszer, hiszen megfelel algoritmusra lesz szksgnk ahhoz, hogy a fa elemeit
bejrhassuk. Egy rekurzv algoritmust fogunk hasznlni, amely stratgitl fggen
az egyes cscsok rszfit majd magt a cscsot ltogatja meg. Hromfle stratgit
ismernk: preorder, inorder s postorder. A preorder elsknt a cscsot majd a bal
s jobb oldali rszft veszi kezelsbe. Inorder mdon a bal oldali rszfa, a cscs s a
jobb oldali rszfa lesz a sorrend vgl pedig a postorder bejrs sorrendje a bal
oldali rszfa, a jobb oldali rszfa vgl pedig a cscs. Nzzk meg, hogyan is
mkdik mindez a gyakorlatban! A fent felptett fn fogunk inorder mdon
vgigmenni.
Az algoritmust a gykrelemre (10) fogjuk meghvni, amely elsknt a bal oldali
rszfa cscst (1) fogja megltogatni. Mivel neki nincsen bal oldali rszfja, ezrt
kirjuk az egyes szmot s lpnk a jobb oldali rszfra (4). Neki mr van bal oldali
ga ezrt t vizsgljuk a tovbbiakban. Itt nincs gyermekelem, ezrt a kvetkez
szm, amit kirhatunk a hrom. Visszalpnk a szlelemre s kirjuk t (4) majd
lpnk jobbra. A hatos cscsnak sincs bal oldali fja, ezrt kirjuk, majd jn a jobb
fban a kilences amit megint csak kirunk hiszen nem rendelkezik gyermekelemmel.
Ezen a ponton vgeztnk a gykrelem bal oldali fjval ezrt t jelentjk meg,
ezutn pedig mr csak egyetlen elem marad. A vgs sorrend teht:
1, 3, 4, 6, 9, 10, 12
A forrskdban ez az algoritmus meglehetsen egyszeren jelenik meg, kvetkezzen
az inorder bejrs:

- 134 -

public void InOrder(Action<int> action)


{
_inOrder(Root, action);
}
private void _inOrder(TreeNode root, Action<int> action)
{
if(root == null) { return; }
_inOrder(root.Left, action);
action(root.Value);
_inOrder(root.Right, action);
}

Az Action<T> osztlyrl a Lambda kifejezsek c. fejezetben olvashat tbbet az


olvas.

- 135 -

20

rklds

rkldssel egy mr ltez tpust terjeszthetnk ki vagy bvthetjk tetszleges


szolgltatssal. A C# csakis egyszeres rkldst engedlyez (vagyis minden
osztly egyetlen sosztlybl szrmazhat, leszmtva a System.Object et),
ugyanakkor megengedi tbb interfsz impementlst (interfszekrl hamarosan).
Ksztsk el az elmleti rsz pldjt (llat-Kutya-Krokodil) C# nyelven. Az
egyszersg kedvrt hagyjuk ki az llat s Kutya kzti specilisabb osztlyokat:
class Animal
{
}
class Dog : Animal
{
}
class Crocodile : Animal
{
}

Az sosztlyt az osztlydeklarci utn rt kettspont mg kell tenni, szintn itt


lesznek majd az osztly ltal megvalstott interfszek is.
A Kutya s Krokodil osztlyok egyarnt megvalstjk az sosztly (egyelre
szegnyes) funkcionalitst. Bvtsk ht ki:
class Animal
{
public Animal(string name)
{
this.Name = name;
}
public string Name { get; set; }
public void Eat()
{
Console.WriteLine("Hamm - Hamm");
}
}

Vegyk szre, hogy paramteres konstruktort ksztettnk az sosztlynak, vagyis t


kell gondolnunk a pldnyostst. Az els vltozatot (az alaprtelmezett
konstruktorral) gy hasznlhattuk:
static public void Main()
{
Dog d = new Dog();
Crocodile c = new Crocodile();
}

- 136 -

Ha ezt az j Animal osztllyal prblnnk meg akkor meglepets fog rni, mivel nem
fordul le a program. Ahhoz, hogy ki tudjuk javtani a hibt tudnunk kell, hogy a
leszrmazott osztlyok elszr mindig a kzvetlen sosztly konstruktort hvjk
meg, vagyis ha nem adunk meg mst az alaprtelmezett konstruktort. A
problma az, hogy az sosztlynak mr nincs ilyenje, ezrt a leszrmazott
osztlyokban explicit mdon hvni kell a megfelel konstruktort:
class Animal
{
public Animal(string name)
{
this.Name = name;
}
public string Name { get; set; }
public void Eat()
{
Console.WriteLine("Hamm - Hamm");
}
}
class Dog : Animal
{
public Dog(string name) : base(name)
{
}

class Crocodile : Animal


{
public Crocodile(string name) : base(name)
{
}
}

Ezutn gy pldnyostunk:
static public void Main()
{
Dog d = new Dog("Bunds");
Crocodile c = new Crocodile("Aladr");
Console.WriteLine("{0} s {1}", d.Name, c.Name);
}

Ugyangy hasznlhatjuk az sosztly metdust is:


static public void Main()
{
Dog d = new Dog("Bunds");
Crocodile c = new Crocodile("Aladr");
d.Eat();
c.Eat();
}

- 137 -

Honnan tudja vajon a fordt, hogy egy sosztlybeli metdust kell meghvnia? A
referenciatpusok specilis mdon jelennek meg a memriban, rendelkeznek tbbek
kzt egy n. metdus-tblval, ami mutatja, hogy az egyes metdushvsoknl
melyik metdust kell meghvni. Persze ezt is meg kell hatrozni valahogy, ez nagy
vonalakban gy trtnik, hogy a fordt a fordts pillanatban megkapja a metdus
nevt s elindul visszafel az osztlyhierarchia mentn. A fenti pldban a hv
osztly nem rendelkezik Eat nev metdussal s nem is definilja t annak a
viselkedst (errl hamarosan), ezrt az eggyel feljebbi st kell megnznnk. Ez
egszen a lehet legjabb metdusdefinciig megy, s amikor megtallja a
megfelel implementcit, bejegyzi azt a metdustblba.

20.1 Virtulis metdusok


Az sosztlyban deklarlt virtulis (vagy polimorfikus) metdusok viselkedst a
leszrmazottak tdefinilhatjk. Virtulis metdust a szignatra el rt virtual kulcssz
segtsgvel deklarlhatunk:
using System;
class Animal
{
public virtual void Eat()
{
Console.WriteLine("Hamm - Hamm");
}
}
class Dog : Animal
{
public override void Eat()
{
Console.WriteLine("Vau - Vau - Hamm - Hamm");
}
}
class Crocodile : Animal
{
public override void Eat()
{
Console.WriteLine("Kro - Kro - Hamm - Hamm");
}
}
class Program
{
static public void Main()
{
Animal a = new Animal();
Dog d = new Dog();
Crocodile c = new Crocodile();
a.Eat();
d.Eat();
c.Eat();
}
}

- 138 -

A leszrmazott osztlyokban az override kulcsszval mondjuk meg a fordtnak,


hogy szndkosan hoztunk ltre az sosztlyval azonos szignatrj metdust s a
leszrmazott osztlyon ezt kvnjuk hasznlni mostantl. Egy overridedal jellt
metdus automatikusan virtulis is lesz, gy az leszrmazottai is tdefinilhatjk a
mkdst:
class Crocodile : Animal
{
public override void Eat()
{
Console.WriteLine("Kro - Kro - Hamm - Hamm");
}
}
class BigEvilCrocodile : Crocodile
{
public override void Eat()
{
Console.WriteLine("KRO - KRO - HAMM - HAMM");
}
}

Az utdosztly metdusnak szignatrja s lthatsga meg kell egyezzen azzal


amit t akarunk definilni.
Tegyk fel, hogy nem ismerjk az sosztly fellett s a hagyomnyos mdon
deklarljuk az Eat metdust (ugye nem tudjuk, hogy mr ltezik). Ekkor a program
ugyan lefordul, de a fordt figyelmezet minket, hogy eltakarjuk az rkltt metdust.
s valban, ha meghvnnk, akkor az j metdus futna le. Ezt a jelensget
rnykolsnak (shadow) nevezik.
Termszetesen mi azt szeretnnk, hogy a fordts hiba nlkl menne vgbe, gy
tjkoztatnunk kell a fordtt, hogy szndkosan takarjuk el az eredeti
implementcit. Ezt a new kulcsszval tehetjk meg:
class Animal
{
public virtual void Eat()
{
Console.WriteLine("Hamm - Hamm");
}
}
class Dog : Animal
{
public new void Eat()
{
Console.WriteLine("Vau - Vau - Hamm - Hamm");
}
}

Ezutn a Dog utdjai mr nem ltjk az eredeti Eat metdust. Viszont kszthetnk
belle virtulis metdust, amelyet az utdai mr kedvkre hasznlhatnak. Azaz, a
new mdostval elltott metdus j sort kezd, amikor a fordt felpti a
metdustblt, vagyis a new virtual kulcsszavakkal elltott metdus lesz az j
metdussorozat gykere.

- 139 -

class Dog : Animal


{
public new virtual void Eat()
{
Console.WriteLine("Vau - Vau - Hamm - Hamm");
}
}

Nem jellhetnk virtulisnak statikus, absztrakt s overridedal jellt tagokat (az


utols kett egybknt virtulis is lesz, de ezt nem kell kln jellni).

20.2 Polimorfizmus
Korbban mr beszltnk arrl, hogy az s s leszrmazottak kzt az-egy (is-a)
relci ll fent. Ez a gyakorlatban azt jelenti, hogy minden olyan helyen, ahol egy
stpust hasznlunk, ott hasznlhatunk leszrmazottat is (pl. egy llatkertben llatok
vannak, de az llatok helyre (nylvn) behelyettesthetek egy specilisabb fajt).
Pldul gond nlkl rhatom a kvetkezt:
Animal d = new Dog("Bunds");

A new opertor meghvsa utn d gy fog viselkedni, mint a Dog osztly egy
pldnya (elvgre az is lesz), hasznlhatja annak metdusait, adattagjait. Arra
azonban figyeljnk, hogy ez visszafel nem mkdik, a fordt hibt jelezne.
Abban az esetben ugyanis, ha a fordt engedn a visszafel konverzit az n.
leszeletelds (slicing) effektus lpne fel, azaz az adott objektum elveszten a
specilisabb osztlyra jellemz karakterisztikjt. A C++ nyelvben sokszor jelent
gondot ez a problma, mivel ott egy pointeren keresztl megtehet a lebutts.
Szerencsre a C# nyelvben ezt megoldottk, gy nem kell aggdnunk miatta.
Mi trtnik vajon a kvetkez esetben:
static public void Main()
{
Animal[] animalArray = new Animal[2];
animalArray[0] = new Animal();
animalArray[1] = new Dog();
animalArray[0].Eat();
animalArray[1].Eat();
}

Amit a fordt lt az az, hogy ksztettnk egy Animal tpus elemekbl ll tmbt
s, hogy az elemein meghvtuk az Eat metdust. Csakhogy az Eat egy virtulis
metdus, radsul van leszrmazottbeli implementcija is, amely tdefinlja az
eredeti viselkedst, s ezt explicit jelltk is az override kulcsszval. gy a fordt el
tudja dnteni a futsidej tpust, s ez ltal temezi a metdushvsokat. Ez az n.
ksi kts (late binding). A kimenet gy mr nem lehet ktsges.
Mr beszltnk arrl, hogyan pl fel a metdustbla, a fordt megkeresi a
legkorbbi implementcit, s most mr azt is tudjuk, hogy az els ilyen
- 140 -

implementci egy virtulis metdus lesz, azaz a keress legksbb az els virtulis
vltozatnl megll.

20.3 Lezrt osztlyok s metdusok


Egy osztlyt lezrhatunk, azaz megtilthatjuk, hogy j osztlyt szrmaztassunk belle:
sealed class Dobermann : Dog
{
}
class MyDobermann : Dobermann // ez nem j
{
}

Egy metdust is deklarlhatunk lezrtknt, ekkor a leszrmazottak mr nem


definilhatjk t a mkdst:
class Dog : Animal
{
public sealed override void Eat()
{
Console.WriteLine("Vau - Vau - Hamm - Hamm");
}
}
sealed class Dobermann : Dog
{
public override void Eat() // ez sem j
{
}

20.4 Absztrakt osztlyok


Egy absztrakt osztlyt nem lehet pldnyostani. A ltrehozsnak clja az, hogy
kzs felletet biztostsunk a leszrmazottainak:
using System;
abstract class Animal
{
abstract public void Eat();
}
class Dog : Animal
{
public override void Eat()
{
Console.WriteLine("Vau - Vau - Hamm - Hamm");
}
}

- 141 -

class Program
{
static public void Main()
{
// Animal a = new Animal(); //ez nem fordul le
Dog d = new Dog();
d.Eat();
}
}

Lthat, hogy mind az osztly, mind a metdus absztraktknt lett deklarlva,


ugyanakkor a metdus (ltszlag) nem virtulis s nincs defincija.
Egy absztrakt osztly csak a fordts kzben absztrakt, a lefordtott kdban teljesen
normlis osztlyknt szerepel, virtulis metdusokkal. A fordt feladata az, hogy
betartassa a r vonatkoz szablyokat. Ezek a szablyok a kvetkezk:
-

absztrakt osztlyt nem lehet pldnyostani


absztrakt metdusnak nem lehet defincija
a leszrmazottaknak definilnia kell az rkltt absztrakt metdusokat.

Absztrakt osztly tartalmazhat nem absztrakt metdusokat is, ezek pont gy


viselkednek, mint a hagyomnyos nem-virtulis trsaik. Az rkltt absztrakt
metdusokat az override kulcssz segtsgvel tudjuk definilni (hiszen virtulisak,
mg ha nem is ltszik).
Amennyiben egy osztlynak van legalbb egy absztrakt metdusa az osztlyt is
absztraktknt kell jellni.
Annak ellenre, hogy egy absztrakt osztlyt nem pldnyosthatunk mg lehet
konstruktora, mgpedig azrt, hogy bellthassuk vele az adattagokat:
abstract class Animal
{
public Animal(string name)
{
this.Name = name;
}
public string Name { get; set; }
abstract public void Eat();
}
class Dog : Animal
{
public Dog(string name) : base(name) { }
public override void Eat()
{
Console.WriteLine("Vau - Vau - Hamm - Hamm");
}
}

Vajon, hogyan mkdik a kvetkez pldban a polimorfizmus elve? :


Animal[] animalArray = new Animal[2];
animalArray[0] = new Dog("Bunds");
animalArray[1] = new Crocodile("Aladr");

- 142 -

Ennek a kdnak hiba nlkl kell fordulnia, hiszen tnylegesen egyszer sem
pldnyostottuk az absztrakt sosztlyt. A fordt csak azt fogja megvizsglni, hogy
mi van a new opertor jobb oldaln, az alaposztly nem rdekli. Termszetesen a
kvetkez esetben nem fordulna le:
animalArray[0] = new Animal("Animal");

- 143 -

21

Interfszek

Az interfszek hasonlak az absztrakt osztlyokhoz, abban az rtelemben, hogy


meghatrozzk egy osztly viselkedst, fellett. A nagy klnbsg a kett kzt az,
hogy mg elbbi eleve meghatroz egy osztlyhierarchit, egy interfsz nem kthet
kzvetlenl egy osztlyhoz, mindssze elr egy mintt, amit meg kell valstani. Egy
msik elnye az interfszek hasznlatnak, hogy mg egy osztlynak csak egy se
lehet, addig brmennyi interfszt megvalsthat. Ezen fell interfszt hasznlhatunk
struktrk esetben is. A kvetkez pldban trjuk az Animal sosztlyt interfszre:
using System;
interface IAnimal
{
void Eat();
}
class Dog : IAnimal
{
public void Eat()
{
Console.WriteLine("Vau - Vau - Hamm - Hamm");
}
}
class Program
{
static public void Main()
{
Dog d = new Dog();
d.Eat();
}
}

Az interfsz nevt konvenci szerint nagy I betvel kezdjk. Lthat, hogy a


metdusokhoz nem tartozik definci, csak deklarci. A megvalst osztly dolga
lesz majd implementlni a tagjait. Egy interfsz a kvetkezket tartalmazhatja:
metdusok, tulajdonsgok, indexelk s esemnyek (errl hamarosan). A tagoknak
nincs kln megadott lthatsguk, mindannyiuk elrhetsge publikus. Magnak az
interfsznek az elrhetsge alapesetben publikus, illetve jellhetjk internalknt,
msfle lthatsgot nem adhatunk meg (illetve osztlyon bell deklarlt
(begyazott) interfsz elrhetsge lehet privt).
Egy interfszt implementl osztlynak meg kell valstania az interfsz metdusait,
egyetlen kivtellel, ha a szban forg osztly egy absztrakt osztly, ekkor az
interfsz metdusait abszraktknt jellve elhalaszthatjuk a metdusdefincit az
absztrakt osztly leszrmazottainak implementlsig:
interface IAnimal
{
void Eat();
}
abstract class AbstractAnimal : IAnimal
{
public abstract void Eat();
}

- 144 -

Fontos, hogy amennyiben egy osztlybl is szrmaztatunk, akkor a felsorolsnl az


sosztly nevt kell elrevenni, utna jnnek az interfszek:
class Base { }
interface IFace { }
class Derived : IFace, Base { } // ez nem fordul le

Egy interfszt szrmaztathatunk ms interfszekbl:


using System;
interface IAnimal
{
void Eat();
}
interface IDog : IAnimal
{
void Vau();
}
class Dog : IDog
{
public void Eat()
{
Console.WriteLine("Vau - Vau - Hamm - Hamm");
}
public void Vau()
{
Console.WriteLine("Vau - Vau");
}
}
class Program
{
static public void Main()
{
Dog d = new Dog();
d.Eat();
d.Vau();
}
}

Ekkor termszetesen az sszes interfszt meg kell valstanunk.


Egy adott interfszt megvalst objektumot implicit mdon tkonvertlhatjuk az
interfsz tpusra:
static public
{
Dog d =
IAnimal
IDog id

void Main()
new Dog();
ia = d;
= d;

ia.Eat();
id.Vau();
}

- 145 -

Az is s as opertorokkal pedig azt is megtudhatjuk, hogy egy adott osztly


megvalste egy interfszt:
static public void Main()
{
Dog d = new Dog();
IAnimal ia = d as IAnimal;
if(ia != null)
{
Console.WriteLine("Az objektum megvalstja az IAnimal -t");
}
if(d is IDog)
{
Console.WriteLine("Az objektum megvalstja az IDog -ot");
}
}

21.1 Explicit interfszimplementci


Ha tbb interfszt implementlunk, az nvtkzshez is vezethet. Ennek
kikszblsre explicit mdon megadhatjuk a megvalstani kvnt funkcit:
using System;
interface IOne
{
void Method();
}
interface ITwo
{
void Method();
}
class Test : IOne, ITwo
{
public void Method()
{
Console.WriteLine("Method!");
}
}
class Program
{
static public void Main()
{
Test t = new Test();
t.Method();
}
}

Ez a forrskd lefordul, s a metdust is meg tudjuk hvni, a problma ott van, hogy
kt metdust kellene implementlnunk, de csak egy van viszont a program
mkdik. Nylvn nem ez az elvrt viselkeds, ezrt ilyen esetekben explicit mdon
meg kell mondanunk, hogy melyik metdus/tulajdonsg/etc... melyik interfszhez
tartozik. rjuk t a fenti kdot:
- 146 -

class Test : IOne, ITwo


{
void IOne.Method()
{
Console.WriteLine("IOne Method!");
}

void ITwo.Method()
{
Console.WriteLine("ITwo Method!");
}

Vegyk szre, hogy nem hasznltunk lthatsgi mdostt, ilyenkor az interfsz


lthatsga rvnyes ezekre a tagokra.
jabb problmnk van, mghozz az, hogy hogyan fogjuk meghvni a metdusokat?
Most fogjuk kihasznlni, hogy egy osztly konvertlhat a megvalstott interfszek
tpusra:
static public void Main()
{
Test t = new Test();
((IOne)t).Method(); // ez mkdik
ITwo it = t;
it.Method(); // ez is mkdik
}

21.2 Virtulis tagok


Egy interfsz tagjai alaprtelmezs szerint lezrtak, de a megvalstsnl jellhetjk
ket virtulisnak. Ezutn az osztly leszrmazottjai tetszs szerint mdosthatjk a
defincit, a mr ismert override kulcsszval:
class Dog : IDog, IAnimal
{
public void Eat()
{
Console.WriteLine("Vau - Vau - Hamm - Hamm");
}
public virtual void Vau()
{
Console.WriteLine("Vau - Vau");
}
}
class WuffDog : Dog
{
public override void Vau()
{
Console.WriteLine("Wuff - Wuff - Vau - Vau");
}
}

Egy leszrmazott jraimplementlhatja az adott interfszt, amennyiben nemcsak az


snl, de az utdnl is jelljk a megvalstst:
- 147 -

class WuffDog : Dog, IAnimal


{
public new void Eat()
{
Console.WriteLine("Wuff - Wuff - Hamm - Hamm");
}

public override void Vau()


{
Console.WriteLine("Wuff - Wuff - Vau - Vau");
}

Ez esetben hasznlnunk kell a new kulcsszt annak jellsre, hogy eltakarjuk az s


megvalstst.

- 148 -

22

Opertor kiterjeszts

Nylvn szeretnnk, hogy az ltalunk ksztett tpusok hasonl funkcionalitssal


rendelkezzenek, mint a beptett tpusok (int, string, stb).
Vegyk pl. azt a pldt, amikor egy mtrix tpust valstunk meg. J lenne, ha az
sszeads, kivons, szorzs, stb. mveleteket gy tudnnk vgrehajtani, mint egy
egsz szm esetben, nem pedig metdushvsokkal. Szerencsre a C# ezt is
lehetv teszi szmunkra, ugyanis engedi az opertorok kiterjesztst (operator
overloading), vagyis egy adott opertort tetszs szerinti funkcival ruhzhatunk fel az
osztlyunkra vonatkoztatva.
static public void Main()
{
Matrix m1 = new Matrix(20, 20);
Matrix m2 = new Matrix(20, 20);
//ez is j
m1.Add(m2);
//de ez mg jobb lenne
m1 += m2;
}

A kiterjeszthet opertorok listja:


+(unris)
-%
>>
>=

-(unris)
+
&
==
<=

!
|
!=

~
*
^
>

++
/
<<
<

A C# nyelvben az opertorok valjban statikus metdusok, paramtereik az


operandusok, visszatrsi rtkk pedig az eredmny. Egy egyszer plda:
class MyInt
{
public MyInt(int value)
{
this.Value = value;
}
public int Value { get; private set; }

static public MyInt operator+(MyInt lhs, MyInt rhs)


{
return new MyInt(lhs.Value + rhs.Value);
}

A + opertort mkdst fogalmaztuk t. A paramterek (operandusok) nevei


konvenci szerint lhs (left-hand-side) s rhs (right-hand-side), utalva a jobb s
baloldali operandusra.
Teht most mr nyugodtan rhatom a kvetkezt:

- 149 -

static public void Main()


{
MyInt x = new MyInt(10);
MyInt y = new MyInt(20);
MyInt result = x + y;
Console.WriteLine(result.Value);
}

Mivel definiltunk az osztlyunkon egy sajt opertort gy a fordt tudni fogja, hogy
azt hasznlja s talaktja a mveletet:
MyInt result = MyInt.operator+(x, y);

22.1 Egyenlsg opertorok


A .NET megfogalmaz nhny szablyt az opertor-kiterjesztssel kapcsolatban.
Ezek egyike az, hogy ha tlterheljk az egyenlsg opertort (==) akkor definilnunk
kell a nem-egyenl (!=) opertort is:
class MyInt
{
public MyInt(int value)
{
this.Value = value;
}
public int Value { get; private set; }
static public MyInt operator+(MyInt lhs, MyInt rhs)
{
return new MyInt(lhs.Value + rhs.Value);
}
static public bool operator==(MyInt lhs, MyInt rhs)
{
return lhs.Value == rhs.Value;
}

static public bool operator!=(MyInt lhs, MyInt rhs)


{
return !(lhs == rhs);
}

A nem-egyenl opertor esetben a sajt egyenlsg opertort hasznltuk fel (a


megvalsts elve nem felttlenl vilgos, elsknt megvizsgljuk, hogy a kt elem
egyenle, de mi a nem-egyenlsgre vagyunk kvncsiak, ezrt tagadjuk az
eredmnyt, ami pontosan ezt a vlaszt adja meg).
Ezekhez az opertorokhoz tartozik az object tpustl rklt virtulis Equals metdus
is, ami a CLS kompatibilitst hvatott megrizni, errl ksbb mg lesz sz. A fenti
esetben ezt a metdust is illik megvalstani. Az Equals azonban egy kicsit
klnbzik, egyetlen object tpus paramtert vr, ezrt meg kell majd gyzdnnk
arrl, hogy valban a sajt objektumunkkal van e dolgunk:
- 150 -

public override bool Equals(object rhs)


{
if(!(rhs is MyInt))
{
return false;
}
return this == (MyInt)rhs;
}

Mivel ez egy pldny tag ezrt a thist hasznljuk az objektum jellsre, amin
meghvtuk a metdust.
Ha az Equals t megvalstottuk, akkor ezt kell tennnk a szintn az object tl
rkltt GetHashCode metdussal is, gy az osztly hasznlhat lesz
gyjtemnyekkel s a HashTable tpussal is. A legegyszerbb implementci
visszaad egy szmot az adattag(ok)bl szmolva (pl.: hatvnyozs, biteltols, stb):
public override int GetHashCode()
{
return this.Value << 2;
}

22.2 A ++/-- opertorok


Ez a kt opertor elg nagy fejfjst tud okozni, nzzk meg a kvetkez kdot:
using System;
class MyInt
{
public MyInt(int value)
{
this.Value = value;
}
public int Value { get; private set; }
static public MyInt operator++(MyInt rhs)
{
++rhs.Value;
return rhs;
}
}
class Program
{
static public void Main()
{
MyInt x = new MyInt(10);
Console.WriteLine(x.Value);
++x;
Console.WriteLine(x.Value);
MyInt y = x++;
Console.WriteLine(x.Value);
Console.WriteLine(y.Value);
}
}

// 10
// 11
// 12
// 12 (!)

- 151 -

Nzzk az utols sort! Mivel ynak a postfixes formban adtunk rtket, ezrt 11et
kellene tartalmaznia, ehelyett x++ esetben pontosan ugyanaz trtnik, mint ha ++xet rtunk volna.
A problma, hogy kifejezetten postfixes opertort nem tudunk definilni, az ltalunk
ksztett kd minden esetben a prefix opertort jelenti. Viszont meghvhatjuk az
ltalunk definilt opertorokat postfixes alakban, ekkor az adott objektumot eltrolja a
rendszer, meghvja az rtket nvel kdrszletet, s visszadja az els lpsben
flrerakott rtket. Ugye ez rtktpusok esetben tkletesen mkdik, de
referenciatpusoknl az els lpsben nem az rtket hanem a referencit troljuk,
vagyis a ksbbi vltozs itt is rvnyben lesz.
Ugyanakkor ltezik mdszer arra, hogy mgis megoldjuk a fenti problmt: az
opertornak egy teljesen j objektumot kell visszaadnia, ekkor erre mr nem mutat
korbbi referencia, vagyis biztonsgosan hasznlhat (viszont az j mvelet (az
objektum ltrehozsa) miatt a teljestmnyre negatv hatssal lehet (ez persze nha
elfogadhat)).

22.3 Relcis opertorok


Hasonlan a logikai opertorokhoz a relcis opertorokat is csak prban lehet
elkszteni, vagyis (<, >) s (<=, >=):
static public bool operator<(MyInt lhs, MyInt rhs)
{
return lhs.Value < rhs.Value;
}
static public bool operator>(MyInt lhs, MyInt rhs)
{
return lhs.Value > rhs.Value;
}

Ebben az esetben az IComparable s IComparable<T> interfszek megvalstsa is


szksges lehet a klnbz gyjtemnyekkel val egyttmkds rdekben.
Ezekkel hamarosan tbbet is foglalkozunk.

22.4 Konverzis opertorok


A C# a szkebbrl tgabbra konverzikat implicit mdon (azaz klnsebb jells
nlkl), mg a tgabbrl szkebbre konvertlst explicite (ezt jellnnk kell) vgzi.
Termszetesen szeretnnk, ha a sajt tpusunk ilyesmire is kpes legyen, s bizony
erre is ltezik opertor. Ezeknl az opertoroknl az implicit illetve explicit
kulcsszavakkal fogjuk jellni a konverzi tpust:
static public implicit operator MyInt(int rhs)
{
return new MyInt(rhs);
}
static public explicit operator MyInt(string rhs)
{
return new MyInt(int.Parse(rhs));

- 152 -

Ezeket most gy hasznlhatjuk:


static public void Main()
{
MyInt x = 10; // implicit konverzi
MyInt y = (MyInt)"20"; //explicit konverzi
Console.WriteLine("x == {0}, y == {1}", x.Value, y.Value);
}

Fontos, hogy a konverzis opertorok mindig statikusak.

22.5 Kompatibilits ms nyelvekkel


Mivel nem minden nyelv teszi lehetv az opertorok kiterjesztst ezrt a CLS
javasolja, hogy ksztsk el a hagyomnyos vltozatot is:
static public MyInt Add(MyInt lhs, MyInt rhs)
{
return new MyInt(lhs + rhs);
}
static public MyInt operator+(MyInt lhs, MyInt rhs)
{
return new MyInt(lhs.Value + rhs.Value);
}

Ez termszetesen nem ktelez, de bizonyos helyzetekben jl jn.

- 153 -

23

Kivtelkezels

Nylvn vannak olyan esetek, amikor az alkalmazsunk, br gond nlkl lefordul,


mgsem gy fog mkdni, ahogy elkpzeltk. Az ilyen abnormlis mkds
kezelsre talltk ki a kivtelkezelst. Amikor az alkalmazsunk rossz llapotba
kerl, akkor egy n. kivtelt fog dobni, ilyennel mr tallkoztunk a tmbknl, amikor
tlindexeltnk:
using System;
class Program
{
static public void Main()
{
int[] array = new int[2];
array[2] = 10;
}
}

Itt az utols rvnyes index az 1 lenne, gy kivtelt kapunk, mgpedig egy


System.IndexOutOfRangeExceptiont. Ezutn a program lell. Termszetesen mi
azt szeretnnk, hogy valahogy kijavthassuk ezt a hibt, ezrt el fogjuk kapni a
kivtelt. Ehhez a mvelethez hrom dologra van szksgnk: kijellni azt a
programrszt, ami dobhat kivtelt, elkapni azt s vgl kezeljk a hibt:
using System;
class Program
{
static public void Main()
{
int[] array = new int[2];
try
{
array[2] = 10;
}
catch(System.IndexOutOfRangeException e)
{
Console.WriteLine(e.Message);
}
}

A try blokk jelli ki a lehetsges hibaforrst, a catch pedig elkapja a megfelel kivtelt
(arra figyeljnk, hogy ezek is blokkok, azaz a blokkon bell deklarlt vltozk a
blokkon kvl nem lthatak). A fenti programra a kvetkez lesz a kimenet:
A hiba: Index was outside the bounds of the array.
Lthat, hogy a kivtel egy objektum formjban ltezik. Minden kivtel se a
System.Exception osztly, gy ha nem specilisan egy kivtelt akarunk elkapni, akkor
rhattuk volna ezt is:

- 154 -

try
{
array[2] = 10;
}
catch(System.Exception e)
{
Console.WriteLine(e.Message);
}

Ekkor minden kivtelt el fog kapni a catch blokk. A System.Exception tulajdonsgai


kzl kettt kell megemltennk:
-

Message: ez egy olvashatbb formja a kivtel oknak.


InnerException: ez alaprtelmezetten null rtkkel rendelkezik, akkor kap
rtket, ha tbb kivtel is trtnik. rtelemszeren ekkor a legjabb kivtelt
kaphatjuk el s az InnerException n keresztl kvethetjk vissza az eredeti
kivtelig.

Nzzk meg, hogyan mkdnek a kivtelek. Kivtel kt mdon keletkezhet: vagy a


throw utastssal szndkosan mi magunk idzzk el, vagy az alkalmazs hibs
mkdse miatt.
Abban a pillanatban, amikor a kivtel megszletik a rendszer azonnal elkezdi keresni
a legkzelebbi megfelel catch blokkot, elsknt abban a metdusban amelyben a
kivtel keletkezett, majd ha ott nem volt sikeres, akkor abban amely ezt a metdust
hvta (s gy tovbb amg nem tall olyat amely kezeln).
A keress kzben kt dolog is trtnhet: ha egy statikus tag vagy konstruktor
inicializlsa trtnik, az ekkor szintn kivtellel jr, mghozz egy
System.TypeInitializationExceptionnel, amely kivtelobjektum InnerException
tulajdonsgba kerl az eredeti kivtel. A msik lehetsg, hogy nem tall megfelel
catch blokkot, ekkor a program futsa hibazenet trsasgban lell.
Ha mgis tallt hasznlhat catch blokkot, akkor a kivtel helyrl a vezrls a tallt
catch re kerl. Ha tbb egymsba gyazott kivtelrl van sz, akkor a megelz
catch blokkhoz tartoz finally blokk fut le, majd ezutn kvetkezik a catch blokk.
Kivtelt a throw utastssal dobhatunk:
using System;
class Program
{
static public void Main()
{
try
{
throw new System.Exception("Kivtel. Hurr!");
}
catch(Exception e)
{
Console.WriteLine(e.Message);
}
}
}

A C++-tl eltren itt pldnyostanunk kell a kivtelt.


- 155 -

A catchnek nem ktelez megadni a kivtel tpust, ekkor minden kivtelt elkap:
try
{
throw new System.Exception();
}
catch
{
Console.WriteLine("Kivtel. Hurr!");
}

Ilyenkor viszont nem hasznlhatjuk a kivtelobjektumot.

23.1 Kivtel hierarchia


Amikor kivtel dobdik, akkor a vezrlst az els alkalmas catch blokk veszi t. Mivel
az sszes kivtel a System.Exception osztlybl szrmazik, gy ha ezt adjuk meg a
catchnl, akkor az sszes lehetsges kivtelt el fogjuk kapni vele. Egyszerre tbb
catch is llhat egyms utn, de ha van olyan, amelyik az s kivtelt kapja el, akkor a
program csak akkor fog lefordulni, ha az az utols helyen ll, hiszen a tbbinek
eslye sem lenne.
using System;
class Program
{
static public void Main()
{
try
{
int[] array = new int[2];
array[3] = 10;
}
catch(System.IndexOutOfRangeException)
{
Console.WriteLine("OutOfRange");
}
catch(System.Exception)
{
Console.WriteLine("Exception");
}
}
}

Lthat, hogy a catchnek elg csak a kivtel tpust megadni, persze ekkor nem
hasznlhatjuk a kivtelobjektumot.

23.2 Kivtel ksztse


Mi magunk is kszthetnk kivtelt, a System.Exceptionbl szrmaztatva:

- 156 -

using System;
class MyException : System.Exception
{
public MyException() { }
public MyException(string message)
: base(message)
{
}

public MyException(string message, Exception inner)


: base(message, inner)
{
}

class Program
{
static public void Main()
{
try
{
throw new MyException("Kivtel. Hurr!");
}
catch(MyException e)
{
Console.WriteLine(e.Message);
}
}
}

23.3 Kivtelek tovbbadsa


Egy kivtelt az elkapsa utn ismt eldobhatunk. Ez hasznos olyan esetekben,
amikor feljegyzst akarunk kszteni illetve, ha egy specifikusabb kivtelkezelnek
akarjuk tadni a kivtelt:
try
{
}
catch(System.ArgumentException e)
{
throw; //tovbbadjuk
throw(new System.ArgumentNullException()); //vagy egy jat dobunk
}

Termszetesen a fenti pldban csak az egyik throw szerepelhetne leglisan,


ebben a formban nem fog lefordulni.
Ilyenkor bellthatjuk az Exception InnerException tulajdonsgt is:
try
{
throw new Exception();
}
catch(System.ArgumentException e)
{
throw(new System.ArgumentNullException("Tovbb", e));
}

- 157 -

Itt az Exception osztly harmadik konstruktort hasznltuk, az j kivtel mr


tartalmazni fogja a rgit is.

23.4 Finally blokk


A kivtelkezels egy problmja, hogy a kivtel keletkezse utn az ppen
vgrehajtott programrsz futsa megszakad, gy elfordulhat, hogy nem szabadulnak
fel idben az erforrsok (megnyitott file, hlzati kapcsolat, stb), illetve objektumok
olyan formban maradnak meg a memriban, amely hibt okozhat.
Megoldst a finallyblokk hasznlata jelent, amely fggetlenl attl, hogy trtnte
kivtel mindig lefut (kivve, ha a try blokk tartalmaz return kifejezst):
using System;
class Program
{
static public void Main()
{
int x = 10;
try
{
Console.WriteLine("x rtke a kivtel eltt: {0}", x);
throw new Exception();
}
catch(Exception)
{
Console.WriteLine("Kivtel. Hurr!");
}
finally
{
Console.WriteLine("Finally blokk");
x = 11;
}
Console.WriteLine("x rtke a kivtel utn {0}", x);
}
}

Valdi erforrsok kezelsekor knyelmesebb a usingblokk hasznlata, mivel az


automatikusan lezrja azokat. Lnyegben a using-blokkal hasznlt erforrsok
fordts utn a megfelel try-catch-finally blokkokk alakulnak.

- 158 -

24

Gyakorl feladatok IV.

24.1 IEnumerator s IEnumerable


Ksztsnk osztlyt,
interfszeket.

amely

megvalstja

az

IEnumerator

IEnumerable

Megolds
Korbban mr tallkoztunk a foreach ciklussal, s mr tudjuk, hogy csak olyan
osztlyokon kpes vgigiterlni, amelyek megvalstjk az IEnumerator s
IEnumerable interfszeket. Mindkett a System.Collections nvtrben tallhat.
Elsknt nzzk az IEnumerable interfszt:
public interface IEnumerable
{
IEnumerator GetEnumerator();
}

Ez a foreachnek fogja szolgltatni a megfelel felletet, ugyanis a ciklus meghvja a


metdust, s annak vissza kell adnia az osztlyt IEnumeratorknt (ld. implicit
konverzi). Ezrt kell megvalstani egyttal az IEnumerator interfszt is, ami gy nz
ki:
public interface IEnumerator
{
bool MoveNext();
void Reset();
object Current { get; }
}

A MoveNext a kvetkez elemre mozgatja a mutatt, ha tudja, ellenkez esetben


(vagyis ha a lista vgre rt) false rtkkel tr vissza. A Reset alaprtelmezsre
lltja a mutatt, azaz -1 re. Vgl a Current (read-only) tulajdonsg az aktulis
poziciban lv elemet adja vissza. Ennek object tpussal kell visszatrnie, hiszen
minden tpusra mkdnie kell (ltezik generikus vltozata is, de errl ksbb).
Hasznljuk az Animal osztlyunk egy kiss mdostott vltozatt:
public class Animal
{
public Animal(string name)
{
this.Name = name;
}
public string Name { get; private set; }
}

- 159 -

Most ksztsnk egy osztlyt, amelyen megvalstjuk a kt interfszt, s ami


tartalmaz egy Animal objektumokbl ll listt:
public class AnimalContainer : IEnumerable, IEnumerator
{
private ArrayList container = new ArrayList();
private int currPosition = -1;
public AnimalContainer()
{
container.Add(new Animal("Rex"));
container.Add(new Animal("Bunds"));
container.Add(new Animal("Parizer"));
}
}

Ez persze mg nem az egsz osztly, felvettnk egy ArrayListet, amiben eltroljuk az


objektumokat, illetve deklarltunk egy egsz szmot, ami az aktulis pozcit trolja
el s kezdrtkl -1et adtunk (ld. Reset).
Ksztsk el az IEnumerator ltal ignyelt metdusokat:
public bool MoveNext()
{
return (++currPosition < container.Count);
}
public object Current
{
get { return container[currPosition]; }
}
public void Reset()
{
currPosition = -1;
}

Vgl az IEnumerable interfszt valstjuk meg:


public IEnumerator GetEnumerator()
{
return (IEnumerator)this;
}

Ezutn hasznlhatjuk is az osztlyt:


static public void Main()
{
AnimalContainer ac = new AnimalContainer();
foreach(Animal animal in ac)
{
Console.WriteLine(animal.Name);
}
}

Amennyiben a foreach-en kvl akarjuk hasznlni az osztlyt pl. ha ksztettnk


indexelt is, akkor gondoskodnunk kell a megfelel konverzirl is (a foreach kivtelt
kpez, mivel ezt megteszi helyettnk).
- 160 -

24.2 IComparable s IComparer


Valstsuk meg az IComparable illetve IComparer interfszt!
Megolds
A msodik gyakorlati pldnkban az IComparable interfszt fogjuk megvalstani,
amelyre gyakran van szksgnk. Ez az interfsz ltalban olyan
adatszerkezeteknl kvetelmny, amelyek az elemeiken megvalstanak valamilyen
rendezst. A generikus List tpusmak is van rendez metdusa (Sort), amely ezzel a
metdussal dolgozik. Az IComparable egyetlen metdussal a CompareToval
rendelkezik, amely egy object tpust kap paramterl:
class ComparableClass : IComparable
{
public ComparableClass(int value)
{
this.Value = value;
}
public int Value { get; private set; }
public int CompareTo(object o)
{
if(o is ComparableClass)
{
ComparableClass c = (ComparableClass)o;
return Value.CompareTo(c.Value);
}
else throw(new Exception("Nem megfelel objektum..."));
}
}

Az osztlyban a beptett tpusok CompareTo metdust hasznltuk, hiszen k mind


megvalstjk ezt az interfszt. Ez a metdus -1et ad vissza, ha a hv fl kisebb,
0t, ha egyenl s 1et ha nagyobb. A hasznlata:
static public void Main()
{
List<ComparableClass> list = new List<ComparableClass>();
Random r = new Random();
for(int i = 0;i < 10;++i)
{
list.Add(new ComparableClass(r.Next(1000)));
}
foreach(ComparableClass c in list)
{
Console.Write("{0} ", c.Value);
}
Console.WriteLine("\nA rendezett lista:");
list.Sort();
foreach(ComparableClass c in list)
{
Console.Write("{0} ", c.Value);

- 161 -

}
Console.WriteLine();
}

A List<T> tpusrl bvebben a Generikusok cm fejezetben olvashatunk.


Hasonl feladatot lt el, de jval rugalmasabb az IComparer interfsz. Az IComparer
osztlyok nem rszei az eredeti osztlyoknak, gy olyan osztlyok esetn is
hasznlhatunk ilyet, amelyek implementcijhoz nem frnk hozz.
Pldul a List<T> rendezsnl megadhatunk egy sszehasonlt osztlyt, amely
megvalstja az IComparer interfszt, gy tetszleges rendezst valsthatunk meg
anlkl, hogy ki kellene egszteni a List<T> -t. Most is csak egy metdust kell
elksztennk, ez a Compare, amely kt object tpust vr paramtereknt:
class ComparableClassComparer : IComparer
{
public int Compare(object x, object y)
{
if(x is ComparableClass && y is ComparableClass)
{
ComparableClass _x = (ComparableClass)x;
ComparableClass _y = (ComparableClass)y;
return _x.CompareTo(_y);
}
else throw(new Exception("Nem megfelel paramter...");
}
}

A metdus elksztsnl az egyszersg miatt feltteleztk, hogy az


sszehasonltott osztly megvalstja az IComparable interfszt (a beptett tpusok
ilyenek), termszetesen mi magunk is megrhatjuk az eredmnyt elllt
programrszt. Ezutn a kvetkezkppen rendezhetjk a listt:
list.Sort(new ComparableClassComparer());

Az IComparer elnye, hogy nem ktdik szorosan az osztlyhoz (akr a nlkl is


megrhatjuk, hogy ismernnk a bels szerkezett), gy tbbfle megvalsts is
lehetsges.

24.3 Mtrix tpus


Ksztsk el a mtrix tpust s valstsuk meg rajta az sszeads mveletet! Az
egyszersg kedvrt tegyk fel, hogy a mtrix csak egsz szmokat trol.
Megolds

- 162 -

class Matrix
{
int[,] matrix;
public Matrix(int n, int m)
{
matrix = new int[n, m];
}
public int N
{
get { return matrix.GetLength(0); }
}
public int M
{
get { return matrix.GetLength(1); }
}
public int this[int idxn, int idxm]
{
get { return matrix[idxn, idxm]; }
set { matrix[idxn, idxm] = value; }
}
static public Matrix operator+(Matrix lhs, Matrix rhs)
{
if(lhs.N != rhs.N || lhs.M != rhs.M) return null;
Matrix result = new Matrix(lhs.N, lhs.M);
for(int i = 0;i < lhs.N;++i)
{
for(int j = 0;j < lhs.M;++j)
{
result[i, j] = lhs[i, j] + rhs[i, j];
}
}
return result;
}
}

Mtrixokat gy adunk ssze, hogy az azonos indexeken lv rtkeket sszeadjuk:


123 145 1+1 2+4 3+5
456 + 532 = (stb)
789 211
Az sszeads mveletet csakis azonos nagysg dimenzik mellet lehet elvgezni
(3x3as mtrixhoz nem lehet hozzadni egy 4x4 eset). Ezt ellenriztk is az
opertor megvalstsnl.
Most csak az sszeads mveletet valstottuk meg, a tbbi a kedves olvasra vr.
Plusz feladatknt indexellenrzst is lehet vgezni az indexelnl.

- 163 -

25

Delegateek

A delegateek olyan tpusok, amelyek egy vagy tbb metdusra hivatkoznak. Minden
delegate klnll objektum, amely egy listt trol a meghvand metdusokrl
(rtelemszeren ez egyttal ers referencia is lesz a metdust szolgltat osztlyra).
Nemcsak pldny-, hanem statikus metdusokra is mutathat. Egy delegate
deklarcijnl megadjuk, hogy milyen szignatrval rendelkez metdusok
megfelelek:
delegate int TestDelegate(int x);

Ez a delegate olyan metdusra mutathat, amelynek visszatrsi rtke int tpus s


egyetlen int tpus paramtere van:
public int Pow(int x)
{
return (x * x);
}

A hasznlata:
TestDelegate dlgt = Pow;
int result = dlgt(10);

A delegatekhez egynl tbb metdust is hozzadhatunk a += s + opertorokkal,


valamint elvehetjk ket a -= s opertorokkal. A delegate hvsakor az sszes a
listjn lv metdust meghvja a megadott paramterre.
class Test
{
public delegate void TestDelegate(string msg);
private TestDelegate handler;
public Test()
{
handler += Test.StaticMethod;
handler += this.InstanceMethod;
}
static public void StaticMethod(string msg)
{
Console.WriteLine(msg);
}
public void InstanceMethod(string msg)
{
Console.WriteLine(msg);
}
public void CallDelegate(string msg)
{
handler(msg);
}
}

- 164 -

A delegateek legnagyobb haszna, hogy nem kell elre megadott metdusokat


hasznlnunk, ehelyett ksbb tetszs szerint adhatjuk meg az elvgzend mveletet:
class Array
{
public delegate void Transformer(ref int item);
private int[] array;
public Array(int length)
{
Length = length;
array = new int[Length];
}
public int Length { get; set; }
public int this[int idx]
{
get { return array[idx]; }
set { array[idx] = value; }
}
public void Transform(Transformer t)
{
for(int i = 0;i < array.Length;++i)
{
t(ref array[i]);
}
}
}

A Transform metdus egy delegateet kap paramterl, amely elvgzi a


vltoztatsokat a tmbn. Pl.:
class Program
{
static public void TransformerMethod(ref int item)
{
item *= item;
}
static public void Main()
{
Array array = new Array(10);
for(int i = 0;i < array.Length;++i)
{
array[i] = i;
}
array.Transform(Program.TransformerMethod);
for(int i = 0;i < array.Length;++i)
{
Console.WriteLine(array[i]);
}
}
}

Kt delegate szerkezetileg nem egyenl, mg akkor sem ha


megegyezik:
- 165 -

a szignatrjuk

using System;
class Program
{
public delegate void Dlgt1();
public delegate void Dlgt2();
static public void Method() { }
static public void Main()
{
Dlgt1 d1 = Program.Method;
Dlgt2 d2 = d1; // ez hiba
}
}

Ugyanakkor ugyanazon delegate tpus pldnyai kztt hasznlhatjuk az == s !=


opertorokat. Kt delegate egyenl, ha mindkett rtke null, illetve ha a
hvslistjukon ugyanazon objektumok ugyanazon metdusai szerepelnek (vagy
ugyanazok a statikus metdusok):
using System;
class C1
{
public void CMethod() { }
}
class Program
{
public delegate void Test1();
static public void Method1() { }
static public void Method2() { }
static public void Main()
{
Test1 t1 = null;
Test1 t2 = null;
Console.WriteLine(t1 == t2); // True
t1 = Program.Method1;
t2 = Program.Method1;
Console.WriteLine(t1 == t2); // True
t1 += Program.Method2;
Console.WriteLine(t1 == t2); // False
t1 -= Program.Method2;
C1
C1
t1
t2

c1
c2
+=
+=

= new C1();
= new C1();
c1.CMethod;
c2.CMethod;

Console.WriteLine(t1 == t2); // False


}
}

- 166 -

25.1 Paramter s visszatrsi rtk


Egy delegatenek tadott metdus paramterei lehetnek olyan tpusok, amelyek az
eredeti paramternl ltalnosabbak:
using System;
class Animal { }
class Dog : Animal { }
class Cat : Animal { }
class Program
{
public delegate void DogDelegate(Dog d);
static public void AnimalMethod(Animal a) { }
static public void Main()
{
DogDelegate d = AnimalMethod;
}
}

Ez az n. kontravarins (contravariant) viselkeds.


Ennek a fordtottja igaz a visszatrsi rtkre, azaz az tadott metdus visszatrsi
rtke lehet specifikusabb az eredetinl:
using System;
class Animal { }
class Dog : Animal { }
class Cat : Animal { }
class Program
{
public delegate Animal GetAnimal();
static public Animal AnimalMethod() { return new Animal(); }
static public Dog DogMethod() { return new Dog(); }
static public Cat CatMethod() { return new Cat(); }
static public void Main()
{
GetAnimal ga = AnimalMethod;
Animal a = ga();
ga = DogMethod;
Dog d = (Dog)ga();
ga = CatMethod;
Cat c = (Cat)ga();
Console.WriteLine("{0}, {1}, {2}",
a.GetType(), d.GetType(), c.GetType());
}
}

- 167 -

Ezt pedig kovarins (covariant) viselkedsnek nevezzk.

25.2 Nvtelen metdusok


Egy delegatenek nem ktelez ltez metdust megadnunk, lehetsgnk van
helyben kifejteni egyet. Termszetesen ez a nvtelen metdus a program tbbi
rszbl kzvetlenl nem hvhat, csak a delegateen keresztl.
using System;
class Program
{
public delegate void Test(int x);
static public void Main()
{
Test t = delegate(int x)
{
Console.WriteLine(x);
};
t(10);
}
}

Egy nvtelen metdus elri az t trol blokk loklis vltozit s mdosthatja is ket:
using System;
class Program
{
public delegate void Test();
static public void Main()
{
int x = 10;
Test t = delegate()
{
x = 11;
Console.WriteLine(x);
};
t();
}
}

Ilyenkor figyelni kell arra, hogy a kls vltozra a delegate is ers referencival
mutat, vagyis a vltoz akkor vlik eltakarthatv, ha a delegate maga is rvnyt
veszti.
Nvtelen metdus nem hasznlhat semmilyen ugr utastst (pl.: goto, break, stb...).

- 168 -

25. Esemnyek
Egy osztly esemnyeket (event) hasznlhat, hogy a sajt llapota megvltozsakor
rtestsen ms osztlyokat. Ehhez a megfigyel osztlyoknak fel kell iratkozni a
megfigyelt osztly esemnyre azltal, hogy az elbbiek rendelkeznek egy, az
esemnynek megfelel szignatrj metdussal, n. esemnykezelvel. Az
esemny megtrtntekor ezek a metdusok fognak lefutni. Eddig ez nagyon gy
hangzik, mintha a delegateekrl beszltnk volna, s valban egy esemny
tulajdonkppen egy specilis delegate, mindssze hrom dologban klnbznek:
-

Esemny lehet rsze interfsznek mg delegate nem.


Egy esemnyt csakis az az osztly hvhat meg amely deklarlta.
Egy esemny rendelkezik add s remove metdusokkal
fellbrlhatak.

amelyek

Egy esemny deklarcijban meg kell adnunk azt a delegateet amely az


esemnyhez szksges szignatrt definilja. Nzznk egy egyszer pldt:
class Test
{
public delegate void EventHandlerDelegate(string message);
public event EventHandlerDelegate TestStatusChange;
private int data = 10;
public int Data
{
get { return data; }
set
{
data = value;
this.OnStatusChange();
}
}
private void OnStatusChange()
{
if(TestStatusChange != null)
{
TestStatusChange("Az osztly llapota megvltozott!");
}
}
}

Nem felttlenl kell delegateet deklarlnunk, mivel rendelkezsnkre ll a beptett


ltalnos EventHandler delegate, amely kt paramterrel (errl hamarosan)
rendelkezik s void visszatrsi tpussal br.
Az esemny akkor fog beindulni, amikor a data mez rtke megvltozik. Ekkor
meghvjuk az OnStatusChanged metdust, amely elsknt megvizsglja, hogy az
esemnyre feliratkoztake vagy sem - utbbi esetben a hvs kivtelt vltana ki. Ezt
az osztly gy hasznlhatjuk:

- 169 -

class Program
{
static public void Handler(string message)
{
Console.WriteLine(message);
}
static public void Main()
{
Test t = new Test();
t.TestStatusChange += Program.Handler;
t.Data = 11;
}
}

A fenti kdban a TestStatusChange gyakorlatilag delegateknt mkdik, vagyis egy


sajt listt tart fenn a meghvand metdusokrl (esemnykezelkrl).
Az esemnyekhez rendelt esemnykezelknek konvenci szerint (ettl eltrhetnk,
de a Framework esemnyei mind ilyenek) kt paramtere van, az els az az
objektum, amely kivltotta az esemnyt, a msodik pedig az esemnyhez
kapcsold informcik. A msodik paramter ekkor olyan tpus lehet, amely az
EventArgs osztlybl szrmazik. Mdostsuk ennek megfelelen a fenti programot.
Elsknt ksztnk egy EventArgs osztlybl szrmaz j osztlyt, amely kpes
trolni az esemnyhez kapcsold zenetet (az EventArgs alaprtelmezetten nem
redelekezik ilyesmivel csak egy alaposztly a specializlt esemnyekhez):
class TestEventArgs : EventArgs
{
public TestEventArgs(string message)
: base()
{
this.Message = message;
}
public string Message { get; set; }
}

Ezutn mr csak mdostani kell a delegateet s az esemny kivltst:


public delegate void EventHandlerDelegate(object sender, TestEventArgs e);
private void OnStatusChange()
{
if(TestStatusChanged != null)
{
TestStatusChanged(this, new TestEventArgs("Az osztly llapota
megvltozott"));
}
}

A sender paramternek a thisszel adjuk meg az rtkt, ezutn explicit konverzival


visszakaphatjuk belle a kld pldnyt (az els paramter szintn konvenci szerint
minden esetben object tpus lesz, mivel ugyanazt az esemnyt hasznlhatjuk
klnbz osztlyokkal).
Mg mdostsuk az esemnykezelt is:
- 170 -

static public void Handler(object sender, TestEventArgs e)


{
Console.WriteLine(e.Message);
}

A kvetkez pldban az esemnyek valdi hasznt fogjuk ltni. Ksztsnk egy


egyszer kliens-szerver alkalmazst (persze nem a hlzaton, csak szimulljuk). A
kliensek csatlakozhatnak a szerverhez (ezutn pedig kilphetnek). A feladat, hogy
minden ilyen esemnyrl kldjnk rtestst az sszes csatlakozott kliensnek.
Normlis esetben a szerver osztlynak trolnia kellene a kliensek hivatkozsait pl.
egy tmbben. A problma, hogy ez azrt nem olyan egyszer, hiszen gondoskodni
kell arrl, hogy a kilpett klienseket trljk a listbl, illetve a lista mrete is gondot
jelenthet. Esemnyek alkalmazsval viszont nagyon egyszer lesz a dolgunk.
Ksztsnk egy EventArgs osztlyt, amely segtsgnkre lesz az esemnykezelk
rtestsben:
class ServerEventArgs : EventArgs
{
public ServerEventArgs(string message)
: base()
{
this.Message = message;
}
public string Message { get; set; }
}

Most pedig a szervert ksztjk el:


class Server
{
public delegate void ServerEvent(object sender, ServerEventArgs e);
public event ServerEvent ServerChange;
public Server() { }
public void Connect(Client client)
{
this.ServerChange += client.ServerMessageHandler;
OnServerChange(string.Format("Felhasznl <{0}> csatlakozott!",
client.Name));
}
public void Disconnect(Client client)
{
OnServerChange(string.Format("Felhasznl <{0}> kilpett!",
client.Name));
this.ServerChange -= client.ServerMessageHandler;
}
protected void OnServerChange(string message)
{
if(ServerChange != null)
{
ServerChange(this, new ServerEventArgs(message));
}
}
}

- 171 -

Lthat, hogy a kliensek kezelse nagyon egyszer, mindssze egy mveletet kell
elvgeznnk az esemnyekre val feliratkozsrt/leiratkozsrt. Nzzk meg a
kliens osztlyt:
class Client
{
public Client(string name)
{
Name = name;
}
public string Name { get; set; }
public void ServerMessageHandler(object sender, ServerEventArgs e)
{
Console.WriteLine("{0} zenetet kapott: {1}",
this.Name, e.Message);
}
}

Vgl a Main:
static public void Main()
{
Server server = new Server();
Client c1 = new Client("Jzsi");
Client c2 = new Client("Bla");
Client c3 = new Client("Tomi");
server.Connect(c1);
server.Connect(c2);
server.Disconnect(c1);
server.Connect(c3);
}

- 172 -

26

Generikusok

Az objektum-orientlt programozs egyik alapkve a kd-jrafelhasznls, vagyis,


hogy egy adott kdrszletet elg ltalnosra rjunk meg ahhoz, hogy minl tbbszr
felhasznlhassuk. Ennek megvalstsra kt eszkz ll rendelkezsnkre, az egyik
az rklds, a msik pedig jelen fejezet trgya a generikusok.

26.1 Generikus metdusok


Vegyk a kvetkez metdust:
static public void Swap(ref int x, ref int y)
{
int tmp = x;
x = y;
y = tmp;
}

Ha szeretnnk, hogy ez a metdus ms tpusokkal is mkdjn, akkor bizony sokat


kell gpelnnk. Kivve, ha runk egy generikus metdust:
static public void Swap<T>(ref T x, ref T y)
{
T tmp = x;
x = y;
y = tmp;
}

A T fogja jelkpezni az aktulis tpust (lehet ms nevet is adni neki, eredetileg a


Template szbl jtt), ezt generikus paramternek hvjk. Generikus paramtere
csakis osztlynak vagy metdusnak lehet s ebbl tbbet is hasznlhatnak. Ezutn a
metdust a hagyomnyos ton hasznlhatjuk, a fordt felismeri, hogy melyik tpust
hasznljuk (ezt megadhatjuk mi magunk is explicite):
static public void Main()
{
int x = 10;
int y = 20;
Program.Swap<int>(ref x, ref y);
Console.WriteLine("x == {0} s y == {1}", x, y);
string s1 = "alma";
string s2 = "di";
Program.Swap<string>(ref s1, ref s2);
Console.WriteLine("s1 == {0} s s2 == {1}", s1, s2);
}

A C# generikusai hasonltanak a C++ sablonjaira, de annl kevsb hatkonyabbak,


cserben sokkal biztonsgosabb a hasznlatuk. Kt fontos klnbsg van a kett
- 173 -

kzt: mg a C++ fordtsi idben kszti el a specializlt metdusokat/osztlyokat,


addig a C# ezt a mveletet futsi idben vgzi el. A msik eltrs az elsbl
kvetkezik, mivel a C++ fordtskor ki tudja szrni azokat az eseteket, amelyek
hibsak, pl., sszeadunk kt tpust a sablonmetdusban, amelyeken nincs
rtelmezve sszeads. A C# ezzel szemben knytelen az ilyen problmkat
megelzni, a fordt csakis olyan mveletek elvgzst fogja engedlyezni, amelyek
mindenkppen mkdni fognak. A kvetkez plda nem fog lefordulni:
static public T Sum<T>(T x, T y)
{
return x + y;
}

Fordtskor csak azt tudja ellenrizni, hogy ltez tpust adtunke meg, gy nem
tudhatja a fordt, hogy sikeres lesze a vgrehajts, ezrt a fenti kd az sszeads
miatt (nem felttlenl valstja meg minden tpus) rossz.

26.2 Generikus osztlyok


Kpzeljk el, hogy azt a feladatot kaptuk, hogy ksztsnk egy verem tpust, amely
brmely tpusra alkalmazhat. Azt is kpzeljk el, hogy mg nem hallottunk
generikusokrl. gy a legkzenfekvbb megolds, ha az elemeket egy object tpus
tmbben troljuk:
class Stack
{
object[] t;
int pointer;
readonly int size;
public Stack(int capacity)
{
t = new object[capacity];
size = capacity;
pointer = 0;
}
public void Push(object item)
{
if(pointer >= size)
{
throw(new StackOverflowException("Tele van..."));
}
t[pointer++] = item;
}
public object Pop()
{
if(pointer-- >= 0) { return t[pointer]; }
pointer = 0;
throw(new InvalidOperationException("res..."));
}
}

- 174 -

Ezt most a kvetkezkppen hasznlhatjuk:


static public void Main()
{
Stack s = new Stack(10);
for(int i = 0;i < 10;++i)
{
s.Push(i);
}
for(int i = 0;i < 10;++i)
{
Console.WriteLine((int)s.Pop());
}

Mkdni mkdik, de se nem hatkony, se nem knyelmes. A hatkonysg az


rtk/referenciatpusok miatt cskken jelentsen (ld. boxing/unboxing), a knyelem
pedig amiatt, hogy mindig figyelni kell, pp milyen tpussal dolgozunk, nehogy olyan
kasztolssal ljnk ami kivtelt dob.
Ezeket a problmkat knnyen kikszblhetjk, ha generikus osztlyt ksztnk:
class Stack<T>
{
T[] t;
int pointer;
readonly int size;
public Stack(int capacity)
{
t = new T[capacity];
size = capacity;
pointer = 0;
}
public void Push(T item)
{
if(pointer >= size)
{
throw(new StackOverflowException("Tele van..."));
}
t[pointer++] = item;
}
public object Pop()
{
if(pointer-- >= 0)
{
return t[pointer];
}
pointer = 0;
throw(new InvalidOperationException("res..."));
}
}

Ezutn akrmelyik tpuson knnyen hasznlhatjuk:

- 175 -

static public void Main()


{
Stack<int> s = new Stack<int>(10);
for(int i = 0;i < 10;++i)
{
s.Push(i);
}

for(int i = 0;i < 10;++i)


{
Console.WriteLine(s.Pop());
}

26.3 Generikus megszortsok


Alaprtelmezetten egy generikus paramter brmely tpust jelkpezhet. A
deklarcinl azonban kikthetnk megszortsokat a paramterre. Ezeket a where
kulcsszval vezetjk be:
where
where
where
where
where
where

T
T
T
T
T
T

:
:
:
:
:
:

alaposztly
interfsz
osztly
struktra
new()
U

Az utols kt sor magyarzatra szorul. A new() megszorts olyan osztlyra utal,


amely rendelkezik alaprtelmezett konstruktorral. Az U pedig ebben az esetben egy
msik generikus paramtert jell, vagyis T olyan tpusnak felel meg amely vagy U
bl szrmazik, vagy egyenl vele.
Nzznk nhny pldt:
class Test<T>
where T : class
{
}

Ezt az osztlyt csak referenciatpus generikus paramterrel pldnyosthatjuk,


minden ms esetben fordtsi hibt kapunk.
class Test<T>
where T : struct
{
}

Ez pedig pp az ellenkezje, rtktpusra van szksg.

- 176 -

class Test<T>
where T : IEnumerable
{
}

Most csakis IEnumerable interfszt megvalst tpussal pldnyosthatjuk az


osztlyt.
class Base { }
class Derived : Base { }
class Test<T>
where T : Base
{
}

Ez mr rdekesebb. Ez a megszorts a generikus paramter sosztlyra


vonatkozik, vagyis pldnyosthatunk a Base s a Derived tpussal is.
class DefConst
{
public DefConst() { }
}
class Test<T>
where T : new()
{
}

Itt olyan tpusra van szksgnk, amely rendelkezik alaprtelmezett konstruktorral, a


DefConst osztly is ilyen.
class Base { }
class Derived : Base { }
class Test<T, U>
where T : U
{
}

Most T tpusnak olyannak kell lennie, amely implicit mdon konvertlhat U


tpusra, vagyis T vagy megegyezik Uval, vagy belle szrmazik:
static public void Main()
{
Test<Derived, Base> t1 = new Test<Derived, Base>(); // ez j
Test<Base, Derived> t2 = new Test<Base, Derived>(); // ez nem j
}

rtelemszeren rhattunk volna <Base, Base>-t, vagy <Derived, Derived>-et is.

- 177 -

26.4 rklds
Generikus osztlybl szrmaztathatunk is, ekkor vagy az sosztly egy specializlt
vltozatt vesszk alapul, vagy a nyers generikus osztlyt:
class Base<T>
{
}
class Derived<T> : Base<T>
{
}
//vagy
class IntDerived : Base<int>
{
}

26.5 Statikus tagok


Generikus tpusok esetben minden tpushoz kln statikus tag tartozik:
using System;
class Test<T>
{
static public int Value;
}
class Program
{
static public void Main()
{
Test<int>.Value = 10;
Test<string>.Value = 20;
Console.WriteLine(Test<int>.Value); // 10
Console.WriteLine(Test<string>.Value); // 20
}
}

26.6 Generikus gyjtemnyek


A C# 2.0 bevezetett nhny hasznos generikus adatszerkezetet, tbbek kzt listt s
vermet. A kvetkezkben megvizsglunk kzlk nhnyat. Ezek a szerkezetek a
System.Collections.Generic nvtrben tallhatak.

- 178 -

26.6.1 List<T>
A List<T> az ArrayList generikus, ersen tpusos megfelelje. A legtbb esetben a
List<T> hatkonyabb lesz az ArrayList nl, emellett pedig tpusbiztos is.
Amennyiben rtktpussal hasznljuk a List<T>-t az alaprtelmezetten nem ignyel
bedobozolst, de a List<T> rendelkezik nhny olyan mvelettel, amely viszont igen,
ezek fleg a keresssel kapcsolatosak. Azrt, hogy az ebbl kvetkez
teljestmnyromlst elkerljk, a hasznlt rtktpusnak meg kell valstania az
IComparable s az IEquatable interfszeket (a legtbb beptett egyszer
(rtk)tpus ezt meg is teszi) (ezeket az interfszeket az sszes tbbi gyjtemny is
ignyli).
using System;
using System.Collections.Generic;
class Program
{
static public void Main()
{
List<int> list = new List<int>();
for(int i = 0;i < 10;++i)
{
list.Add(i);
}

foreach(int item in list)


{
Console.WriteLine(item);
}

Az Add metdus a lista vghez adja hozz a paramterknt megadott elemet,


hasonlan az ArrayListhez. Hasznlhatjuk rajta az indexel opertort is.
A lista elemeit knnyen rendezhetjk a Sort metdussal (ez a metdus ignyli, hogy
a lista tpusa megvalstsa az IComparable interfszt):
using System;
using System.Collections.Generic;
class Program
{
static public void Main()
{
List<int> list = new List<int>();
Random r = new Random();
for(int i = 0;i < 10;++i)
{
list.Add(r.Next(1000));
}
list.Sort();
}
}

- 179 -

Kereshetnk is az elemek kztt a BinarySearch metdussal, amely a keresett


objektum indext adja vissza:
using System;
using System.Collections.Generic;
class Program
{
static public void Main()
{
List<string> list = new List<string>()
{
"alma", "di", "krte", "barack"
};
Console.WriteLine(list[list.BinarySearch("krte")]);
}

Megkereshetjk az sszes olyan elemet is, amely eleget tesz egy felttelnek a Find
s FindAll metdusokkal. Elbbi az els, utbbi az sszes megfelel pldnyt adja
vissza egy List<T> szerkezetben:
using System;
using System.Collections.Generic;
class Program
{
static public void Main()
{
List<int> list = new List<int>();
Random r = new Random();
for(int i = 0;i < 100;++i)
{
list.Add(r.Next(1000));
}
Console.WriteLine("Az elso pros szm a listban: {0}",
list.Find(delegate(int item)
{
return item % 2 == 0;
}));
List<int> evenList = list.FindAll(delegate(int item)
{
return item % 2 == 0;
});
Console.WriteLine("Az sszes pros elem a listban:");
evenList.ForEach(delegate(int item)
{
Console.WriteLine(item);
});
}
}

A felttelek megadsnl s a pros szmok listjnak kiratsnl nvtelen


metdusokat hasznltunk.

- 180 -

jdonsgot jelent a listn metdusknt hvott foreach ciklus. Ezt a C# 3.0 vezette be
s az sszes generikus adatszerkezet rendelkezik vele, lnyegben teljesen
ugyangy mkdik mint egy igazi foreach. paramtereknt egy void Method(T
item) szignatrj metdust (vagy nvtelen metdust) vr, ahol T a lista elemeinek
tpusa.

26.6.2 SortedList<T, U> s SortedDictionary<T, U>


A SortedList<T, U> kulcs rtk prokat trol el s a kulcs alapjn rendezi is ket:
using System;
using System.Collections.Generic;
class Program
{
static public void Main()
{
SortedList<string, int> list = new SortedList<string, int>();
list.Add("egy", 1);
list.Add("kett", 2);
list.Add("hrom", 3);
}

A lista elemei tulajdonkppen nem a megadott rtkek, hanem a kulcs rtk


prokat reprezentl KeyValuePair<T, U> objektumok. A lista elemeinek elrshez
is hasznlhatjuk ezeket:
foreach(KeyValuePair<string, int> item in list)
{
Console.WriteLine("Kulcs == {0}, rtk == {1}",
item.Key, item.Value);
}

A lista kulcsai csakis olyan tpusok lehetnek, amelyek megvalstjk az IComparable


interfszt, hiszen ez alapjn trtnik a rendezs. Ha ez nem igaz, akkor mi magunk is
definilhatunk ilyet, rszletekrt ld. az Interfszek fejezetet.
A listban minden kulcsnak egyedinek kell lennie (ellenkez esetben kivtelt
kapunk), illetve kulcs helyn nem llhat null rtk (ugyanez viszont nem igaz az
rtkekre).
A SortedDictionary<T, U> hasznlata gyakorlatilag megegyezik a SortedList<T, U>val, a klnbsg a teljestmnyben s a bels szerkezetben van.
A SD j (rendezetlen) elemek beszrst gyorsabban vgzi, mint a SL (O(Log n) s
O(n)). Elre rendezett elemek beszrsnl pont fordtott a helyzet. Az elemek kzti
keress mindkt szerkezetben O(Log n). Ezen kvl a SL kevesebb memrit
hasznl fel.

- 181 -

26.6.3 Dictionary<T, U>


A SortedDictionary<T, U> rendezetlen prja a Dictionary<T, U>:
using System;
using System.Collections.Generic;
class Program
{
static public void Main()
{
Dictionary<string, int> list = new Dictionary<string, int>();
list.Add("egy", 1);
list.Add("kett", 2);
list.Add("hrom", 3);

foreach(KeyValuePair<string, int> item in list)


{
Console.WriteLine("Kulcs == {0}, rtk == {1}",
item.Key, item.Value);
}

Teljestmny szempontjbl a Dictionary<T, U> mindig jobb eredmnyt fog elrni


(egy elem keresse kulcs alapjn O(1)), ezrt ha nem fontos szempont a
rendezettsg, akkor hasznljuk ezt.

26.6.4 LinkedList<T>
A LinkedList<T> egy ktirny lncolt lista. Egy elem beillesztse illetve eltvoltsa
O(1) nagysgrend mvelet. A lista minden tagja klnll objektum, egy-egy
LinkedListNode<T> pldny. A LLN<T> Next s Previous tulajdonsgai a megelz
illetve a kvetkez elemre mutatnak.
A lista First tulajdonsga az els, Last tulajdonsga pedig az utols tagra mutat.
Elemeket az AddFirst (els helyre szr be) s AddLast (utols helyre tesz)
metdusokkal tudunk beilleszteni.
using System;
using System.Collections.Generic;
class Program
{
static public void Main()
{
LinkedList<string> list = new LinkedList<string>();
list.AddLast("alma");
list.AddLast("di");
list.AddLast("krte");
list.AddFirst("narancs");
LinkedListNode<string> current = list.First;
while(current != null)

- 182 -

{
Console.WriteLine(current.Value);
current = current.Next;
}

Ebben a pldban bejrunk egy lncolt listt, a kimeneten a narancs elemet ltjuk
majd els helyen, mivel t az AddFirst metdussal helyeztk be.

26.6.5 ReadOnlyCollection<T>
Ahogy a nevbl ltszik ez az adatszerkezet az elemeit csak olvassra adja oda. A
listhoz nem adhatunk j elemet sem (ezt nem is tmogatja), csakis a konstruktorban
tlthetjk fel.
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel; // ez is kell
class Program
{
static public void Main()
{
List<string> list = new List<string>()
{
"alma", "krte", "di"
};
ReadOnlyCollection<string> roc = new
ReadOnlyCollection<string>(list);
foreach(string item in roc)
{
Console.WriteLine(item);
}
}

26.7 Generikus interfszek, delegate ek s esemnyek


A legtbb hagyomnyos interfsznek ltezik generikus vltozata is. Pldul az
IEnumerable s IEnumerator is ilyen:
class MyClass<T> : IEnumerable<T>, IEnumerator<T>
{
}

Ekkor a megvalsts teljesen ugyangy mkdik, mint a hagyomnyos esetben,


csak pp hasznlnunk kell a generikus paramter(eke)t.

- 183 -

A generikus adatszerkezetek (tbbek kztt) a generikus ICollection, IList s


IDictionary interfszeken alapulnak, gy ezeket megvalstva akr mi magunk is
ltrehozhatunk ilyet.
Az interfszekhez hasonlan a delegateek s esemnyek is lehetnek generikusak.
Ez az esetkben egyltaln nem jr semmilyen extra ktelezettsggel.

26.8 Kovariancia s kontravariancia


Nzzk a kvetkez osztlyhierarchit:
class Person
{
}
class Student : Person
{
}

A polimorfizmus elve miatt minden olyan helyen, ahol egy Person objektum
hasznlhat ott egy Student objektum is megfelel, legalbbis elvileg. Lssuk, a
kvetkez kdot:
List<Student> studentList = new List<Student>();
List<Person> personList = studentList;

A fenti kt sor, pontosabban a msodik nem fordul le, mivel a .NET nem tekinti
egyenlnek a generikus paramtereket, mg akkor sem, ha azok kompatibilisak
lennnek. Azt mondjuk, hogy a generikus paramterek nem kovarinsak
(covariance). A dolog fordtottja is igaz, vagyis nincs kompatibilits az ltalnosabb
tpusrl a szkebbre sem, nem kontravarinsak (contravariance).
Mirt van ez gy? Kpzeljk el azt a helyzetet, amikor a fenti osztlyokat kiegsztjk
mg egy Teacher osztllyal, amely szintn a Person osztlybl szrmazik. Ha a
generikus paramterek kovarinsan viselkednnek, akkor lehetsges lenne Student
s Teacher objektumokat is egy listba tenni ez pedig azzal a problmval jr, hogy
lehetsges lenne egy elem olyan tulajdonsgt mdostani amellyel nem rendelkezik,
ez pedig nylvn hibt okoz (persze tpusellenrzssel ez is thidalhat, de ezzel az
egsz generikus adatszerkezet rtelmt veszten).
A .NET 4.0 bevezeti a kovarins s kontravarins tpusparamtereket, gy oldva
meg a fent vzolt problmt, hogy a krdses tpusok csak olvashatak illetve csak
rhatak lesznek. A kvetkez pldban egy generikus delegate segtsgvel nzzk
meg az j lehetsgeket (j listaszerkezetet rni bonyolultabb lenne) (a plda
megrtshez szksg van a lambda kifejezsek ismeretre). Elszr egsztsk ki
a Person osztly egy Name tulajdonsggal:
class Student : Person
{
public string Name { get; set; }
}

- 184 -

Most lssuk a forrst:


delegate void Method<T>();
class Program
{
static public void Main()
{
Method<Student> m1 = () => new Student();
Method<Person> m2 = m1;
}
}

A fenti kd nem fordul le, mdostsuk a delegate deklarcijt:


delegate void Method<out T>();

Most viszont minden mkdik, hiszen biztostottuk, hogy minden tpust megfelelen
kezeljk. Most lssuk a kontravariancit:
delegate void Method<in T>(T t);
class Program
{
static public void Main()
{
Method<Person> m1 = (person) => Console.WriteLine(person.Name);
Method<Student> m2 = m1;
}
}

A .NET 4.0tl kezdve az sszes fontosabb interfsz (pl.: IEnumerable,


IComparable, etc...) kpes a kovarins illetve kontravarins viselkedsre.

- 185 -

27

Lambda kifejezsek

A C# 3.0 bevezeti a lambda kifejezseket. Egy lambda kifejezs gyakorlatilag


megfelel egy nvtelen metdus civilizltabb, elegnsabb vltozatnak (ugyanakkor
els rnzsre taln ijesztbb, de ha megszokta az ember sokkal olvashatbb kdot
eredmnyez).
Minden lambda kifejezs tartalmazza az n. lambda opertort (=>), ennek jelentse
nagyjbl annyi, hogy legyen. Az opertor bal oldaln a bemen vltozk, jobb
oldaln pedig a bemenetre alkalmazott kifejezs ll.
Mivel nvtelen metdus ezrt egy lambda kifejezs llhat egy delegate
rtkadsban is, elsknt ezt nzzk meg:
using System;
class Program
{
public delegate int IntFunc(int x);

static public void Main()


{
IntFunc func = (x) => (x * x);
Console.WriteLine(func(10));
}

Egy olyan metdusra van teht szksg amely egy int tpus bemen paramtert vr
s ugyanilyen tpust ad vissza. A lambda kifejezs bal oldaln a bemen paramter
(x) jobb oldaln pedig a visszadatott rtkrl gondoskod kifejezs (x * x) ll. A
bemen paramternl nem kell (de lehet) explicit mdon jeleznnk a tpust, azt a
fordt magtl kitallja (a legtbb esetre ez igaz, de nha szksg lesz r, hogy
jelljk a tpust).
Termszetesen nem csak egy bemen paramtert hasznlhatunk, a kvetkez
pldban sszeszorozzuk a lambda kifejezs kt paramtert:
using System;
class Program
{
public delegate int IntFunc2(int x, int y);
static public void Main()
{
IntFunc2 func = (x, y) => (x * y);
Console.WriteLine(func(10, 2));
}
}

27.1 Generikus kifejezsek


Generikus kifejezseknek (tulajdonkppen ezek generikus delegateek) is
megadhatunk lambda kifejezseket, amelyek nem ignylik egy elzleg definilt
delegate jelenltt, ezzel nll lambda kifejezseket hozva ltre (ugyanakkor a
- 186 -

generikus kifejezsek kaphatnak nvtelen metdusokat is). Ktfle generikus


kifejezs ltezik a Func, amely adhat visszatrsi rtket s az Action, amely nem
(void) (lsd: fggvny s eljrs). Elsknt a Funcot vizsgljuk meg:
using System;
class Program
{
static public void Main()
{
Func<int, int> func = (x) => (x * x);
Console.WriteLine(func(10));
}
}

A generikus paramterek kztt utols helyen mindig a visszatrsi rtk ll, eltte
pedig a bemen paramterek (maximum ngy) kapnak helyet.
using System;
class Program
{
static public void Main()
{
Func<int, int, bool> func = (x, y) => (x > y);
Console.WriteLine(func(10, 5)); // True
}

Most megnztk, hogy az els paramter nagyobbe a msodiknl. rtelemszeren


a lambda opertor jobb oldaln lv kifejezsnek megfelel tpust kell
eredmnyeznie, ezt a fordt ellenrzi.
A Func minden esetben rendelkezik legalbb egy paramterrel, mgpedig a
visszatrsi rtk tpusval, ez biztostja, hogy mindig legyen visszatrsi rtk.
using System;
class Program
{
static public void Main()
{
Func<bool> func = () => true;
Console.WriteLine(func()); // True
}

A Func prja az Action, amely szintn maximum ngy bemen paramtert kaphat, de
nem lehet visszatrsi rtke:

- 187 -

using System;
class Program
{
static public void Main()
{
Action<int> act = (x) => Console.WriteLine(x);
act(10);
}
}

27.2 Kifejezsfk
Generikus kifejezsek segtsgvel felpthetnk kifejezsfkat, amelyek olyan
formban troljk a kifejezsben szerepl adatokat s mveleteket, hogy futsi
idben a CLR ki tudja azt rtkelni. Egy kifejezsfa egy generikus kifejezst kap
generikus paramterknt:
using System;
using System.Linq.Expressions; // ez kell
class Program
{
static public void Main()
{
Expression<Func<int, int, bool>> expression =
(x, y) => (x > y);
Console.WriteLine(expression.Compile().Invoke(10, 2)); // True
}
}

A programban elszr IL kdra kell fordtani (Compile), csak azutn hvhatjuk meg.

27.3 Lambda kifejezsek vltozinak hatkre


Egy lambda kifejezsben hivatkozhatunk annak a metdusnak a paramtereire s
loklis vltozira, amelyben definiltuk. A kls vltozk akkor rtkeldnek ki,
amikor a delegate tnylegesen meghvdik, nem pedig a deklarlskor, vagyis az
adott vltoz legutols rtkadsa szmt majd. A felhasznlt vltozkat inicializlni
kell, mieltt hasznlnnk egy lambda kifejezsben. A lambda kifejezs fenntart
magnak egy msolatot a loklis vltozbl/paramterbl, mg akkor is, ha az
idkzben kifut a sajt hatkrbl:
using System;
class Test
{
public Action<int> act;
public void Method()
{
int local = 11;
act = (x) => Console.WriteLine(x * local);

- 188 -

local = 100;
}

class Program
{
static public void Main()
{
Test t = new Test();
t.Method();
t.act(100);
}
}

Ez a program 10000et fog kirni, vagyis valban a legutols rtkt hasznlta a


lambda a loklis vltoznak.
A loklis vltozk s paramterek mdosthatak egy lambda kifejezsben. A lambda
kifejezsben ltrehozott vltozk ugyangy viselkednek, mint a hagyomnyos loklis
vltozk, a delegate minden hvsakor j pldny jn ltre bellk.

27.4 Nvtelen metdusok kivltsa lambda kifejezsekkel


Lambda kifejezst hasznlhatunk minden olyan helyen, ahol nvtelen metdus
llhat. Nzzk meg pl., hogy hogyan hasznlhatjuk gy a List<T> tpust:
using System;
using System.Collections.Generic;
class Program
{
static public void Main()
{
List<int> list = new List<int>();
for(int i = 1;i < 10;++i)
{
list.Add(i);
}
int result = list.Find((item) => (item % 2 == 0));
Console.WriteLine("Az els pros szm: {0}", result);
List<int> oddList = list.FindAll((item) => (item % 2 != 0));
Console.WriteLine("Az sszes pratlan szm:");
oddList.ForEach((item) => Console.WriteLine(item));
}
}

Esemnykezelt is rhatunk gy:

- 189 -

class Test
{
public event EventHandler TestEvent;

public void OnTestEvent()


{
if(TestEvent != null)
{
TestEvent(this, null);
}
}

Az EventHandler ltalnos delegateet hasznltuk az esemny deklarcijnl. Az


esemny elindtsnl nincs szksgnk most EventArgs objektumra, ezrt itt
nyugodtan hasznlhatunk null rtket.
Most nzzk a programot:
class Program
{
static public void Main()
{
Test t = new Test();
t.TestEvent += (sender, e) =>
{
Console.WriteLine("Esemnykezel!");
};
t.OnTestEvent();
}

Lambda kifejezs helyett n. lambda lltst rtunk, gy akr tbb soros utastsokat
is adhatunk.

- 190 -

28

Attribtumok

Mr tallkoztunk nyelvbe ptett mdostkkal, mint amilyen a static vagy a virtual.


Ezek ltalban be vannak betonozva az adott nyelvbe, mi magunk nem kszthetnk
jakat kivve, ha a .NET Frameworkkel dolgozunk.
Egy attribtum a fordts alatt bepl a Metadata informcikba, amelyeket a futtat
krnyezet (a CLR) felhasznl majd az objektumok krelsa sorn.
Egy tesztels sorn ltalnosan hasznlt attribtum a Conditional, amely egy
elfordt ltal definilt szimblumhoz kti programrszek vgrehajtst. A
Conditional a System.Diagnostics nvtrben rejtzik:
#define DEBUG // definiljuk a DEBUG szimblumot
using System;
using System.Diagnostics; // ez is kell
class DebugClass
{
[Conditional("DEBUG")] // ha a DEBUG ltezik
static public void DebugMessage(string message)
{
Console.WriteLine("Debugger zenet: {0}", message);
}
}
class Program
{
static public void Main()
{
DebugClass.DebugMessage("Main metdus");
}
}

Egy attribtumot mindig szgletes zrjelek kzt adunk meg. Ha a programban nem
lenne definilva az adott szimblum a metdus nem futna le.
Szimblumok defincijt mindig a forrsfile elejn kell megtennnk, ellenkez
esetben a program nem fordul le.
Minden olyan osztly, amely brmilyen mdon a System.Attribute absztrakt
osztlybl szrmazik felhasznlhat attribtumknt. Konvenci szerint minden
attribtum osztly neve a nvbl s az utna rt Attribute szbl ll, gy a
Conditional eredeti neve is ConditionalAttribute, de az uttagot tetszs szerint
elhagyhatjuk, mivel a fordt gy is kpes rtelmezni a kdot.
Ahogy az mr elhangzott, mi magunk is kszthetnk attribtumokat:
class TestAttribute : System.Attribute { }
[Test]
class C { }

- 191 -

Ez nem volt tl nehz, persze az attribtum sem nem tl hasznos. Mdostsuk egy
kicsit! Egy attribtum-osztlyhoz tbb szablyt is kthetnk a hasznlatra
vonatkozan, pl. megadhatjuk, hogy milyen tpusokon hasznlhatjuk. Ezeket a
szablyokat az AttributeUsage osztllyal deklarlhatjuk. Ennek az osztlynak egy
ktelezen megadand s kt opcionlis paramtere van: ValidOn illetve
AllowMultiple s Inherited. Kezdjk az elsvel: a ValidOn azt hatrozza meg, hogy
hol hasznlhatjuk az adott attribtumot, pl. csak referenciatpuson vagy csak
metdusoknl (ezeket akr kombinlhatjuk is a bitenknti vagy opertorral(|)).
[AttributeUsage(AttributeTargets.Class)]
class TestAttribute : System.Attribute { }
[Test]
class C { }
[Test] // ez nem lesz j
struct S { }

gy nem hasznlhatjuk ezt az attribtumot. Mdostsunk rajta:


[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)]
class TestAttribute : System.Attribute { }

Most mr mkdni fog rtk- s referenciatpusokkal is. Az AttributeTargets.All pedig


azt mondja meg, hogy brhol hasznlhatjuk az attribtumot.
Lpjnk az AllowMultiple tulajdonsgra! azt fogja jelezni, hogy egy szerkezet
hasznlhat-e tbbet is egy adott attribtumbl:
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
class TestAttribute : System.Attribute { }
[Test]
[Test] // ez mr sok
class C { }

Vgl az Inherited tulajdonsg azt jelli, hogy az attribtummal elltott osztly


leszrmazottai is rklik-e az attribtumot:
[AttributeUsage(AttributeTargets.Class, Inherited = true)]
class TestAttribute : System.Attribute { }
[Test]
class C { }
class CD : C { }

A CD osztly a C osztly leszrmazottja s mivel az attribtum Inherited


tulajdonsga true rtket kapott ezrt is rkli az sosztlya attribtumt.
Attribtumok ktfle paramterrel rendelkezhetnek: positional s named. Elbbinek
mindig ktelez rtket adnunk, tulajdonkppen ez a konstruktor paramtere lesz
(ilyen az AttributeUsage osztly ValidOn tulajdonsga, amelynek mindig kell rtket
- 192 -

adnunk). Utbbiak az opcionlis paramterek, amelyeknek van alaprtelmezett


rtke, gy nem kell ktelezen megadnunk ket.
Eljtt az ideje, hogy egy hasznlhat attribtumot ksztsnk, mghozz egy olyat,
amellyel megjegyzseket fzhetnk egy osztlyhoz vagy struktrhoz. Az attribtumosztly nagyon egyszer lesz:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct,
AllowMultiple = false, Inherited = false)]
class DescriptionAttribute : System.Attribute
{
public DescriptionAttribute(string description)
{
this.Description = description;
}
public string Description { get; set; }
}

Az attribtumok rtkeihez pedig gy frnk hozz:


using System;
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct,
AllowMultiple = false, Inherited = false)]
class DescriptionAttribute : System.Attribute
{
public DescriptionAttribute(string description)
{
this.Description = description;
}
public string Description { get; set; }
}
[Description("Ez egy osztly")]
class C { }
class Program
{
static public void Main()
{
Attribute[] a = Attribute.GetCustomAttributes(typeof(C));
foreach(Attribute attribute in a)
{
if(attribute is DescriptionAttribute)
{
Console.WriteLine(((DescriptionAttribute)attribute).Description);
}
}
}

- 193 -

29

Unsafe kd

A .NET platform legnagyobb eltrse a natv nyelvektl a memria kezelsben


rejlik. A menedzselt kd nem enged kzvetlen hozzfrst a memrihoz, vagyis
annyi a dolgunk, hogy megmondjuk, hogy szeretnnk egy ilyen s ilyen tpus
objektumot, a rendszer elkszti neknk s kapunk hozz egy referencit, amelyen
elrjk. Nem fogjuk tudni, hogy a memriban hol van s nem is tudjuk thelyezni.
pp ezrt a menedzselt kd biztonsgosabb, mint a natv, mivel a fentiek miatt egy
egsz sor hibalehetsg egsz egyszeren eltnik. Nylvn ennek ra van,
mghozz a sebessg, de ezt behozzuk a memria gyorsabb elrsvel/kezelsvel
ezrt a kt mdszer kztt lnyegben nincs teljestmnybeli klnbsg.
Vannak azonban helyzetek, amikor igenis fontos, hogy kzvetlenl elrjk a
memrit:
-

A lehet legjobb teljestmnyt szeretnnk elrni egy rendkvl


szmtsignyes feladathoz (pl.: szmtgpes grafika).
.NETen kvli osztlyknyvtrakat akarunk hasznlni (pl.: Windows API
hvsok).

A C# a memria direkt elrst mutatkon (pointer) keresztl teszi lehetv. Ahhoz,


hogy mutatkat hasznlhassunk az adott metdust, osztlyt, adattagot vagy blokkot
az unsafe kulcsszval kell jellnnk (ez az n. unsafe context). Egy osztlyon bell
egy adattagot vagy metdust jellhetnk unsafe mdostval, de ez nem jelenti azt,
hogy maga az osztly is unsafe lenne. Nzzk a kvetkez pldt:
using System;
class Test
{
public unsafe int* x;
}
class Program
{
static public void Main()
{
unsafe
{
Test t = new Test();
int y = 10;
t.x = &y;
Console.WriteLine(*t.x);
}
}
}

Elszr deklarltunk egy unsafe adattagot, mghozz egy int tpusra mutat pointert.
A pointer tpus az rtk- s referenciatpusok mellett a harmadik tpuskategria. A
pointerek nem szrmaznak a System.Objectbl s konverzis kapcsolat sincs
kzttk (br az egyszer numerikus tpusokrl ltezik explicit konverzi).
rtelemszeren boxing/unboxing sem alkalmazhat rajtuk. Egy pointer mindig egy
memriacmet hordoz, amely memriaterleten egy teljesen normlis objektum van.
- 194 -

Ebbl kvetkezen a fenti deklarciban nem adhatok azonnal rtket az unsafe


pointernek, mivel numerikus rtkads esetn nem fordul le a program (hiszen nem
egy int objektumrl van sz), ms objektum memriacmt viszont nem tudom.
Sebaj, erre val az n. cmeopertor (&) amellyel tadhatom egy hagyomnyos
objektum cmt.
A programban ki akarjuk rni a memriaterleten lv objektum rtkt, ezt a
dereference opertorral (*) tehetjk meg. Ez visszaadja a mutatott rtket, mg ha
magt a vltozt hasznljuk az csak a memriacmet. A memriacm a memria
egy adott bytejra mutat (vagyis a pointer nvelse/cskkentse a pointer
tpusnak megfelel mennyisg byteal rakja odbb a mutatt, teht egy int pointer
esetn ez ngy byte lesz), amely az adott objektum kezdcme. A pointer gy tudja
visszaadni az rtkt, hogy tudja mekkora mret az objektum (pl. egy int pointer egy
32 bites 4 byte mret terletet vesz majd el).
A programot parancssorbl a /unsafe kapcsolval fordthatjuk le, Visual Studio
esetn jobb klikk a projecten, Properties s ott lltsuk be.
csc /unsafe main.cs
A megfelel explicit konverzival a memriacmet is lekrhetjk:
using System;
class Program
{
static public void Main()
{
unsafe
{
int x = 10;
int* y = &x;
Console.WriteLine((int)y);
}

Pointer csakis a beptett numerikus tpusokra (belertve a char is), logikai tpusokra,
felsorolt tpusokra, ms pointerekre illetve minden olyan ltalunk ksztett struktrra
hivatkozhat, amely nem tartalmaz az eddig felsoroltakon kvl mst. Ezeket a
tpusokat sszefoglal nven unmanaged tpusoknak nevezzk.
Explicit konverzi ltezik brmely kt pointer tpus kztt, ezrt fennllhat a veszlye,
hogy ha A s B pointer nem ugyanakkora mret terletre mutat akkor az Arl Bre
val konverzi nem definilt mkdst okoz:
int x = 10;
byte y = 20;
int* p1 = &x; // ez j
p1 = (int*)&y; // ez nem biztos, hogy j

Implicit konverzi van viszont brmely pointer tpusrl a void* univerzlis pointer
tpusra. A void*-on nem hasznlhat a dereference opertor:
- 195 -

using System;
class Program
{
static public void Main()
{
unsafe
{
int x = 10;
void* p1 = &x;
Console.WriteLine( *((int*)p1) );
}
}

Egy struktrra is hivatkozhatunk pointerrel, ekkor a tagjait ktflekppen rhetjk el:


vagy a mutatn keresztl, vagy a nyl (->) opertorral amely tulajdonkppen az elbbi
rvidtse:
using System;
struct Test
{
public int x;
}
class Program
{
static public void Main()
{
unsafe
{
Test t = new Test();
t.x = 10;
Test* p = &t;
Console.WriteLine((*p).x); // 10
Console.WriteLine(p->x); // 10
}
}

29.1 Fix objektumok


Normlis esetben a szemtgyjt a memria tredezettsg mentestse rdekben
mozgatja az objektumokat a memriban. Egy pointer azonban mindig egy fix helyre
mutat, hiszen a mutatott objektumnak a cmt krtk le, ami pedig nem fog frisslni.
Ez nhny esetben gondot okozhat (pl. amikor hosszabb ideig van a memriban a
mutatott adat, pl. valamilyen erforrs). Ha szeretnnk, hogy az objektumok a
helykn maradjanak, akkor a fixed kulcsszt kell hasznlnunk.
Mieltt jobban megnznnk a fixedet vegyk szemgyre a kvetkez forrskdot:

- 196 -

using System;
class Program
{
static public void Main()
{
unsafe
{
int[] array = new int[] { 1, 3, 4, 6, 7 };
int* p = &array;
}
}

Ez a program nem fordul le. Br a tmbk referenciatpusok mgis kivtelt kpeznek,


mivel hasznlhatunk rajtuk pointert, igaz nem az eddig ltott mdon (valamint a tmb
elemeinek unmanaged tpusnak kell lennik). Referenciatpuson bell deklarlt
rtktpusok esetn (ilyenek a tmbelemek is) fixlni kell az objektum helyzett, hogy
a GC ne mozdthassa el. A kvetkez kd mr mkdni fog:
using System;
class Program
{
static public void Main()
{
unsafe
{
int[] array = new int[] { 1, 3, 4, 6, 7 };
fixed(int* p = array)

for(int i = 0;i < 5;++i)


{
Console.WriteLine(*(p + i));
}

}
}

29.2 Natv DLL kezels


Elfordu, hogy olyan metdust akarunk hvni, amelyet egy natv (pl. C++ nyelven rt)
DLL tartalmaz. A kvetkez pldban egy Windows API metdust hvunk meg, amely
megjelent egy MessageBoxot. Ahhoz, hogy ezt meg tudjuk tenni elssorban
ismernnk kell az eredeti metdus szignatrjt, ehhez szksgnk lesz pl. a Win32
Programmers Reference cm dokumentcira amelyet letlthetnk:
http://www.winasm.net/win32hlp.html
Keressk ki a MessageBox metdust, s a kvetkezt fogjuk ltni:

- 197 -

int MessageBox(
HWND hWnd,
// handle of owner window
LPCTSTR lpText,
// address of text in message box
LPCTSTR lpCaption, // address of title of message box
UINT uType
// style of message box
);

Ezen kvl mg tudnunk kell azt is, hogy melyik DLL trolja az adott fggvnyt, ez
jelen esetben a user32.dll lesz.
Mindent tudunk, ksztsk el a programot! A fggvny importlshoz a DllImport
attribtumot fogjuk hasznlni:
using System;
using System.Runtime.InteropServices;
public class Program
{
static public void Main()
{
API.MessageBox(0, "Hello!", "Nincs", 0);
}
}
public class API
{
[DllImport("user32.dll")]
public static extern int MessageBox(int hWnd,
string text, string caption, uint type);
}

Az attribtumnak meg kell adnunk a dll nevt, valamint a metdus-deklarcit az


extern kulcsszval kell jellnnk, hogy tudassuk a fordtval, hogy a fggvnyt egy
kld forrsbl kell elkertenie.
A forrskdot a szoksos mdon fordtjuk, s ha elindtjuk, akkor a kvetkezt kell
ltnunk:

- 198 -

30

Tbbszl alkalmazsok

Egy Windows alap opercis rendszerben minden futtathat llomny indtsakor


egy n. process jn ltre, amely teljes mrtkben elklnl az sszes tbbitl. Egy
processt az azonostja (PID Process ID) alapjn klnbztetnk meg a tbbitl.
Minden egyes process rendelkezik egy n. f szllal (primary vagy main thread),
amely a belpsi pontja a programnak (ld. Main).
Azokat az alkalmazsokat, amelyek csak f szllal rendelkeznek thread-safe
alkalmazsoknak nevezzk, mivel csak egy szl fr hozz az sszes erforrshoz.
Ugyanakkor ezek az alkalmazsok hajlamosak elaludni, ha egy komplexebb
feladatot hajtanak vgre, hiszen a f szl ekkor nem tud figyelni a felhasznl
interakcijra.
Az ilyen helyzetek elkerlsre a Windows (s a .NET) lehetv teszi msodlagos
szlak (n. worker thread) hozzadst a f szlhoz. Az egyes szlak (a process
ekhez hasonlan) nllan mkdnek a folyamaton bell s versenyeznek az
erforrsok hasznlatrt (concurrent access).
J plda lehet a szlkezels bemutatsra egy szvegszerkeszt hasznlata: amg
kinyomtatunk egy dokumentumot (egy mellkszllal) az alkalmazs f szla tovbbra
is figyeli a felhasznltl rkez utastsokat.
A tbbszl programozs legnagyobb kihvsa a szlak s feladataik
megszervezse, az erforrsok elosztsa.
Fontos megrtennk, hogy valjban a tbbszlsg a szmtgp ltal nyjtott
illzi, hiszen a processzor egyszerre csak egy feladatot tud vgrehajtani (br
terjednek a tbbmagos rendszerek, de gondoljunk bele, hogy amikor hozz sem
nylunk a szmtgphez is tven szz szl fut), gy el kell osztania az egyes
feladatok kzt a processzoridt (ezt a szlak prioritsa alapjn teszi) ez az n.
idosztsos (time slicing) rendszer. Amikor egy szlnak kiosztott id lejr, akkor a
futsi adatait eltrolja az n. Thread Local Storageben (ebbl minden szlnak van
egy) s tadja a helyet egy msik szlnak, amely ha szksges betlti a sajt
adatait a TLS-bl s elvgzi a feladatt.
A .NET szmos osztlyt s metdust bocst rendelkezsnkre, amelyekkel az egyes
processeket felgyelhetjk, ezek a System.Diagnostics nvtrben vannak. rjunk
egy programot, amely kirja az sszes fut folyamatot s azonostjukat:
using System;
using System.Diagnostics;
class Program
{
static public void Main()
{
Process[] processList = Process.GetProcesses(".");

foreach(Process process in processList)


{
Console.WriteLine("PID: {0}, Process - nv: {1}",
process.Id, process.ProcessName);
}

- 199 -

Amennyiben
tudjuk
a
process
azonostjt,
Process.GetProcessById(azonost) metdust is.

akkor

hasznlhatjuk

A kvetkez programunk az sszes fut process minden szlt s azoknak adatait


fogja kilistzni:
using System;
using System.Diagnostics;
class Program
{
static public void Main()
{
Process[] processList = Process.GetProcesses(".");
foreach(Process process in processList)
{
Console.WriteLine("A folyamat ({0}) szlai",
process.ProcessName);
ProcessThreadCollection ptc = process.Threads;
foreach(ProcessThread thread in ptc)
{
Console.WriteLine("Id: {0}, llapot: {1}",
thread.Id, thread.ThreadState);
}
}
}

Elg valszn, hogy a program futsakor kivtelt kapunk, hiszen a szlak listjba
olyan szl is bekerlhet, amely a kirskor mr befejezte futst (ez szinte mindig az
Idle process esetben fordul el, a meggondols hzi feladat, akrcsak a program
kivtel-biztoss ttele).
A fenti osztlyok segtsgvel remekl bele lehet ltni a rendszer lelkbe, az
MSDNen megtalljuk a fenti osztlyok tovbbi metdusait, tulajdonsgait amelyek
az utazshoz szksgesek.
A kvetkez programunk a processek irnytst szemllteti, indtsuk el az Internet
Explorert, vrjunk t msodpercet s lltsuk le:
using System;
using System.Diagnostics;
using System.Threading;
class Program
{
static public void Main()
{
Process explorer = Process.Start("iexplore.exe");
Thread.Sleep(5000);
explorer.Kill();
}
}

- 200 -

Egyttal felhasznltuk az els igazi szlkezelshez tartoz metdusunkat is, a


Thread osztly statikus Sleep metdust (a Thread osztly a System.Threading
nvtrben tallhat).

30.1 Application Domain -ek


Egy .NET program nem direkt mdon processknt fut, hanem be van gyazva egy
n. application domainbe a processen bell (egy process tbb ADt is tartalmazhat
egymstl teljesen elszeparlva).
Ezzel a megoldssal egyrszt elsegtik a platformfggetlensget, hiszen gy csak az
Application Domaint kell portolni egy msik platformra, a benne fut folyamatoknak
nem kell ismernik az opercis rendszert, msrszt biztostja a programok
stabilitst ugyanis ha egy alkalmazs sszeomlik egy ADben, attl a tbbi mg
tkletesen mkdik majd.
Amikor elindtunk egy .NET programot elsknt az alaprtelmezett AD (default
application domain) jn ltre, ezutn ha szksges a CLR tovbbi ADket hoz
ltre.
A kvetkez program kirja az aktulis AD nevt:
using System;
class Program
{
static public void Main()
{
AppDomain currAD = AppDomain.CurrentDomain;
Console.WriteLine(currAD.FriendlyName);
}
}

Az alkalmazs neve fog megjelenni, hiszen az alaprtelmezett AD s egyelre


nincs is tbb.
Hozzunk ltre egy msodik AppDomaint:
using System;
class Program
{
static public void Main()
{
AppDomain secondAD = AppDomain.CreateDomain("second");
Console.WriteLine(secondAD.FriendlyName);
AppDomain.Unload(secondAD); // megszntetjk
}
}

30.2 Szlak
Elrkeztnk a fejezet eredeti trgyhoz, mr eleget tudunk ahhoz, hogy megrtsk a
tbbszl alkalmazsok elvt. Els programunkban lekrjk az adott programrsz
szlnak az azonostjt:
- 201 -

using System;
using System.Threading;
class Program
{
static public void Main()
{
Console.WriteLine("Szl-Id: {0}",
Thread.CurrentThread.ManagedThreadId);
}
}

A kimenetre minden esetben az egyes szmot kapjuk, jelezvn, hogy az els, a f


szlban vagyunk.
A program utastsainak vgrehajtsa szerint megklnbztetnk szinkron- s
aszinkron mkdst. A fenti program szinkron mdon mkdik, az utastsait
egyms utn hatja vgre, ha esetleg egy hosszas algoritmusba tkzik, akkor csak
akkor lp a kvetkez utastsra, ha azt befejezte,
Az aszinkron vgrehajts ennek pp az ellentte az egyes feladatokat el tudjuk
kldeni egy msik szlba, a f szl pedig fut tovbb, amg a mellkszl(ak) vissza
nem trnek.

30.3 Aszinkron delegate-ek


A kvetkezkben delegateek segtsgvel fogunk aszinkron programot rni. Minden
egyes delegate rendelkezik azzal a kpessggel, hogy aszinkron mdon hvjuk meg,
ezt a BeginInvoke s EndInvoke metdusokkal fogjuk megtenni.
Vegyk a kvetkez delegateet:
delegate int MyDelegate(int x);

Ez valjban gy nz ki:
public sealed class MyDelegate : System.MulticastDelegate
{
//...metdusok...
public IAsyncResult BeginInvoke(int x, AsyncCallback cb, object state);
public int EndInvoke(IAsyncResult result);
}

Egyelre ne foglalkozzunk az ismeretlen dolgokkal, nzzk meg azt amit ismernk. A


BeginInvokeval fogjuk meghvni a delegate-et, ennek els paramtere megegyezik
a delegate paramtervel (vagy paramtereivel). Az EndInvoke fogja majd az
eredmnyt szolgltatni, ennek visszatrsi rtke megegyezik a delegateval. Az
IAsyncResult objektum, amit a BeginInvoke visszatrt segt elrni az eredmnyt s
az EndInvoke is ezt kapja majd paramterl.
A BeginInvoke msik kt paramtervel most nem foglalkozunk, ksztsnk egy
egyszer metdust a delegate-hez s hvjuk meg aszinkron:

- 202 -

using System;
using System.Threading;
class Program
{
public delegate int MyDelegate(int x);
static int Square(int x)
{
Console.WriteLine("Szl-ID: {0}",
Thread.CurrentThread.ManagedThreadId);
return (x * x);
}
static public void Main()
{
MyDelegate d = Square;
Console.WriteLine("Szl-ID: {0}",
Thread.CurrentThread.ManagedThreadId);
IAsyncResult iar = d.BeginInvoke(12, null, null);
Console.WriteLine("BlaBla...");
int result = d.EndInvoke(iar);
Console.WriteLine(result);
}

A kimenet a kvetkez lesz:


Szl-ID: 1
BlaBla...
Szl-ID: 3
144
Lthat, hogy egy j szl jtt ltre. Amit fontos megrtennk, hogy a BeginInvoke
azonnal megkezdi a feladata vgrehajtst, de az eredmnyhez csak az EndInvoke
hvsakor jutunk hozz, teht kls szemllknt gy ltjuk, hogy csak akkor fut le a
metdus. A httrben fut szl zenete is ltszlag csak az eredmny
kirtkelsnl jelenik meg, az igazsg azonban az, hogy a Main zenete elbb rt a
processzorhoz, ezt hamarosan ltni fogjuk.
Tbbszl program rsnl ssze kell tudnunk hangolni a szlak munkavgzst,
pl. ha az egyik szlban kiszmolt eredmnyre van szksge egy msik, ksbb indult
szlnak. Ezt szinkronizlsnak nevezzk.
Szinkronizljuk az eredeti programunkat, vagyis vrjuk meg amg a delegate befejezi
a futst (termszetesen a szinkronizls ennl jval bonyolultabb, errl a
kvetkez fejezetekben olvashatunk):

- 203 -

using System;
using System.Threading;
class Program
{
public delegate int MyDelegate(int x);
static int Square(int x)
{
Console.WriteLine("Szl-ID: {0}",
Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(100);
return (x * x);
}
static public void Main()
{
MyDelegate d = Square;
Console.WriteLine("Szl-ID: {0}",
Thread.CurrentThread.ManagedThreadId);
IAsyncResult iar = d.BeginInvoke(12, null, null);
while(!iar.IsCompleted)
{
Console.WriteLine("BlaBla...");
}
int result = d.EndInvoke(iar);
Console.WriteLine(result);
}

Ezt a feladatot az IAsyncResult interfsz IsCompleted tulajdonsgval oldottuk meg.


A kimenet:
Szl-ID: 1
BlaBla...
BlaBla...
BlaBla...
Szl-ID: 3
BlaBla...
BlaBla...
BlaBla...
BlaBla...
144
Itt mr tisztn ltszik, hogy az aszinkron metdus futsa azonnal elkezddtt, igaz a
Main itt is megelzte.
A Square metdusban azrt hasznltuk a Sleep metdust, hogy lssunk is valamit,
ellenkez esetben tl gyorsan lefut ez az egyszer program. Ersebb
szmtgpeken nem rt mdostani az alvs idejt akr 1000 msre is.
Valljuk be, elg macers mindig meghvogatni az EndInvokeot, felmerlhet a
krds, hogy nem lehetne valahogy automatizlni az egszet. Nos, pp ezt a gondot
- 204 -

oldja meg a BeginInvoke harmadik AsyncCallback tpus paramtere. Ez egy


delegate, amely egy olyan metdusra mutathat, amelynek visszatrsi rtke void,
valamint egy darab IAsyncResult tpus paramterrel rendelkezik. Ez a metdus
azonnal le fog futni, ha a mellkszl elvgezte a feladatt:
using System;
using System.Threading;
using System.Runtime.Remoting.Messaging;
class Program
{
public delegate int MyDelegate(int x);
static int Square(int x)
{
Console.WriteLine("Szl-ID: {0}",
Thread.CurrentThread.ManagedThreadId);
return (x * x);
}
static void AsyncMethodComplete(IAsyncResult iar)
{
Console.WriteLine("Aszinkron szl ksz...");
AsyncResult result = (AsyncResult)iar;
MyDelegate d = (MyDelegate)result.AsyncDelegate;
Console.WriteLine("Eredmny: {0}", d.EndInvoke(iar));
}
static public void Main()
{
MyDelegate d = Square;
Console.WriteLine("Szl-ID {0}",
Thread.CurrentThread.ManagedThreadId);
IAsyncResult iar = d.BeginInvoke(12,
new AsyncCallback(AsyncMethodComplete), null);
Console.WriteLine("BlaBla...");
}
}

Ha futtatjuk ezt a programot, akkor azt fogjuk ltni, hogy nem rja ki az eredmnyt. Ez
azrt van, mert a BlaBla utn a program futsa megll, mivel elrte a Main vgt s
nincs tbb utasts, valamint ez gyorsabban trtnik, minthogy az aszinkron metdus
ksz lenne. pp ezrt rdemes egy ReadKey vagy egy Sleep metdust hasznlni a
program vgn.
A kimenet a kvetkez lesz:
Szl-ID 1
BlaBla...
Szl-ID: 3
Aszinkron szl ksz...
Eredmny: 144

- 205 -

Egyetlen dolog van htra, mgpedig a BeginInvoke utols paramternek


megismerse. Ez egy object tpus vltoz, azaz brmilyen objektumot tadhatunk.
Ezt a paramtert hasznljuk, ha valamilyen plusz informcit akarunk tovbbtani. A
BeginInvoke most gy nz ki:
IAsyncResult iar = d.BeginInvoke(12,
new AsyncCallback(AsyncMethodComplete), "zenet a jvbl :)");

Az zenetet az IAsyncResult AsyncState tulajdonsgval krdezhetjk le:


static void AsyncMethodComplete(IAsyncResult iar)
{
Console.WriteLine("Aszinkron szl ksz...");
AsyncResult result = (AsyncResult)iar;
MyDelegate d = (MyDelegate)result.AsyncDelegate;
Console.WriteLine("zenet: {0}", iar.AsyncState);
Console.WriteLine("Eredmny: {0}", d.EndInvoke(iar));
}

30.3.1 Prhuzamos delegate hvs


A .NET 4.0 lehetv teszi, hogy delegate-eket illetve nll metdusokat is
egyszerre, prhuzamosan hvjunk meg. Ehhez a feladathoz a korbban mr
megismert Task Parallel Library lesz a segtsgnkre a Parallel.Invoke metdussal.
Egyetlen szably azrt van, hagyomnyos delegateet gy nem hvhatunk, csakis a
C# 3.0tl hasznlt Action megfelel:
using System;
using System.Threading.Tasks;
class Program
{
static public void Method()
{
Console.WriteLine("3");
}
static public void Main()
{
Action m = () => Console.WriteLine("1");
m += () => Console.WriteLine("2");
m += Program.Method;
Parallel.Invoke(m, () => Console.WriteLine("4"));
}
}

A Parallel.Invoke Action tpus elemeket tartalmaz tmbt vr paramterknt.


Termszetesen ez a metdus leginkbb tbb processzormaggal elltott
szmtgpen lesz igazn hatkony.

- 206 -

30.4 Szlak ltrehozsa


Ahhoz, hogy msodlagos szlakat hozzunk ltre nem felttlenl kell delegateeket
hasznlnunk, mi magunk is elkszthetjk ket. Vegyk a kvetkez programot:
using System;
using System.Threading;
class Test
{
public void ThreadInfo()
{
Console.WriteLine("Szl-nv: {0}", Thread.CurrentThread.Name);
}
}
class Program
{
static public void Main()
{
Thread current = Thread.CurrentThread;
current.Name = "Current-Thread";
Test t = new Test();
t.ThreadInfo();
}
}

Elsknt lekrtk s elneveztk az elsdleges szlat, hogy ksbb azonostani


tudjuk, mivel alaprtelmezetten nincs neve.
A kvetkez programban a Test objektum metdust egy httrben fut szlbl
fogjuk meghvni:
using System;
using System.Threading;
class Test
{
public void ThreadInfo()
{
Console.WriteLine("Szl-nv: {0}", Thread.CurrentThread.Name);
}
}
class Program
{
static public void Main()
{
Test t = new Test();
Thread backgroundThread = new Thread(
new ThreadStart(t.ThreadInfo));
backgroundThread.Name = "Background-Thread";
backgroundThread.Start();
}

- 207 -

A Thread konsturktorban szerepl ThreadStart delegate-nek kell megadnunk azt a


metdust amelyet a msodlagos szl majd futtat.
Ez eddig szp s j, de mi van akkor, ha a meghvott metdusnak paramterei is
vannak? Ilyenkor a ThreadStart parametrizlt vltozatt hasznlhatjuk, ami igen
eredeti mdon a ParameterizedThreadStart nvre hallgat. A ThreadStarthoz
hasonlan ez is egy delegate, szintn void visszatrsi tpusa lesz, a paramtere
pedig object tpus lehet:
using System;
using System.Threading;
class Test
{
public void ThreadInfo(object parameter)
{
Console.WriteLine("Szl-nv: {0}", Thread.CurrentThread.Name);
if(parameter is string)
{
Console.WriteLine("Paramter: {0}", parameter);
}
}

class Program
{
static public void Main()
{
Test t = new Test();
Thread backgroundThread = new Thread(
new ParameterizedThreadStart(t.ThreadInfo));
backgroundThread.Name = "Background-Thread";
backgroundThread.Start("Hello");
}
}

Nylvn a metdusban nem rt ellenrizni a paramter tpust mieltt brmit


csinlunk vele.

30.5 Foreground s background szlak


A .NET kt klnbz szltpust klnbztet meg: amelyek eltrben, s amelyek a
httrben futnak. A kett kzti klnbsg a kvetkez: a CLR addig nem lltja le az
alkalmazst, amg egy eltrbeli szl is dolgozik, ugyanez a httrbeli szlakra nem
vonatkozik (az aszinkron delegate esetben is ezrt kellett a program vgre a
lassts).
Logikus (lenne) a felttelezs, hogy az elsdleges s msodlagos szlak fogalma
megegyezik jelen fejezetnk trgyaival. Az igazsg viszont az, hogy ez az llts nem
llja meg a helyt, ugyanis alaprtelmezs szerint minden szl (a ltrehozs mdjtl
s idejtl fggetlenl) eltrben fut. Termszetesen van arra is lehetsg, hogy a
httrbe kldjk ket:

- 208 -

using System;
using System.Threading;
class Test
{
public void ThreadInfo()
{
Thread.Sleep(5000);
Console.WriteLine("Szl-nv: {0}", Thread.CurrentThread.Name);
}
}
class Program
{
static public void Main()
{
Test t = new Test();
Thread backgroundThread = new Thread(
new ThreadStart(t.ThreadInfo));
backgroundThread.IsBackground = true;
backgroundThread.Name = "Background-Thread";
backgroundThread.Start();
}
}

Ez a program semmit nem fog kirni s pont ezt is vrtuk tle, mivel belltottuk az
IsBackground tulajdonsgot, ezrt az ltalunk ksztett szl valdi httrben fut
szl lett, vagyis a fszlnak nem kell megvrnia.

30.6 Szinkronizci
A szlak szinkronizcijnak egy primitvebb formjt mr lttuk a delegateek
esetben, most egy kicsit komolyabban kzeltnk a tmhoz.
Ngyflekppen szinkronizlhatjuk a szlainkat, ezek kzl az els a blokkols.
Ennek mr ismerjk egy mdjt, ez a Thread.Sleep metdus:
using System;
using System.Threading;
class Program
{
static public void Main()
{
Console.WriteLine("Start...");
Thread.Sleep(2000);
Console.WriteLine("Stop...");
}
}

Amikor egy szlat leblokkolunk az azonnal elereszti a processzort s addig inaktv


marad, amg a blokkols felttelnek a krnyezet eleget nem tesz, vagy a folyamat
valamilyen mdon megszakad.

- 209 -

A Join metdus addig vrakoztatja a hv szlat, amg az a szl, amin meghvtk


nem vgezte el a feladatt:
using System;
using System.Threading;
class Program
{
static public void Main()
{
Thread t = new Thread(delegate() { Thread.Sleep(2000); } );
t.Start();
t.Join();
}
}

A Joinnak megadhatunk egy timeout paramtert (ezredmsodpercben), amely id


lejrta utn ha a szl nem vgzett feladatval hamis rtkkel tr vissza. A
kvetkez plda (ezttal lambda kifejezssel) ezt mutatja meg:
using System;
using System.Threading;
class Program
{
static public void Main()
{
Thread t = new Thread(() => Thread.Sleep(2000));
t.Start();

if(t.Join(1000) == false)
{
Console.WriteLine("Az id lejrt...");
t.Abort(); // megszaktjuk a szl futst
}

A kvetkez szinkronizcis mdszer a lezrs (locking). Ez azt jelenti, hogy


erforrsokhoz, illetve a program bizonyos rszeihez egyszerre csak egy szlnak
engednk hozzfrst. Ha egy szl hozz akar frni az adott dologhoz, amelyet egy
msik szl mr hasznl, akkor automatikusan blokkoldik, s vrlistra kerl,
ahonnan rkezsi sorrendben lehet hozzjutni az erforrshoz (ha az elz szl mr
vgzett).
Nzzk a kvetkez pldt:
using System;
using System.Threading;
class Test
{
static int x = 10;
static int y = 20;
static public void Divide()
{
if(Test.x != 0)
{

- 210 -

Thread.Sleep(2);
Console.WriteLine(Test.y / Test.x);
Test.x = 0;
}
}

class Program
{
static public void Main()
{
Thread t1 = new Thread(new ThreadStart(Test.Divide));
Thread t2 = new Thread(new ThreadStart(Test.Divide));
t1.Start();
t2.Start();
}
}

Tegyk fel, hogy megrkezik egy szl, eljut odig, hogy kirja az eredmnyt, s pp
ekkor rkezik egy msik szl is. Megvizsglja a felttelt, rendben tallja s tovbblp.
Ebben a pillanatban azonban az elsknt rkezett szl lenullzza a vltozt, s
amikor a msodik szl osztani akar, akkor kap egy szp kis kivtelt. A Divide
metdus felttelben nem vletlenl van ott a Sleep, ezzel tesznk rla, hogy tnyleg
legyen kivtel, mivel ez egy egyszer program muszj lelasstani egy kicsit az els
szlat (rdemes tbbszr lefuttatni, nem biztos, hogy azonnal kivtelt kapunk).
A mveletet lezrhatjuk a kvetkez mdon:
static object locker = new object();
static public void Divide()
{
lock(locker)
{
if(Test.x != 0)
{
Thread.Sleep(2);
Console.WriteLine(Test.y / Test.x);
Test.x = 0;
}
}
}

A lock kijell egy blokkot, amelyhez egyszerre csak egy szl fr hozz. Ahhoz
azonban, hogy ezt megtehessk ki jellnnk egy n. tokent, amelyet lezrhat. A
tokennek minden esetben referenciatpusnak kell lennie.
A lock helyett bizonyos esetekben egy lehetsges megolds volatile kulcsszval jellt
mezk hasznlata. A jegyzet ezt a tmt nem trgyalja, mivel a megrtshez
tisztban kell lennnk a fordt sajtossgaival, a kvetkez angol nyelv
weboldalon
bvebb
informcit
tall
a
kedves
olvas:
http://igoro.com/archive/volatile-keyword-in-c-memory-model-explained/
Termszetesen nem csak statikus metdusokbl ll a vilg, egy normlis metdust
is lezrhatunk, de ilyen esetekben az egsz objektumra kell vonatkoztatni a zrolst,
hogy ne vltozzon meg az llapota egy msik szl keze nyomn:

- 211 -

class Test
{
int x = 10;
int y = 20;
public void Divide()
{
lock(this)
{
if(x != 0)
{
Thread.Sleep(2);
Console.WriteLine(y / x);
x = 0;
}
}
}
}

A lockhoz hasonlan mkdik a Mutex is, a legnagyobb klnbsg az a kett kzt,


hogy utbbi processz szinten zrol, azaz a szmtgpen fut sszes folyamat ell
elzrja a hasznlat lehetsgt. Az erforrs/metdus/stb. hasznlata eltt meg kell
hvnunk a WaitOne metdust, a hasznlat utn pedig el kell engednnk az erforrst
a ReleaseMutex metdussal (ha ezt nem tesszk meg a kdbl, akkor az alkalmazs
futsnak vgn a CLR automatikusan megteszi helyettnk). A kvetkez pldban
ltrehozunk tbb szlat s versenyeztetjk ket egy metdus hasznlatrt. Elsknt
ksztsk el az osztlyt, amely trolja az erforrst s a Mutex objektumot:
class Test
{
private Mutex mutex = new Mutex();
public void ResourceMetod()
{
mutex.WaitOne();
Console.WriteLine("{0} hasznlja az erforrst...",
Thread.CurrentThread.Name);
Thread.Sleep(1000);
mutex.ReleaseMutex();
Console.WriteLine("{0} elengedi az erforrst...",
Thread.CurrentThread.Name);
}
}

Most pedig jjjn a fprogram:


class Program
{
static Test t = new Test();
static public void ResourceUserMethod()
{
for(int i = 0;i < 10;++i)
{
t.ResourceMetod();
}
}

- 212 -

static public void Main()


{
List<Thread> threadList = new List<Thread>();
for(int i = 0;i < 10;++i)
{
threadList.Add(
new Thread(new ThreadStart(Program.ResourceUserMethod))
{
Name = "Thread" + i.ToString()
});
}
threadList.ForEach((thread) => thread.Start());
}
}

A Semaphore hasonlt a lockra s a Mutexre, azzal a klnbsggel, hogy


megadhatunk egy szmot, amely meghatrozza, hogy egy erforrshoz maximum
hny szl frhet hozz egy idben. A kvetkez program az elz tirata, egy idben
maximum hrom szl frhet hozz a metdushoz:
class Test
{
private Semaphore semaphore = new Semaphore(3, 3);
public void ResourceMetod()
{
semaphore.WaitOne();
Console.WriteLine("{0} hasznlja az erforrst...",
Thread.CurrentThread.Name);
Thread.Sleep(1000);
semaphore.Release();
Console.WriteLine("{0} elengedi az erforrst...",
Thread.CurrentThread.Name);
}

A fprogram pedig ugyanaz lesz.

30.7 ThreadPool
Kpzeljk el a kvetkez szitucit: egy kliens-szerver alkalmazst ksztnk, a
szerver a fszlban figyeli a bejv kapcsolatokat, s ha kliens rkezik, akkor kszt
neki egy szlat majd a httrben kiszolglja. Tegyk mg hozz azt is, hogy a
kliensek viszonylag rvid ideig tartjk a kapcsolatot a szerverrel, viszont sokan
vannak.
Ha gy ksztjk el a programot, hogy a bejv kapcsolatoknak mindig j szlat
ksztnk, akkor nagyon gyorsan teljestmnyproblmkba fogunk tkzni:

- 213 -

Egy objektum ltrehozsa kltsges.


Ha mr nem hasznljuk, akkor a memriban marad, amg el nem takartja a
GC.
Mivel sok bejv kapcsolatunk van, ezrt hamarabb lesz tele a memria
szemttel, vagyis gyakrabban fut majd a GC.
A problma gykere az egyes pont, vagyis az, hogy minden egyes kapcsolatnak j
szlat ksztnk, majd eldobjuk azt. Sokkal hatkonyabbak lehetnnk, ha
megprblnnk valahogy jrahasznostani a szlakat. Ezt a mdszert threadpoolingnak nevezzk s szerencsnk van, mivel a .NET beptett megoldssal
rendelkezik (ugyanakkor ha igazn hatkonyak akarunk lenni, rhatunk sajt, az
adott kvetelmnyeknek legjobban megfelel ThreadPool osztlyt is).
Egy ksbbi fejezetben elksztnk majd egy, a fenti felvzolt helyzethez hasonl
programot, most csak egy egyszer pldn keresztl fogjuk megvizsglni ezt a
technikt. Nzzk meg a kvetkez programot:
using System;
using System.Threading;
class Program
{
static public void Do(object inObj)
{
Console.WriteLine("A kvetkez adatot hasznljuk: {0}",
(int)inObj);
Thread.Sleep(500);
}
static public void Main()
{
ThreadPool.SetMaxThreads(5, 0);
for(int i = 0;i < 20;++i)
{
ThreadPool.QueueUserWorkItem(
new WaitCallback(Program.Do),
i);
}
Console.ReadKey();
}
}

Ami elssorban feltnhet, az az, hogy a ThreadPool egy statikus osztly,vagyis nem
tudjuk pldnyostani ehelyett a metdusait hasznlhatjuk. A SetMaxThread metdus
a maximlisan memriban tartott szlak szmt lltja be, az els paramter a
rendes a msodik az aszinkron szlak szmt jelzi (utbbira most nincs szksg
ezrt kapott nulla rtket).
A QueueUserWorkItem metdus lesz a ThreadPool lelke, itt indtjuk tjra az egyes
szlakat. Ha egy feladat bekerl a listra, de nincs az adott pillanatban szabad szl,
akkor addig vr, amg nem kap egyet. A metdus els paramtere egy delegate,
amely olyan metdusra mutathat, amelynek visszatrsi rtke nincs (void) s
egyetlen object tpus paramterrel rendelkezik. Ezt a paramtert adjuk meg a
msodik paramterben.
- 214 -

Fontos tudni, hogy a ThreadPool osztly csakis background szlakat indt, vagyis a
program nem fog vrni amg minden szl vgez hanem kilp. Ennek
megakadlyozsra tettnk a program vgre egy Console.ReadKey parancsot, gy
ltni is fogjuk, hogy mi trtnik ppen (erre pl. a fent emltett kliens-szerver pldban
nincs szksg, mivel a szerver a fszlban vgtelen ciklusban vrja a bejv
kapcsolatokat).

- 215 -

31

Reflection

A reflection fogalmt olyan programozsi technikra alkalmazzuk, ahol a program


(futs kzben) kpes megvltoztatni sajt struktrjt s viselkedst. Az erre a
paradigmra pl programozst reflektv programozsnak nevezzk.
Ebben a fejezetben csak egy rvid pldt tallhat a kedves olvas, a Reflection
tmakre risi s a mindennapi programozsi feladatok vgzse kzben viszonylag
ritkn szorulunk a hasznlatra (ugyanakkor bizonyos esetekben nagy
hatkonysggal jrhat a hasznlata).
Vegyk a kvetkez pldt:
using System;
class Test
{
}
class Program
{
static public void Main()
{
Test t = new Test();
Type type = t.GetType();
Console.WriteLine(type); // Test
}
}

Futsidben lekrtk az objektum tpust ( a GetType metdust minden osztly rkli


az objecttl), persze a reflektv programozs ennl tbbrl szl, lpjnk elre egyet:
mi lenne, ha az objektumot gy ksztennk el, hogy egyltaln nem rjuk le a new
opertort:
using System;
using System.Reflection; // ez kell
class Test
{
}
class Program
{
static public void Main()
{
Type type = Assembly.GetCallingAssembly().GetType("Test");
var t = Activator.CreateInstance(type);
Console.WriteLine(t.GetType()); // Test
}
}

Megjegyzend, hogy fordtsi idben semmilyen ellenrzs sem trtnik a


pldnyostand osztlyra nzve, gy ha elgpeltnk valamit az bizony kivtelt fog
dobni futskor (System.ArgumentNullException).

- 216 -

A kvetkez pldban gy fogunk meghvni egy pldnymetdust, hogy nem


pldnyostottunk hozz tartoz osztlyt:
using System;
using System.Reflection; // ez kell
class Test
{
public void Method()
{
Console.WriteLine("Hello!");
}
}
class Program
{
static public void Main()
{
Type type = Assembly.GetCallingAssembly().GetType("Test");
type.InvokeMember("Method", BindingFlags.InvokeMethod,
null, Activator.CreateInstance(type), null);
}
}

A felhasznlt InvokeMember
metdus els paramtere annak a
konstruktornak/metdusnak/tulajdonsgnak/ a neve amelyet meghvunk. A
msodik paramterrel jelezzk, hogy mit s hogyan fogunk hvni (a fenti pldban
egy metdust). Kvetkez a sorban a binder paramter, ezzel bellthatjuk, hogy az
rkltt/tlterhelt tagokat hogyan hvjuk meg. Ezutn maga a tpus, amin a hvst
elvgezzk, vgl a hvs paramterei (ha vannak)).

- 217 -

32

llomnykezels

Ebben a fejezetben megtanulunk fileokat rni/olvasni s a knyvtrstruktra


llapotnak lekrdezst illetve mdostst.

32.1 Olvass/rs filebl/fileba


A .NET szmos osztlyt biztost szmunkra, amelyekkel fileokat tudunk kezelni,
ebben a fejezetben a leggyakrabban hasznltakkal ismerkednk meg. Kezdjk egy
file megnyitsval s tartalmnak a kpernyre rsval. Legyen pl. a szveges file
tartalma a kvetkez:
alma
krte
di
cskny
pnz
knyv

A program pedig:
using System;
using System.IO;
class Program
{
static public void Main()
{
FileStream fs = new FileStream("test.txt", FileMode.Open);
StreamReader sr = new StreamReader(fs);
string s = sr.ReadLine();
while(s != null)
{
Console.WriteLine(s);
s = sr.ReadLine();
}
sr.Close();
fs.Close();
}

Az IO osztlyok a System.IO nvtrben vannak. A C# n. streameket,


adatfolyamokat hasznl az IO mveletek vgzshez. Az els sorban megnyitottunk
egy ilyen folyamot s azt is megmondtuk, hogy mit akarunk tenni vele.
A FileStream konstruktornak els paramtere a file neve (ha nem tall ilyen nev
filet, akkor kivtelt dob a program). Amennyiben nem adunk meg teljes elrsi utat,
akkor automatikusan a sajt knyvtrban fog keresni a program. Ha kls
knyvtrbl szeretnnk megnyitni az llomnyt, akkor azt a kvetkezkppen
tehetjk meg:
- 218 -

FileStream fs = new FileStream("C:\\Dir1\\Dir2\\test.txt", FileMode.Open);

Azrt hasznlunk dupla backslasht (\), mert ez egy n. escape karakter,


nmagban nem lehetne hasznlni (persze ez nem azt jelenti, hogy minden ilyen
karaktert kettzve kellene rni, minden specilis karakter eltt a backslasht kell
hasznlnunk).
Egy msik lehetsg, hogy az at jelet (@) hasznljuk az elrsi t eltt, ekkor nincs
szksg dupla karakterekre, mivel mindent normlis karakterknt fog rtelmezni:
FileStream fs = new FileStream(@'C:\Dir1\Dir2\test.txt', FileMode.Open);

A FileMode enumnak a kvetkez rtkei lehetnek:


Create
CreateNew
Open
OpenOrCreate
Append

Truncate

Ltrehoz egy j filet, ha mr ltezik a


tartalmt kitrli
Ugyanaz mint az elz, de ha mr ltezik
a file akkor kivtelt dob.
Megnyit egy filet, ha nem ltezik kivtelt
dob.
Ugyanaz mint az elz, de ha nem
ltezik akkor ltrehozza a filet.
Megnyit egy filet, s automatikusan a
vgre pozcionl. Ha nem ltezik,
ltrehozza.
Megnyit egy ltez filet s trli a
tartalmt. Ebben a mdban a file
tartalmt nem lehet olvasni (egybknt
kivtelt dob).

A FileStream konstruktornak tovbbi kt paramtere is lehet, amelyek rdekesek


szmunkra (tulajdonkppen 15 klnbz konstruktora van), mindkett felsorolt
tpus. Az els a FileAccess, amellyel belltjuk, hogy pontosan mit akarunk csinlni
az llomnnyal:
Read
Write
ReadWrite

Olvassra nyitja meg.


rsra nyitja meg.
Olvassra s rsra nyitja meg

A fenti pldt gy is rhattuk volna:


FileStream fs = new FileStream("test.txt", FileMode.Open, FileAccess.Read);

Vgl a FileSharerel azt lltjuk be, ahogy ms folyamatok frnek hozz a filehoz:
None
Read
Write
ReadWrite

Ms folyamat nem frhet hozz a filehoz,


amg azt be nem zrjuk.
Ms folyamat olvashatja a filet.
Ms folyamat rhatja a filet.
Ms folyamat rhatja s olvashatja is a
- 219 -

Delete
Inheritable

filet.
Ms folyamat trlhet a filebl (de nem
magt a filet).
A gyermek processzek is hozzfrhetnek
a filehoz.

Ha a fenti programot futtatjuk, akkor elfordulhat, hogy az kezetes karakterek


helyett krdjel jelenik meg. Ez azrt van, mert az ppen aktulis karaktertbla nem
tartalmazza ezeket a karaktereket, ez tipikusan nem magyar nyelv opercis
rendszer esetn fordul el. A megolds, hogy kzzel lltjuk be a megfelel tblt, ezt
a StreamReader konstruktorban tehetjk meg (a tbla pedig iso-8859-2 nven
szerepel):
StreamReader rs = new StreamReader(fs, Encoding.GetEncoding("iso-8859-2"),
false);

Ehhez szksg lesz mg a System.Text nvtrre is.


Most rjunk is a fileba. Erre a feladatra a StreamReader helyett a StreamWriter
osztlyt fogjuk hasznlni:
using System;
using System.IO;
class Program
{
static public void Main()
{
FileStream fs = new FileStream("Test.txt", FileMode.Open,
FileAccess.Write, FileShare.None);
StreamWriter sw = new StreamWriter(fs);
Random r = new Random();
for(int i = 0;i < 10;++i)
{
sw.Write(r.Next());
sw.Write(Environment.NewLine);
}
sw.Close();
fs.Close();
}
}

Hzi feladat kvetkezik: mdostsuk a programot, hogy ne dobja el az eredeti


tartalmt a filenak, s az rs utn azonnal rjuk ki a kpernyre az j tartalmt.
Az Environment.NewLine egy jsor karaktert ad vissza.
Binris fileok kezelshez a BinaryReader/BinaryWriter osztlyokat hasznlhatjuk:

- 220 -

using System;
using System.IO;
class Program
{
static public void Main()
{
BinaryWriter bw = new BinaryWriter(File.Create("file.bin"));
for(int i = 0;i < 100;++i)
{
bw.Write(i);
}
bw.Close();
BinaryReader br = new BinaryReader(
File.Open("file.bin", FileMode.Open));
while(br.PeekChar() != -1)
{
Console.WriteLine(br.ReadInt32());
}
br.Close();
}

Ksztettnk egy binris filet, s belertuk a szmokat egytl szzig. Ezutn


megnyitottuk s elkezdtk kiolvasni a tartalmt. A PeekChar metdus a soron
kvetkez karaktert (byteot) adja vissza, illetve -1et, ha elrtk a file vgt. A
folyambeli aktulis pozcit nem vltoztatja meg.
A cikluson bell van a trkks rsz. A ReadTpusnv metdus a megfelel tpus
adatot adja vissza, de vigyzni kell vele, mert ha nem megfelel mret a
beolvasand adat, akkor hibzni fog. A fenti pldban, ha a ReadString metdust
hasznltuk volna, akkor kivtel (EndOfStreamException) keletkezik, mivel a kett
nem ugyanakkora mennyisg adatot olvas be. Az egsz szmokat kezel metdus
nylvn mkdni fog, hiszen tudjuk, hogy szmokat rtunk ki.
Eddig kzzel zrtuk le a stream-eket, de ez nem olyan biztonsgos, mivel gyakran
elfelejtkezik rla az ember. Hasznlhatjuk ehelyett a using blokkokat, amelyek ezt
automatikusan megteszik. Fent pldul rhatnnk ezt is:
using(BinaryWriter bw = new BinaryWriter(File.Create("file.bin")))
{
for(int i = 0;i < 100;++i)
{
bw.Write(i);
}
}

32.2 Knyvtrstruktra kezelse


A fileok mellett a .NET a knyvtrstruktra kezelst is szleskren tmogatja. A
System.IO nvtr ebbl a szempontbl kt rszre oszlik: informcis- s opercis
- 221 -

eszkzkre. Elbbiek (ahogyan a nevk is sugallja) informcit szolgltatnak, mg az


utbbiak (tbbsgkben statikus metdusok) bizonyos mveleteket (j knyvtr
ltrehozsa, trlse, stb.) vgeznek a filerendszeren. Els pldnkban rjuk ki
mondjuk a C meghajt gykernek knyvtrait:
using System;
using System.IO;
class Program
{
static public void Main()
{
foreach(string s in Directory.GetDirectories("C:\\"))
{
Console.WriteLine(s);
}
}
}

Termszetesen nem csak a knyvtrakra, de a fileokra is kvncsiak lehetnk. A


programunk mdostott vltozata nmi plusz informcival egytt ezeket is kirja
neknk:
using System;
using System.IO;
class Program
{
static public void PrintFileSystemInfo(FileSystemInfo fsi)
{
if((fsi.Attributes & FileAttributes.Directory) != 0)
{
DirectoryInfo di = fsi as DirectoryInfo;
Console.WriteLine("Knyvtr: {0}, Kszlt: {1}",
di.FullName, di.CreationTime);
}
else
{
FileInfo fi = fsi as FileInfo;
Console.WriteLine("File: {0}, kszlt: {1}",
fi.FullName, fi.CreationTime);
}
}
static public void Main()
{
foreach(string s in Directory.GetDirectories("C:\\"))
{
PrintFileSystemInfo(new DirectoryInfo(s));
}
foreach(string s in Directory.GetFiles("C:\\"))
{
PrintFileSystemInfo(new FileInfo(s));
}
}

Elszr a mappkon, majd a fileokon megynk vgig. Ugyanazzal a metdussal rjuk


ki az informcikat kihasznlva azt, hogy a DirectoryInfo s a FileInfo is egy kzs
stl a FileSystemInfobl szrmazik (mindkett konstruktora a vizsglt alany elrsi
- 222 -

tjt vrja paramterknt), gy a metdusban csak meg kell vizsglni, hogy ppen
melyikkel van dolgunk s tkonvertlni a megfelel tpusra. A vizsglatnl egy
bitenknti s mveletet hajtottunk vgre, hogy ez mirt s hogyan mkdik, annak
meggondolsa az olvas feladata.
Eddig csak informcikat
knyvtrstruktrt:

krtnk le,

most

megtanuljuk

mdostani

is

using System;
using System.IO;
class Program
{
static public void Main()
{
string dirPath = "C:\\test";
string filePath = dirPath + "\\file.txt";
// ha nem ltezik a knyvtr
if(Directory.Exists(dirPath) == false)
{
// akkor elksztjk
Directory.CreateDirectory(dirPath);
}
FileInfo fi = new FileInfo(filePath);
// ha nem ltezik a file
if(fi.Exists == false)
{
// akkor elksztjk s runk bele
StreamWriter sw = fi.CreateText();
sw.WriteLine("Dio");
sw.WriteLine("Alma");
sw.Close();
}
}

A FileInfo CreateText metdusa egy StreamWriter objektumot ad vissza amellyel


rhatunk egy szveges fileba.

32.3 Inmemory streamek


A .NET a MemoryStream osztlyt biztostja szmunkra, amellyel memriabeli
adatfolyamokat rhatunk/olvashatunk. Mire is jk ezek a folyamok? Gyakran van
szksgnk arra, hogy sszegyjtsnk nagy mennyisg adatot, amelyeket majd a
folyamat vgn ki akarunk rni a merevlemezre. Nylvn egy tmb vagy egy lista nem
nyjtja a megfelel szolgltatsokat, elbbi rugalmatlan, utbbi memriaignyes,
ezrt a legegyszerbb, ha kzvetlenl a memriban troljuk el az adatokat. A
MemoryStream osztly jelentsen megknnyti a dolgunkat, mivel lehetsget ad
kzvetlenl fileba rni a tartalmt. A kvetkez program erre mutat pldt:

- 223 -

using System;
using System.IO;
class Program
{
static public void Main()
{
MemoryStream mstream = new MemoryStream();
StreamWriter sw = new StreamWriter(mstream);
for(int i = 0;i < 1000;++i)
{
sw.WriteLine(i);
}
sw.Flush();
FileStream fs = File.Create("inmem.txt");
mstream.WriteTo(fs);
sw.Close();
fs.Close();
mstream.Close();
}
}

A MemoryStreamre rlltottunk egy StreamWriter objektumot. Miutn minden


adatot a memriba rtunk a Flush metdussal (amely egyttal kirti a
StreamWritert is) ltrehoztunk egy filet s a MemoryStream WriteTo metdusval
kirtuk bel az adatokat.

32.4 XML
Az XML ltalnos cl lernyelv, amelynek elsdleges clja strukturlt szveg s
informci megosztsa az interneten keresztl. Lssunk egy pldt:
<?xml version="1.0" encoding="UTF-8" ?>
<list>
<item>1</item>
<item>2</item>
<item>3</item>
</list>

Az els sor megmondja, hogy melyik verzit s milyen kdolssal akarjuk hasznlni,
ezutn kvetkeznek az adatok. Minden XML dokumentum egyetlen gykrelemmel
rendelkezik (ez a fenti pldban a <list>), amelynek minden ms elem a
gyermekeleme (egyttal minden nem-gykrelem egy msik elem gyermeke kell
legyen, ez nem felttlenl jelenti a gykrelemet). Minden elemnek rendelkeznie kell
nyit (<list>) s zr (</list>) tagekkel. res elemeknl ezeket egyszerre is
deklarlhatjuk (<res />). Az egyes elemek trolhatnak attribtumokat a kvetkez
formban:
<item value="10" />
<item value="10"></item>

Itt mindkt forma leglis.


- 224 -

A .NET Framework ersen pt az XMLre, mind offline (konfigurcis fileok, inmemory adatszerkezetek) mind online (SOAP alap informcicsere, etc...) tren.
A szmunkra szksges osztlyok a System.Xml nvtrben vannak, a kt
legalapvetbb ilyen osztly az XmlReader s az XmlWriter. Ezeknek az absztrakt
osztlyoknak a segtsgvel hajthatak vgre a szoksos irs/olvass mveletek.
Elszr nzzk meg, hogyan tudunk beolvasni egy XML filet. A megnyitshoz az
XmlReader egy statikus metdust a Createet fogjuk hasznlni, ez egy streamtl
kezdve egy szimpla filenvig mindent elfogad:
XmlReader reader = XmlReader.Create("test.xml");

Fggetlenl attl, hogy az XmlReader egy absztrakt osztly tudjuk pldnyostani,


mgpedig azrt, mert ilyenkor valjban egy XmlTextReaderImpl tpus objektum jn
ltre, amely az XmlReader egy bels, internal elrs leszrmazottja (teht
kzvetlenl nem tudnnk pldnyostani).
Miutn teht megnyitottuk a filet, vgig tudunk iterlni rajta:
while (reader.Read())
{
switch (reader.NodeType)
{
case XmlNodeType.Element:
Console.WriteLine("<{0}>", reader.Name);
break;
case XmlNodeType.EndElement:
Console.WriteLine("</{0}>", reader.Name);
break;
case XmlNodeType.Text:
Console.WriteLine(reader.Value);
break;
default:
break;
};
}

Az XmlReader NodeType tulajdonsga egy XmlNodeType felsorols egy tagjt adja


vissza, a pldban a legalapvetbb tpusokat vizsgltuk s ezek fggvnyben rtuk
ki a file tartalmt.
A kvetkez pldban hasznlni fogjuk az XmlWriter osztly egy leszrmazottjt, az
XmlTextWritert, ugyanis a filet kdbl fogjuk ltrehozni:
XmlTextWriter writer = new XmlTextWriter("newxml.xml", Encoding.UTF8);
writer.Formatting = Formatting.Indented;

A Formatting tulajdonsg, az Xml fileoktl megszokott hierarchikus szerkezetet fogja


megteremteni (ennek az rtkt is bellthatjuk).
writer.WriteStartDocument();
writer.WriteComment(DateTime.Now.ToString());

- 225 -

Elkezdtk az adatok feltltst, s beszrtunk egy kommentet is. A file tartalma most
a kvetkez:
<?xml version="1.0" encoding="utf-8"?>
<!--2010.08.20. 12:04:21-->

A file szemlyek adatait fogja tartalmazni:


writer.WriteStartElement("PersonsList");
writer.WriteStartElement("Person");
writer.WriteElementString("Name", "Reiter Istvan");
writer.WriteElementString("Age", "22");
writer.WriteEndElement();

A StartElement egy j tagcsoportot kezd a paramtereknt megadott nvvel, a


WriteElementString pedig feltlti azt rtkekkel, kulcs-rtk prokkal.
A file:
<?xml version="1.0" encoding="utf-8"?>
<!--2008.10.17. 20:02:05-->
<PersonsList>
<Person>
<Name>Reiter Istvan</Name>
<Age>24</Age>
</Person>
</PersonsList>

Az egyes tagekhez attribtumokat is rendelhetnk:


writer.WriteAttributeString("Note", "List of persons");

Els paramter az attribtum neve, utna pedig a hozz rendelt rtk jn. Ekkor a file
gy alakul:
<?xml version="1.0" encoding="utf-8"?>
<!--2008.10.17. 20:05:24-->
<PersonsList Note="List of persons">
<Person>
<Name>Reiter Istvan</Name>
<Age>22</Age>
</Person>
</PersonsList>

Az XmlReader rendelkezik nhny MoveTo eltaggal rendelkez metdussal,


ezekkel a file egy olyan pontjra tudunk naviglni, amely megfelel egy felttelnek.
Nhny plda:
bool l = reader.MoveToAttribute("Note");

Ez a metdus a paramtereknt megadott attribtumra lltja a readert, s igaz


rtkkel tr vissza, ha ltezik az attribtum. Ellenkez esetben a pozci nem
vltozik, s a visszatrsi rtk hamis lesz.
reader.MoveToContent();

- 226 -

Ez a metdus a legkzelebbi olyan csompontra ugrik, amely tartalmaz adatot is,


ekkor az XmlReader (vagy leszrmazottainak) Value tulajdonsga ezt az rtket
fogja tartalmazni.
A MoveToElement metdus visszalp arra a csompontra, amely azt az attribtumot
tartalmazza, amelyen a reader ll (rtelemszeren kvetkezik, hogy az elz kett
metdust gyakran hasznljuk egytt):
while (reader.Read())
{
if (reader.HasAttributes)
{
for (int i = 0; i < reader.AttributeCount; ++i)
{
reader.MoveToAttribute(i);
Console.WriteLine("{0} {1}", reader.Name, reader.Value);
}
reader.MoveToElement();
}
}

A HasAttribute metdussal megtudakoljuk, hogy vane a csomponton attribtum,


utna pedig az AttributeCount tulajdonsg segtsgvel (amely azoknak a szmt
adja vissza) vgigiterlunk rajtuk. Miutn vgeztnk, visszatrnk a kiindulsi pontra
(hogy a Read metdus tudja vgezni a dolgt).
Emltst rdemelnek mg a MoveToFirstAttribute s a MovetoNextAttribute
metdusok, amelyek nevkhz mltan az els illetve a kvetkez attribtumra
pozcionlnak. Mdostsuk egy kicsit az elz pldt:
for (int i = 0; i < reader.AttributeCount; ++i)
{
//reader.MoveToAttribute(i);
reader.MoveToNextAttribute();
Console.WriteLine("{0} {1}", reader.Name, reader.Value);
}

Vgl, de nem utolssorban a Skip metdus maradt, amely tugorja egy csompont
gyermekeit s a kvetkez azonos szinten lv csompontra ugrik.

32.5 XML DOM


Eddig az XML llomnyokat hagyomnyos fileknt kezeltk, ennl azonban van egy
nmileg knyelmesebb lehetsgnk is. Az XML DOM (Document Object Model) a
fileokat a hierarchikus felptsk szerint kezeli. Az osztly amelyen keresztl elrjk
a DOM ot az az XmlDocument lesz. Egy msik fontos osztly, amelybl egybknt
maga az XmlDocument is szrmazik, az az XmlNode. Egy XML file egyes
csompontjait egy-egy XmlNode objektum fogja jelkpezni.
Ahogy azt mr megszokhattuk az XmlDocument szmos forrsbl tpllkozhat, pl.
stream vagy egyszer filenv. A forrs betltst az XmlDocument Load illetve
LoadXml metdusaival vgezzk:
- 227 -

XmlDocument xdoc = new XmlDocument();


xdoc.Load("test.xml");

Egy XmlDocument bejrhat foreach ciklussal a ChildNodes tulajdonsgon keresztl,


amely egy XmlNodeList objektumot ad vissza. Persze nem biztos, hogy lteznek
gyermekei, ezt a HasChildNodes tulajdonsggal tudjuk ellenrizni. A kvetkez
forrskdban egy XmlDocument els szinten lv gyermekeit jrjuk be:
foreach (XmlNode node in xdoc.ChildNodes)
{
Console.WriteLine(node.Name);
}

Az XmlDocument a ChildNodes tulajdonsgt az XmlNode osztlytl rkli (ugyangy


a HasChildNodest is, amely igaz rtket tartalmaz, ha lteznek gyermek-elemek),
gy az egyes XmlNodeokat bejrva a teljes ft megkaphatjuk.
Most nzzk meg, hogyan tudjuk manipullni a filet. Hozzunk ltre kdbl egy
teljesen j dokumentumot:
XmlDocument xdoc = new XmlDocument();
XmlElement element = xdoc.CreateElement("Test");
XmlText text = xdoc.CreateTextNode("Hello XML DOM!");
XmlNode node = xdoc.AppendChild(element);
node.AppendChild(text);
xdoc.Save("domtest.xml");

Az XmlText illetve XmlElement osztlyok is az XmlNode leszrmazottai. A Save


metdussal el tudjuk menteni a dokumentumot, akr filenevet, akr egy streamet
megadva. A fenti kd eredmnye a kvetkez lesz:
<Test>Hello XML DOM!</Test>

A CloneNode metdussal mr ltez cscsokat klnozhatunk:


XmlNode source = xdoc.CreateNode(XmlNodeType.Element, "test", "test");
XmlNode destination = source.CloneNode(false);

A metdus egyetlen paramtert vr, amely jelzi, hogy a cscs gyermekeit is


tmsoljuke.
Kt msik metdusrl is megemlkeznk, els a RemoveChild, amely egy ltez
cscsot tvolt el a cscsok listjbl, a msik pedig a ReplaceChild, amely fellr
egy cscsot.
Az XmlDocument esemnyeket is szolgltat, amelyek segtsgvel teljes kr
felgyeletet nyerhetnk. Kezeljk le pldul azt az esemnyt, amikor egy j cscsot
adunk hozz:

- 228 -

XmlNodeChangedEventHandler handler = null;


handler = (sender, e) =>
{
Console.WriteLine(e.Node.Value);
};
xdoc.NodeInserting += handler;

32.6 XML szerializci


Mi is az a szrializls? A legegyszerbben egy pldn keresztl lehet megrteni a
fogalmat, kpzeljk el, hogy rtunk egy jtkot, s a jtkosok pontszmt szeretnnk
eltrolni. Az elrt pontszm mell jn a jtkos neve s a teljests ideje is. Ez elsre
semmi klns, hiszen simn kirhatjuk az adatokat egy fileba, majd
visszaolvashatjuk onnan. Egy htult mgis van, mgpedig az, hogy ez egy elg
bonyolult feladat: figyelni kell a beolvasott adatok tpusra, formtumra, stb
Nem lenne egyszerbb, ha a kirt adatokat meg tudnnk feleltetni egy osztlynak? Ez
megtehetjk, ha ksztnk egy megfelel osztlyt, amelyet szrializlva kirhatunk
XML formtumba s onnan vissza is olvashatjuk (ezt deszrializlsnak hvjk).
Termszetesen a szrializls jelentsge ennl sokkal nagyobb s nemcsak XML
segtsgvel tehetjk meg. Tulajdonkppen a szrializls annyit tesz, hogy a
memriabeli objektumainkat egy olyan szekvencilis formtumba (pl. binris, vagy
most XML) konvertljuk, amelybl vissza tudjuk alaktani az adatainkat.
Mr emltettk, hogy tbb lehetsgnk is van a szerializcira, ezek kzl az XML a
legltalnosabb, hiszen ezt ms krnyezetben is felhasznlhatjuk.
Kezdjk egy egyszer pldval (hamarosan megvalstjuk a pontszmos pldt is),
szrializljunk egy hagyomnyos beptett objektumot:
FileStream fstream = new FileStream("serxml.xml", FileMode.Create);
XmlSerializer ser = new XmlSerializer(typeof(DateTime));
ser.Serialize(fstream, DateTime.Now);

Ezutn ltrejn egy XML file, benne a szrializlt objektummal:


<?xml version="1.0"?>
<dateTime>2008-10-23T16:19:44.53125+02:00</dateTime>

Az XmlSerializer a System.Xml.Serialization nvtrben tallhat.


A deszrializls hasonlkppen mkdik:
FileStream fstream = new FileStream("serxml.xml", FileMode.Open);
XmlSerializer ser = new XmlSerializer(typeof(DateTime));
DateTime deser = (DateTime)ser.Deserialize(fstream);

Most mr eleget tudunk ahhoz, hogy ksztsnk egy szrializlhat osztlyt. Egy
nagyon fontos dolgot kell megjegyeznnk, az osztlynak csakis a publikus tagjai
szrializlhatak a private vagy protected elrsek automatikusan kimaradnak
(emellett az osztlynak magnak is publikus elrsnek kell lennie). Ezeken kvl
mg szksg lesz egy alaprtelmezett konstruktorra is (a deszrializlshoz, hiszen
ott mg nem tudja, hogy milyen objektumrl van sz).
- 229 -

public class ScoreObject


{
public ScoreObject()
{
}
private string playername;
[XmlElement("PlayerName")]
public string PlayerName
{
get { return playername; }
set { playername = value; }
}
private int score;
[XmlElement("Score")]
public int Score
{
get { return score; }
set { score = value; }
}
private DateTime date;
[XmlElement("Date")]
public DateTime Date
{
get { return date; }
set { date = value; }
}
}

Az egyes tulajdonsgoknl belltottuk, hogy az miknt jelenjen meg a fileban (sima


element helyett lehet pl. attribtum is). Enlkl is lefordulna s mkdne, de gy
jobban kezelhet. Rendben, most szrializljuk:
ScoreObject so = new ScoreObject();
so.PlayerName = "Player1";
so.Score = 1000;
so.Date = DateTime.Now;
FileStream fstream = new FileStream("scores.xml", FileMode.Create);
XmlSerializer ser = new XmlSerializer(typeof(ScoreObject));
ser.Serialize(fstream, so);

s az eredmny:
<?xml version="1.0"?>
<ScoreObject xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<PlayerName>Player1</PlayerName>
<Score>1000</Score>
<Date>2008-10-23T16:34:32.734375+02:00</Date>
</ScoreObject>

- 230 -

33

Konfigurcis file hasznlata

Eddig, amikor egy programot rtunk minden apr vltoztatsnl jra le kellett
fordtani, mg akkor is, ha maga a program nem, csak a hasznlt adatok vltoztak.
Sokkal knyelmesebb lenne a fejleszts s a ksz program hasznlata is, ha a
kls adatokat egy kln fileban trolnnk. Termszetesen ezt megtehetjk gy is,
hogy egy sima szveges filet ksztnk, s azt olvassuk/rjuk, de a .NET ezt is
megoldja helyettnk a konfigurcis fileok bevezetsvel. Ezek tulajdonkppen XML
dokumentumok amelyek a kvetkezkppen plnek fel:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<appSettings>
<add key="1" value="alma" />
<add key="2" value="korte" />
</appSettings>
</configuration>

Az els sor egy szabvnyos XML file fejlce, vele nem kell foglalkoznunk. Sokkal
rdekesebb viszont az appSettings szekci, ahov mr be is raktunk nhny adatot.
Az appSettings az ltalnos adatok trolsra szolgl, vannak specilis szekcik (pl.
adatbzisok elrshez) s mi magunk is kszthetnk ilyet (errl hamarosan).
A file ugyan megvan, de mg nem tudjuk, hogy hogyan hasznljuk fel azt a
programunkban. A konfig file neve mindig alkalmazsneve.exe.config formban
hasznland (vagyis a main.cs forrshoz ha nem adunk meg mst a
main.exe.config elnevezst kell hasznlnuk), ekkor a fordtsnl ezt nem kell kln
jellni, automatikusan felismeri a fordtprogram, hogy van ilyennk is. rjunk is egy
egyszer programot:
using System;
using System.Configuration; // ez kell
class Program
{
static public void Main()
{
string s = ConfigurationManager.AppSettings["1"];
Console.WriteLine(s); // alma
}
}

Lthat, hogy az AppSettings rendelkezik indexelvel, amely visszaadja a megfelel


rtket a megadott kulcshoz.
Ugyanezt elrhetjk a ConfigurationSettings.AppSettings szel is, de az mr elavult
erre a fordt is figyelmeztet csak a korbbi verzik kompatibilitsa miatt van mg
benne a frameworkben.
Az AppSettings indexelje minden esetben stringet ad vissza, teht ha ms tpussal
akarunk dolgozni, akkor konvertlni kell. Vezessnk be a konfig-fileba egy j kulcsrtk prost:
- 231 -

<add key="3" value="42" />

s hasznljuk is fel a forrsban:


using System;
using System.Configuration;
class Program
{
static public void Main()
{
int x = int.Parse(ConfigurationManager.AppSettings["3"]);
Console.WriteLine(x);
}
}

33.1 Konfigurci-szekci ksztse


Egyszerbb feladatokhoz megteszi a fenti mdszer is, de hosszabb programok
esetben nem knyelmes mindig figyelni a konverzikra. pp ezrt kszthetnk
olyan osztlyt is, amely egyrszt tpusbiztos msrszt a konfigurcis fileban is
megjelenik.
A kvetkez pldban elksztnk egy programot, amely konfigurcis filebl kiolvas
egy filenevet s a kdtblt s ezek alapjn kirja a file tartalmt. Els lpsknt
ksztsnk egy j forrsfilet AppDataSection.cs nven, amely a kvetkezt
tartlamazza:
using System;
using System.Configuration;
public class AppDataSection : ConfigurationSection
{
private static AppDataSection settings =
ConfigurationManager.GetSection("AppDataSettings")
as AppDataSection;
public static AppDataSection Settings
{
get { return AppDataSection.settings; }
}
[ConfigurationProperty("fileName", IsRequired=true)]
public string FileName
{
get
{
return this["fileName"] as string;
}
set
{
this["fileName"] = value;
}
}
[ConfigurationProperty("encodeType", IsRequired=true)]
public string EncodeType

- 232 -

{
get
{

return this["encodeType"] as string;

}
}

Az osztlyt a ConfigurationSection osztlybl szrmaztattuk, ennek az indexeljt


hasznljuk. Itt arra kell figyelni, hogy az AppSettingsszel ellenttben object
tpussal tr vissza, vagyis muszj konverzit vgrehajtani.
A Settings property segtsgvel az osztlyon keresztl egyttal hozzfrnk az
osztly egy pldnyhoz is, gy soha nem kell pldnyostanunk azt.
A tulajdonsgok attribtumval belltottuk az konfig-fileban szerepl nevet illetve,
hogy muszj megadnunk ezeket az rtkeket (a DefaultValue segtsgvel
kezdrtket is megadhatunk). Most jn a konfig-file itt regisztrlnunk kell az j
szekcit:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
<section name="AppDataSettings" type="AppDataSection, main" />
</configSections>
<AppDataSettings fileName="file.txt" encodeType="iso-8859-2" />
</configuration>

A type tulajdonsgnl meg kell adnunk a tpus teljes elrsi tjt nvtrrel egytt (ha
van), illetve azt az assemblyt amelyben a tpus szerepel, ez a mi esetnkben a main
lesz.
Most jjjn az osztly, amely hasznlja az j szekcit, ksztsnk egy
DataHandlerClass.cs nev filet is:
using
using
using
using

System;
System.IO;
System.Text;
System.Configuration;

public class DataHandlerClass


{
public void PrintData()
{
Encoding enc = Encoding.GetEncoding(
AppDataSection.Settings.EncodeType);
string filename = AppDataSection.Settings.FileName;
using(FileStream fs = new FileStream(filename, FileMode.Open))
{
using(StreamReader sr = new StreamReader(fs, enc, false))
{
string s = sr.ReadLine();
while(s != null)
{
Console.WriteLine(s);
s = sr.ReadLine();
}
}

- 233 -

}
}
}

Most mr knnyen hasznlhatjuk az osztlyt:


using System;
using System.Configuration;
class Program
{
static public void Main()
{
DataHandlerClass handler = new DataHandlerClass();
handler.PrintData();
}
}

A fileokat a fordtprogram utn egyms utn rva fordthatjuk le:


csc main.cs AppDataSection.cs DataHandlerClass.cs

- 234 -

34

Hlzati programozs

A .NET meglehetsen szles eszkztrral rendelkezik hlzati kommunikci


kialaktshoz. A lista a legegyszerbb hobbiszint programokban hasznlhat
szerver-kliens osztlyoktl egszen a legalacsonyabb szint osztlyokig terjed.
Ebben a fejezetben ebbl a listbl szemezgetnk.
A fejezethez tartoz forrskdok megtallhatak a jegyzethez tartoz
Sources\Network knyvtrban.

34.1 Socket
A socketek szmtgpes hlzatok (pl. az Internet) kztti kommunikcis
vgpontok. Minden socket rendelkezik kt adattal, amelyek ltal egyrtelmen
azonosthatak s elrhetek: ezek az IP cm s a port szm.
Mi az az IP cm? Az Internet az n. Internet Protocol (IP) szabvnya szerint mkdik.
Eszerint a hlzaton lv minden szmtgp egyedi azonostval IP cmmel
rendelkezik (ugyanakkor egy szmtgp tbb cmmel is rendelkezhet, ha tbb
hlzati hardvert hasznl). Egy IP cm egy 32 bites egsz szm, amelyet 8 bites
rszekre osztunk (pl.: 123.255.0.45), ezltal az egyes szekcik legmagasabb rtke
255 lesz (ez a jellemz az IP negyedik genercijra vonatkozik, az j hatos
generci mr 128 bites cmeket hasznl, igaz ez egyelre kevsb elterjedt (a .NET
tmogatja az IPv4 s IPv6 verzikat is)).
Az IP cmet tekinthetjk az orszg/vros/utca/hzszm ngyesnek, mg a
portszmmal egy szobaszmra hivatkozunk. A portszm egy 16 bites eljel nlkli
szm 1 s 65535 kztt. A portszmokat ki lehet sajttani, vagyis ezltal biztostjk
a nagyobb szoftvergyrtk, hogy ne legyen semmilyen tkzs a termk
hasznlatakor. A portszmok hivatalos regisztrcijt az Internet Assigned Numbers
Authority (IANA) vgzi.
Az 1 s 1023 portokat n. well-knowed portknt ismerjk, ezeken olyan szleskrben
elterjedt szolgltatsok futnak amelyek a legtbb rendszeren megtalhatak
(opercis rendszertl fggetlenl). Pl. a bngszk a HTTP protokollt a 80as
porton rik el, a 23 a Telnet, mg a 25 az SMTP szerver portszma (ezektl el lehet
s biztonsgi okokbl a nagyobb cgek el is szoktak trni, de a legtbb
szmtgpen ezekkel az rtkekkel tallkozunk).
Az 1024 s 49151 kztti portokat regisztrlt portoknak nevezik, ezeken mr olyan
szolgltatsokat is felfedezhetnk amelyek opercis rendszerhez (is) ktttek pl. az
1433 a Microsoft SQL Server portja, ami rtelemszeren Windows rendszer alatt fut.
Ugyanitt megtallunk szoftverhez kttt portot is, pl a World of Warcraft a 3724 portot
hasznlja. Ez az a tartomny amit az IANA kezel.
Az efelettieket dinamikus vagy privt portoknak nevezik, ezt a tartomnyt nem lehet
lefoglalni, programfejleszts alatt clszer ezeket hasznlni.
Mieltt nekillunk a Socket osztly megismersnek, jtsszunk egy kicsit az IP
cmekkel! Azt tudjuk mr, hogy a hlzaton minden szmtgp sajt cmmel
rendelkezik, de szmokat viszonylag nehz megjegyezni, ezrt feltalltk a domainnv vagy tartomnynv intzmnyt, amely rl egy adott IP cmre, vagyis ahelyett,
- 235 -

hogy 65.55.21.250 rhatjuk azt, hogy www.microsoft.com. A kvetkez


programunkban lekrdezzk egy domain-nv IP cmt s fordtva. Ehhez a
System.Net nvtr osztlyai lesznek segtsgnkre:
using System;
using System.Net; // ez kell
class Program
{
static public void Main()
{
IPHostEntry host1 = Dns.GetHostEntry("www.microsoft.com");
foreach(IPAddress ip in host1.AddressList)
{
Console.WriteLine(ip.ToString());
}
IPHostEntry host2 = Dns.GetHostEntry("91.120.22.150");
Console.WriteLine(host2.HostName);
}
}

Most mr ideje mlyebb vizek fel venni az irnyt, elksztjk az els szervernket. A
legegyszerbb mdon fogjuk csinlni a beptett TcpListener osztllyal, amely a
TCP/IP protokollt hasznlja (errl hamarosan rszletesebben). Nzzk meg a forrst:
using System;
using System.Net;
using System.Net.Sockets;
public class Server
{
static public void Main(string[] args)
{
IPAddress ip = IPAddress.Parse(args[0]);
int port = int.Parse(args[1]);
IPEndPoint endPoint = new IPEndPoint(ip, port);
TcpListener server = new TcpListener(endPoint);
server.Start();
Console.WriteLine("A szerver elindult!");
}

Ezt a programot gy futtathatjuk:


.\Server.exe 127.0.0.1 50000
A 127.0.0.1 egy specilis cm, ez az n. localhost vagyis a helyi cm, amely jelen
esetben a sajt szmtgpnk (ehhez Internet kapcsolat sem szksges mindig
rendelkezsre ll). Ez az ip cm lesz az, ahol a szerver bejv kapcsolatokra fog
vrni (relemszeren ehhez az IP cmhez csak a sajt szmtgpnkrl tudunk
kapcsoldni, ha egy tvoli gpet is szeretnnk bevonni, akkor szksg lesz a valdi
IP re, ezt pl. a parancssorba bert ipconfig paranccsal tudhatjuk meg).

- 236 -

using System;
using System.Net;
using System.Net.Sockets;
public class Server
{
static public void Main(string[] args)
{
TcpListener server = null;
try
{
IPAddress ipAddr = IPAddress.Parse(args[0]);
int portNum = int.Parse(args[1]);
IPEndPoint endPoint = new IPEndPoint(ipAddr, portNum);
server = new TcpListener(endPoint);
server.Start();
Console.WriteLine("A szerver elindult!");
}
catch(Exception e)
{
Console.WriteLine(e.Message);
}
finally
{
server.Stop();
Console.WriteLine("A szerver lellt!");
}
}

A TcpListener konstruktorban meg kell adnunk egy vgpontot a szervernek, ahol


figyelheti a bejv kapcsolatokat, ehhez szksgnk lesz egy IP cmre s egy
portszmra. Elbbit az IPAddress.Parse statikus metdussal egy karaktersorozatbl
nyertk ki.
A kvetkez lpsben bejv kapcsolatot fogadunk, ehhez viszont szksgnk lesz
egy kliensre is, t a TcpClient osztllyal ksztjk el, amely hasonlan mkdik, mint
a prja, szintn egy cm-port kettsre lesz szksgnk (de most nem kell vgpontot
definilnunk):
using System;
using System.Net;
using System.Net.Sockets;
class Program
{
static public void Main(string[] args)
{
TcpClient client = null;
try
{
client = new TcpClient(args[0], int.Parse(args[1]));
}
catch(Exception e)
{
Console.WriteLine(e.Message);
}

- 237 -

finally
{
client.Close();
}
}

A TcpClient nek vagy a konstruktorban rgtn megadjuk az elrni kvnt szerver


nevt s portjt (s ekkor azonnal csatlakozik is), vagy meghvjuk az alaprtelmezett
konstruktort s a Connect metdussal elhalasztjuk a kapcsoldst egy ksbbi
idpontra (termszetesen ekkor neki is meg kell adni a szerver adatait).
Ahhoz, hogy a szerver fogadni tudja a kliensnket meg kell mondanunk neki, hogy
vrjon amg bejv kapcsolat rkezik, ezt a TcpListener osztly AcceptTcpClient
metdusval tehetjk meg:
TcpClient client = server.AcceptTcpClient();

Persze szerveroldalon egy TcpClient objektumra lesz szksgnk, ezt adja vissza a
metdus.
A programunk jl mkdik, de nem csinl tl sok mindent. A kvetkez lpsben
adatokat kldnk oda-vissza. Nzzk a mdostott klienst:
using
using
using
using

System;
System.Net;
System.Net.Sockets;
System.Text;

class Program
{
static public void Main(string[] args)
{
TcpClient client = null;
NetworkStream stream = null;
try
{
client = new TcpClient(args[0], int.Parse(args[1]));
byte[] data = Encoding.ASCII.GetBytes("Hello szerver!");
stream = client.GetStream();
stream.Write(data, 0, data.Length);
data = new byte[256];
int length = stream.Read(data, 0, data.Length);
Console.WriteLine("A szerver zenete: {0}",
Encoding.ASCII.GetString(data, 0, length));

}
catch(Exception e)
{
Console.WriteLine(e.Message);
}
finally
{
stream.Close();
client.Close();
}

- 238 -

Az elkldeni kvnt zenetet bytetmb formjban kell tovbbtanunk, mivel a


TCP/IP byet-ok sorozatt tovbbtja, ezrt szmra emszthet formba kell
hoznunk az zenetet. A fenti pldban a legegyszerbb, ASCII kdolst vlasztottuk,
de mst is hasznlhatunk a lnyeg, hogy tudjuk byte-onknt kldeni (rtelemszeren
mind a kliens, mind a szerver ugyanazt a kdolst kell hasznlja).
A NetworkStream ugyanolyan adatfolyam, amit a filkezelssel foglalkoz fejezetben
megismertnk, csak ppen az adatok most a hlzaton keresztl folynak t.
Mr ismerjk az alapokat, eljtt az ideje, hogy egy szinttel lejjebb lpjnk a socketek
vilgba.
using
using
using
using
using

System;
System.Net;
System.Net.Sockets;
System.Text;
System.IO;

class Program
{
static public void Main(string[] args)
{
Socket server = null;
Socket client = null;
try
{
server = new Socket(
AddressFamily.InterNetwork,
SocketType.Stream,
ProtocolType.Tcp);
IPEndPoint endPoint = new IPEndPoint(
IPAddress.Parse(args[0]),
int.Parse(args[1]));
server.Bind(endPoint);
server.Listen(2);
client = server.Accept();
byte[] data = new byte[256];
int length = client.Receive(data);
Console.WriteLine("A kliens zenete: {0}",
Encoding.ASCII.GetString(data, 0, length));
data = new byte[256];
data = Encoding.ASCII.GetBytes("Hello kliens!");
client.Send(data, data.Length, SocketFlags.None);
}
catch(Exception e)
{
Console.WriteLine(e.Message);
}
finally
{
client.Close();
server.Close();
}
}
}

- 239 -

A Socket osztly konstruktorban megadtuk a cmzsi mdot (InterNetwork, ez az


IPv4), a socket tpust (Stream, ez egy oda-vissza kommunikcit biztost kapcsolat
lesz) s a hasznlt protokollt, ami jelen esetben TCP. A hrom paramter nem
fggetlen egymstl, pl. Stream tpus socketet csak TCP portokoll felett
hasznlhatunk.
Ezutn ksztettnk egy IPEndPoint objektumot, ahogyan azt az egyszerbb
vltozatban is tettk. Ezt a vgpontot a Bind metdussal ktttk a sockethez, majd a
Listen metdussal megmondtuk, hogy figyelje a bejv kapcsolatokat. Ez utbbi
paramtere azt jelzi, hogy maximum hny bejv kapcsolat vrakozhat.
Innentl kezdve nagyon ismers a forrskd, lnyegben ugyanazt tesszk, mint
eddig, csak pp ms a metdus neve.
A kliens osztly sem okozhat nagy meglepetst:
using
using
using
using
using

System;
System.Net;
System.Net.Sockets;
System.Text;
System.IO;

class Program
{
static public void Main(string[] args)
{
Socket client = null;
try
{
client = new Socket(
AddressFamily.InterNetwork,
SocketType.Stream,
ProtocolType.Tcp);
IPEndPoint endPoint = new IPEndPoint(
IPAddress.Parse(args[0]),
int.Parse(args[1]));
client.Connect(endPoint);
byte[] data = new byte[256];
data = Encoding.ASCII.GetBytes("Hello szerver!");
client.Send(data, data.Length, SocketFlags.None);
data = new byte[256];
int length = client.Receive(data);
Console.WriteLine("A szerver zenete: {0}",
Encoding.ASCII.GetString(data, 0, length));
}
catch(Exception e)
{
Console.WriteLine(e.Message);
}
finally
{
client.Close();
}
}
}

- 240 -

A klnbsget a Connect metdus jelenti, mivel most kapcsoldni akarunk, nem


hallgatzni.

34.2 Blokk elkerlse


Az eddigi programjaink mind megegyeztek abban, hogy bizonyos mveletek
blokkoltk a fszlat s gy vrakozni knyszerltnk. Ilyen mvelet volt pl. az
Accept/AcceptTcpClient, de a Read/Receive is.
A blokk elkerlsre n. elre-ellenrzst (prechecking) fogunk alkalmazni, vagyis
megvizsgljuk, hogy adott idpillanatban van-e bejv adat, vagy rrnk ksbb
jraellenrizni, addig pedig csinlhatunk mst.
A TcpListerner/TcpClient osztlyok a rjuk csatlakoztatott NetworkStream objektum
DataAvailable tulajdonsgn keresztl tudjk vizsglni, hogy jn-e adat vagy sem. A
kvetkez pldban a kliens rendszeres idkznknt ellenrzi, hogy rkezett-e
vlasz a szervertl s ha az eredmny negatv akkor foglalkozhat a dolgval:
bool l = false;
while(!l)
{
if(stream.DataAvailable)
{
data = new byte[256];
int length = stream.Read(data, 0, data.Length);
Console.WriteLine("A szerver zenete: {0}",
Encoding.ASCII.GetString(data, 0, length));
l = true;
}
else
{
Console.WriteLine("A szerver mg nem kldtt vlaszt!");
System.Threading.Thread.Sleep(200);
}
}

Ugyanezt a hatst socketek esetben a Socket osztly Available tulajdonsgval


rhetjk el, amely jelzi, hogy van-e mg vrakoz adat a csatornn (egszen
pontosan azoknak a byte-oknak a szmt adja vissza amelyeket mg nem olvastunk
be):
while(true)
{
if(client.Available > 0)
{
int length = client.Receive(data);
Console.WriteLine("A szerver zenete: {0}",
Encoding.ASCII.GetString(data, 0, length));
break;
}
else
{
Console.WriteLine("Vrunk a szerver vlaszra...");
System.Threading.Thread.Sleep(200);
}
}

- 241 -

Itt egy msik mdszert vlasztottunk a ciklus kezelsre.


Most nzzk meg, hogy mi a helyzet szerveroldalon. Itt a tipikus problma az, hogy
az AcceptTcpClient/Accept teljesen blokkol, amg bejv kapcsolatra vrakozunk.
Erre is van persze megolds, a TcpListener esetben ezt a Pending- , mg a Socket
osztlynl a Poll metdus jelenti.
Nzzk elsknt a TcpListener t:
while(true)
{
if(server.Pending())
{
client = server.AcceptTcpClient();
Console.WriteLine("Kliens kapcsoldott...");
stream = client.GetStream();
byte[] data = new byte[256];
int length = stream.Read(data, 0, data.Length);
Console.WriteLine("A kliens zenete: {0}",
Encoding.ASCII.GetString(data, 0, length));
data = Encoding.ASCII.GetBytes("Hello kliens!");
stream.Write(data, 0, data.Length);
}
else
{
Console.WriteLine("Most valami mst csinlunk");
System.Threading.Thread.Sleep(500);
}
}

A Pending azt az informcit osztja meg velnk, hogy vrakozik-e bejv kapcsolat.
Tulajdonkppen ez a metdus a kvetkez forrsban szerepl (a Socket osztlyhoz
tartoz) Poll metdust hasznlja:
while(true)
{
if(server.Poll(0, SelectMode.SelectRead))
{
client = server.Accept();
/* itt pedig kommuniklunk a klienssel */
}
else
{
Console.WriteLine("A szerver bejv kapcsolatra vr!");
System.Threading.Thread.Sleep(500);
}
}

A Poll metdus els paramtere egy egsz szm, amely mikromsodpercben (nem
milli-, itt valban a msodperc milliomod rszrl van sz, vagyis ha egy
msodpercig akarunk vrni akkor 1000000ot kell megadnunk) adja meg azt az idt
amg vrunk bejv kapcsolatra/adatra. Amennyiben az els paramter negatv
szm, akkor addig vrunk, amg nincs kapcsolat (vagyis blokkoljuk a programot), ha
pedig nullt adunk meg akkor hasznlhatjuk pre-checkingre is a metdust.
A msodik paramterrel azt mondjuk meg, hogy mire vrunk. A SelectMode felsorolt
tpus hrom taggal rendelkezik:
- 242 -

1. SelectRead: a metdus igaz rtkkel tr vissza, ha meghvtuk a Listen


metdust, s vrakozik bejv kapcsolat vagy van bejv adat illetve ha a
kapcsolat megsznt, minden ms esetben a visszatrsi rtk false lesz.
2. SelectWrite: igaz rtket kapunk vissza, ha a Connect metdus hva sikeres
volt, azaz csatlakoztunk a tvoli llomshoz, illetve ha lehetsges adat
kldse.
3. SelectError: true rtket ad vissza, ha a Connect metdus hvsa sikertelen
volt.
Egy msik lehetsg a blokk feloldsra, ha a Socket objektum Blocking
tulajdonsgt false rtkre lltjuk. Ekkor a Socket osztly Receive s Send
metdusainak megadhatunk egy SocketError tpus (out) paramtert, amely
WouldBlock rtkkel tr vissza, ha a metdushvs blokkot okozna (vagyis gy jra
prblhatjuk kldeni/fogadni ksbb az adatokat). Azt azonban nem rt tudni, hogy
ez s a fenti Poll metdust hasznl megoldsok nem hatkonyak, mivel folyamatos
metdushvsokat kell vgrehajtanunk (vagy a httrben a rendszernek). A
kvetkez fejezetben tbb kliens egyidej kezelsvel egy sokkal hatkonyabb
mdszer(eke)t fogunk megvizsglni.

34.3 Tbb kliens kezelse


Az eddigi programjainkban csak egy klienst kezeltnk, ami nagyon knyelmes volt,
mivel egy sor dolgot figyelmen kvl hagyhattunk:
1. A kliensekre mutat referencia trolsa
2. A szerver akkor is tudjon kapcsolatot fogadni, amg a bejelentkezett
kliensekkel foglalkozunk.
3. Minden kliens zavartalanul (lehetleg blokk nlkl) tudjon kommuniklni a
szerverrel s viszont.
A kvetkezkben hromflekppen fogjuk krljrni a problmt.

34.3.1 Select
Az els versenyznk a Socket osztly Select metdusa lesz, amelynek segtsgvel
meghatrozhatjuk egy vagy tbb Socket pldny llapott. Lnyegben arrl van
sz, hogy egy listbl kivlaszthatjuk azokat az elemeket, amelyek megfelelnek
bizonyos kvetelmnyeknek (rhatak, olvashatak). A Select (statikus) metdus
szignatrja a kvetkezkppen nz ki:
public static void Select(
IList checkRead,
IList checkWrite,
IList checkError,
int microSeconds
)

- 243 -

Az els hrom paramter olyan IList interfszt implementl gyjtemnyek


(lnyegben az sszes beptett gyjtemny ilyen belertve a sima tmbket is)
amelyek Socket pldnyokat tartalmaznak. Az els paramternek megadott listt
olvashatsgra, a msodikat rhatsgra, mg a harmadikat hibkra ellenrzi a
Select, majd a felttelnek megfelel listaelemeket megtartja a listban a tbbit
eltvoltja (vagyis ha szksgnk van a tbbi elemre is akkor clszer egy msolatot
hasznlni az eredeti lista helyett). Az utols paramterrel azt adjuk meg, hogy
mennyi ideig vrjunk a kliensek vlaszra (mikroszekundum).
Ksztsnk egy Selectet hasznl kliens-szerver alkalmazst! A kliens oldalon
lnyegben semmit nem vltoztatunk azt leszmtva, hogy folyamatosan zenetet
kldnk a szervernek (most csak egyirny lesz a kommunikci):
Random r = new Random();
while(true)
{
if(r.Next(1000) % 2 == 0)
{
byte[] data = new byte[256];
data = Encoding.ASCII.GetBytes("Hello szerver!");
client.Send(data, data.Length, SocketFlags.None);
}
System.Threading.Thread.Sleep(500);
}

Most nzzk a szervert:


int i = 0;
while(i < MAXCLIENT)
{
Socket client = server.Accept();
clientList.Add(client);
++i;
}
while(true)
{
ArrayList copyList = new ArrayList(clientList);
Socket.Select(copyList, null, null, 1000);

foreach(Socket client in clientList)


{
Program.CommunicateWithClient(client);
}

A MAXCLIENT vltoz egy egyszer egsz szm, meghatrozzuk vele, hogy


maximum hny klienst fogunk kezelni. Miutn megfelel szm kapcsolatot hoztunk
ltre elkezdnk beszlgetni a kliensekkel. A ciklus minden itercijban meghvja a
Select metdust, vagyis kivlasztjuk, hogy melyik kliensnek van mondandja. A
CommunicateWithClient statikus metdus a mr ismert mdon olvassa a kliens
zenett:

- 244 -

static public void CommunicateWithClient(Socket client)


{
byte[] data = new byte[256];
int length = client.Receive(data);
Console.WriteLine("A kliens zenete: {0}",
Encoding.ASCII.GetString(data, 0, length));
}

34.3.2 Aszinkron socketek


Kliensek szinkron kezelsre is felhasznlhatjuk a Socket osztlyt, lnyegben az
aszinkron delegate -ekre pl ez a megolds. Az aszinkron hvhat metdusok Begin
s End eltagot kaptak, pl. kapcsolat elfogadsra most a BeginAccept metdust
fogjuk hasznlni.
A kliens ezttal is vltozatlan, nzzk a szerver oldalt:
while(true)
{
done.Reset();
Console.WriteLine("A szerver kapcsolatra vr...");
server.BeginAccept(Program.AcceptCallback, server);
done.WaitOne();
}

A done vltoz a ManualResetEvent osztly egy pldnya, segtsgvel szablyozni


tudjuk a szlakat. A Reset metdus alaphelyzetbe lltja az objektumot, mg a
WaitOne meglltja (blokkolja) az aktulis szlat, amg egy jelzst (A Set metdussal)
nem kap. A BeginAccept aszinkron metdus els paramtere az a metdus lesz,
amely a kapcsolat fogadst vgzi, msodik paramternek pedig tadjuk a szervert
reprezentl Socket objektumot. Teht: meghvjuk a BeginAcceptet, ezutn pedig
vrunk, hogy az AcceptCallback metdus visszajelezzen a fszlnak, hogy a kliens
csatlakozott s folytathatja a figyelst. Nzzk az AcceptCallbackot:
static public void AcceptCallback(IAsyncResult ar)
{
Socket client = ((Socket)ar.AsyncState).EndAccept(ar);
done.Set();
Console.WriteLine("Kliens kapcsoldott...");
StringState state = new StringState();
state.Client = client;
client.BeginReceive(state.Buffer, 0, State.BufferSize,
SocketFlags.None, Program.ReadCallback, state);
}

A kliens fogadsa utn meghvtuk a ManualResetEvent Set metdust, ezzel


jeleztk, hogy ksz vagyunk, vrhatjuk a kvetkez klienst (lthat, hogy csak addig
kell blokkolnunk, amg hozz nem jutunk a klienshez, az adatcsere nyugodtan elfut a
httrben).
- 245 -

A StringState osztlyt knyelmi okokbl mi magunk ksztettk el, ezt fogjuk tadni a
BeginReceive metdusnak:
class State
{
public const int BufferSize = 256;
public State()
{
Buffer = new byte[BufferSize];
}
public Socket Client { get; set; }
public byte[] Buffer { get; set; }
}
class StringState : State
{
public StringState() : base()
{
Data = new StringBuilder();
}
public StringBuilder Data { get; set; }
}

Vgl nzzk a BeginReceive callback metdust:


static public void ReadCallback(IAsyncResult ar)
{
StringState state = (StringState)ar.AsyncState;
int length = state.Client.EndReceive(ar);
state.Data.Append(Encoding.ASCII.GetString(state.Buffer, 0, length));
Console.WriteLine("A kliens zenete: {0}", state.Data);
state.Client.Close();
}

A fordtott irny adatcsere ugyangy zajlik, a megfelel metdusok Begin/End


eltag vltozataival.

34.3.3 Szlakkal megvalstott szerver


Ez a mdszer nagyon hasonl az aszinkron vltozathoz, azzal a klnbsggel, hogy
most manulisan hozzuk ltre a szlakat, illetve nem csak a beptett aszinkron
metdusokra tmaszkodhatunk, hanem tetszs szerinti mveletet hajthatunk vgre a
httrben.
A kvetkez programunk egy szmkitalls jtkot fog megvalstani gy, hogy a
szerver gondol egy szmra s a kliensek ezt megprbljk kitallni. Minden kliens
tszr tallgathat, ha nem sikerl kitallnia, akkor elveszti a jtkot. Mindent tipp
utn a szerver visszakld egy szmot a kliensnek: -1et, ha a gondolt szm
nagyobb, 1et ha a szm kissebb, 0t ha eltallta a szmot s 2t, ha valaki mr

- 246 -

kitallta a szmot. Ha egy kliens kitallta a szmot, akkor a szerver megvrja, mg


minden kliens kilp, s j szmot sorsol.
Ksztettnk egy osztlyt, amely megknnyti a dolgunkat. A Listen metdussal
indtjuk el a szervert:
public void Listen()
{
if(EndPoint == null)
{
throw new Exception("IPEndPoint missing");
}
server = new Socket(AddressFamily.InterNetwork,
SocketType.Stream,
ProtocolType.Tcp);
server.Bind(EndPoint);
server.Listen(5);
ThreadPool.SetMaxThreads(MaxClient, 0);
NewTurnEvent += NewTurnHandler;
Console.WriteLine("A szerver elindult, a szm: {0}", NUMBER);
Socket client = null;
while(true)
{
client = server.Accept();
Console.WriteLine("Kliens bejelentkezett);
ThreadPool.QueueUserWorkItem(ClientHandler, client);
}

A kliensek kezelshez a ThreadPool osztlyt fogjuk hasznlni, amelynek


segtsgvel a kliens objektumokat tadjuk a ClientHandler metdusnak:
private void ClientHandler(object socket)
{
Socket client = null;
string name = String.Empty;
int result = 0;
try
{
client = socket as Socket;
if(client == null)
{
throw new ArgumentException();
}
++ClientNumber;
byte[] data = new byte[7];
int length = client.Receive(data);
name = Encoding.ASCII.GetString(data, 0, length);
Console.WriteLine("j jtkos: {0}", name);
int i = 0;

- 247 -

bool win = false;


while(i < 5 && win == false)
{
data = new byte[128];
length = client.Receive(data);
GuessNumber(name,
int.Parse(Encoding.ASCII.GetString(
data, 0, length)),
out result);
data = Encoding.ASCII.GetBytes(result.ToString());
client.Send(data, data.Length, SocketFlags.None);
if(result == 0) { win = true; }
++i;
}
}
catch(Exception e)
{
Console.WriteLine(e.Message);
}
finally
{
client.Close();

if(--ClientNumber == 0 && result == 0)


{
NewTurnEvent(this, null);
}

Minden kliens rendelkezik nvvel is, amely 7 karakter hossz (7 byte) lehet, elsknt
ezt olvassuk be. A GuessNumber metdusnak adjuk t a tippnket s a result
vltozt out paramterknt. Vgl a finally blokkban ellenrizzk, hogy van-e
bejelentkezett kliens, ha pedig nincs akkor j szmot sorsolunk.
Nzzk a GuessNumber metdust:
private void GuessNumber(string name, int number, out int result)
{
lock(locker)
{
if(NUMBER != -1)
{
Console.WriteLine("{0} szerint a szm: {1}",
name, number);
if(NUMBER == number)
{
Console.WriteLine("{0} kitallta a szmot!", name);
result = 0;
NUMBER = -1;
}
else if(NUMBER < number)
{
result = 1;
}
else
{
result = -1;
}

- 248 -

}
else result = 2;
}
Thread.Sleep(300);
}

A metdus trzst le kell zrnunk, hogy egyszerre csak egy szl (egy kliens) tudjon
hozzfrni. Ha valamelyik kliens kitallta szmot, akkor annak -1et adunk vissza,
gy gyorsan ellenrizhetjk, hogy a felttel melyik gba kell bemennnk.
Kliens oldalon sokkal egyszerbb dolgunk van, ezt a forrskdot itt nem
rszletezzk, de megtallhat a jegyzethez tartoz forrsok kztt.

34.4 TCP s UDP


Ez a fejezet elsdlegesen a TCP protokollt hasznlta, de emltst kell tennnk az
Internet msik alapprotokolljrl az UDPrl is.
A TCP egy megbzhat, de egyttal egy kicsit lassabb mdszer. A csomagokat
sorszmmal ltja el, ez alapjn pedig a fogad fl nyugtt kld, hogy az adott
csomag rendben megrkezett. Amennyiben adott idn bell a nyugta nem rkezik
meg, akkor a csomagot jra elkldi. Ezen kvl ellenrzi, hogy a csomagok
srlsmentesek legyenek, illetve kiszri a dupln elkldtt (redundns) adatokat is.
Az UDP pp az ellenkezje, nem biztostja, hogy minden csomag megrkezik,
cserben gyors lesz. Jellemzen olyan helyeken hasznljk, ahol a gyorsasg
szmt s nem nagy problma, ha egy-kt csomag elveszik, pl. vals idej mdia
lejtszsnl illetve jtkoknl.
A .NET a TCPhez hasonlan biztostja szmunkra az UdpListener/Client
osztlyokat, ezek kezelse gyakorlatilag megegyezik TCP t hasznl prjaikkal,
ezrt itt most nem rszletezzk.
Hagyomnyos Socketek esetn is elrhet ez a protokoll, ekkor a Socket osztly
konstruktora gy fog kinzni:
Socket server = new Socket(
AddressFamily.InterNetwork,
SocketType.Dgram,
ProtocolType.Udp);

Ezutn ugyangy hasznlhatjuk ezt az objektumot, mint a TCPt hasznl trst.

- 249 -

35

LINQ To Objects

A C# 3.0 bevezette a LINQ t (Language Integrated Query), amely lehetv teszi,


hogy knnyen, uniformizlt ton kezeljk a klnbz adatforrsokat, vagyis
pontosan ugyangy fogunk kezelni egy adatbzist, mint egy memriban lv
gyjtemnyt. Mirt j ez neknk? Napjainkban rengeteg adatforrs ll
rendelkezsnkre, ezek kezelshez pedig j eszkzk hasznlatt illetve j
nyelveket kell megtanulnunk (SQL a relcis adatbzisokhoz, XQuery az XMLhez,
stb...).A LINQ lehetv teszi, hogy egy plusz rteg (a LINQ fellet) bevezetsvel
mindezeket thidaljuk mghozz teljes mrtkben fggetlenl az adatforrstl. Egy
msik elnye pedig, hogy a LINQ lekrdezsek ersen tpusosak, vagyis a legtbb
hibt mg fordtsi idben el tudjuk kapni s kijavtani.
A LINQ csald jelenleg hrom fcsapst jellt ki, ezek a kvetkezek:
-

LINQ To XML: XML dokumentumok lekrdezst s szerkesztst teszi


lehetv.
LINQ To SQL (vagy DLINQ) s LINQ To Entities (Entity Framework): relcis
adatbzisokon (elssorban MS SQL-Server) vgezhetnk mveleteket velk.
A kett kzl a LINQ To Entites a fnk, a LINQ To SQL inkbb csak
technolgiai demnak kszlt, a Microsoft nem fejleszti tovbb (de tovbbra is
elrhet marad, mivel kisebb projectekre illetve hobbifejlesztsekhez kivl).
Az Entity Framework hasznlathoz a Visual Studio 2008 els
szervizcsomagjra van szksg.
LINQ To Objects: ennek a fejezetnek a trgya, memriban lv
gyjtemnyek, listk, tmbk feldolgozst teszi lehetv (lnyegben minden
olyan osztllyal mkdik amely megvalstja az IEnumerable<T> interfszt).

A fentieken kvl szmos third-party/hobbi project ltezik, mivel a LINQ framework


viszonylag knnyen kiegszthet tetszs szerinti adatforrs hasznlathoz.
A fejezethez tartoz forrskdok megtallhatak a Sources\LINQ knyvtrban.

35.1 Nyelvi eszkzk


A C# 3.0ban megjelent nhny jdonsg rszben a LINQ miatt kerlt a nyelvbe,
jelenltk jelentsen megknnyti a dolgunkat. Ezek a kvetkezk:
Kiterjesztett
metdusok
(extension
method):
velk
mr
korbban
megismerkedtnk, a LINQ To Objects teljes funkcionalitsa ezekre pl, lnyegben
az sszes mvelet az IEnumerable<T>/IEnumerable interfszeket egszti ki.
Objektum s gyjtemny inicializlk: vagyis a lehetsg, hogy az objektum
deklarlsval egyidben bellthassuk a tulajdonsgaikat, illetve gyjtemnyek
esetben az elemeket:

- 250 -

using System;
using System.Collections.Generic;
class Program
{
static public void Main()
{
/*Objektum inicializl*/
MyObject mo = new MyObject()
{
Property1 = "value1";
Property2 = "value2";
};

/*Gyjtemny inicializl*/
List<string> list = new List<string>()
{
"alma", "krte", "di", "kakukktojs"
};

Lambda kifejezsek: a lekrdezsek tbbsgnl nagyon knyelmes lesz a


lambdk hasznlata, ugyanakkor lehetsg van a hagyomnyos nvtelen
metdusok felhasznlsra is.
A var: a lekrdezsek egy rsznl egszen egyszeren lehetetlen elre megadni
az eredmny-lista tpust, ezrt ilyenkor a var t fogjuk hasznlni.
Nvtelen tpusok: sok esetben nincs szksgnk egy objektum minden adatra,
ilyenkor feleslegesen foglaln egy lekrdezs eredmnye a memrit. A nvtelen
tpusok beveztse lehetv teszi, hogy helyben deklarljunk egy nvtelen osztlyt:
using System;
class Program
{
static public void Main()
{
var type1 = new { Value1 = "alma", Value2 = "krte" };
Console.WriteLine(type1.Value1); //alma
}
}

35.2 Kivlaszts
A legegyszerbb dolog, amit egy listval tehetnk, hogy egy vagy tbb elemt
valamilyen kritrium alapjn kivlasztjuk. A kvetkez forrskdban pp ezt fogjuk
tenni, egy egsz szmokat tartalmaz List<T> adatszerkezetbl az sszes szmot
lekrdezzk. Termszetesen ennek gy sok rtelme nincsen, de szrsrl majd csak
a kvetkez fejezetben fogunk tanulni.

- 251 -

using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
static public void Main()
{
List<int> list = new List<int>()
{
10, 2, 4, 55, 22, 75, 30, 11, 12, 89
};
var result = from number in list select number;
foreach(var item in result)
{
Console.WriteLine("{0}", item);
}
}

Elssorban vegyk szre az j nvteret a System.Linqt. R lesz szksgnk


mostantl az sszes lekrdezst tartalmaz programban, teht ne felejtsk le.
Most pedig jjjn a lnyeg, nzzk a kvetkez sort:
var result = from number in list select number;

Egy LINQ lekrdezs a legegyszerbb formjban a kvetkez sablonnal rhat le:


eredmny = from azonost in kifejezs select kifejezs
A lekrdezs els fejben meghatrozzuk az adatforrst:
from azonost in kifejezs
Egszen pontosan a kifejezs jelli a forrst, mg az azonost a forrs egyes
tagjait jelli a kivlaszts minden itercijban, lnyegben pontosan ugyangy
mkdik, mint a foreach ciklus:
foreach(var azonost in kifejezs)
Vagyis a lekrdezs alatt a forrs minden egyes elemvel tehetnk, amit jlesik. Ezt
a valamit a lekrdezs msodik fele tartalmazza:
select kifejezs
A fenti pldban egyszeren vissza akarjuk kapni a szmokat eredeti formjukban,
de akr ezt is rhattuk volna:
var result = from number in list select (number + 10);

Azaz minden szmhoz hozzadunk tzet.


- 252 -

Az SQL t ismerk szmra furcsa lehet, hogy elszr az adatforrst hatrozzuk


meg, de ennek megvan az oka, mgpedig az, hogy ilyen mdon a fejleszteszkz (a
Visual Studio) tmogatst illetve tpusellenrzst adhat a select utni kifejezshez
(illetve a lekrdezs tbbi rszhez).
A fenti kdban SQL szer lekrdezst ksztettnk (n. Query Expression Format),
de mshogyan is megfogalmazhattuk volna a mondanivalnkat. Emlkezznk,
minden LINQ To Objects mvelet egyben kiterjesztett metdus, vagyis ezt is rhattuk
volna:
var result = list.Select(number => number);

Pontosan ugyanazt rtk el, s a fordts utn pontosan ugyanazt a kifejezst is


fogja hasznlni a program, mindssze a szintaxis ms (ez pedig az Extension
Method Format) (a fordt is ezt a formtumot tudja rtelmezni, teht a Query Syntax
is erre alakul t). A Select az adatforrs elemeinek tpust hasznl Func<T, V>
generikus delegateet kap paramterl, jelen esetben ezt egy lambda kifejezssel
helyettestettk, de rhattuk volna a kvetkezket is:
var result1 = list.Select(
delegate (int number)
{
return number;
});
Func<int, int> selector = (x) => x;
var result2 = list.Select(selector);

A kt szintaxist keverhetjk is (Query Dot Format), de ez az olvashatsg rovsra


mehet, ezrt nem ajnlott (leszmtva olyan eseteket, amikor egy mvelet csak az
egyik formval hasznlhat), ha csak lehet, ragaszkodjunk csak az egyikhez.
A kvetkez pldban a Distinct metdust hasznljuk, amely a lekrdezs
eredmnybl eltvoltja a dupliklt elemeket. t csakis Extension Method formban
hvhatjuk meg:
using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
static public void Main()
{
List<int> list = new List<int>()
{
1, 1, 3, 5, 6, 6, 10, 11, 1
};
var result = (from number in list select number).Distinct();
foreach(var item in result)
{
Console.WriteLine("{0}", item);
}
}

- 253 -

A jegyzet ezutn mindkt vltozatot bemutatja a forrskdokban.

35.2.1 Projekci
Vegyk a kvetkez egyszer osztly-hieraarchit:
class Address
{
public string Country { get; set; }
public int PostalCode { get; set; }
public int State { get; set; }
public string City { get; set; }
public string Street { get; set; }
}
class Customer
{
public int ID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set;}
public string Email { get; set; }
public string PhoneNumber { get; set; }
public Address Address { get; set; }
}

Minden vsrlhoz tartozik egy Address objektum, amely a vev cmt trolja.
Tegyk fel, hogy egy olyan lekrdezst akarok rni, amely visszaadja az sszes vev
nevt, email cmt s telefonszmt. Ez nem egy bonyolult dolog, a kd a kvetkez
lesz:
var result = from customer in custList select customer;
foreach(var customer in result)
{
Console.WriteLine("Nv: {0}, Email: {1}, Telefon: {2}",
customer.FirstName + + customer.LastName,
customer.Email, Customer.PhoneNumber);
}

A problma a fenti kddal, hogy a szksges adatokon kvl megkaptuk az egsz


objektumot belertve a cm pldnyt is amelyre pedig semmi szksgnk nincsen. A
megolds, hogy az eredeti eredmnyhalmazt leszktjk, gy, hogy a lekrdezsben
ksztnk egy nvtelen osztlyt, amely csak a krt adatokat tartalmazza. Ezt
projekcinak nevezzk. Az j kd:
var result = from customer in custList select new
{
Name = customer.FirstName + Customer.LastName,
Email = customer.Email,
Phone = customer.PhoneNumber
};

Termszetesen nem csak nvtelen osztlyokkal dolgozhatunk ilyen esetekben,


hanem kszthetnk specializlt direkt erre a clra szolgl osztlyokat is.
- 254 -

A lekrdezs eredmnynek tpust eddig nem jelltk, helyette a var kulcsszt


hasznltuk, mivel ez rvidebb. Minden olyan lekrdezs, amelytl egynl tbb
eredmnyt vrunk (teht nem azok az opertorok, amelyek pontosan egy elemmel
trnek vissza errl ksbb rszletesebben) IEnumerable<T> tpus eredmnyt
(vagy annak leszrmazott, specializlt vltozatt) ad vissza.

35.2.2 Let
A let segtsgvel a lekrdezs hatkrn belli vltozkat hozhatunk ltre,
amelyek segtsgvel elkerlhetjk egy kifejezs ismtelt felhasznlst. Nzznk
egy pldt:
string[] poem = new string[]
{
"Ej mi a k! tykany, kend",
"A szobban lakik itt bent?",
"Lm, csak j az isten, jt d,",
"Hogy flvitte a kend dolgt!"
};
var result = from line in poem
let words = line.Split(' ')
from word in words
select word;

A let segtsgvel minden sorbl egy jabb string-tmbt ksztettnk, amelyeken


egy bels lekrdezst futattunk le.

35.3 Szrs
Nylvn nincs szksgnk mindig az sszes elemre, ezrt kpesnek kell lennnk
szrni az eredmnylistt. A legalapvetbb ilyen mvelet a where, amelyet a
kvetkez sablonnal rhatunk le:
from azonost in kifejezs where kifejezs select azonost
Nzznk egy egyszer pldt:
using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
static public void Main()
{
List<int> list = new List<int>()
{
12, 4, 56, 72, 34, 0, 89, 22
};
var result1 = from number in list
where number > 30

- 255 -

select number;
var result2 = list.Where(number => number > 30);
var result3 = (from number in list select number)
.Where(number => number > 30);
}
}

A forrsban mindhrom lekrdezs szintaxist lthatjuk, mind pontosan ugyanazt


fogja visszaadni s teljesen szablyosak.
A where egy paramterrel rendelkez, bool visszatrsi rtk metdust (anonim
metdust, lambda kifejezst, stb...) vr paramtereknt:
Func<int, bool> predicate = (x) => x > 30;
var result1 = from number in list
where predicate(number)
select number;
var result2 = list.Where(predicate);

A where feltteleinek megfelel elemek nem a lekrdezs hvsakor kerlnek az


eredmnylistba, hanem akkor, amikor tnylegesen felhasznljuk ket, ezt
elhalasztott vgrehajtsnak (deferred execution) nevezzk (ez all kivtelt jelent, ha
a lekrdezs eredmnyn azonnal meghvjuk pl. a ToList metdust, ekkor az elemek
szrse azonnal megtrtnik).
A kvetkez pldban teszteljk a fenti lltst:
using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
static public void Main()
{
List<int> list = new List<int>()
{
12, 4, 56, 72, 34, 0, 89, 22
};
Func<int, bool> predicate = (x) =>
{
Console.WriteLine("Szrs...");
return x > 30;
};
Console.WriteLine("Lekrdezs eltt...");
var result = from number in list
where predicate(number)
select number;
Console.WriteLine("Lekrdezs utn...");
foreach(var item in result)
{

- 256 -

Console.WriteLine("{0}", item);
}

A kimenet pedig ez lesz:


Lekrdezs eltt...
Lekrdezs utn...
Szrs...
Szrs...
Szrs...
56
Szrs...
72
Szrs...
34
Szrs...
Szrs...
89
Szrs...
Jl lthat, hogy a foreach ciklus vltotta ki a szr elindulst.
A where kt alakban ltezik, az elst mr lttuk, most nzzk meg a msodikat is:
var result = list.Where((number, index) => index % 2 == 0);

Ez a vltozat kt paramtert kezel, ahol index az elem indext jelli, termszetesen


nulltl szmozva. A fenti lekrdezs a pros index elemeket vlasztja ki.

35.4 Rendezs
A lekrdezsek eredmnyt knnyen rendezhetjk az orderby utastssal, a
lekrdezs sablonja ebben az esetben gy alakul:
from
azonost
in
kifejezs
where
ascending/descending select kifejezs

kifejezs

orderby

Az els pldban egyszeren rendezzk egy szmokbl ll lista elemeit:


var result1 = list.OrderBy(x => x); // nvekv sorrend
var result2 = from number in list
orderby number ascending
select number; // szintn nvekv
var result3 = from number in list
orderby number descending
select number; // cskken sorrend

- 257 -

tulajdonsg

A rendezs a gyorsrendezs algoritmust hasznlja.


Az elemeket tbb szinten is rendezhetjk, a kvetkez pldban neveket fogunk
sorrendbe rakni, mgpedig gy, hogy az azonos kezdbetvel rendelkezket tovbb
rendezzk a nv msodik karaktere alapjn:
using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
static public void Main()
{
List<string> names = new List<string>()
{
"Istvn", "Ivn", "Judit", "Joln", "Jen", "Bla",
"Balzs", "Viktria", "Vazul", "Thtm", "Tams"
};
var result1 = names.OrderBy(name => name[0])
.ThenBy(name => name[1]);
var result2 = from name in names
orderby name[0], name[1]
select name;
foreach(var item in result2) { Console.WriteLine(item); }
}
}

Az Extension Method formban a ThenBy volt segtsgnkre, mg a Query


szintaxissal csak annyi a dolgunk, hogy az alapszably mg rjuk a tovbbi
kritriumokat. Egy lekrdezs pontosan egy orderby/OrderByt s brmennyi
ThenByt tartalmazhat.
Az OrderBy metdus egy msik vltozata kt paramtert fogad, az els a rendezs
alapszablya, mg msodik paramterknt megadhatunk egy tetszleges
IComparer<T> interfszt megvalst osztlyt.

35.5 Csoportosts
Lehetsgnk van egy lekrdezs eredmnyt csoportokba rendezni a group
by/GroupBy metdus segtsgvel. A sablon ebben az esetben gy alakul:
from
azonost
in
kifejezs
where
kifejezs
orderby
tulajdonsg
ascending/descending group kifejezs by kifejezs into azonost select kifejezs
Hasznljuk fel az elz fejezetben elksztett program neveit s rendezzk ket
csoportokba a nv els betje szerint:

- 258 -

using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
static public void Main()
{
List<string> names = new List<string>()
{
"Istvn", "Ivn", "Judit", "Joln", "Jen", "Bla",
"Balzs", "Viktria", "Vazul", "Thtm", "Tams"
};
var result1 = names.OrderBy(name => name[0])
.GroupBy(name => name[0]);
var result2 = from name in names
orderby name[0]
group name by name[0]
into namegroup
select namegroup;
foreach(var group in result1)
{
Console.WriteLine(group.Key);

foreach(var name in group)


{
Console.WriteLine("-- {0}", name);
}

}
}

A kimenet a kvetkez lesz:


B
--I
--J
---T
--V
---

Bla
Balzs
Istvn
Ivn
Judit
Joln
Jen
Thtm
Tams
Viktria
Vazul

A csoportostshoz meg kell adnunk egy kulcsot, ez lesz az OrderBy paramtere. Az


eredmny tpusa a kvetkez lesz:

- 259 -

IEnumerable<IGrouping<TKey, TElement>>
Az IGrouping interfsz tulajdonkppen maga is egy IEnumerable<T> leszrmazott
kiegsztve a rendezshez hasznlt kulccsal, vagyis lnyegben egy lista a listban
tpus adatszerkezetrl van sz.

35.5.1 Null rtkek kezelse


Elfordul, hogy olyan listn akarunk lekrdezst vgrehajtani, amelynek bizonyos
indexein null rtk van. A Select kpes kezelni ezeket az eseteket, egyszeren null
rtket tesz az eredmnylistba, de amikor rendeznk, akkor szksgnk van az
objektum adataira s ha null rtket akarunk vizsglni akkor gyorsan kivtelt
kaphatunk. Hasznljuk fel az elz programok listjt, egsztsk ki nhny null
rtkkel s rjuk t a lekrdezst, hogy kezelje ket:
var result1 = names.GroupBy(name =>
{
return name == null ? '0' : name[0];
});
var result2 = from name in names
group name by
name == null ? '0' : name[0]
into namegroup
select namegroup;
foreach(var group in result2)
{
Console.WriteLine(group.Key);
foreach(var name in group)
{
Console.WriteLine("-- {0}", name == null ? "null" : name);
}
}

Tbbfle megolds is ltezik erre a problmra, hasznlhattuk volna a ?? opertort


is, vagy akr kszthetnk egy metdust, amely a megfelel alaprtelmezett rtket
adja vissza, a lnyeg, hogy amikor a listban elfordulhatnak null rtkek akkor azt
figyelembe kell venni a lekrdezs megrsakor. Ez akkor is szmt, ha egybknt a
lekrdezs nem vgez mveleteket az elemekkel, hanem csak kivlasztst
vgznk, hiszen az eredmnylistban attl mg ott lesznek a null elemek amelyek
ksbb fejfjst okozhatnak.

35.5.2 sszetett kulcsok


Kulcs meghatrozsnl lehetsgnk van egynl tbb rtket kulcsknt megadni,
ekkor nvtelen osztlyknt kell azt definilni. Hasznljuk fel a korbban megismert
Customer illetve Address osztlyokat, ezeket, illetve a hozzjuk tartoz listkat a
jegyzet mell csatolt forrskdok kzl a Data.cs file-ban tallja az olvas.

- 260 -

class Address
{
public string Country { get; set; }
public int PostalCode { get; set; }
public int State { get; set; }
public string City { get; set; }
public string Street { get; set; }
}
class Customer
{
public int ID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set;}
public string Email { get; set; }
public string PhoneNumber { get; set; }
public Address Address { get; set; }
}

A lekrdezst pedig a kvetkezkppen rjuk meg:


using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
static public void Main()
{
var result = from customer in DataClass.GetCustomerList()
group customer by
new
{
customer.Address.Country,
customer.Address.State
}
into customergroup
select customergroup;
foreach(var group in result)
{
Console.WriteLine("{0}, {1}",
group.Key.Country, group.Key.State);
foreach(var customer in group)
{
Console.WriteLine("-- {0}",
customer.FirstName + " " + customer.LastName);
}
}
}

Fordtani gy tudunk:
csc main.cs Data.cs

- 261 -

35.6 Listk sszekapcsolsa


A relcis adatbzisok egyik alapkve, hogy egy lekrdezsben tbb tblt
sszekapcsolhatunk (join) egy lekrdezssel, pl. egy webruhzban a vev-rrendels adatokat. Memriban lv gyjtemnyek esetben ez viszonlag ritkn
szksges, de a LINQ To Objects tmogatja ezt is.
A joinmveletek azon az egyszer felttelen alapulnak, hogy az
sszekapcsoland objektum-listk rendelkeznek kzs adattagokkal (relcis
adatbzisoknl ezt elsdleges kulcs (primary key) idegen kulcs (foreign key)
kapcsolatknt kezeljk). Nzznk egy pldt:
class Customer
{
public int ID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set;}
public string Email { get; set; }
public string PhoneNumber { get; set; }
public Address Address { get; set; }
}
class Order
{
public
public
public
public
public
}

int CustomerID { get; set; }


int ProductID { get; set; }
DateTime OrderDate { get; set; }
DateTime? DeliverDate { get; set; }
string Note { get; set; }

class Product
{
public int ID { get; set; }
public string ProductName { get; set; }
public int Quantity { get; set; }
}

Ez a fent emltett webruhz egy egyszer megvalstsa. Mind a Customer mind a


Product osztly rendelkezik egy ID nev tulajdonsggal, amely segtsgvel
egyrtelmen meg tudjuk klnbztetni ket, ez lesz az elsdleges kulcs (tegyk fel,
hogy egy listban egy pldnybl s gy kulcsbl csak egy lehet). Az Order
osztly mindkt pldnyra tartalmaz referencit (hiszen minden rendels egy vevtermk prost ignyel), ezek lesznek az idegen kulcsok. rjunk egy lekrdezst,
amely visszaadja minden vsrl rendelst.
Elsknt rjuk fel a joinnal felszerelt lekrdezsek sablonjt:
from azonost in kifejezs where kifejezs join azonost in kifejezs on kifejezs
equals kifejezs into azonost orderby tulajdonsg ascending/descending group
kifejezs by kifejezs into azonost select kifejezs
Most pedig jjjn a lekrdezs (az adatokat most is a Data.cs trolja):

- 262 -

var result = from order in DataClass.GetOrderList()


join customer in DataClass.GetCustomerList()
on order.CustomerID equals customer.ID
select new
{
Name = customer.FirstName + " " + customer.LastName,
Products = DataClass.GetProductList()
.Where(order.ProductID == product.ID)
};
foreach(var orders in result)
{
Console.WriteLine(orders.Name);

foreach(var product in orders.Products)


{
Console.WriteLine("-- {0}", product.ProductName);
}

A join tipikusan az a lekrdezs tpus, ahol az SQL-szer szintaxis olvashatbb,


ezrt itt csak ezt rtuk meg. A lekrdezs nagyon egyszer, elsknt csatoltuk az
elsdleges kulcsokat tartalmaz listt az idegen kulccsal rendelkez listhoz, majd
megmondtuk, hogy milyen felttelek szerint prostsa az elemeket (itt is
hasznlhatunk sszetett kulcsokat ugyangy nvtelen osztly ksztsvel). Az
eredmnylistt persze ki kell egsztennk a vsrolt termkek listjval, ezrt egy
bels lekrdezst is rtunk. A kimenet a kvetkez lesz:
Istvan Reiter
-- Eloszt
Istvan Reiter
-- Papr zsebkend
Jzsef Fekete
-- Elektromos vzforral

35.7 Outer join


Az elz fejezetben azokat az elemeket vlasztottuk ki, amelyek kapcsoldtak
egymshoz, de gyakran van szksgnk azokra is, amelyek ppen hogy nem
kerlnnek bele az eredmnylistba, pl. azokat a vsrlkat keressk, akik eddig
mg nem rendeltek semmit. Ez a feladat a join egy specilis outer joinnak nevezett
vltozata. A LINQ To Objects br kzvetlenl nem tmogatja ezt (pl. az SQL
tartalmaz OUTER JOIN utastst), de knnyen szimullhatjuk a DefaultIfEmpty
metdus hasznlatval, amely egyszeren egy null elemet helyez el az
eredmnylistban az sszes olyan elem helyn, amely nem szerepelne a
hagyomnyos join ltal kapott lekrdezsben.
A kvetkez pldban a lekrdezs visszaadja a vsrlk rendelsnek a
sorszmt (most nincs szksgnk a Products listra), vagy ha mg nem rendelt,
akkor megjelentnk egy nincs rendels feliratot.:

- 263 -

var result = from customer in DataClass.GetCustomerList()


join order in DataClass.GetOrderList()
on customer.ID equals order.CustomerID into tmpresult
from o in tmpresult.DefaultIfEmpty()
select new
{
Name = customer.FirstName + " " + customer.LastName,
Product = o == null ?
"nincs rendels" : o.ProductID.ToString()
};
foreach(var order in result)
{
Console.WriteLine("{0}: {1}",
order.Name, order.Product);
}

35.8 Konverzis opertorok


A LINQ To Objects lehetsget ad listk konverzijra a kvetkez opertorok
segtsgvel:
OfType s Cast: k a sima IEnumerable interfszrl konvertlnak generikus
megfelelikre, elsdlegesen a .NET 1.0val val kompatibilits miatt lteznek, hiszen
a rgi ArrayList osztly nem valstja meg a generikus IEnumerablet, ezrt kasztolni
kell, ha LTO t akarunk hasznlni.
A kett kzti klnbsget a hibakezels jelenti: az OfType egyszeren figyelmen
kvl hagyja a konverzis hibkat s a nem megfelel elemeket kihagyja az
eredmnylistbl, mg a Cast kivtelt (System.InvalidCastException) dob:
using
using
using
using

System;
System.Collections;
System.Collections.Generic;
System.Linq;

class Program
{
static public void Main()
{
ArrayList list = new ArrayList();
list.Add("alma");
list.Add("di");
list.Add(12345);
var result1 = from item in list.Cast<string>()
select item;
var result2 = from item in list.OfType<string>()
select item;

foreach(var item in result1)


{
Console.WriteLine(item); // kivtel
}

- 264 -

A program ugyan kirja a lista els kt elemt, de a harmadiknl mr kivtelt dob,


teht a konverzi elemenknt trtnik, mgpedig akkor, amikor az eredmnylistt
tnylegesen felhasznljuk nem pedig a lekrdezsnl.
ToArray, ToList, ToLookup, ToDictionary: ezek a metdusok, ahogyan a nevkbl
is ltszik az IEnumerable<T> eredmnylistt tmbb vagy generikus gyjtemnny
alaktjk. A kvetkez pldban a ToList metdus lthat:
using
using
using
using

System;
System.Collections;
System.Collections.Generic;
System.Linq;

class Program
{
static public void Main()
{
List<int> list = new List<int>()
{
10, 32, 45, 2, 55, 32, 21
};
var result = (from number in list
where number > 20
select number).ToList<int>();
result.ForEach((number) => Console.WriteLine(number));
}
}

Amikor ezeket az opertorokat hasznljuk akkor a Where vgrehajtsa nem toldik


el, hanem azonnal kiszri a megfelel elemeket, vagyis itt rdemes figyelni a
teljestmnyre.
A ToArray metdus rtelemszeren tmbb konvertlja a bemen adatokat. A
ToDictionary s ToLookup metdusok hasonl feladatot ltnak el abban az
rtelemben, hogy mindkett kulcs-rtk prokkal operl adatszerkezetet hoz ltre. A
klnbsg a dupliklt kulcsok kezelsben van, mg a Dictionary<T, V> szerkezet
ezeket nem engedlyezi (st kivtelt dob), addig a ToLookup ILookup<T, V>
szerkezetet ad vissza, amelyben az azonos kulccsal rendelkez adatok listt
alkotnak a listn bell, ezrt t kivlan alkalmazhatjuk a join mveletek sorn. A
kvetkez pldban ezt a metdust hasznljuk, hogy a vsrlkat megye szerint
rendezze:
var result = (from customer in DataClass.GetCustomerList()
select customer)
.ToLookup((customer) => customer.Address.State + " megye");
foreach(var item in result)
{
Console.WriteLine(item.Key);
foreach(var customer in item)
{
Console.WriteLine("-- {0} {1}",
customer.FirstName, customer.LastName);
}
}

- 265 -

A ToLookUp paramtereknt a lista kulcsrtkt vrja.


AsEnumerable: Ez az opertor a megfelel IEnumerable<T> tpusra konvertlja
vissza a megadott IEnumerable<T> interfszt megvalst adatszerkezetet.

35.9 Element opertorok


A kvetkez opertorok megegyeznek abban, hogy egyetlen elemmel (vagy egy
elre meghatrozott alaprtkkel) trnek vissza az eredmnylistbl.
First/Last s FirstOrDefault/LastOrDefault: ezeknek a metdusoknak kt vltozata
van: a paramter nkli az els/utols elemmel tr vissza a listbl, mg a msik egy
Func<T, bool> tpus metdust kap paramternek s e szerint a szr szerint
vlasztja ki az els elemet. Amennyiben nincs a felttelnek megfelel elem a listban
(vagy res listrl beszlnk) akkor az els kt opertor kivtelt dob, mg a msik
kett az elemek tpusnak megfelel alaprtelmezett null rtkkel tr vissza (pl. int
tpus elemek listjnl ez nulla mg stringek esetn null lesz):
using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
static public void Main()
{
List<int> list = new List<int>()
{
10, 3, 56, 67, 4, 6, 78, 44
};
var result1 = list.First(); // 10
var result2 = list.Last(); // 44
var result3 = list.First((item) => item > 10); // 56
try
{
var result4 = list.Last((item) => item < 3); // kivtel
}
catch(Exception e)
{
Console.WriteLine(e.Message);
}
var result5 = list.FirstOrDefault((item) => item < 3); // 0
Console.WriteLine("{0}, {1}, {2}, {3}",
result1, result2, result3, result5);
}
}

Single/SingleOrDefault: ugyanaz, mint a First/FirstOrDefault pros, de mindkett


kivtelt dob, ha a felttelnek tbb elem is megfelel.

- 266 -

ElementAt/ElementAtOrDefault: visszaadja a paramterknt tadott indexen


tallhat elemet. Az els kivtelt dob, ha az index nem megfelel a msik pedig a
megfelel alaprtelmezett rtket adja vissza.
DefaultIfEmpty: a megfelel alaprtelmezett rtkkel tr vissza, ha egy lista nem
tartalmaz elemeket, t hasznltuk korbban a join mveleteknl.

35.10 Halmaz opertorok


Nzzk a halmaz opertorokat, amelyek kt lista kztti halmazmveleteket tesznek
lehetv.
Concat s Union: mindkett kpes sszefzni kt lista elemeit, de az utbbi egy
elemet csak egyszer tesz t az j listba:
List<int> list1 = new List<int>()
{
10, 3, 56, 67, 4, 6, 78, 44
};
List<int> list2 = new List<int>()
{
10, 5, 67, 89, 3, 22, 99
};
var result1 = list1.Concat(list2);
/*
10, 3, 56, 67, 4, 6, 78, 44, 10, 5, 67, 89, 3, 22, 99
*/
var result2 = list1.Union(list2);
/*
10, 3, 56, 67, 4, 6, 78, 44, 5, 89, 22, 99
*/

Intersect s Except: az els azokat az elemeket adja vissza, amelyek mindkt


listban szerepelnek, mg a msodik azokat amelyek csak az elsben:
List<int> list1 = new List<int>()
{
10, 3, 56, 67, 4, 6, 78, 44
};
List<int> list2 = new List<int>()
{
10, 5, 67, 89, 3, 22, 99
};
var result1 = list1.Intersect(list2);
/*
10, 3, 67
*/
var result2 = list1.Except(list2);
/*
56, 4, 6, 78, 44
*/

- 267 -

35.11 Aggregt opertorok


Ezek az opertorok vgigrnak egy listt, elvgeznek egy mveletet minden elemen,
s vgeredmnyknt egyetlen rtket adnak vissza (pl. elemek sszege vagy
tlagszmts).
Count s LongCount: visszaadjk az elemek szmt egy listban. Alaprtelmezs
szerint az sszes elemet szmoljk, de megadhatunk felttelt is. A kt opertor
kztti klnbsg az eredmny nagysgban van, a Count 32 bites egsz szmmal
(int), mg a LongCount 64 bites egsz szmmal (int64) tr vissza:
List<int> list = new List<int>()
{
10, 3, 56, 67, 4, 6, 78, 44
};
var result1 = list.Count(); // 8
var result2 = list.Count((item) => item > 10); // 4

Min s Max: a lista legkisebb illetve legnagyobb elemt adjk vissza. Mindkt
opertornak megadhatunk egy szelektor kifejezst, amelyet az sszes elemre
alkalmaznak, s e szerint vlasztjk ki a megfelel elemet:
List<int> list = new List<int>()
{
10, 3, 56, 67, 4, 6, 78, 44
};
var result1 = list.Max(); // 78
var result2 = list.Max((item) => item % 3); // 2

Termszetesen a msodik eredmny maximum kett lehet, hiszen hrommal val


oszts utn ez lehet a legnagyobb maradk.
Mindkt metdus az IComparable<T> interfszt hasznlja, gy minden ezt
megvalst tpuson hasznlhatak.
Average s Sum: a lista elemeinek tlagt illetve sszegt adjk vissza. k is
rendelkeznek szelektor kifejezst hasznl vltozattal:
List<int> list = new List<int>()
{
10, 3, 56, 67, 4, 6, 78, 44
};
var result1 = list.Sum(); // 268
var result2 = list.Average(); // 33.5
var result3 = list.Sum((item) => item * 2); // 536

Aggregate: ez az opertor lehetv teszi tetszleges mvelet elvgzst s a


rszeredmnyek felhalmozst. Az Aggregate hrom formban ltezik, tetszs
szerint megadhatunk neki egy kezdrtket, illetve egy szelektor kifejezst is:

- 268 -

List<int> list = new List<int>()


{
1, 2, 3, 4
};
var sum = list.Aggregate((result, item) => result + item);
var max = list.Aggregate(-1, (result, item) => item > result ? item :
result);
var percent = list.Aggregate(0.0, (result, item) => result + item,
result => result / 100);

Az els esetben a Sum opertort szimulltuk, ezt nem kell magyarzni. A msodik
vltozatban maximumkeresst vgeztnk, itt megadtunk egy kezdrtket, amelynl
biztosan van nagyobb elem a listban. Vgl a harmadik mveletnl kiszmoltuk a
szmok sszegnek egy szzalkt (itt figyelni kell arra, hogy double tpusknt lssa
a fordt a kezdrtket, hiszen tizedes trtet akarunk visszakapni).
A vgeredmnyt trol vltoz brmilyen tpus lehet, mg tmb is.

35.12 PLINQ Prhuzamos vgrehajts


Napjainkban mr egyltaln nem jelentenek jdonsgot a tbb maggal rendelkez
processzorok, gy teljesen jogosan jelentkezett az igny, hogy minl jobban ki tudjuk
ezt hasznlni. A .NET 4.0 bevezeti neknk a Parallel Task Libraryt s a jelen fejezet
trgyt a ParallelLINQt, vagyis a lekrdezsek prhuzamostsnak lehetsgt.

35.12.1 Tbbszlsg vs. Prhuzamossg


Amikor tbbszl programokat ksztnk alapveten nem trdnk a hardver
lehetsgeivel, ltrehozunk szlakat amelyek versengnek a processzoridrt.
Ilyenkor rtelemszeren nem fogunk klnsebb teljestmnynvekedst kapni,
hiszen minden mvelet ugyanannyi ideig tart, nem tudjuk ket kzvetlenl
sztosztani az esetleges tbb processzor kztt. Ezt a mdszert pl. olyankor
hasznljuk, amikor nem akarjuk, hogy a httrben fut szlak megzavarjk a fszl
kezelhetsgt (pl. ha egy bngszben tbb oldalt is megnyitunk, nem akarjuk
megvrni amg mindegyik letltdik), vagy egyszerre tbb krst kell kezelnnk (pl.
egy kliens-szerver kapcsolat).
A prhuzamos programozs ugyanezt kpes nyjtani, de kpes a processzorok
szmnak fggvnyben sztosztani a munkt a CPU k kztt, ezzel pedig
teljestmnynvekedst r el. Ennek a mdszernek a nagy htrnya, hogy olyan
algoritmust kell talljunk, amely minden helyzetben a lehet leghatkonyabban tudja
elosztani a munkt, ankl, hogy brmely processzor resjratban lljon.

35.12.2 Teljestmny
Nagyon knnyen azt gondolhatjuk, hogy a processzorok szmnak nvelsvel
egyenes arnyban n a teljestmny, magyarul kt processzor ktszer gyorsabb,
mint egy. Ez az llts nem teljesen igaz (ezt ksbb a sajt szemnkkel is ltni
fogjuk), ezt pedig Gene Amdahl bizonytotta (Amdahls Law), miszerint:
- 269 -

Egy prhuzamos program maximum olyan gyors lehet, mint a leghosszabb


szekvencilis (tovbb mr nem prhuzamosthat) rszegysge.
Vegynk pldul egy programot, amely 10 rn keresztl fut nem prhuzamosan.
Ennek a programnak 9 rnyi rsze prhuzamosthat, mg a maradk egy ra
nem. Ha ezt a 9 rt prhuzamostjuk, akkor a ttel alapjn a programnak gy is
minimum egy rs futsideje lesz.
Amdahl a kvetkez kpletet tallta ki:
(

Ahol P a program azon rsze, amely prhuzamosthat, mg (1 P) az amelyik nem,


N pedig a processzorok szma.
Nzzk meg, hogy mekkora a maximum teljestmny, amit a fenti esetbl ki tudunk
prselni. P ekkor 0,9 lesz (9 ra = 90% = 0,9), s a kplet (kt processzort
hasznlva):
(

Knnyen kiszmolhat, hogy az eredmny 1/0,55 (1,81) lesz vagyis 81% -os
teljestmnynvekedst rhetnk el kt processzor bevezetsvel. Vegyk szre,
hogy a processzorok szmnak nvelsvel P/N a nullhoz tart, vagyis
kimondhatjuk, hogy minden prhuzamosthat feladat maximum 1/(1 P)
nagysgrend teljestmnynvekedst eredmnyezhet (felttelezve, hogy mindig
annyi processzor ll rendelkezsnkre, hogy P/N a lehet legkzelebb legyen
nullhoz: ez nem felttlenl jelent nagyon sokat, a plda esetben 5 processzor mr
550%-os nvekedst jelent, innen pedig egyre lassabban n az eredmny, mivel
ekkor P/N rtke mr 0,18, hat processzornl 0,15 s gy tovbb), teht a fenti
konkrt esetben a maximlis teljestmny a hagyomnyos futsid tzszerese lehet
(1 / (1 0,9), vagyis pontosan az az egy ra, amelyet a nem prhuzamosthat
programrsz hasznl fel.

35.12.3 PLINQ a gyakorlatban


Ebben a fejezetben sszehasonltjuk a hagyomnyos s prhuzamos LINQ
lekrdezsek teljestmnyt. Elssorban szksgnk lesz valamilyen, megfelelen
nagy adatforrsra, amely jelen esetben egy szveges file lesz. A jegyzethez mellkelt
forrskdok kztt megtalljuk a DataGen.cs nevt, amely az els paramterknt
megadott fileba a msodik paramterknt megadott mennyisg vezetknvkeresztnv-kor-foglalkozs-megye adatokat r. A kvetkez pldban tzmilli
szemllyel fogunk dolgozni, teht gy futassuk a programot:
DataGen.exe E:\Data.txt 10000000

- 270 -

Az elrsi t persze tetszleges. Most ksztsk el a lekrdezst. Felhasznljuk,


hogy a .NET 4.0ban a File.ReadLines metdus IEnumerable<string>-gel tr vissza,
vagyis kzvetlenl hajthatunk rajta vgre lekrdezst:
var lines = File.ReadLines(@"E:\Data.txt"); // System.IO kell
var result = from line in lines
let data = line.Split( new char[] { ' ' })
let name = data[1]
let age = int.Parse(data[2])
let job = data[3]
where name == "Istvn" &&
(age > 24 && age < 55) &&
job == "brtnr"
select line;

Keressk az sszes 24 s 55 v kztti Istvn nev brtnrt. Elsknt sztvgunk


minden egyes sort, majd a megfelel indexekrl (az adatok sorrendjrt ltogassuk
meg a DataGen.cst) sszeszedjk a szksges adatokat. Az eredmny rassuk ki
egy egyszer foreach ciklussal:
foreach(var line in result)
{
Console.WriteLine(line);
}

A fenti lekrdezs egyelre nem prhuzamostott, nzzk meg, hogy mi trtnik:

Ez a kp a processzorhasznlatot mutatja, kt dolog vilgosan ltszik: 1. a kt


processzormag nem ugyanazt a teljestmnyt adja le, 2. egyik sem teljest
maximumon.
Most rjuk t a lekrdezst prhuzamosra, ezt rendkvl egyszeren tehetjk meg,
mindssze az AsParallel metdust kell meghvnunk az adatforrson:
var result = from line in lines.AsParallel()
let data = line.Split( new char[] { ' ' })
let name = data[1]
let age = int.Parse(data[2])
let job = data[3]
where name == "Istvn" &&
(age > 24 && age < 55) &&
job == "brtnr"
select line;

Lssuk az eredmnyt:

- 271 -

A kp nmagrt beszl, de a teljestmnyklnbsg is, a tesztgpen tlagosan 25%


volt a prhuzamos lekrdezs elnye a hagyomnyoshoz kpest (ez termszetesen
mindenkinl ms s ms lehet).
Az AsParallel kiterjesztett metdus egy ParalellQuery objektumot ad vissza, amely
megvalstja az IEnumerable interfszt.
A ktoperandus LINQ opertorokat (pl. join, Concat) csakis gy hasznlhatjuk
prhuzamosan, ha mindkt adatforrst AsParallel lel jelljk, ellenkez esetben
nem fordul le:
var result = list1.AsParallel().Concat(list2.AsParallel());

Br gy tnhet, hogy nagyon egyszeren felgyorsthatjuk a lekrdezseinket a


valsg ennl kegyetlenebb. Ahhoz, hogy megrtsk, hogy mirt csak bizonyos
esetekben kell prhuzamostanunk, ismerni kell a prhuzamos lekrdezsek
munkamdszert:
1. Analzis: a keretrendszer megvizsglja, hogy egyltaln megri-e
prhuzamosan vgrehajtani a lekrdezst. Minden olyan lekrdezs, amely
nem tartalmaz legalbb viszonylag sszetett szrst vagy egyb drga
mveletet szinte biztos, hogy szekvencilisan fog vgrehajtdni (s egyttal a
lekrdezs futsidejhez hozzaddik a vizsglat ideje is). Termszetesen az
ellenrzst vgrehajt algoritmus sem tvedhetetlen, ezrt lehetsgnk van
manulisan kiknyszerteni a prhuzamossgot:
var result = select x from data.AsParallel()
.WithExecutionMode(ParallelExecutionMode.ForceParallelism);

2. Ha az tlet a prhuzamossgra nzve kedvez, akkor a kvetkez lpsben


a feldolgozand adatot a rendszer a rendelkezsre ll processzorok szma
alapjn elosztja. Ez egy meglehetsen bonyolult tma, az adatforrstl
fggen tbb stratgia is ltezik, ezt itt nem rszletezzk.
3. Vgrehajts.
4. A rszeredmnyek sszeillesztse. Erre a rszre is lehet rhatsunk,
miszerint egyszerre szeretnnk a vgeredmnyt megkapni, vagy megfelelnek
a rszeredmnyek is:
var result = from x in data.AsParallel()
.WithMergeOptions(ParallelMergeOptions.NotBuffered);

A ParallelMergeOptions hrom taggal rendelkezik: a NotBuffered hatsra azonnal


visszakapunk minden egyes elemet a lekrdezsbl, az AutoBuffered peridikusan

- 272 -

ad vissza tbb elemet, mg a FullyBuffered csak akkor kldi vissza az eredmnyt, ha


a lekrdezs teljesen ksz van.
Lthatjuk, hogy komoly szmtsi kapacitst ignyel a rendszertl egy prhuzamos
lekrdezs vgrehajtsa, pp ezrt a nem megfelel lekrdezsek parallel mdon
val indtsa nem fogja az elvrt teljestmnyt hozni, tulajdonkppen mg
teljestmnyromls is lehet az eredmnye. Tipikusan olyan helyzetekben akarunk
ilyen lekrdezseket hasznlni, ahol nagy mennyisg elemen sok vagy drga
mveletet vgznk el, mivel itt nyerhetnk a legtbbet.

35.12.4 Rendezs
Nzzk a kvetkez kdot:
List<int> list = new List<int>()
{
0, 1, 2, 3, 4, 5, 6, 7, 8, 9
};
var result1 = from x in list select x;
var result2 = from x in list.AsParallel() select x;

Vajon mit kapunk, ha kiratjuk mindkt eredmnyt? A vlasz meglep lehet:


result1: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
result2: 0, 6, 3, 4, 8, 2, 5, 1, 9, 7
Hogyan kaphattunk egy rendezett listbl rendezetlen eredmnyt? A vlasz nagyon
egyszer, hiszen tudjuk, hogy a PLINQ rszegysgekre bontja az adatforrst, vagyis
nem fogjuk sorban visszakapni az eredmnyeket (a fent mutatott eredmny nem lesz
mindenkinl ugyanaz minden futsnl ms sorrendet kaphatunk vissza).
Amennyiben szmt az eredmny rendezettsge, akkor vagy hasznlnunk kell az
orderbyt, vagy az AsParallel mellett meg kell hvnunk az AsOrdered kiterjesztett
metdust:
var result2 = from x in list.AsParallel().AsOrdered() select x;
var result3 = from x in list.AsParallel() orderby x select x;

A kt lekrdezs hasonlnak tnik, de nagy klnbsg van kzttk. Ha lemrjk a


vgrehajtsi idt, akkor ltni fogjuk, hogy az els valamivel gyorsabban vgzett, mint
a msodik, ennek pedig az az oka, hogy amg az orderby mindig vgrehajtja a
rendezst addig az AsOrdered egyszeren megrzi az eredeti sorrendet, s aszerint
osztja szt az adatokat (nagy adatmennyisggel a kett kztti sebessgklnbsg
is jelentsen megn, rdemes nhnyezer elemre is tesztelni).
Gyakran elfordul, hogy az eredeti llapot fenntartsa nem elnys szmunkra, pl.
kivlasztunk nhny elemet egy listbl s ezek alapjn akarunk egy join mveletet
vgrehajtani. Ekkor nem clszer fenntartani a sorrendet, hasznljuk az
AsUnordered metdust, amely minden rvnyes rendezst rvnytelent.

- 273 -

35.12.5 AsSequential
Az AsSequential metdus pp ellenkezje az AsParallelnek, vagyis
szablyozhatjuk, hogy egy lekrdezs mely rsze legyen szekvencilis s melyik
prhuzamos. A kvetkez pldban megnzzk, hogy ez mirt j neknk. A PLINQ
egyelre mg nem teljesen kiforrott, annak a szablyai, hogy mely opertorok
lesznek mindig szekvencilisan kezelve a jvben valsznleg vltozni fognak, ezrt
a pldaprogramot illik fenntartssal kezelni:
var result = (from x in list.AsParallel() select x).Take(10);

Kivlasztjuk az eredmnylistbl az els tz elemet. A problma az, hogy a Take


opertor szekvencilisan fut majd le fggetlenl attl, hogy mennyire bonyolult a
bels lekrdezs, pp ezrt nem kapunk semmifle teljestmnynvekedst. rjuk t
egy kicsit:
var result2 = (from x in list.AsParallel() select x)
.AsSequential().Take(10);

Most pontosan azt kapjuk majd, amire vrunk, a bels prhuzamos mveletsor
vgeztvel visszavltottunk szekvencilis mdba, vagyis a Take nem fogja vissza a
sebessget.

- 274 -

36

Visual Studio

A .NET Framework programozshoz az els szm fejleszteszkz a Microsoft


Visual Studio termkcsaldja. A nagy fizets Professional/Ultimate/stb... vltozatok
mellett az Express vltozatok ingyenes s teljes kr szolgltatst nyjtanak
szmunkra.
Ebben a fejezetben a Visual Studio 2008 verzival fogunk megismerkedni (a jegyzet
rsnak pillanatban mr ltezik a 2010 vltozat is, de ez egyrszt mg nem annyira
elterjedt, msrszt nagy klnbsg nincs kzttk aki az egyiket tudja hasznlni
annak a msikkal sem lehet problmja.
rdemes telepteni a Visual Studio 2008 szervzcsomagjt is, ez letlthet a
Microsoft oldalrl.

36.1 Az els lpsek


A VS 2008 teleptshez legalbb Windows XP SP2 szksges.
A VS els indtsakor megkrdezi, hogy melyik nyelvhez tartoz belltsokkal
mkdjn, ez a mi esetnkben a C# lesz. Ezutn alapbellts szerint a kezdlap
jelenik meg amely a korbban megnyitott projectek mellett l internetes kapcsolat
esetn a fejleszti blogok illetve hrcsatornk legfrissebb bejegyzseit is megjelenti.
Bellthatjuk, hogy mi jelenjen meg indtskor, ehhez vlasszuk ki a Tools men
Options pontjt. Ekkor megjelenik a kvetkez ablak:

- 275 -

Amennyiben a Show all settings jellngyzet res, akkor jelljk be, majd a
megfelen listbl vlasszuk ki a Startupot:

Itt bellthatjuk, hogy mit szeretnnk ltni indtskor.


Most ksztsk el az els programunkat, ehhez a File men New Project pontjt
vlasszuk ki:

- 276 -

Itt a Visual C# fl alatt a Windows elem kvetkezik, ahol kivlaszthatjuk az elre


elksztett sablonok kzl, hogy milyen tpus projectet akarunk kszteni, ez most
egy Console Application lesz, a jegyzetben eddig is ilyeneket ksztettnk.
A jobb fls sarokban bellthatjuk, hogy a Framework melyik verzijval akarunk
dolgozni. Alul pedig a project nevt s knyvtrt adhatjuk meg.
Az OK gombra kattintva elkszthetjk a projectet. A most megjelen forrskd
valsznleg ismers lesz, br nhny (eddig ismeretlen) nvteret mr elre belltott
a VS.
rjunk egy egyszer Hello World! programot, ezt az F5 gomb segtsgvel fordthatjuk
s futtathatjuk (ne felejtsnk el egy Console.ReadKey utastst tenni a vgre,
klnben nem ltunk majd semmit).
A Ctrl+Shift+B kombincival csak fordtunk, mg az F5 egyszerre fordt s futtat. A
nem mentett vltozsokat a VS mindkt esetben automatikusan menti.
jdonsgot jelenthet, hogy a Visual Studio kiegszti a forrskdot ezzel rengeteg
gpelstl megkmlve minket, ezt IntelliSensenek hvjk. Amennyiben nem
mkdik az (valsznleg) azt jelenti, hogy nincs bekapcsolva. A Tools menbl
vlasszuk ki ismt az Optionst s azon bell pedig a Text Editor elemet. Ekkor a
listbl kivlaszthatjuk a C# nyelvet s azon bell pedig az IntelliSense ment:

Ahogy a kpen is lthat a Show completion list... jellngyzetet kell megjellni.


Nzzk meg, hogy hogyan nz ki egy Visual Studio project. Nyissuk meg a
knyvtrat ahov mentettk, ekkor egy .sln s egy .suo filet illetve egy mappt kell
ltnunk.

- 277 -

Az sln file a projectet tartalamz n. Solutiont rja le, ez lesz minden project gykere
(egy Solution tartalmazhat tbb projectet is). Ez a file egy egyszer szveges file,
valami ilyesmit kell ltnunk:
Microsoft Visual Studio Solution File, Format Version 10.00
# Visual Studio 2008
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConsoleApplication1",
"ConsoleApplication1\ConsoleApplication1.csproj", "{4909A576-5BF0-48AA-AB70-4B96835C00FF}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{4909A576-5BF0-48AA-AB70-4B96835C00FF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4909A576-5BF0-48AA-AB70-4B96835C00FF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4909A576-5BF0-48AA-AB70-4B96835C00FF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4909A576-5BF0-48AA-AB70-4B96835C00FF}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

A projectek mellett a fordtsra vonatkoz informcik is megtallhatak itt. A .suo


file pedig a Solution tnyleges fordtsi adatait kapcsolit tartalmazza binris
formban (hexa editorral tbb-kevsb olvashatv vlik).
Most nyissuk meg a mappt is, megtallhatjuk benne a projectet ler .csproj filet,
amely a project fordtsnak informciit trolja. A Properties mappban tallhat
AssemblyInfo.cs file amelyben pl. a program verziszma, tulajdonosa, a gyrtja
illetve a COM interoperabilitsra vonatkoz informcik vannak. Az obj mappa a
fordts sorn keletkezett mellktermket trolja, ezek lnyegben tmeneti fileok a
vgleges program mkdshez nem szksgesek.
A bin mappban talljuk a lefordtott vgleges programunkat vagy a debug vagy a
release mappban attl fggen, hogy hogyan fordtottuk (errl ksbb bvebben).
Ha megnzzk a mappa tartalmt, lthatjuk, hogy br egyetlen exe kiterjeszts
filera szmtottunk tallunk mg hrom ismeretlen filet is. Ezek a Visual Studio
szmra hordoznak informcit, segtik pl. a debuggolst (errl is ksbb).
A Visual Studio 2008 hajlamos eldobni fileokat a projectekbl, vagyis br fizikailag
jelen vannak, de a Solution Explorerben (s gy fordtskor sem) nem szerepelnek.
Ilyenkor kt megolds van: vagy kzzel jra fel kell venni (jobb klikk a projecten, majd
Add/Existing Item), vagy a csproj filet kell szerkeszteni. Ezt a problmt
megelzhetjk rendszeres biztonsgi mentssel (nagyobb programok esetn
rdemes valamilyen verzikezel eszkzt hasznlni).

36.2 Fellet
Trjnk most vissza a VShez s nzzk meg, hogy miknt pl fel a kezelfellet.
Kezdjk a Solution Explorerrel, amelyben az aktulis Solution elemeit kezelhetjk.
Amennyiben nem ltjuk ezt az ablakot (ha az Auto Hide be van kapcsolva, akkor
csak egy flecske jelenik meg az ablak szln), akkor a View menben keressk
meg.
- 278 -

A rajzszgre kattintva az ablakot rgzthetjk is. A Solutionn vagy a projecten


jobb egrgombbal kattintva j forrsfileokat, projecteket, stb. adhatunk a
programunkhoz.

A Properties ablakban a kivlasztott elem tulajdonsgait llthatjuk. Tbbfle


terleten is felhasznljuk majd, tbbek kztt a grafikus fellet alkalmazsok
alkotelemeinek belltsakor.

- 279 -

A kpen a Program.cs file fordtsi belltsait lthatjuk.


A Server Explorer segtsgvel (tvoli) adatbzisokhoz kapcsoldhatunk, azokat
szerkeszthetjk, illetve lekrdezseket hajthatunk vgre.

Vgl a ToolBox ablak maradt, neki fknt grafikus fellet alkalmazsok


fejlesztsekor
vesszk
hasznt,
ekkor
innen
tudjuk
kivlasztani
a
komponenseket/vezrlket.

36.3 Debug
A Visual Studio segtsgvel a programok javtsa, illetve a hibk oknak feldertse
sem okoz nagy nehzsget. Ebben a rszben elsajttjuk a hibakeress alapjait:
elsknt ismerkedjnk meg a breakpoint (vagy trspont) fogalmval. Nylvn gy
tudjuk a legjobban felderteni a programjaink hibit, ha mkds kzben lthatjuk az
objektumok llapott. Erre olyan fapados mdszereket is hasznlhatunk, mint, hogy
kirjuk a konzolra ezeket az rtkeket. Ennek persze megvan a maga htrnya,
hiszen egyrszt be kell gpelni a parancsokat, msrszt beszennyezzk a
forrskdot ami gy nehezebben olvashatv vlik. A breakpointok segtsgvel a
- 280 -

program egy adott pontjn lehetsgnk van meglltani annak futst, gy


knyelmesen megvizsglhatjuk az objektumokat.
A Visual Studioban a forrskd ablak bal oldaln lv res sv szolgl trspontok
elhelyezsre:

Ezutn, ha futtatjuk a programot, akkor ezen a ponton a futsa megll. Fontos, hogy
a trst tartalmaz sor mr nem fog lefutni, vagyis a fenti kpen s rtke mg baba
marad.

A kpen lthat a Locals ablak, amely a vltozk llapott/rtkt mutatja. Ugyangy


elrhetjk ezeket az rtkeket, ha az egrmutatt kzvetlenl a vltoz fl visszk:

- 281 -

Mind itt, mind a Locals ablakban mdosthatjuk az objektumokat.


Az F11 gombbal utastsonknt lpkedhetnk debug mdban.

36.4 Debug s Release


A Visual Studio ktfle mdban Debug s Release tud fordtani. Az elbbit
tipikusan a fejleszts sorn hasznljuk, mivel ahogyan a nevben is benne van a
debuggolst segti, mivel a lefordtott programot kiegszti a szksges
informcikkal. Ebbl kifolylag ez a md a lassabb, akr 100% -os
sebessgvesztesget is kaphatunk, ha gy prblunk szmtsignyes mveleteket
vgezni (pp ezrt a Debug mdot csakis a program ltalnos funkciinak
fejlesztsekor hasznljuk). Amikor a sebessget is tesztelni akarjuk, akkor a Release
mdot kell bevetnnk.
Alaprtelmezs szerint a Visual Studio Toolbar jn ki kell tudnunk vlasztani, hogy
mit szeretnk:

Ha mgsincs ott, akkor a ToolBar on jobb gombbal kattintva jelljk be a Debug


elemet:

- 282 -

37

Osztlyknyvtr

Eddig mindig egyetlen futtathat llomnyt ksztettnk, ennek viszont megvan az a


htrnya, hogy a nagyobb osztlyokat minden egyes alkalommal, amikor hasznlni
akarjuk jra be kell msolni a forrskdba. Sokkal egyszerbb lenne a dolgunk, ha az
jrahasznostand forrskdot kln tudnnk vlasztani a tnyleges programtl,
ezzel idt s helyet takartva meg. Erre a clra talltk ki a shared library
(~megosztott knyvtr) fogalmt, amely a Windows alap rendszereken a DLL -t
(Dynamic Link Library) jelenti.
A DLL knyvtrak felptse a hasznlt fejleszti platformtl fgg, teht egy COM
DLL nem hasznlhat kzvetlenl egy .NET programban (de megoldhat).
Ksztsk el az els osztlyknyvtrunkat elszr parancssort hasznlva, utna a
Visual Studio segtsgvel. Az osztlyunk (TestLib.cs a file neve) nagyon egyszer
lesz:
using System;
namespace TestNamespace
{
public class TestClass
{
public string Text = "Hello DLL!";
}
}

Ezttal nvteret is megadtunk, ez clszer, mivel nem akarjuk, hogy ms osztllyal


tkzzn, vagyis egyrtelmen meg tudjuk mondani, hogy mire gondoltunk. Amire
mg figyelni kell, az az osztly elrhetsge, hiszen most kt klnbz assembly
rl van sz, vagyis ha kvlrl is el akarjuk rni ezt az osztlyt, akkor publikusknt kell
deklarlnunk. Fordtani a kvetkezkppen tudjuk:
csc /target:library TestLib.cs
Ezutn egy TestLib.dll nev filet kapunk.
A fprogramot pedig gy ksztjk el:
using System;
using TestNamespace;
class Program
{
static public void Main()
{
TestClass tc = new TestClass();
Console.WriteLine(tc.Text);
}
}

s gy fordtjuk:
csc /reference:TestLib.dll main.cs

- 283 -

Most ksztsk el ugyanezt Visual Studioval. Csinljunk elsknt egy Console


Applicationt mint az elz fejezetben, majd kattintsunk jobb egrgombbal a
Solutionn (a Solution Explorerben) s az Add menbl vlasszuk ki a New
Projectet:

Majd a Class Library sablont

- 284 -

Az OK gombra kattintva a Solutionhz hozzaddik az osztlyknyvtr.


Elfordulhat, hogy ezutn nem tudjuk futtatni a programot, mivel egy Project with an
Output Type... kezdet hibazenetet kapunk. Ez azt jelenti, hogy a frissen
hozzadott osztlyknyvtr lett a Solution fprojectje. Ezt megvltoztathatjuk, ha a
valban futtatni kvnt projecten jobb egrgombbal kattintva kivlasztjuk a Set as
StartUp Project pontot:

Ksztsk el az osztlyknyvtrat s fordtsuk le (jobb egrgomb a projecten s


Build). Most trjnk vissza a fprojecthez s nyissuk le a References flet, ez a mr
hozzadott osztlyknyvtrakat tartalmazza:

- 285 -

Kattintsunk jobb egrgombbal rajta s vlasszuk ki az Add Reference pontot:

A megjelen ablakban kivlaszthatjuk, hogy a mr beptett osztlyknyvtrakbl


(.NET) akarunk vlogatni vagy megkeressk a neknk kell filet (Browse), esetleg az
egyik aktulis projectre van szksgnk (Projects). Neknk termszetesen ez utbbi
kell:

Az Ok gombra kattintva a References alatt is megjelenik az j knyvtr. Ezutn a


szerkesztablakban is lthatjuk az osztlyknyvtrunk nvtert (ha mgsem akkor
fordtsuk le az egsz Solutiont):

- 286 -

You might also like