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 bemut atkoznom: Reiter Istvn vagyok, 24 ves programoz. B tz ve foglalkozom informatikv al, 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 2 Jogi felttelek ...........................................................................................................9 Microsoft .NET Framework .......................................................................................... 10 2.1 A .NET platform .................................................................................................... 10 2.1.1 2.1.2 2.1.3 2.2 2.3 MSIL/CIL ....................................................................................................... 10 Fordts s futtats........................................................................................... 11 BCL ................................................................................................................ 11

A C# programozsi nyelv ....................................................................................... 11 Alternatv megoldsok ............................................................................................ 12 SSCLI ............................................................................................................. 12 Mono............................................................................................................... 12 DotGNU.......................................................................................................... 13

2.3.1 2.3.2 2.3.3 3

Hello C#! .................................................................................................................. 14 3.1 A C# szintaktikja .................................................................................................. 15 3.1.1 3.1.2 3.2 Kulcsszavak .................................................................................................... 15 Megjegyzsek ................................................................................................. 16

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

Vltozk ....................................................................................................................... 18 4.1 Deklarci s definci ........................................................................................... 18 4.2 4.3 4.4 4.5 4.6 4.7 4.8 4.9 4.10 Tpusok .................................................................................................................. 18 Loklis s globlis vltozk .................................................................................... 20 Referencia- s rtktpusok ..................................................................................... 20 Referencik ............................................................................................................ 22 Boxing s unboxing ................................................................................................ 23 Konstansok............................................................................................................. 25 A felsorolt tpus ...................................................................................................... 25 Null tpusok............................................................................................................ 27 A dinamikus tpus ............................................................................................... 28

Opertorok.................................................................................................................... 30 5.1 Opertor precedencia .............................................................................................. 30 5.2 5.3 5.4 5.5 5.6 5.7 rtkad opertor ................................................................................................... 31 Matematikai opertorok .......................................................................................... 32 Relcis opertorok ................................................................................................ 32 Logikai s feltteles opertorok .............................................................................. 33 Bit opertorok ........................................................................................................ 36 Rvid forma ........................................................................................................... 39 -3-

5.8 6

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

Vezrlsi szerkezetek .................................................................................................... 42 6.1 Szekvencia ............................................................................................................. 42 6.2 6.3 Elgazs ................................................................................................................. 42 Ciklus ..................................................................................................................... 45 Yield ............................................................................................................... 50 Prhuzamos ciklusok ....................................................................................... 50

6.3.1 6.3.2 7

Gyakorl feladatok ....................................................................................................... 52 7.1 Szorztbla ............................................................................................................. 52 7.2 7.3 7.4 Szmolgp ............................................................................................................ 55 K Papr Oll ................................................................................................... 57 Szmkitall jtk .................................................................................................. 59

Tpuskonverzik ........................................................................................................... 64 8.1 Ellenrztt konverzik ........................................................................................... 64 8.2 8.3 Is s as .................................................................................................................... 65 Karakterkonverzik ................................................................................................ 66

9 10

Tmbk ........................................................................................................................ 67 9.1 Tbbdimenzis tmbk .......................................................................................... 68 Stringek ........................................................................................................................ 71 10.1 Metdusok .......................................................................................................... 72 10.2 10.3 StringBuilder ...................................................................................................... 73 Regulris kifejezsek .......................................................................................... 74

11

Gyakorl feladatok II. ................................................................................................... 77 11.1 Minimum- s maximumkeress........................................................................... 77 11.2 11.3 11.4 Szigetek .............................................................................................................. 77 tlaghmrsklet ................................................................................................ 79 Buborkrendezs................................................................................................. 79

12

Objektum-orientlt programozs - elmlet .................................................................... 81 12.1 UML ................................................................................................................... 81 12.2 12.3 12.4 12.5 12.6 Osztly ............................................................................................................... 81 Adattag s metdus ............................................................................................. 82 Lthatsg .......................................................................................................... 82 Egysgbezrs .................................................................................................... 83 rklds ........................................................................................................... 83

13

Osztlyok...................................................................................................................... 85 13.1 Konstruktorok ..................................................................................................... 86 13.2 13.3 13.4 Adattagok ........................................................................................................... 89 Lthatsgi mdostk ........................................................................................ 90 Parcilis osztlyok .............................................................................................. 90 -4-

13.5 13.6 13.7 14

Begyazott osztlyok .......................................................................................... 92 Objektum inicializlk ........................................................................................ 93 Destruktorok ....................................................................................................... 93

13.7.1 IDisposable ................................................................................................... 100 Metdusok .................................................................................................................. 102 14.1 Paramterek ...................................................................................................... 104 14.1.1 Alaprtelmezett paramterek ......................................................................... 109 14.1.2 Nevestett paramterek .................................................................................. 110 14.2 14.3 15 16 17 Visszatrsi rtk .............................................................................................. 110 Kiterjesztett metdusok..................................................................................... 111

Tulajdonsgok ............................................................................................................ 113 Indexelk .................................................................................................................... 115 Statikus tagok ............................................................................................................. 117 17.1 Statikus adattag ................................................................................................. 117 17.2 17.3 17.4 17.5 Statikus konstruktor .......................................................................................... 118 Statikus metdus ............................................................................................... 120 Statikus tulajdonsg .......................................................................................... 120 Statikus osztly ................................................................................................. 120

18

Struktrk ................................................................................................................... 122 18.1 Konstruktor....................................................................................................... 122 18.2 18.3 18.4 18.5 Destruktor ......................................................................................................... 123 Adattagok ......................................................................................................... 124 Hozzrendels ................................................................................................... 124 rklds ......................................................................................................... 126

19

Gyakorl feladatok III................................................................................................. 127 19.1 Faktorilis s hatvny ....................................................................................... 127 19.2 19.3 19.4 Gyorsrendezs .................................................................................................. 128 Lncolt lista ...................................................................................................... 130 Binris keresfa ................................................................................................ 131

20

rklds ................................................................................................................... 136 20.1 Virtulis metdusok .......................................................................................... 138 20.2 20.3 20.4 Polimorfizmus .................................................................................................. 140 Lezrt osztlyok s metdusok .......................................................................... 141 Absztrakt osztlyok ........................................................................................... 141

21

Interfszek .................................................................................................................. 144 21.1 Explicit interfszimplementci ........................................................................ 146 21.2 Virtulis tagok .................................................................................................. 147 Opertor kiterjeszts ................................................................................................... 149 22.1 Egyenlsg opertorok ...................................................................................... 150 -5-

22

22.2 22.3 22.4 22.5 23

A ++/-- opertorok ............................................................................................ 151 Relcis opertorok .......................................................................................... 152 Konverzis opertorok ...................................................................................... 152 Kompatibilits ms nyelvekkel ......................................................................... 153

Kivtelkezels............................................................................................................. 154 23.1 Kivtel hierarchia .............................................................................................. 156 23.2 23.3 23.4 Kivtel ksztse ............................................................................................... 156 Kivtelek tovbbadsa ...................................................................................... 157 Finally blokk ..................................................................................................... 158

24

Gyakorl feladatok IV. ............................................................................................... 159 24.1 IEnumerator s IEnumerable ............................................................................. 159 24.2 24.3 IComparable s IComparer ............................................................................... 161 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 26.3 26.4 26.5 26.6 Generikus osztlyok .......................................................................................... 174 Generikus megszortsok .................................................................................. 176 rklds ......................................................................................................... 178 Statikus tagok ................................................................................................... 178 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 26.8 27 Generikus interfszek, delegate ek s esemnyek ............................................ 183 Kovariancia s kontravariancia ......................................................................... 184

Lambda kifejezsek .................................................................................................... 186 27.1 Generikus kifejezsek ....................................................................................... 186 27.2 27.3 27.4 Kifejezsfk ...................................................................................................... 188 Lambda kifejezsek vltozinak hatkre ......................................................... 188 Nvtelen metdusok kivltsa lambda kifejezsekkel ........................................ 189

28 29

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

29.2 30

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

Tbbszl alkalmazsok ............................................................................................. 199 30.1 Application Domain -ek .................................................................................... 201 30.2 30.3 30.4 30.5 30.6 30.7 Szlak ............................................................................................................... 201 Aszinkron delegate-ek....................................................................................... 202 Szlak ltrehozsa ............................................................................................. 207 Foreground s background szlak ..................................................................... 208 Szinkronizci .................................................................................................. 209 ThreadPool ....................................................................................................... 213

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

31 32

Reflection ................................................................................................................... 216 llomnykezels ........................................................................................................ 218 32.1 Olvass/rs filebl/fileba .................................................................................. 218 32.2 32.3 32.4 32.5 32.6 Knyvtrstruktra kezelse ............................................................................... 221 Inmemory streamek ........................................................................................ 223 XML ................................................................................................................. 224 XML DOM ....................................................................................................... 227 XML szerializci............................................................................................. 229

33 34

Konfigurcis file hasznlata ...................................................................................... 231 33.1 Konfigurci-szekci ksztse ......................................................................... 232 Hlzati programozs ................................................................................................. 235 34.1 Socket ............................................................................................................... 235 34.2 34.3 Blokk elkerlse ............................................................................................... 241 Tbb kliens kezelse ......................................................................................... 243

34.3.1 Select ............................................................................................................ 243 34.3.2 Aszinkron socketek ....................................................................................... 245 34.3.3 Szlakkal megvalstott szerver ..................................................................... 246 34.4 35 TCP s UDP ..................................................................................................... 249 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 35.4 35.5 Szrs ............................................................................................................... 255 Rendezs .......................................................................................................... 257 Csoportosts .................................................................................................... 258

35.5.1 Null rtkek kezelse ..................................................................................... 260 35.5.2 sszetett kulcsok ........................................................................................... 260 35.6 Listk sszekapcsolsa ...................................................................................... 262 -7-

35.7 35.8 35.9 35.10 35.11 35.12

Outer join.......................................................................................................... 263 Konverzis opertorok ...................................................................................... 264 Element opertorok ....................................................................................... 266 Halmaz opertorok............................................................................................ 267 Aggregt opertorok ......................................................................................... 268 PLINQ Prhuzamos vgrehajts ..................................................................... 269 Tbbszlsg vs. Prhuzamossg ............................................................... 269 Teljestmny .............................................................................................. 269 PLINQ a gyakorlatban ............................................................................... 270 Rendezs ................................................................................................... 273 AsSequential.............................................................................................. 274

35.12.1 35.12.2 35.12.3 35.12.4 35.12.5 36

Visual Studio .............................................................................................................. 275 36.1 Az els lpsek ................................................................................................. 275 36.2 36.3 36.4 Fellet............................................................................................................... 278 Debug ............................................................................................................... 280 Debug s Release .............................................................................................. 282

37

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 l etlthetek 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 u gyanaz, 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 i lyen).

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 el segtsre. 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, met dusok, 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# nyelve n: 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)

Lers

byte char bool sbyte short ushort int uint float double decimal long ulong string object

System.Byte System.Char System.Boolean System.SByte System.Int16 System.Uint16 System.Int32 System.Uint32 System.Single System.Double System.Decimal System.Int64 System.Uint64 System.String System.Object

1 2 1 1 2 2 4 4 4 8 16 8 8 N/A N/A

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 blokk jra 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 deklarl unk: 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 haszn ltuk 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 meg felel 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. Plda:
using System; public class Program { static public void Main() { Console.WriteLine(10 & 2); //1010 & 0010 = 0010 = 2 } }

hogy

az

igazsgtblt

hasznltuk

az

eredmny

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 vissza adni, 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 kett vel 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 esetb en:
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 opera ndust, 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
a program utastsainak sorrendisgt szablyoz

Vezrlsi szerkezetnek konstrukcikat nevezzk.

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 vgrehajta ni, 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 meg mondanm, 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. pp en ezrt egy foreach ciklus nem mdosthatja egy gyjtemny elemeit (le sem fordul na 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 paramter knt 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 jutnun k 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 el kszteni 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, h ogy 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 karakter eket 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 cserl geti 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 mv eletekre helyeztk a hangslyt, mert akkoriban mg fleg matematikai szmtsokat vgez tek 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 tallkoz hatunk (szintn a kvetkez rszekben tallhat meg nhny elmleti fogalom is, amelyek gyakorlati pldkon keresztl rthetbben megfogalmazhat k, ezrt ezek csak ksbb lesznek trgyalva,pl. polimorfizmus).

12.1 UML
Az OOP tervezs elsegtsre hoztk lt re 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 tudn iuk, 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 rsze iknt 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 hasznl hatjk 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 meghvj a 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 kvetke z rsz foglalkozik bvebben). Egy osztlynak paramterlisttl fgg en 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 tagok at 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 k ln 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 osztly pldny 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 heap pel 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), az utn 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: e lsknt 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 l etciklusban 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 definiltu nk, 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), kivtel t 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, h ogy 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 metdu s 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 mk dik, 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 referencia tpusknt 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:
structTest { int _x = 10; // ez nem j int _y; public Test(int x, int y) { _y = y;// ez sem j, x nem kap rtket } }

rtkadsrl

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 C

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, vag yis 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 oldal i 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 mint t, 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 hamar osan). 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 t kletesen 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 ko nvertlst 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 kelet kezhet: 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 s 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 dimenz ik 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 metdus sal, 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 delegate et 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 l egtbb 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 kul csnak 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 kifejez s (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 paramte rt 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 mem riacm 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 szl hoz. 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 Storage ben (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 delegate val. 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 p aramter 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 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).

Truncate

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 A fenti pldt gy is rhattuk volna:
FileStream fs = new FileStream("test.txt", FileMode.Open, FileAccess.Read);

Olvassra nyitja meg. rsra nyitja meg. Olvassra s rsra nyitja meg

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 a ktulis 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:
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(); } } }

krtnk le,

most

megtanuljuk

mdostani

is

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 t pusokat 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 reader t, 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 el s 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 teljest s 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 vi ssza 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 pontszmo s 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 amel yek 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 tartal mt. 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 ha sznl, 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 k ell hoznunk az zenetet. A fenti pldban a legegysz erbb, 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 nega tv 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 meg adott 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 kapcso latot 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 TCP t 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 deklar ljunk 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 tulajdonsg

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 -

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 param tert 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 vev termk 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 harmadik nl 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 helyzetb en 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, meg felelen 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 vezetknv keresztnv-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 ele men 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, ho gy 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 verzijv al 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 menb l 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 szksg esek. 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 kezelfell et. 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 libr ary (~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, his zen 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 Ad d 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