You are on page 1of 288

A knyv nyomtatott verzija megvsrolhat a knyvesboltokban,

s a kiad webruhzban: www.joskiado.hu

Reiter Istvn

C# programozs
lpsrl lpsre

JEDLIK OKTATSI STDI


Budapest, 2012

Minden jog fenntartva.


A szerz s a kiad a knyv rsa sorn trekedtek arra, hogy a lert tartalom a lehet legpontosabb s
napraksz legyen. Ennek ellenre elfordulhatnak hibk, vagy bizonyos informcik elavultt vlhattak.
A pldkat s a mdszereket mindenki csak sajt felelssgre alkalmazhatja. Javasoljuk, hogy felhasznls
eltt prblja ki s dntse el sajt maga, hogy megfelel-e a cljainak. A knyvben foglalt informcik
felhasznlsbl fakad esetleges krokrt sem a szerz, sem a kiad nem vonhat felelssgre.
Az oldalakon elfordul mrka- valamint kereskedelmi vdjegyek bejegyzjk tulajdonban llnak.

Reiter Istvn, 2012

Bort: Varga Tams


Anyanyelvi lektor: Dr. Bonhardtn Hoffmann Ildik

Kiad: Jedlik Oktatsi Stdi Kft.


1215 Budapest, v u. 8-12.
Internet: http://www.jos.hu
E-mail: jos@jos.hu
Felels kiad: a Jedlik Oktatsi Stdi Kft. gyvezetje

Nyomta: LAGrade Kft.


Felels vezet: Szutter Lnrd

ISBN: 978-615-5012-17-4
Raktri szm: JO-0340

ELSZ
Amikor 2008. augusztusban elkszltem ennek a knyvnek az els vltozatval mg problmt okozott tlpni
a bvs 100 oldalas hatrt. Ha jl emlkszem, vgl 108 oldal lett, szerencsre mr csak az Internet egy stt
sarkban lehet megtallni.
rdekes mdon ezttal is az oldalszm okozott gondot, igaz, fordult a kocka: valamivel tbb mint 300 oldalt
kellett kitltenem, nmi kompromisszum rn sikerrel is jrtam.
Ha jobban belegondolok, az elmlt 4 vem alapjaiban hatrozta meg ez a knyv. Rengeteget ksznhetek neki,
de szintn szlva nha gylltem is. Valjban most is ezt teszem, hiszen jjel 11 van s lmos vagyok.
4 v alatt elkpeszt vltozson esett t az informatika vilga. Ebben a pillanatban is jabb csodk szletnek.
Borzalmasan nehz lpst tartani a technolgia vgtelen evolcijval, szinte lehetetlennek ltsz feladat.
Ennek a knyvnek a clja, hogy olyan alapokat nyjtson, amelyre nyugodtan tmaszkodhatunk a kalandos ton,
amelyet szoftverfejlesztsnek hvnak.
Tbb mint 40000 olvas tbbszz jtancsnak, visszajelzsnek eredmnye az amit most a kezedben tartasz!
Nem fogok hazudni, ez nem egy mindentud lexikon s nem is csodalmpa, amit megdrzslve azonnal fejedbe
szll a tuds - br prblkozni lehet. Tekintsd trsnak, jbartnak, aki utat mutat, ha elakadtl. Remlem, pp
annyi rmet okoz majd Neked, mint nekem tette!
Ktelessgem ksznetet mondani mindazoknak, akik hozzjrultak a knyv ltrejtthez! Sajnos annyi helyem
nincs, hogy mindenki elfrjen, mgis szeretnm kiemelni nhnyukat:
Kovcs Gyula s Balzs Gbor tanr uraknak, a gymri Teleki Lszl Gimnzium s Informatikai Szakkzp
iskolbl.
Lipp Szabolcsnak s a Microsoft Magyarorszgnak, ksznm a lehetsget, remlem hamarosan elrjk a
hatszmjegy letltsszmot is!
Ecsegi Szandrnak, hogy vgig mellettem llt, rmutatott olyan dolgokra, amik nekem eszembe sem jutottak.
Ksznm.
A knyv s a hozztartoz programozsi pldk a weben is elrhetek a Devportal.hu oldalon.

Talms, 2012. augusztus

Reiter Istvn

TARTALOMJEGYZK
MICROSOFT .NET FRAMEWORK ___________________________________________________________ 15
A .NET platform ___________________________________________________________________________ 15
MSIL/CIL _________________________________________________________________________________ 15
Fordts s futtats ________________________________________________________________________ 16
BCL _____________________________________________________________________________________ 16
A C# programozsi nyelv ____________________________________________________________________ 16
Alternatv megoldsok _____________________________________________________________________ 16

ISMERKEDNK A NYELVVEL _______________________________________________________________ 18


Visual Studio _____________________________________________________________________________ 18
Hello World! ______________________________________________________________________________ 18
A C# szintaktikja __________________________________________________________________________ 21
Kulcsszavak ______________________________________________________________________________ 22
Megjegyzsek_____________________________________________________________________________ 22
Nvterek _________________________________________________________________________________ 23

VLTOZK ____________________________________________________________________________________ 24
Deklarci s definci______________________________________________________________________ 24
Tpusok __________________________________________________________________________________ 24
Loklis s globlis vltozk __________________________________________________________________ 25
Referencia- s rtktpusok _________________________________________________________________ 25
Referencik ______________________________________________________________________________ 27
Boxing s unboxing ________________________________________________________________________ 28
Konstansok _______________________________________________________________________________ 30
A felsorolt tpus ___________________________________________________________________________ 30
Null tpusok ______________________________________________________________________________ 32
A dinamikus tpus _________________________________________________________________________ 33

OPERTOROK ________________________________________________________________________________ 35
Opertor precedencia ______________________________________________________________________ 35
rtkad opertor _________________________________________________________________________ 36
Matematikai opertorok ____________________________________________________________________ 36
Relcis opertorok________________________________________________________________________ 37
Logikai s feltteles opertorok ______________________________________________________________ 37
Bit opertorok ____________________________________________________________________________ 40
Rvid forma ______________________________________________________________________________ 43
Egyb opertorok _________________________________________________________________________ 43

VEZRLSI SZERKEZETEK__________________________________________________________________ 46
Szekvencia _______________________________________________________________________________ 46
Elgazs _________________________________________________________________________________ 46
Ciklus ___________________________________________________________________________________ 51

GYAKORL FELADATOK____________________________________________________________________ 57
Szorztbla _______________________________________________________________________________ 57
Szmolgp ______________________________________________________________________________ 60
K Papr Oll___________________________________________________________________________ 62
Szmkitall jtk _________________________________________________________________________ 64

TPUSKONVERZIK _________________________________________________________________________ 68
Ellenrztt konverzik______________________________________________________________________ 68
Is s as __________________________________________________________________________________ 69
Karakterkonverzik ________________________________________________________________________ 70

TMBK ______________________________________________________________________________________ 71
Tbbdimenzis tmbk _____________________________________________________________________ 72

STRINGEK _____________________________________________________________________________________ 75
Metdusok _______________________________________________________________________________ 76
StringBuilder _____________________________________________________________________________ 77

Regulris kifejezsek _______________________________________________________________________ 78

GYAKORL FELADATOK II. ________________________________________________________________ 81


Minimum- s maximumkeress ______________________________________________________________ 81
Szigetek _________________________________________________________________________________ 81
tlaghmrsklet _________________________________________________________________________ 82
Buborkrendezs __________________________________________________________________________ 83

OBJEKTUM-ORIENTLT PROGRAMOZS - ELMLET __________________________________ 85


UML ____________________________________________________________________________________ 85
Osztly __________________________________________________________________________________ 85
Adattag s metdus________________________________________________________________________ 85
Lthatsg _______________________________________________________________________________ 86
Egysgbezrs ____________________________________________________________________________ 86
rklds ________________________________________________________________________________ 86

OSZTLYOK __________________________________________________________________________________ 88
Konstruktorok ____________________________________________________________________________ 89
Adattagok ________________________________________________________________________________ 91
Lthatsgi mdostk _____________________________________________________________________ 92
Parcilis osztlyok _________________________________________________________________________ 92
Begyazott osztlyok _______________________________________________________________________ 94
Objektuminicializlk ______________________________________________________________________ 95
Destruktorok _____________________________________________________________________________ 95

METDUSOK ________________________________________________________________________________ 103


Paramterek ____________________________________________________________________________ 105
Visszatrsi rtk _________________________________________________________________________ 110
Kiterjesztett metdusok ___________________________________________________________________ 111

TULAJDONSGOK___________________________________________________________________________ 113
INDEXELK __________________________________________________________________________________ 115
9

STATIKUS TAGOK __________________________________________________________________________ 117


Statikus adattag __________________________________________________________________________ 117
Statikus konstruktor ______________________________________________________________________ 118
Statikus metdus _________________________________________________________________________ 119
Statikus tulajdonsg ______________________________________________________________________ 120
Statikus osztly __________________________________________________________________________ 120

STRUKTRK _______________________________________________________________________________ 121


Konstruktor _____________________________________________________________________________ 121
Destruktor ______________________________________________________________________________ 122
Adattagok _______________________________________________________________________________ 123
Hozzrendels ___________________________________________________________________________ 123
rklds _______________________________________________________________________________ 125

OSZTLYKNYVTRAK ___________________________________________________________________ 126


GYAKORL FELADATOK III. ______________________________________________________________ 130
Faktorilis s hatvny _____________________________________________________________________ 130
Gyorsrendezs ___________________________________________________________________________ 131
Lncolt lista _____________________________________________________________________________ 133
Binris keresfa __________________________________________________________________________ 134

RKLDS _________________________________________________________________________________ 138


Virtulis metdusok ______________________________________________________________________ 139
Polimorfizmus ___________________________________________________________________________ 141
Lezrt osztlyok s metdusok ______________________________________________________________ 142
Absztrakt osztlyok _______________________________________________________________________ 142

INTERFSZEK _______________________________________________________________________________ 145


Explicit interfszimplementci _____________________________________________________________ 147
Virtulis tagok ___________________________________________________________________________ 148

10

OPERTOR KITERJESZTS ________________________________________________________________ 150


Egyenlsg opertorok ____________________________________________________________________ 151
A ++/-- opertorok ________________________________________________________________________ 152
Relcis opertorok_______________________________________________________________________ 153
Konverzis opertorok ____________________________________________________________________ 153

KIVTELKEZELS ___________________________________________________________________________ 154


Kivtel hierarchia _________________________________________________________________________ 156
Kivtel ksztse _________________________________________________________________________ 156
Kivtelek tovbbadsa ____________________________________________________________________ 157
Finally blokk _____________________________________________________________________________ 157

GYAKORL FELADATOK IV. _______________________________________________________________ 159


IEnumerator s IEnumerable _______________________________________________________________ 159
IComparable s IComparer _________________________________________________________________ 160
Mtrix tpus _____________________________________________________________________________ 162

DELEGATE ___________________________________________________________________________________ 164


Paramter s visszatrsi rtk _____________________________________________________________ 167
Nvtelen metdusok ______________________________________________________________________ 168

GENERIKUSOK ______________________________________________________________________________ 174


Generikus metdusok _____________________________________________________________________ 174
Generikus osztlyok_______________________________________________________________________ 175
Generikus megszortsok __________________________________________________________________ 177
rklds _______________________________________________________________________________ 178
Statikus tagok ___________________________________________________________________________ 179
Generikus gyjtemnyek___________________________________________________________________ 179
Generikus interfszek, delegateek s esemnyek ______________________________________________ 184
Kovariancia s kontravariancia ______________________________________________________________ 184

11

LAMBDA KIFEJEZSEK _____________________________________________________________________ 187


Generikus kifejezsek _____________________________________________________________________ 187
Kifejezsfk _____________________________________________________________________________ 189
Lambda kifejezsek vltozinak hatkre _____________________________________________________ 189
Nvtelen metdusok kivltsa lambda kifejezsekkel ___________________________________________ 190

UNSAFE KD ________________________________________________________________________________ 192


Fix objektumok __________________________________________________________________________ 194
Natv DLL kezels _________________________________________________________________________ 195

TBBSZL ALKALMAZSOK ____________________________________________________________ 196


Application Domain -ek ____________________________________________________________________ 198
Szlak __________________________________________________________________________________ 198
Aszinkron delegate-ek _____________________________________________________________________ 199
Szlak ltrehozsa ________________________________________________________________________ 202
Foreground s background szlak____________________________________________________________ 204
Szinkronizci ___________________________________________________________________________ 205
ThreadPool ______________________________________________________________________________ 208

PRHUZAMOS PROGRAMOZS TASK PARALLEL LIBRARY ________________________ 210


Tbbszlsg vs. Prhuzamossg ____________________________________________________________ 210
Teljestmny _____________________________________________________________________________ 210
Prhuzamos ciklusok ______________________________________________________________________ 211
Parallel.Invoke ___________________________________________________________________________ 215
Task____________________________________________________________________________________ 217
Async/Await _____________________________________________________________________________ 219

REFLECTION _________________________________________________________________________________ 220


LLOMNYKEZELS _______________________________________________________________________ 221
Olvass/rs fjlbl/fjlba __________________________________________________________________ 221
Knyvtrstruktra kezelse ________________________________________________________________ 224

12

Inmemory streamek _____________________________________________________________________ 226


XML____________________________________________________________________________________ 226
XML DOM _______________________________________________________________________________ 229
XML szerializci _________________________________________________________________________ 231

KONFIGURCIS FJL HASZNLATA ____________________________________________________ 233


Konfigurci-szekci ksztse ______________________________________________________________ 234

HLZATI PROGRAMOZS _______________________________________________________________ 237


Socket __________________________________________________________________________________ 237
Blokk elkerlse __________________________________________________________________________ 244
Tbb kliens kezelse ______________________________________________________________________ 246
TCP s UDP ______________________________________________________________________________ 251

LINQ TO OBJECTS ___________________________________________________________________________ 252


Nyelvi eszkzk __________________________________________________________________________ 252
Kivlaszts ______________________________________________________________________________ 253
Szrs __________________________________________________________________________________ 257
Rendezs _______________________________________________________________________________ 259
Csoportosts ____________________________________________________________________________ 260
Listk sszekapcsolsa_____________________________________________________________________ 263
Outer join _______________________________________________________________________________ 265
Konverzis opertorok ____________________________________________________________________ 265
Element opertorok _____________________________________________________________________ 267
Halmaz opertorok _______________________________________________________________________ 268
Aggregt opertorok ______________________________________________________________________ 269
PLINQ Prhuzamos vgrehajts ____________________________________________________________ 270

GRAFIKUS FELLET ALKALMAZSOK WINDOWS FORMS ________________________ 274


Hello Windows Forms ___________________________________________________________________ 274
Vezrlk ________________________________________________________________________________ 278

13

GYAKORL FELADATOK V.________________________________________________________________ 286


Szmolgp _____________________________________________________________________________ 286
Szvegszerkeszt _________________________________________________________________________ 287

14

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.
Vlaszkpp a Microsoft a kilencvenes vek vgn elindtotta a Next Generation Windows Services fednev
projektet, amelybl aztn megszletett a .NET, ami a kiss elavult s nehzkesen programozhat COM
platformot hivatott levltani (ettl fggetlenl a COM ma is ltez, viszonylag npszer eszkz ez fleg a
hatalmas szoftverbzisnak ksznhet, minden Windows rendszernek rszt kpezi, s szmos .NET knyvtr is
pt r).
Az vek folyamn a .NET Framework szinte teljesen tvette az uralmat a Microsoft fejleszti platformpalettjn. Ott van a hagyomnyos asztali alkalmazsokban (Windows Forms, WPF), a weben (ASP.NET,
ASP.NET MVC, Silverlight) s az okostelefonokon is (Windows Phone).
Nem hagyhatjuk sz nlkl azt sem, hogy az egyes technolgik kztt meglehetsen egyszer az tjrs, elg
csak a WPF-Silverlight-Windows Phone triumvirtusra gondolni.

A .NET PLATFORM
Maga a .NET platform a Microsoft, a Hewlett Packard, az Intel s msok kzremkdsvel megfogalmazott CLI
(Common Language Infrastructure) egy implementcija. A CLI egy szablyrendszer, amely maga is tbb rszre
oszlik:

A CTS (Common Type System) az adatok kezelst, a memriban val megjelenst, az egymssal val
interakcit stb. rja le.
A CLS (Common Language Specification) a CLI kompatibilis nyelvekkel kapcsolatos elvrsokat
tartalmazza.
A VES (Virtual Execution System) a futsi krnyezetet specifiklja, nevezik CLR -nek (Common
Language Runtime) is.

ltalnos tvhit, hogy a VES/CLR t virtulis gpknt azonostjk. Ez abbl a szintn tves elkpzelsbl alakult
ki, hogy a .NET ugyanaz, mint a Java, csak Microsoft kntsben. A valsgban nincs .NET virtulis gp, helyette
n. felgyelt (vagy managed) kdot hasznl, vagyis a program teljes mrtkben natv mdon, kzvetlenl a
processzoron fut, mellette pedig ott a keretrendszer, amely felels pl. a memriafoglalsrt vagy a kivtelek
kezelsrt.
A .NET nem programozsi nyelv, hanem krnyezet. Gyakorlatilag brmelyik programozsi nyelvnek lehet .NET
implementcija. Jelenleg kb. 50 nyelvnek ltezik hivatalosan .NET megfelelje, nem beszlve a szmtalan
hobbifejlesztsrl.

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 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

15

feladathoz minden csepp erforrst ki kell prselni a hardverbl. Msfell a felgyelt krnyezet a hatkonyabb
memriakezels miatt jobban teljest olyan helyzetekben, ahol nagy mennyisg adatot mozgatunk a
memrin bell (pl. szmos rendez algoritmus ilyen).

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, s 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
fjlbl is llhat, tipikusan .exe (futtathat llomny) vagy .dll (osztlyknyvtr) kiterjesztssel.

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

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, tpusbiztos, 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.

ALTERNATV MEGOLDSOK
A Microsoft .NET Framework jelen pillanatban csak s kizrlag Microsoft Windows opercis
rendszerek alatt rhet el. 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 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.
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 is fut.

16

Az SSCLIt kimondottan tanulsi clra ksztette a Microsoft, ezrt a licence 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 projektet mr nem fejleszti tovbb a Microsoft, 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
Mono
A Mono projekt szlatyja Miguel de Icaza, 2000ben kezdte meg a fejlesztst, s egy vvel ksbb mutatta be
az 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.
2011-ben az Attachmate felvsrolta a Novell-t, ezzel egytt pedig tbb szz fejleszttl belertve a Mono
csapatot is megvltak. A Mono jvje ekkor ktsges volt, de Icaza alig egy hnapon bell j cget alaptott
Xamarin nven, amely tvette a projektet.
A Mono pillanatnyilag a 2.10.8 verziszmnl jr, tmogatja a C# 4.0 nyelvi elemeit, illetve megvalstja a
Microsoft-fle BCL nagy rszt.
A Mono elrhet Windows, Linux, UNIX, BSD, Mac OSX s Solaris rendszereken is. Napjainkban a Mono mutatja
a leggretesebb fejldst, mint a Microsoft .NET 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
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 DotGNU projektet mr nem fejlesztik tovbb.
A projekt hivatalos oldala: http://www.gnu.org/software/dotgnu/

17

ISMERKEDNK A NYELVVEL
Mieltt elkezdennk hossz utunkat, fel kell fegyverkeznnk a megfelel fejleszteszkzzel! A .NET
programozshoz a legjobb vlaszts a Microsoft sajt termke, a Visual Studio. A nagy fizets vltozatok
(Professional, Ultimate) mellett ltezik az ingyenes Express csald is, amelyek szinte teljes kr szolgltatst
nyjtanak.
Az Express vltozatok klnlegessge, hogy a nagy vltozatokkal ellenttben csak egy-egy rszterletre
koncentrlnak, kln vltozat van a webes s asztali alkalmazsok ksztshez.

VISUAL STUDIO
A jegyzetben a Visual C# Express 2010-et hasznljuk, br jelen pillanatban mr ltezik a Visual Studio 11 is, igaz,
csak bta vltozatban. Nem kell azonban aggdni, a Visual Studio kifejezetten konzisztens felpts maradt az
elmlt vekben, aki az egyik vltozatot tudja hasznlni, az a tbbivel is elboldogul.
A Visual C# Express 2010 az albbi webhelyrl tlthet le:
http://www.microsoft.com/visualstudio/en-us/products/2010-editions/visual-csharp-express
A Visual C# Express a telepts utn 30 napig hasznlhat, utna regisztrcis kdot fog krni. Ehhez mindssze
egy Live ID szksges (a megadott e-mail cm), ezutn kapunk kdot, amelyet berva immr tovbbi
elfelttelek nlkl tetszs szerint hasznlhatjuk a programot.
A Visual Studio elindtsakor tegyk a kvetkezt:
1.
2.

Tools men -> Settings


A Basic Settings helyett vlasszuk az Expert Settings belltst!

Ez az egyszer lps a ksbbiekben mg hasznunkra vlik majd.

HELLO WORLD!
Ksztsk el els C# nyelven rt programunkat!
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.
A File menben kattintsunk a New Project menpontra, ekkor a megjelen ablakban kivlaszthatjuk a projekt
tpust. A jegyzetben nagyrszt Console Application sablont fogunk hasznlni, ezzel egy parancssorban fut
programot hozhatunk ltre.

18

Az OK gombra kattintva elkszl az els projektnk, a forrskd gy nz ki:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace TestApp
{
class Program
{
static void Main(string[] args)
{
}
}
}
Meglepen sok mindent ltunk, ahhoz kpest, hogy ez a program a vilgon semmit nem csinl. Menjnk
sorjban, egyelre klnsebb magyarzat nlkl, a kvetkez fejezetekben mindenre fny derl. Az els ngy
sorral azokat a nvtereket jelltk ki, amelyeket hasznlni szeretnnk. Ebben az esetben a Visual Studio egy
kicsit tlbuzg volt, egyelre az els sor is elg lett volna, de nyugodtan hagyjunk mindent, ahogy van, mg
nincs jelentsge.
A kvetkez lpsben megadjuk a programunk nvtert, ez alaprtelmezs szerint az lesz, amit a New Project
ablakban a Name mezben megadtunk.
A class Program sor az, ami egy kicsit ijeszt lehet a kezd programoz szmra. A C# tisztn objektumorientlt
nyelv, ami egyelre azt jelenti szmunkra, hogy brmit tesznk, azt csakis egy osztlyon bell egy fggvnyben
vagy metdusban tehetjk. Egy osztlyt a class kulcsszval vezetnk be, amely utn az osztly nevt kell rnunk.
Ebben a pillanatban mg nem kell ennl tbbet tudnunk az objektumorientlt programozsrl, de nemsokra
ennek is eljn az ideje.

19

Nem maradt ms htra, mint a Main fggvny. Ez nagyon fontos sszetevje minden C# programnak, hiszen ez
az alkalmazsunk belpsi pontja, itt kezddik el a futsa. Minden C# programnak tartalmaznia kell egy Main
nev fggvnyt, ellenkez esetben le sem fordul.
Vizsgljuk meg egy kicsit a fejleszteszkzt is!

Bal oldalon a forrskdot lthatjuk, mg a jobb oldalt az n. Solution Explorer foglalja el. Minden esetben,
amikor egy projektet ksztnk, egy n. Solution jn ltre. Egy Solution tbb projektet is tartalmazhat, jobb
gombbal kattintva hozhatjuk el a hozz tartoz helyi ment, amely segtsgvel jabb projektet kszthetnk.
Ugyangy minden projekthez is tartozik ilyen men, amellyel pl. j elemeket adhatunk hozz.
Lthat, hogy egy faszer szerkezetrl van sz. A flkvr betvel kiemelt projekt a StartUp elem, ez fog
elindulni, ha futtatjuk a Solution-t. Tetszs szerint megvltoztathatjuk ezt a belltst a kvnt projekt helyi
menjben (Set as StartUp project).
A projekteket lenyitva a hozz tartoz fjlokat talljuk, illetve kt mappt, amelyek specilis clt szolglnak. A
Properties mappa a programrl tartalmaz metaadatokat, pldul a kszt nevt, az aktulis verziszmot stb.
A References mappa pedig a projektben hasznlt osztlyknyvtrakat sorolja fel. Ebbl rgtn ktfle is van, a
BCL-hez tartoz knyvtrak (alapbellts szerint) nem msoldnak be a program kimeneti mappjba, mg a
kls knyvtrak igen. j projekt ltrehozsakor is tbb knyvtrat tallunk, ezek kzl egyelre a System s
System.Core nlklzhetetlen.
Ahhoz, hogy a programunkat futtassuk, elszr le kell fordtanunk. Ezt a Build menben tallhat Build
Solution paranccsal tehetjk meg. Ha tbb projektnk is van, egyenknt is fordthatunk a helyi menbl.
Amennyiben nem kapunk hibazenetet, illetve a bal als sarokban megjelenik a Build Succeeded felirat, akkor
a programunk szintaktikailag helyes, megprblhatjuk futtatni.
Ehhez a Debug men Start Debugging illetve Start Without Debugging parancsait kell hasznlnunk. Ha a
projekt a legutbbi fordts ta megvltozott, akkor automatikusan le is fordtja a programunkat a Visual
Studio. Hasznlhatunk gyorsbillentyket is, F6 a fordts, mg F5 a futtats (s fordts, ha szksges). Vgezetl
szintn elrhetjk a clunk, ha a mensorban tallhat kis zld fektetett hromszgre kattintunk.
Lpjnk tovbb, egyelre a Main fggvnyen bell fogunk tevkenykedni. Ahhoz, hogy elkszthessk a
programunkat, tudnunk kell, hogyan kezelhetjk a parancssort a programunkbl. A .NET Framework BCL-e erre
is knl megoldst, a Console osztly lesz segtsgnkre:

20

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace TestApp
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World");
Console.ReadKey();
}
}
}
rmmel tapasztalhatjuk, hogy a Console sz utn tett pont elhozza a vlaszthat fggvnyek listjt. Ezt a
Visual Studio IntelliSense-nek hvja. Ha nem ajnlja fel a listt a fejleszteszkz, akkor elkpzelhet, hogy a
kdkiegszts nincs bekapcsolva. A visszakapcsolshoz a kvetkezket kell tenni:
1.
2.
3.

Tools men -> Options


Ha nincs bejellve a Show All Settings ngyzet, akkor kapcsoljuk be!
Text Editor -> C# -> IntelliSense -> Legyen bejellve a Show completion list after a character is typed
felirat ngyzet!

A WriteLine fggvny kirja a parancssorba a paramterknt kapott szveget, majd j sort nyit. Amennyiben
szeretnnk ugyanabban a sorban maradni, hasznljuk a Console.Write fggvnyt!
A Console.ReadKey fggvny feladata, hogy vr egy billentyletst a felhasznltl, mieltt tovbblpne a
program. Mirt van erre szksg? A legegyszerbb, ha kiprbljuk a programot nlkle. Amit ltni fogunk, az
egy villans, hiszen a programunk csak annyit tesz, hogy kir egy szveget s kilp. Ez utbbi miatt kellett
valami, amivel megszakthatjuk futst, s itt jn a kpbe a ReadKey.

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. Ha a forrskd szintaxisa nem megfelel, a program nem
fordul le.
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 rnnk, akkor a
program nem fordulna le.
A program egysgeit (osztlyok, metdusok stb.) n. blokkokkal jelljk ki, kapcsos zrjelek ({ s })
segtsgvel.

21

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 nv is beptett
tpusra utal, azaz kulcssz, teht nem fog lefordulni a program.
int int; //hiba
A legtbb fejleszteszkz (gy a Visual Studio is) megsznezi a kulcsszavakat, ezrt knny elkerlni a fenti hibt.
A C# 5.0 mr 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, osztlyok
ltrehozsnl:
add
ascending
by
descending

equals
from
get
global

group
in
into
join

let
on
orderby
partial

Remove
Select
Set
Value

var
where
yield

Nhnyuk a krnyezettl fggen ms-ms jelentssel is brhat, a megfelel fejezet bvebb informcit ad
majd ezekrl az esetekrl.

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:
static void Main(string[] args)
{
Console.WriteLine("Hello World"); // Ez egy egysoros komment
Console.ReadKey();
/* Ez
egy

22

tbbsoros komment */
}
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.

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.

23

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

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; // deklarci s definci

TPUSOK
A C# ersen (statikusan) tpusos nyelv, ami azt jelenti, hogy minden egyes vltoz tpusnak ismertnek kell
lennie fordtsi idben, ezzel biztostva, hogy a program csakis 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:
C# tpus
byte
char
bool

.NET tpus
System.Byte
System.Char
System.Boolean

Mret (byte)
1
2
1

sbyte
short
ushort
int

System.SByte
System.Int16
System.Uint16
System.Int32

1
2
2
4

uint
float
double
decimal
long

System.Uint32
System.Single
System.Double
System.Decimal
System.Int64

4
4
8
16
8

24

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

ulong
string
object

System.Uint64
System.String
System.Object

8
N/A
N/A

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 World programot gy, hogy a kirand szveget egy vltozba tesszk:
using System;
namespace TestApp
{
class Program
{
static void Main(string[] args)
{
string message = "Hello World";
Console.WriteLine(message);
Console.ReadKey();
}
}
}
A C# 3.0-tl kezdve mr lehetsges egy metdus hatkrben deklarlt vltoz tpusnak meghatrozst a
fordtra bzni. 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 (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 x = 10; // int tpus vltoz
var z = 10; // int tpus vltoz
z = "string"; // fordtsi hiba
var w; //fordtsi hiba

LOKLIS S GLOBLIS VLTOZK


Egy blokkon bell deklarlt vltoz loklis lesz a blokkjra nzve, vagyis a program tbbi rszbl nem lthat
(gy is mondhatjuk, hogy a vltoz hatkre a blokkjra terjed ki). A fenti pldban a message egy loklis
vltoz, ha egy msik fggvnybl vagy osztlybl prblnnk meg elrni, akkor a program nem fordulna le.
Ebben az esetben az IntelliSense sem ajnlan fel, ez az eszkz teht erre is segtsgnkre van.
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.

REFERENCIA- S RTKTPUSOK
A .NET minden tpusa 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.

25

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 visszahelyezi:
int x = 10;
int y = 11;
x+y
A verem:
|11|
|10| --sszeads mvelet--|21|
A referenciatpusok 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.
Metduson bell, loklisan deklarlt rtktpusok a verembe kerlnek, a referenciatpuson bell adattagknt
deklarlt rtktpusok pedig a halomban foglalnak helyet.
Nzznk nhny pldt!
using System;
namespace TestApp
{
class Program
{
static void Main(string[] args)
{
int x = 10;
}
}
}
Ebben a programban x-et loklisan deklarltuk egy metduson bell, ezrt biztosak lehetnk benne, hogy a
verembe fog kerlni.
class MyClass
{
private int x = 10;
}
Most x egy referenciatpuson (esetnkben egy osztlyon) belli adattag, ezrt a halomban foglal majd helyet.
class MyClass
{
private int x = 10;
public void MyMethod()
{
int y = 10;
}
}

26

Ezttal egy kicsit bonyolultabb a helyzet. Az y nev vltozt egy referenciatpuson bell, de egy metdusban,
loklisan deklarltuk, gy a veremben fog troldni, x pedig mg mindig adattag, ezrt marad a halomban.
Meg kell emlteni ugyanakkor, hogy a programoz szemszgbl a vilgon semmi jelentsge nincs, hogy mi
hol troldik.
Vgl nzzk meg, hogy mi lesz rtk- s mi referenciatpus! rtktpus lesz az sszes olyan objektum,
amelyeket a kvetkez tpusokkal deklarlunk:

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


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

Referenciatpusok lesznek a kvetkezk:

Osztlyok (class)
Interfsz tpusok (interface)
Delegate tpusok (delegate)
Stringek
Minden olyan tpus, amely kzvetlen mdon szrmazik a System.Objectbl vagy brmely class
kulcsszval bevezetett szerkezetbl.

REFERENCIK
Az rtk- illetve referenciatpusok 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.

10
Msol

10

A helyzet ms lesz referenciatpusok esetben. Mivel k sszetett tpusok, ezrt fizikailag lehetetlen lenne az
rtkeikkel dolgozni, gy egy referenciatpusknt ltrehozott vltozban tulajdonkppen csak olyan informcik
vannak, amelyek segtsgvel a CLR kpes meghatrozni, hogy hol keresse az igazi objektumot a
memriban. Viszont vigyzzunk, a referencik nem mutatk (mint pldul a C++-beli pointerek), teht azon
kvl, hogy elhzzuk a kvnt objektumot, nincs hatsunk a memrira. Az egyszersg kedvrt a
tovbbiakban is a memriaterletre mutat kifejezst hasznljuk. Nzzk meg ezt kzelebbrl:

27

using System;
namespace TestApp
{
class MyClass
{
public int x;
}
class Program
{
static void Main(string[] args)
{
MyClass s = new MyClass();
s.x = 10;
MyClass p = s;
p.x = 14;
Console.WriteLine(s.x);
Console.ReadKey();
}
}
}
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
s, vagyis tulajdonkppen annak egy lneve (alias) lesz. rtelemszeren, ha p mdosul, akkor s is gy tesz, ezrt a
fenti program kimenete 14 lesz.

x = 10

BOXING S UNBOXING
Boxingnak (bedobozols) azt a folyamatot nevezzk, amely megengedi egy rtktpusnak, hogy gy
viselkedjen, mint egy referenciatpus. 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 referenciatpusokhoz 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:

28

public static void WriteLine(


Object value
)
Lthat, hogy a paramter tpusa object, ami a System.Object rvidebb elnevezse, vagyis egy referenciatpus.
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 referenciatpusok 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 lesz.
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 referenciatpusok kztt nincs szoros relci! Az ilyen kapcsolatot implicit
konverzbilis kapcsolatnak 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 referenciatpus 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. Ez
majdnem teljesen igaz, egyetlen apr kivtellel: amikor vissza akarjuk kapni a bedobozolt rtktpusunkat,
akkor valjban az unbox IL (Intermediate Language) 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 a teljestmnyvesztesg.
Fontos mg megjegyezni, hogy a bedobozols utn teljesen j objektum keletkezik, amelynek semmi kze az
eredetihez:

29

int x = 10;
object z = x;
z = (int)z + 10;
Console.WriteLine(x); // 10
Console.WriteLine(z); // 20
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).

KONSTANSOK
A const tpusmdost kulcssz segtsgvel egy objektumot konstanss, megvltoztathatatlann tehetnk. A
konstansoknak egyetlenegyszer adhatunk (s ekkor ktelez is adnunk) rtket, mgpedig a deklarcinl.
Brmely ksbbi prblkozs fordtsi hibt okoz.
const int x; // Hiba
const int y = 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:
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 (amely a sor vgt jelzi, pl. 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 karaktersorozatot vr, s egsz szmot ad
vissza. A paramterknt megadott karaktersor nem tartalmazhat numerikus karakteren kvl mst, ellenkez
esetben a program kivtelt dob. Ennek kikszblsre rendelkezsnkre llnak a TryParse fggvnyek is, ezek
logikai rtket adnak vissza, amellyel jelzik, hogy a konverzi sikerlt vagy sem, utbbi esetben pedig nem
dobnak kivtelt.

A FELSOROLT TPUS
A felsorolt tpus olyan adatszerkezet, amely meghatrozott rtkek nvvel elltott halmazt kpviseli. Felsorolt
tpust az enum kulcssz segtsgvel deklarlhatunk:
using System;
namespace TestApp
{
class Program
{
enum Animal { Cat, Dog, Tiger, Wolf };
static void Main(string[] args)
{
Console.ReadKey();
}

30

}
}
Enum tpust csakis metduson kvl (osztlyon bell vagy nll tpusknt) deklarlhatunk, ellenkez esetben
a program nem fordul le. Ezutn gy hasznlhatjuk:
using System;
namespace TestApp
{
class Program
{
enum Animal { Cat, Dog, Tiger, Wolf };
static void Main(string[] args)
{
Animal b = Animal.Tiger;
if (b == Animal.Tiger) // Ha b egy tigris
{
Console.WriteLine("b egy tigris...");
}
Console.ReadKey();
}
}
}
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:
Animal a = Animal.Cat;
int x = (int)a; // x == 0
a = Animal.Wolf;
x = (int)a; // x == 3
A tagok rtkei alaprtelmezetten int tpusak, de 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. Ilyen mdon csakis a beptett egsz numerikus
tpusokat hasznlhatjuk (pl. byte, long, uint stb.). Azok a nevek amelyekhez nem rendeltnk rtket implicit
mdon, az ket megelz nv rtktl szmtva kapjk meg azt, nvekv sorrendben. gy a lenti pldban
Tiger vltoz rtke ngy lesz:
using System;
namespace TestApp
{
class Program
{
enum Animal { Cat = 1, Dog = 3, Tiger, Wolf }

31

static void Main(string[] args)


{
Animal a = Animal.Tiger;
Console.WriteLine((int)a); // 4
Console.ReadKey();
}
}
}
Az Enum.TryParse metdussal string rtkekbl gyrthatunk felsorolt rtkeket:
using System;
namespace TestApp
{
class Program
{
enum Animal { Cat = 1, Dog = 3, Tiger, Wolf }
static void Main(string[] args)
{
string s1 = "1";
string s2 = "Dog";
Animal a1, a2;
Enum.TryParse(s1, true, out a1);
Enum.TryParse(s2, true, out a2);
Console.ReadKey();
}
}
}

NULL TPUSOK
A referenciatpusok az inicializls eltt automatikusan null rtket vesznek fel, illetve mi magunk is
megjellhetjk ket belltatlannak:
using System;
namespace TestApp
{
class MyClass { }
class Program
{
static void Main(string[] args)
{
MyClass mc = null;
Console.ReadKey();
}
}
}

32

Ugyanez az rtktpusoknl mr nem alkalmazhat:


int x = null; // ez nem j
Azt mr lttuk, hogy a referenciatpusokra referencikkal, azaz a nekik megfelel memriacmmel mutatunk,
ezrt lehetsges null rtket megadni nekik. Az rtktpusok pedig az ltaluk trolt adatot reprezentljk, ezrt
k nem vehetnek fel null rtket, mivel az ilyen tpusokon ez a fogalom nincs rtelmezve. Minden ilyen tpus a
deklarcija pillanatban (nhny kivteltl eltekintve) automatikusan a megfelel nulla rtkre inicializldik.
Azonban elfordulhat, hogy mgis szeretnnk, ha ezek a tpusok is rendelkeznnek egy nem definilt
llapottal, s ebben lesznek segtsgnkre a nullable tpusok. Ezek ugyan rtktpusok, mgis kaphatnak null
rtket.
A nullable tpusokat a rendes tpus utn rt krdjellel jelezzk:
int? x = null; // ez mr j
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

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.0 verzijig 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 dynamic kulcsszval 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;
namespace TestApp
{
class Program
{
static void Main(string[] args)
{
dynamic x = 10;
Console.WriteLine(x); // x most 10
x = "szalmi";
Console.WriteLine(x); // x most szalmi
Console.ReadKey();
}
}
}

33

Vegyk a kvetkez osztlyt:


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

34

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 * -gal

2. lps: i = * > i 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 nemcsak kt operandusa lehet. A C# nyelv egy- (unris) s hrom-operandus (ternris)
opertorokkal is rendelkezik.
A kvetkez fejezetekben megismerkednk nhny opertorral, de nem az sszessel. Ennek oka az, hogy
bizonyos opertorok nmagukban nem hordoznak jelentst, egy-egy specilis rszterlet kapcsoldik hozzjuk.
Ezrt ezeket az opertorokat majd a megfelel helyen vizsgljuk meg (pl. az indexel opertor most kimarad,
elsknt a tmbknl tallkozhat majd vele a kedves olvas).

OPERTOR-PRECEDENCIA
Amikor tbb opertor is szerepel egy kifejezsben, a fordtnak muszj valamilyen sorrendet (n. 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 gy nzne ki helyesen zrjelezve:
(10 * 5) + 1
A C# nyelv precedencia szerint 14 kategriba sorolja az opertorokat (a kisebb sorszmt rtkeli ki hamarabb
a fordt):
1.
2.
3.
4.
5.
6.
7.
8.

Zrjel, adattag hozzfrs (pont (.) opertor), metdushvs, postfix inkrementl s dekrementl
opertorok, a new opertor, typeof, sizeof, checked s unchecked
Pozitv s negatv opertorok (x = -5), logikai s binris tagads, prefix inkrementl s dekrementl
opertorok, explicit tpuskonverzi
Szorzs, maradkos s maradk nlkli oszts
sszeads, kivons
Bit-eltol (>> s <<) opertorok
Kisebb (vagy egyenl), nagyobb (vagy egyenl), as, is
Egyenl s nem egyenl opertorok
Logikai S

35

9.
10.
11.
12.
13.
14.

Logikai XOR
Logikai VAGY
Feltteles S
Feltteles VAGY
Feltteles opertor ( ? : )
rtkad opertor, illetve a rvid formban hasznlt opertorok (pl: x +=y)

RTKAD OPERTOR
Az egyik legltalnosabb mvelet, amit elvgezhetnk az, hogy egy vltoznak rtket adunk. A C# nyelvben
ezt az egyenlsgjel segtsgvel tehetjk meg:
int x = 10;
Ltrehoztunk egy int tpus vltozt, elneveztk xnek, majd kezdrtknek tzet 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;
Ennek ellenre ajnlott akkor rtket adni egy vltoznak, amikor deklarljuk.
Egy vltoznak nemcsak 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).
int x = 10;
int y = x; // y rtke most 10

MATEMATIKAI OPERTOROK
A kvetkez pldban a matematikai opertorok hasznlatt vizsgljuk meg:
using System;
namespace TestApp
{
class Program
{
static void Main(string[] args)
{
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

36

Console.WriteLine(z); // Az oszts maradkt rja ki: 1


Console.ReadKey(); //Vr egy billenty letsre
}
}
}

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;
namespace TestApp
{
class Program
{
static void Main(string[] args)
{
int x = 10;
int y = 23;
Console.WriteLine(x > y); // Kirja az eredmnyt: false
Console.WriteLine(x == y); // false
Console.WriteLine(x != y); // x nem egyenl y nal: true
Console.WriteLine(x <= y); // x kisebb-egyenl mint y: true
Console.ReadKey();
}
}
}
Az els sor egyrtelm, a msodikban az egyenlsget vizsgljuk a ketts egyenlsgjellel. Ilyen esetekben
figyelni kell, mert egy elts is nehezen kiderthet hibt okoz, amikor egyenlsg helyett az rtkad
opertort hasznljuk. Az esetek tbbsgben ugyanis gy is lefordul 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

LOGIKAI S FELTTELES OPERTOROK


A C# logikai tpusa (bool) kt rtket kpes felvenni: igaz (true) s hamis (false). Ennek a tpusnak a segtsgvel
elvgezhetek a szoksos logikai mveletek, ezeket nzzk most meg.
using System;

37

namespace TestApp
{
class Program
{
static void Main(string[] args)
{
bool l = true;
bool k = false;
if (l == true && k == false)
{
Console.WriteLine("Igaz");
}
Console.ReadKey();
}
}
}
A fenti pldban elszr felvettnk kt logikai vltozt, az elsnek igaz, a msodiknak hamis rtket
adtunk. Ezutn egy elgazs kvetkezik, errl bvebben egy ksbbi fejezetben lehet olvasni, a lnyeg az, hogy
ha a zrjelben megadott felttel igaz, akkor vgrehajtja a blokkjban lv utasts(oka)t.
A pldban az S (&&) opertort hasznltuk, ez kt operandust vr, s akkor ad vissza igaz rtket, ha
mindkett igaz 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
hamis
hamis
igaz
igaz

B
hamis
igaz
hamis
igaz

Eredmny
hamis
hamis
hamis
igaz

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;
namespace TestApp
{
class Program
{
static void Main(string[] args)
{
bool l = true;
bool k = false;
if (l == true || k == true)
{
Console.WriteLine("Igaz");
}
Console.ReadKey();

38

}
}
}
A VAGY (||) opertor akkor trt vissza igaz rtket, ha az operandusai kzl legalbb az egyik igaz. 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
hamis
hamis
igaz
igaz

B
hamis
igaz
hamis
igaz

Eredmny
hamis
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 vagy negci (!):
using System;
namespace TestApp
{
class Program
{
static void Main(string[] args)
{
int x = 10;
if (!(x == 11))
{
Console.WriteLine("X nem egyenl 11 -gyel!");
}
Console.ReadKey();
}
}
}
Ennek az opertornak egy operandusa van (ez a pldban a zrjelben lv kifejezs), s akkor ad vissza igaz
rtket, ha az operandusban megfogalmazott felttel hamis. A tagads igazsgtblja:
A
hamis
igaz

Eredmny
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");

39

}
A logikai S:
if (l == true & k == false)
{
Console.WriteLine("Igaz");
}
A logikai opertorok csaldjhoz tartozik (ha nem is szorosan) a feltteles opertor. Ez az egyetlen
hromoperandus (ternris) opertor, s a kvetkezkppen mkdik:
felttel ? igaz-g : hamis-g;
using System;
namespace TestApp
{
class Program
{
static void Main(string[] args)
{
int x = 10;
int y = 10;
Console.WriteLine((x == y) ? "Egyenl" : "Nem egyenl");
Console.ReadKey();
}
}
}
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 a kettspont
utni. Termszetesen a kt kifejezs ugyanazt a tpust kell, hogy kpviselje, ellenkez esetben nem fordul el a
program.
Egyszerbb if-else (errl ksbb) gakat lehet ltala kivltani, sok gpelst megsprolhatunk vele s a kdunk is
kompaktabb, ttekinthetbb lesz tle.

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 jn ngy msik opertor is. A
mveletek:
Bitenknti S: veszi a kt operandus binris alakjt, s a megfelel bitprokon elvgzi az S mveletet, azaz
ha mindkt bit 1 llsban van, akkor az adott helyen az eredmnyben is az lesz, egybknt pedig 0:
01101101

40

00010001 AND
00000001
Elg egyrtelm, hogy az S igazsgtblt hasznltuk az eredmny kiszmolshoz.
Plda:
using System;
namespace TestApp
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine(10 & 2); // 1010 & 0010 = 0010 = 2
Console.ReadKey();
}
}
}
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
Plda a VAGY-ra:
using System;
namespace TestApp
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine(10 | 2); // 1010 | 0010 = 1010 = 10
Console.ReadKey();
}
}
}
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:

41

using System;
namespace TestApp
{
class Program
{
static void Main(string[] args)
{
int x = 143;
Console.WriteLine(x << 1); // 10001111 (=143) << 1 = 100011110 = 286
Console.ReadKey();
}
}
}
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;
namespace TestApp
{
class Program
{
static void Main(string[] args)
{
byte x = 143;
Console.WriteLine(x >> 1); // 10001111 (=143) >> 1 = 01000111 = 71
Console.ReadKey();
}
}
}
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 n-edik 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

42

Amikor olyan programot ksztnk, amely ersen pt kettvel vagy hatvnyaival val szorzsra/osztsra, akkor
ajnlott bitmveleteket hasznlni, mivel ezeket a processzor sokkal gyorsabban vgzi el, mint a hagyomnyos
szorzst (tulajdonkppen a szorzs a processzor egyik leglassabb mvelete).

RVID FORMA
Vegyk a kvetkez pldt:
x = x + 10;
Az x nev vltozt megnveltk tzzel. Csakhogy ez a megolds nem tl hatkony. Mi trtnik valjban?
Elsknt rtelmezni kell a jobb oldalt, azaz ki kell rtkelni xet, hozz kell adni tzet, s eltrolni a veremben.
Ezutn ismt kirtkeljk xet, ezttal a bal oldalon.
Szerencsre van megolds, mgpedig az n. rvid forma. A fenti sorbl ez lesz:
x + = 10;
Rvidebb, szebb s hatkonyabb. Az sszes aritmetikai opertornak ltezik rvid formja.
Az igazsghoz azrt az is hozztartozik, hogy a fordtprogram elvileg felismeri a fent felvzolt szitucit, s a
rvid formval egyenrtk IL t kszt belle (ms krds, hogy a forrskd gy viszont szebb s olvashatbb).
A problma ugyanaz, de a megolds ms a kvetkez esetben:
x = x + 1;
Szemmel lthatan ugyanaz a baj, azonban az eggyel val nvelsre/cskkentsre van nll opertorunk:
++x s --x
x++ s x-Ebbl az opertorbl rgtn kt verzit is kapunk, prefixes (++/-- ell) s postfixes (++/-- htul) formt. A
prefixes alak pontosan azt teszi, amit elvrunk tle, azaz megnveli (vagy cskkenti) az operandust eggyel.
A postfixes forma egy kicsit bonyolultabb, elsknt ltrehoz egy tmeneti vltozt, amiben eltrolja az
operandusa rtkt, majd megnveli eggyel az operandust, vgl visszaadja az tmeneti vltozt. Ez elsre
taln nem tnik hasznosnak, de vannak helyzetek, amikor lnyegesen megknnyti az letnket a hasznlata.
Attl fggen, hogy nveljk vagy cskkentjk az operandust, inkrementl illetve dekrementl opertorrl
beszlnk. Ez az opertor hasznlhat az sszes beptett numerikus tpuson, valamint a char illetve enum
tpusokon is, mivel ezeknek van numerikus megfeleljk.

EGYB OPERTOROK
Unris (+ s -): az adott szm pozitv illetve negatv rtkt jelezzk vele:
using System;
namespace TestApp
{
class Program
{
static void Main(string[] args)

43

{
int x = 10;
int y = 10 + (-x);
Console.WriteLine(y);
Console.ReadKey();
}
}
}
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 kt sor fordtsi hibt okoz, mivel az int tpus nem fr el a byte tpusban:
byte x = 10;
byte y = -x;
A typeof az operandusa tpust adja vissza:
using System;
namespace TestApp
{
class Program
{
static void Main(string[] args)
{
int x = 143;
if (typeof(int) == x.GetType())
{
Console.WriteLine("x tpusa int");
}
Console.ReadKey();
}
}
}
A vltozn meghvott GetType metdus a vltoz tpust adja vissza (ez egy System.Objecthez tartoz
metdus, gy a hasznlathoz dobozolni kell az objektumot).
A sizeof opertor a paramtereknt megadott rtktpus mrett adja vissza byte-ban. Ez az opertor
kizrlag unsafe (ld. Unsafe md fejezet) mdban hasznlhat s csakis rtktpusokon (illetve pointer
tpusokon):
using System;
namespace TestApp
{
class Program
{
static void Main(string[] args)
{
unsafe
{

44

Console.WriteLine(sizeof(int));
}
Console.ReadKey();
}
}
}
Ez a program ngyet fog kirni, hiszen az int tpus 32 bites, azaz 4 byte mret tpus. A programot az unsafe
kapcsolval kell lefordtanunk parancssorbl val fordtskor:
csc /unsafe main.cs
Illetve ha Visual Studio-val dolgozunk, akkor kattintsunk jobb gombbal a Solution Explorerben a projekten, s
vlasszuk a Properties elemet:

Ezutn a megnyl oldalban a Build fln jelljk be az Allow unsafe code ngyzetet:

45

VEZRLSI SZERKEZETEK
Vezrlsi szerkezetnek a program utastsainak sorrendisgt szablyoz konstrukcikat nevezzk.

SZEKVENCIA
A legegyszerbb vezrlsi szerkezet a szekvencia. Ez tulajdonkppen egyms utn megszabott sorrendben
vgrehajtott utastsokbl ll. A szekvencia minden ms vezrlsi szerkezet ptkve.

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;
namespace TestApp
{
class Program
{
static void Main(string[] args)
{
int x = 10;
if (x == 10) // Ha x egyenl 10-zel
{
Console.WriteLine("x rtke 10");
}
Console.ReadKey();
}
}
}
Az elgazs felttelben az egyenlsg opertort hasznltuk, s ide kapcsoldik egy viszonylag gyakori hiba: ha
vletlenl csak sima egyenlsgjelet runk, az fordtsi hibnak minsl:
if (x = 10) // hiba
{
}
Vannak nyelvek, amelyek ezt a formt is megengedik, ezzel automatikusan rtket adva a felttelben szerepl
vltoznak, de a C# nem ilyen.
A felttelben szerepl kifejezsnek minden esetben logikai rtket kell visszaadnia, vagy pedig az eredmnynek
konvertlhatnak kell lennie bool tpusra.
A felttelbe nem rhatunk numerikus rtket visszaad kifejezst, illetve null rtket sem vizsglhatunk pusztn
a referencia nevnek megadsval:

46

using System;
namespace TestApp
{
class Program
{
static void Main(string[] args)
{
int x = 11;
if (x) // ez nem fordul le
{
}
if (x > 0) // ez mr j
{
}
Program p = null;
if (!p) // ez sem fordul le
{
}
if (p == null) // ez mr j
{
}
Console.ReadKey();
}
}
}
Termszetes az igny arra is, hogy azt a helyzetet is kezelni tudjuk, amikor x rtke nem tz. Ilyenkor hasznljuk
az else gat:
using System;
namespace TestApp
{
class Program
{
static void Main(string[] args)
{
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");
}
Console.ReadKey();

47

}
}
}
Az else szerkezet akkor lp mkdsbe, 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;
namespace TestApp
{
class Program
{
static void Main(string[] args)
{
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");
}

Console.ReadKey();
}
}
}
Ez a program pontosan ugyanazt csinlja, mint az elz, de van egy nagy klnbsg kzttk: 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 szerkezetet hasznlunk:
using System;
namespace TestApp
{
class Program
{
static void Main(string[] args)
{
int x = 13;
if (x == 10) // ha x rtke 10
{
Console.WriteLine("x rtke 10");
}
else if (x == 12) // vagy 12
{
Console.WriteLine("x rtke 12");
}

48

else // ha pedig valami ms


{
Console.WriteLine("x rtke nem 10 vagy 12");
}
Console.ReadKey();
}
}
}

A program az els olyan gat hajtja vgre, amelynek a felttele teljesl (vagy ha egyik felttel sem bizonyult
igaznak, akkor az else gat ha adtunk meg ilyet).
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. Nyilvn 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;
namespace TestApp
{
class Program
{
static void Main(string[] args)
{
int x = 11;
switch (x)
{
case 10:
Console.WriteLine("x rtke 10");
break;
case 11:
Console.WriteLine("x rtke 11");
break;
}
Console.ReadKey();
}
}
}

A switch szerkezeten bell megadhatjuk azokat az llapotokat, amelyekre reaglni szeretnnk. Az egyes esetek
utastsai utn meg kell adnunk, hogy mi trtnjen ezutn. Az gak a kijellt feladatuk vgrehajtsa utn a
break utastssal kilpnek a szerkezetbl.
Termszetesen nem kell minden lehetsges llapotot megvizsglnunk, csak azokat, amelyek szmunkra
rdekesek. A fenti pldban elg nagy bajban is lennnk, ha minden egyes szmot kln akarnnk vizsglni.
Erre a clra hasznlhatjuk a default gat, amely gyakorlatilag az else gnak felel meg.
Ha nincs olyan g, amely kezeln az ppen aktulis rtket, akkor vagy a default g kapja meg a vezrlst, vagy
ha nem rtunk ilyet, akkor a switch szerkezetbl kilpve folytatdik a program futsa.

using System;

49

namespace TestApp
{
class Program
{
enum Animal { TIGER, WOLF, CAT, DOG };
static void Main(string[] args)
{
Animal animal = Animal.DOG;
switch (animal)
{
case Animal.TIGER:
Console.WriteLine("Tigris");
break;
default:
Console.WriteLine("Nem ismerem ezt az llatot!");
break;
}
Console.ReadKey();
}
}
}

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

A break utastson kvl hasznlhatjuk a goto-t is, ekkor tugrunk a megadott gra:

using System;

50

namespace TestApp
{
class Program
{
enum Animal { TIGER, WOLF, CAT, DOG };
static void Main(string[] args)
{
Animal animal = Animal.DOG;
switch (animal)
{
case Animal.TIGER:
goto default;
case Animal.DOG:
goto default;
default:
Console.WriteLine("Ez egy llat!");
break;
}

Console.ReadKey();
}
}
}

CIKLUS
Amikor egy adott utastssorozatot egyms utn tbbszr kell vgrehajtanunk, akkor ciklust hasznlunk. A C#
ngyfle ciklust biztost szmunkra.
For ciklus
Az els az n. szmlls ciklus (nevezzk for-ciklusnak). Nzzk a kvetkez programot:
using System;
namespace TestApp
{
class Program
{
static void Main(string[] args)
{
for (int i = 0; i < 10; ++i)
{
Console.WriteLine(i);
}
Console.ReadKey();
}
}
}

Vajon mit r ki a program? Mieltt ezt megmondanm, elszr inkbb nzzk meg azt, hogy mit csinl: a for
utni zrjelben talljuk az n. ciklusfelttelt, ez minden ciklus rsze lesz, s azt adjuk meg benne, hogy milyen
felttele van a ciklus futsnak. 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? Hogyan?

51

Menjnk sorjban: a Honnan?-ra 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 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, hiszen i mr ltezik:
using System;
namespace TestApp
{
class Program
{
static void Main(string[] args)
{
for (int i = 0; i < 10; ++i)
{
Console.WriteLine(i);
}
int i = 10; // itt a hiba, i mr ltezik
Console.ReadKey();
}
}
}

Kvetkezzen a Meddig?! Most azt kell megvlaszolnunk, hogy a ciklusvltoz mely rtke tesz eleget a
ciklusfelttelnek. Ebben a pldban i-nek kisebbnek kell lennie tznl, vagyis kilenc mg j, de ha ennl
nagyobb, akkor a ciklust be kell fejezni.
Termszetesen bonyolultabb kifejezst is megadhatunk:
using System;
namespace TestApp
{
class Program
{
static void Main(string[] args)
{
for (int i = 1; i < 10 && i != 4; ++i)
{
Console.WriteLine(i);
}
Console.ReadKey();
}
}
}

52

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 (vagy dekrementl) opertor hasznlata,
de itt is megadhatunk sszetett kifejezst:
using System;
namespace TestApp
{
class Program
{
static void Main(string[] args)
{
for (int i = 0; i < 10; i += 2)
{
Console.WriteLine(i);
}
Console.ReadKey();
}
}
}

Ebben a kdban kettesvel nveljk a ciklusvltozt, vagyis a pros szmokat ratjuk ki a kpernyre.
Most mr eleget tudunk ahhoz, hogy vlaszt adjunk arra, 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.
using System;
namespace TestApp
{
class Program
{
static void Main(string[] args)
{
for (;;)
{
Console.WriteLine("Vgtelen ciklus");
}
}
}
}

A program futst a Ctrl+C billentykombincival llthatjuk le, ha parancssorbl futtattuk.


While ciklus
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 ciklustrzs egyszer sem
fut le:

53

using System;
namespace TestApp
{
class Program
{
static void Main(string[] args)
{
int i = 0; // ciklusvltoz deklarci
while (i < 10) // ciklusfelttel: fuss amg i kisebb, mint 10
{
Console.WriteLine("i rtke: {0}", i);
++i; // ciklusvltoz nvelse
}
Console.ReadKey();
}
}
}

A program ugyanazt csinlja, mint a szmlls ciklusnl bemutatott plda, 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. formtumsztringet kap paramterl. Az els paramterben a kapcsos zrjelek kztt megadhatjuk, hogy a tovbbi
paramterek kzl melyiket helyettestse be a helyre (nulltl szmozva).
Do-While ciklus
A harmadik versenyz kvetkezik, t htul-tesztels ciklusnak hvjk (legyen do-while), nem nehz kitallni,
hogy azrt kapta ezt a nevet, mert a ciklusmag vgrehajtsa utn ellenrzi a ciklusfelttelt, gy legalbb egyszer
biztosan lefut:
using System;
namespace TestApp
{
class Program
{
static void Main(string[] args)
{
int i = 0;
do
{
Console.WriteLine("i rtke: {0}", i);
++i;
} while (i < 10);
Console.ReadKey();
}
}
}

54

Foreach ciklus
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 string
objektumon:
using System;
namespace TestApp
{
class Program
{
static void Main(string[] args)
{
string str = "abcdefghijklmnopqrstuvwxyz";
foreach (char ch in str)
{
Console.Write(ch);
}
Console.ReadKey();
}
}
}

A ciklusfejben felvesznk egy char tpus vltozt (egy string karakterekbl ll), utna az in kulcssz kvetkezik,
amivel kijelljk azt a listt, amelynek elemein vgig szeretnnk menni. A pldban hasznlt ch vltoz nem
ciklusvltoz, hanem n. itercis vltoz, amely felveszi az iterlt gyjtemny aktulis elemnek rtkt.
Gyakorlatilag itt egy referencirl van sz (legalbbis referenciatpus elemek esetn), de a foreach ciklus nem
mdosthatja egy gyjtemny elemeit (le sem fordulna ebben az esetben a program).
Ez a ciklus ktfle mdban kpes mkdni: ha a lista, amin alkalmazzuk, megvalstja az IEnumerable s
IEnumerator interfszeket, akkor azokat fogja hasznlni, ha nem, akkor pedig 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 kpes vgigiterlni (azaz megvalstjuk az IEnumerable s IEnumerator
interfszeket).
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.
A yield hasznlathoz szksg van a System.Collections nvtrre. Amennyiben ez nem szerepel a felvett
nvterek kztt, az IntelliSense nem nyjt segtsget, illetve fordtsi hibt is okoz.
Ha mr bertuk az IEnumerable kifejezst, s a Visual Studio alhzza pirossal (mivel nem adtuk meg a
nvteret), akkor kt lehetsgnk van ennek a problmnak a megoldsra (valjban hrom, ha kzzel berjuk
a nvteret, de ht a programozk lustk):
1.
2.

Jobb klikk rajta, s a helyi menbl vlasszuk a Resolve->Using System.Collections; sort!


Ctrl + . billentykombinci

55

using System;
using System.Collections;
namespace TestApp
{
class Program
{
static public IEnumerable EnumerableMethod(int max)
{
for (int i = 0; i < max; ++i)
{
yield return i;
}
}
static void Main(string[] args)
{
foreach (int i in EnumerableMethod(10))
{
Console.Write(i);
}
Console.ReadKey();
}
}
}

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.

56

GYAKORL FELADATOK
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)
szrevehettk mr, hogy a Main fggvny rendelkezik egy paramterrel, ami egy string tpus elemekbl ll
tmb (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: fordtsunk le egy C# nyelv
forrskdot:
csc main.cs
Ebben az esetben a csc a fordtprogram neve, mg a forrskdot tartalmaz fjl neve 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
Ebben az esetben a 12 es szorztblt szeretnnk ltni.
Eddig parancssoros futtatsrl beszltnk, de ehhez valjban nem kell elhagyni a Visual Studio-t. Nyissuk meg
a Properties ablakot (jobb klikk a projekten s Properties), majd a Debug fln a Command Line Arguments
szvegdobozba be is rhatjuk a paramtereket. Ha tbb is van, szkzzel vlasszuk el ket!

A kvetkez lpsben fejlesszk tovbb a programot, hogy rja ki a paramterknt megadott szm ktszerest!
Ehhez mg szksgnk van arra is, hogy szmtpuss 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).
A forrskd most gy alakul:
using System;
namespace TestApp
{

57

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

Mivel a tmbket mindig nulltl kezdve indexeljk, ezrt az els parancssori paramter a megadott szm a
nulladik helyen lesz.
A program futtatsakor megkapjuk az eredmnyt, ami 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;
namespace TestApp
{
class Program
{
static 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);
}
Console.ReadKey();
}
}
}

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):
using System;
namespace TestApp
{
class Program
{
static void Main(string[] args)
{
if (args.Length == 0)
{
Console.WriteLine("Adj meg egy paramtert!");
Console.ReadKey();
return;

58

}
int number = int.Parse(args[0]);
Console.WriteLine(number * 2);
Console.ReadKey();
}
}
}

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;
namespace TestApp
{
class Program
{
static void Main(string[] args)
{
int number;
if (args.Length == 0)
{
Random r = new Random();
number = r.Next(100);
}
else
{
number = int.Parse(args[0]);
}
Console.WriteLine(number * 2);
Console.ReadKey();
}
}
}

Vletlenszmot a Next metdussal generltunk, a fenti formjban 0 s 100 kztt generl egy szmot, de
tetszleges rtket is berhatunk.
Mr nincs ms dolgunk, mint megrni a feladat lnyegt, a szorztblt:
using System;
namespace TestApp
{
class Program
{
static void Main(string[] args)
{
int number;
if (args.Length == 0)
{
Random r = new Random();
number = r.Next(100);
}
else
{

59

number = int.Parse(args[0]);
}
for (int i = 1; i <= 10; ++i)
{
Console.WriteLine("{0} x {1} = {2}",
i, number, i * number);
}
Console.ReadKey();
}
}
}

SZMOLGP
Ksztsnk egy egyszer szmolgpet! A program indtsakor krjen be kt szmot s egy mveleti jelet, majd
rja ki az eredmnyt!
Ezutn bvtsk ki a programot, hogy a kt szmot illetve a mveleti jelet parancssori paramterknt is
megadhassuk (ekkor nincs kln mveletvlaszt men, hanem rjuk ki rgtn az eredmnyt):
main.exe 12 23 +
(az eredmny pedig 35 lesz).
Megolds (7/Calculator.cs)
Most is kt rszbl ll a programunk, elszr hozz kell jutnunk a szmokhoz s az opertorhoz, majd
elvgezzk a megfelel mveletet s kirjuk az eredmnyt.
Ezttal is a szksges vltozk deklarcijval kezdjk a programrst, hrom darab kell, kt numerikus (legyen
most int) s egy karaktertpus. Ezutn bekrjk a felhasznltl a szksges adatokat, vagy pedig felhasznljuk
a paramtereket. Ez utbbi esetben vgezznk egy kis hibaellenrzst, vizsgljuk meg, hogy pontosan hrom
paramtert kaptunke!
A forrskd eddig gy nz ki:
using System;
namespace TestApp
{
class Program
{
static 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

60

{
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]);
}
}
Console.ReadKey();
}
}
}

Az opertor megszerzshez egyrszt a Console.Read fggvnyt 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 nyilvn a beolvasott
opertor alapjn fogjuk megtenni, ebben a helyzetben pedig a legkzenfekvbb, ha a switch szerkezetet
hasznljuk:
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. Persze mi tudjuk, hogy az
rtkads megtrtnik, a fordt viszont nem!
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, 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, ezzel pedig megnyugtatjuk a fordtt.

61

K PAPR OLL
Ksztsnk k-papr-oll jtkot! Ebben az esetben is hasznljuk a vletlenszm-genertort. A jtk folyamatos
legyen, vagyis addig tart, amg a felhasznl kilp (neheztskppen lehet egy karakter, amelyre a jtk vget
r)! Tartsuk nyilvn 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.
Ksztsk el a program vzt a vltoz-deklarcikkal, illetve a fciklussal (a ciklustrzsben krdezznk r, hogy
akarunk-e mg jtszani):
using System;
namespace TestApp
{
class Program
{
static void Main(string[] args)
{
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.
szrevehetjk, hogy az elgazsnl most elhagytuk a blokkot kijell kapcsos zrjeleket. Amennyiben az
elgazs (vagy ciklus) trzse egyetlen sorbl ll, akkor nem ktelez kirakni a blokkhatrolkat.
Most krjk be az adatokat:
Console.WriteLine("Mit vlasztasz? (k/p/o)");
switch (Console.ReadKey(true).KeyChar)

62

{
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;
}

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);
}
else if (playerChoice == compChoice)
{
Console.WriteLine("Dntetlen! Az lls:\nSzmtgp: {0}\nJtkos:{1}",
compScore, playerScore);
}
else
{
Console.WriteLine("Nyertl! Az lls:\nSzmtgp: {0}\nJtkos:{1}",
compScore, ++playerScore);
}

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

63

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:
static void Main(string[] args)
{
// 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 alacsony szint 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;
namespace TestApp
{
class Program
{
static void Main(string[] args)
{
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;
}

64

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;
}
}
}
}

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 vget r. Ezt knnyen
kijavthatjuk, ha egy kicsit gondolkodunk. Nyilvn 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 vletlenszm-genertor, 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!");
}
else if(x > number)
{
Console.WriteLine("A szm ennl kisebb!");
}
else

65

{
Console.WriteLine("Nyertl!");
goto END;
}
++i;
}
Console.WriteLine("\nVesztettl, a szm {0} volt.", number);
goto END;

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

66

}
Console.WriteLine("A szmtgp nem tudta kitallni a szmot.");
goto END;

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 nyilvn nem vltozik, mr csak az j xet kell kiszmolni, vagyis hozz kell adni a fels s
als hatrok klnbsgnek a felt: (100 50) / 2 = 25 (ellenkez esetben pedig nyilvn le kellene vonni
ugyanezt).
Amirl mg nem beszltnk, az az a felttel, amelyben x egyenlsgt vizsgljuk hrommal (vagyis azt akarjuk
tudni, hogy eljtte az utols tipp ideje). Ez az elgazs fogja visszaadni az utols tippet a vletlenszmgenertorral a megfelelen leszktett intervallumban.

67

TPUSKONVERZIK
Azt mr megtanultuk, 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 (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 vgbemegy. 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.

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;
}

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);

68

Az ajnls szerint ellenrztt konverzikat csak a fejleszts ideje alatt (tesztelsre) hasznljunk, mivel nmi
teljestmnyvesztssel jr!

IS S AS
Az is opertort futsidej tpus-lekrdezsre hasznljuk:
using System;
namespace TestApp
{
class Program
{
static void Main(string[] args)
{
int x = 10;
if (x is int) // ha x egy int
{
Console.WriteLine("x tpusa int");
}
Console.ReadKey();
}
}
}

Ez a program lefordul, de figyelmeztetst kapunk, mivel a fordt felismeri, hogy a felttel mindig igaz lesz.
Ennek az opertornak a leggyakoribb felhasznlsi terlete az interfsz-megvalsts lekrdezse (errl
ksbb).
Prja, az as az ellenrzs mellett egy explicit tpuskonverzit is vgrehajt. Ezzel az opertorral csakis referenciatpusra konvertlhatunk, rtktpusra nem (ekkor le sem fordul a program).
Az as opertort csakis akkor hasznljuk tpusellenrzsre, ha egyttal konverzit is szeretnnk vgezni, pusztn
vizsglat cljbl az is opertort kell alkalmazni. Nzznk egy pldt:
using System;
namespace TestApp
{
class Program
{
static void Main(string[] args)
{
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
Console.ReadKey();
}

69

}
}

Amennyiben ez a konverzi nem hajthat vgre, a clvltozhoz null rtk rendeldik (ezrt is van a referenciatpusokra korltozva ez az opertor).

KARAKTERKONVERZIK
A char tpust implicit mdon tudjuk numerikus tpusra konvertlni, ekkor a karakter Unicode rtkt kapjuk
vissza:
using System;
namespace TestApp
{
class Program
{
static void Main(string[] args)
{
for (char ch = 'a'; ch <= 'z'; ++ch)
{
Console.WriteLine((int)ch);
}
Console.ReadKey();
}
}
}

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.
Az Unicode nemzetkzi karakter kdolsi szabvny, amely a vilg rsrendszereinek nagy rszt tartalmazza
(jelenleg tbb mint 110000 karaktert tartalmaz).

70

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 referenciatpusok. 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 null rtkre inicializldnak (ebben az esetben 10 darab nullt fog
tartalmazni a tmbnk). Ez a szably referenciatpusoknl 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 referenciatpusoknl
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;
namespace TestApp
{
class Program
{
static void Main(string[] args)
{
int[] array = new int[10];
Random r = new Random();
for (int i = 0; i < array.Length; ++i)
{
array[i] = r.Next(0, 100);
}
foreach (int item in array)
{
Console.WriteLine(item);
}
Console.ReadKey();
}
}
}

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
helytelen indexels esetn futs idben IndexOutOfRangeException kivtelt fog dobni a program.
A tmb adott elemt az rtkadssal egy idben is kirhattuk volna, a pldban csak szemlltets cljbl
hasznltunk ktfle ciklust.
Egy tmbt akr a deklarci pillanatban is feltlthetnk a neknk megfelel rtkekkel:
char[] charArray = new char[] { 'b', 'd', 'a', 'c' };

71

Egy tmb ltal trolhat 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

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) felel meg, az egyes elemekre kt indexszel 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 =
{
{12, 23,
{13, 67,
{45, 55,
};

new int[,]
2},
52},
1}

Az elemszm most is meghatrozott, nem vltoztathat.


Nyilvn 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;
namespace TestApp
{
class Program
{
static void Main(string[] args)
{
int[,] matrix = new int[3, 3];
Random r = new Random();
for (int i = 0; i < matrix.GetLength(0); ++i) // sorok

72

{
for (int j = 0; j < matrix.GetLength(1); ++j) // oszlopok
{
matrix[i, j] = r.Next(0, 100);
}
}
Console.ReadKey();
}
}
}

Most nem rjuk ki a szmokat, ez mr nem okozhat gondot az olvasnak. 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 maguk is nll
vektorok) rrnk ksbb megadni, s nem kell ugyanolyan hossznak lennik.
using System;
namespace TestApp
{
class Program
{
static void Main(string[] args)
{
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;
Console.Write("{0}, ", jarray[i][j]);
}
Console.WriteLine();
}
Console.ReadKey();
}
}
}

Vletlenszm-genertorral adtuk 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:

73

int[][] jarray = new int[][]


{
new int[]{ 1, 2, 3, 4, 5 },
new int[]{ 1, 2, 3 },
new int[]{ 1 }
};

74

STRINGEK
A C# beptett karaktertpusa (char) egy Unicode karaktert kpes trolni kt byte-on. A szintn beptett string
tpus ilyen karakterekbl ll (teht az egyes betket char pldnyknt kezelhetjk).
using System;
namespace TestApp
{
class Program
{
static void Main(string[] args)
{
string s = "ezegystring";
Console.WriteLine(s);
Console.ReadKey();
}
}
}

A ltszat ellenre a string referenciatpus, viszont nem ktelez hasznlnunk a new opertort a deklarcijakor.
Egy string egyes karaktereire az indexel opertorral hivatkozhatunk (vagyis minden stringet kezelhetnk
tmbknt is):
using System;
namespace TestApp
{
class Program
{
static void Main(string[] args)
{
string s = "ezegystring";
Console.WriteLine(s[0]); // e
Console.ReadKey();
}
}
}

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 nemcsak 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 ilyen mveletekre.

75

METDUSOK
A .NET szmos hasznos metdust biztost a stringek hatkony kezelshez. Most megvizsglunk nhnyat, de
tudni kell, hogy a van, amelyiknek szmos vltozata is lehet, most a leggyakrabban hasznltakat nzzk meg:
sszehasonlts:
using System;
namespace TestApp
{
class Program
{
static void Main(string[] args)
{
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");
}
Console.ReadKey();
}
}
}

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;
namespace TestApp
{
class Program
{
static void Main(string[] args)
{
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

76

Console.ReadKey();
}
}
}

Az IndexOf s LastIndexOf metdusok egy karakter vagy karaktersorozat els illetve utols elfordulsi indext
(utbbi 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;
namespace TestApp
{
class Program
{
static void Main(string[] args)
{
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
Console.ReadKey();
}
}
}

A Replace metdus az els paramternek megfelel karaktereket lecserli a msodik paramterre. A Trim a
string elejn s vgn lv karaktereket vgja le, a Substring pedig kivg egy karaktersorozatot, paramterei a
kezd s vgindexek (van egyparamteres vltozata is, ekkor 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 kis- illetve 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.

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+ alkalom) 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. Plda a StringBuilder hasznlatra:
using System;
using System.Text;
namespace TestApp
{

77

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

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.

REGULRIS KIFEJEZSEK
Regulris kifejezsek segtsgvel vizsglhatjuk, hogy egy karaktersorozat megfelel-e 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;
namespace TestApp
{
class Program
{
static void Main(string[] args)
{
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"));
Console.ReadKey();
}
}
}

A regulris kifejezst egy Regex pldnynak adjuk t, lssuk, hogy hogyan pl fel:

78

^: 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 hivatott az elhvt illetve a krzetszmot
ellenrizni: a mr ismert ^-vel az illesztst a 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 [1-9][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 \d
vel (d mint digit) jellnk.
A \d eltt szerepl {2} az egsz zrjelben lv kifejezsre vonatkozik, ahol megmondtuk, hogy hrom szmjegy
utn egy ktjel kvetkezik.

79

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

80

GYAKORL FELADATOK II.


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.

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:

Adjuk meg a leghosszabb egybefgg szrazfld hosszt!


Adjuk meg, hogy hny szigetet talltunk!

81

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 rtnk-e 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.

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 vletlenszmgenertorral (sszer kereteken bell) sorsoljuk ki).

82

Keressk meg az v legmelegebb s leghidegebb napjt!


Adjuk meg az v legmelegebb s leghidegebb hnapjt!
Volte 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).

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

Kezdjk a bels ciklussal! Ez a tmb vgrl fog visszafel menni s cserlgeti az elemeket, hogy a legkisebbet
vigye tovbb magval. Legyen pl. a tmb utols nhny eleme:
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;
}

83

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).

84

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

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

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 pldnyostsnak nevezzk. Az osztly s pldny kztti klnbsgre j plda a recept (osztly) s a
stemny (pldny). A pldnyokat objektumnak is nevezik.

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.

85

LTHATSG
Az egyes tulajdonsgokat s metdusokat nem felttlenl kell kzszemlre bocstani. Az OOP egyik alapelve,
hogy a felhasznl csak annyi adatot kapjon meg, amennyi felttlenl szksges. A kutys pldban az eszik()
mvelet magba foglalja a rgst, nyelst, emsztst is, de errl nem fontos tudniuk, csak az evs tnye szmt.
Ugyangy egy tulajdonsg (adattag) esetben sem j, ha mindenki hozzjuk fr (az elfogadhat, ha a kzvetlen
csald hozzfr a szmlmhoz, de idegenekkel nem akarom megosztani).
Az s-OOP szablyai hromfle lthatsgot fogalmaznak meg (ez nyelvtl fggen bvlhet), a C#
lthatsgairl a kvetkez rszekben lesz sz.
A hromfle lthatsg:

Public: mindenki lthatja (UML jells: +).

Private: csakis az osztlyon bell elrhet, a leszrmazott osztlyok nem lthatjk s nem is
mdosthatjk (a szrmaztats s rklds hamarosan jn) (UML jells: -).

Protected: ugyanaz, mint a private, de a leszrmazott osztlyok mdosthatjk is (UML jells: #).

A Kutya osztly most gy nz ki:


Kutya
-jollak : int
+eszik() : void

EGYSGBE ZRS
A hagyomnyos, nem OO programnyelvek (pl. a C) az adatokat s a rajtuk vgezhet mveleteket a program
kln rszeiknt kezelik. Bevett szoks ezeket elklnteni egy nll forrsfjlba, de ez mg mindig nem elg
biztonsgos. A kett kztt nincs sszerendels, ezrt ms programozk gond nlkl trhatjk egyiket vagy
msikat, illetve hozzfrnek a struktrkhoz, s nem megfelelen hasznlhatjk fel azokat.
Az OO paradigma egysgbe zrja az adatokat s a hozzjuk tartoz felletet, ez az n. egysgbe zrs
(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).

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 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.

86

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
megadhatunk egy absztrakt evs metdust, amit az utdok megvalstanak - egy krokodil mshogy eszik,
mint egy hangya, de az evs az llatokhoz kthet valami, ezrt kzs).

87

OSZTLYOK
Osztlyt a class kulcssz segtsgvel deklarlhatunk:
using System;
namespace TestApp
{
class Dog
{
}
class Program
{
static void Main(string[] args)
{
Console.ReadKey();
}
}
}

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
specilis fggvny, 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.
A fenti pldban a lehet legegyszerbb osztlyt hoztuk ltre. A C++ nyelvet ismerk figyeljenek arra, hogy az
osztly-deklarci vgn nincs ktelez pontosvessz, ugyanakkor a fordt nem szl rte, ha odarjuk!

88

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.

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

Ez a forrskd(rszlet) nem fordul le, mivel a Base osztly csakis paramteres konstruktorral rendelkezik.
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.

89

Az adattagok ha vannak automatikusan a nekik megfelel nullrtkre inicializldnak(pl: int-> 0, bool>false, referencia- s nullable tpusok ->null).
A C++ nyelvet ismerk vigyzzanak, mivel itt csak alaprtelmezett konstruktort kapunk automatikusan,
rtkad opertort illetve msol konstruktort nem! Ugyanakkor minden osztly a System.Objectbl
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;
namespace TestApp
{
class Dog
{
private string name;
private int age;
public Dog(string name, int age)
{
this.name = name;
this.age = age;
}
}
class Program
{
static void Main(string[] args)
{
Dog d = new Dog("Fli", 2);
Console.ReadKey();
}
}
}

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

Ha tbb konstruktor is van, akkor a paramter tpushoz leginkbb illeszked fut le.
A pldban a konstruktor trzsben rtket adtunk a mezknek a this hivatkozssal, amely mindig arra a
pldnyra mutat, amelyen meghvtk (a this kifejezs gy minden olyan helyen hasznlhat, ahol az
osztlypldnyra van szksg). Nem ktelez hasznlni, ugyanakkor hasznos lehet, hogyha 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.

90

Az adattagok private elrsek (ld. elmleti rsz), azaz most csakis az osztlyon bell hasznlhatjuk s
mdosthatjuk ket, pldul a konstruktorban.
Nemcsak 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 a kutya alaprtelmezetten 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:
class Dog
{
private string _name = "Rex";
private int _age = 5;
public Dog(string name, int age)
{
this.name = name;
this.age = age;
}
public Dog(Dog otherDog)
: this(otherDog.name, otherDog.age)
{ }
}

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

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),

91

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

LTHATSGI MDOSTK
Korbban beszltnk az OOP lthatsgairl. A C# az alap hromhoz mg kettt hozztesz:

public: az osztlyon/struktrn kvl s bell teljes mrtkben hozzfrhet.

private: csakis a tartalmaz osztlyon bell


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.

lthat,

leszrmazottak

sem

lthatjk,

Ezek kzl leggyakrabban az els hrmat fogjuk hasznlni.

PARCILIS OSZTLYOK
C# nyelvben ltrehozhatunk n. parcilis (darab, tredk) osztlyokat (partial class), ha egy osztlydeklarciban hasznljuk a partial kulcsszt (ezt minden darabnl meg kell tennnk). Egy parcilis osztly
defincija tbb rszbl (tipikusan tbb forrsfjlbl) 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 egy idben).
Nzznk egy pldt:
// main.cs
using System;
partial class PClass
{
}
class Program
{
static public void Main()

92

{
PClass p = new PClass();
p.Do();
}
}

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


Ksztsnk egy msik forrsfjlt is:
// partial.cs
using System;
partial class PClass
{
public void Do()
{
Console.WriteLine("Hello!");
}
}

A kt fjlt 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.

93

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

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

94

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

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.

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.
Person p = new Person(); // mc egy Person objektumra mutat
p = null; // az objektumra mr nem mutat semmi, felszabadthat

Objektumok alatt ebben a fejezetben csak s kizrlag referenciatpusokat 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:

95

using System;
namespace TestApp
{
class Program
{
static void Main(string[] args)
{
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));
Console.ReadKey();
}
}
}

A GC osztly GetTotalMemory fggvnye a program ltal lefoglalt byte-ok szmt adja vissza, paramterknt
pedig megadhatjuk, hogy meg szeretnnk-e hvni a szemtgyjtt.
A fenti program kimenete valami ilyesmi lesz, az rtkek persze vltozhatnak:
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.
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:

96

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 eltakarthat.
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
alkalmazs szempontjbl). Nyilvn 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
tredkt 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

97

Large Object Heap, ezt 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.
A GC hromfle mdban tud mkdni:

A GC prhuzamosan fut az alkalmazssal


A GC felfggesztheti az alkalmazst
Szerver md

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

98

Most pedig megnzzk, hogy hogyan hasznlhatjuk a GC t a gyakorlatban. A konstruktor(ok) mellett egy msik
specilis metdust is kaphat minden referenciatpus, ez pedig a destruktor. Ugyanakkor a konstruktortl
eltren destruktor nem jn ltre automatikusan, csakis ha magunk definiltuk.
A GC megsemmists eltt az objektumokon meghvja a hozzjuk tartoz destruktort, ms nven Finalizer-t.
Ennek a metdusnak a feladata, hogy felszabadtsa az osztly ltal hasznlt erforrsokat (pl., hogy lezrja a
hlzati kapcsolatokat, bezrjon minden egyes megnyitott fjlt stb.). Vegyk a kvetkez kdot:
using System;
namespace TestApp
{
class DestructableClass
{
public DestructableClass()
{
Console.WriteLine("Konstruktor");
}
~DestructableClass()
{
Console.WriteLine("Destruktor");
}
}
class Program
{
static void Main(string[] args)
{
DestructableClass dc = new DestructableClass();
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 felesleges metdushvs lenne.
Ha lefordtjuk ezt a kdot s elindtjuk a programot, elszr a Konstruktor szt fogjuk ltni, majd egy gomb
lenyomsa utn megjelenik a prja is. Ha ltni is akarjuk, nem rt parancssorbl futtatni, de egy villansnyi
idre Visual Studio-bl futtatva is lthat. A fenti kd valjban a kvetkez formban ltezik:
class DestructableClass
{
public DestructableClass()
{
Console.WriteLine("Konstruktor");
}
protected override void Finalize()
{
try
{
Console.WriteLine("Destruktor");
}
finally
{

99

base.Finalize();
}
}
}

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

class Program
{
static void Main(string[] args)
{
Derived d = new Derived();
Console.ReadKey();
}
}
}

A destruktorokra vonatkozik nhny szably, ezek a kvetkezk:

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, fjl stb...) hasznlunk.
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;

100

namespace TestApp
{
class DisposableClass : IDisposable
{
public void Dispose()
{
// Takartunk
GC.SuppressFinalize(this);
}
}
class Program
{
static void Main(string[] args)
{
DisposableClass dc = new DisposableClass();
Console.ReadKey();
}
}
}

Az interfsz ltal deklarlt Dispose metdusban meg kell hvnunk a GC.SuppressFinalize metdust, hogy
jelezzk a GCnek, 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
nyilvn 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);
}
}

101

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 blokkban, ami azt jelenti, hogy a blokk
hatkrn kvlre rve a Dispose metdus automatikusan meghvdik:
using System;
namespace TestApp
{
class DisposableClass : IDisposable
{
public void Dispose()
{
Console.WriteLine("Takartunk...");
GC.SuppressFinalize(this);
}
}
class Program
{
static void Main(string[] args)
{
using (DisposableClass dc = new DisposableClass())
{
}
Console.ReadKey();
}
}
}

A program kirja a Takartunk szveget, ahogy elhagyja a vezrls a using blokkot. A legtbb I/O mvelettel
(fjl- s hlzatkezels) kapcsolatos osztly megvalstja az IDisposable t, ezrt ezeket ajnlott mindig using
blokkban hasznlni.

102

METDUSOK
Az objektumorientlt 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 nemcsak lgna a semmiben, hanem tenne is
valamit. Ksztsnk nhny metdust a legfontosabb mveletekhez: az evshez s az alvshoz:
using System;
namespace TestApp
{
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 void Main(string[] args)
{
Dog d = new Dog("Fli", 4);
d.Eat();
d.Sleep();
Console.ReadKey();
}
}
}

Nzzk meg, hogyan pl fel egy metdus! Elsknt megadjuk a lthatsgot, s itt is rvnyes a szably, hogy
ennek hinyban az alaprtelmezett privt elrs lesz rvnyben. Ezutn a visszatrsi rtk tpusa ll, jelen
esetben a voiddal jeleztk, hogy nem vrunk ilyesmit. Kvetkezik a metdus neve, ez konvenci szerint
nagybetvel kezddik, vgl a sort a paramterlista zrja.
Egy metdust az objektum neve utn rt pont opertorral hvhatunk meg (ugyanez rvnyes a publikus
adattagokra, tulajdonsgokra stb. is).

103

A hagyomnyos procedurlis programozs (pl. a C vagy Pascal nyelv) a metdusokhoz hasonl, de


filozfijban ms eszkzket hasznl, ezek a fggvny (function) s az eljrs (procedure). Mi a klnbsg? Azt
mondtuk, hogy a metdusok egy osztlyhoz kthetek, annak letciklusban jtszanak szerepet.
Nzznk egy pldt:
using System;
namespace TestApp
{
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 void Main(string[] args)
{
NewString1 ns1 = new NewString1("baba");
NewString2 ns2 = new NewString2();
ns1.PrintUpper();
ns2.PrintUpper("baba");
Console.ReadKey();
}
}
}

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

104

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, de klszably, hogy kt paramternl tbbet csak
kivteles esetben hasznljunk.
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;
namespace TestApp
{
class Test
{
public void Method(string param)
{
Console.WriteLine("A paramter: {0}", param);
}
}
class Program
{
static void Main(string[] args)
{
Test t = new Test();
t.Method("Paramter");
Console.ReadKey();
}
}
}

A C# nyelvben paramtereket tadhatunk rtk s cm szerint is. Elbbi esetben 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 referenciatpusok 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 referenciatpus
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.
using System;
namespace TestApp
{
class Test
{
public int x = 10;
public void TestMethod(Test t)
{
t = new Test();
t.x = 11;
}

105

}
class Program
{
static void Main(string[] args)
{
Test t = new Test();
Console.WriteLine(t.x); // 10;
t.TestMethod(t);
Console.WriteLine(t.x); // 10
Console.ReadKey();
}
}
}

Ha mgis mdostani akarjuk egy referenciatpus 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.
Referenciatpust gyakorlatilag soha nem kell ilyen mdon tadnunk (persze nincs megtiltva, de gondos
tervezssel elkerlhet), kivtelt kpez, ha ezt valamilyen .NETen kvli eszkz megkveteli (a lnyeg, hogy
mr alloklt objektumra mutat referencit optimlis esetben nem lltunk mshov).
A ref rtktpusok esetben mr sokkal hasznosabb, nzzk a kvetkez forrskdot:
using System;
namespace TestApp
{
class Test
{
public void Swap(int x, int y)
{
int tmp = x;
x = y;
y = tmp;
}
}
class Program
{
static void Main(string[] args)
{
int x = 10;
int y = 20;
Test t = new Test();
t.Swap(x, y);
Console.WriteLine("x = {0}, y = {1}", x, y);
Console.ReadKey();
}
}
}

106

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;
namespace TestApp
{
class Test
{
public void Swap(ref int x, ref int y)
{
int tmp = x;
x = y;
y = tmp;
}
}
class Program
{
static void Main(string[] args)
{
int x = 10;
int y = 20;
Test t = new Test();
t.Swap(ref x, ref y);
Console.WriteLine("x = {0}, y = {1}", x, y);
Console.ReadKey();
}
}
}

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


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

107

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 cm szerinti tads msik formjban nem inicializlt paramtert is tadhatunk, de ekkor felttel, hogy a
metduson bell lltsuk be (tadhatunk gy mr inicializlt 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:
using System;
namespace TestApp
{
class Init
{
public void TestInit(out Test t)
{
t = new Test() { s = "Hello!" };
}
}
class Test
{
public string s = null;
}
class Program
{
static void Main(string[] args)
{
Test t = null;
Init i = new Init();
i.TestInit(out t);
Console.WriteLine(t.s); // Hello!
Console.ReadKey();
}
}
}

108

A fenti programokban pontosan tudtuk, hogy hny paramtere van egy metdusnak. Elfordul viszont, hogy
ezt nem tudjuk egyrtelmen megmondani, ekkor n. paramtertmbket kell hasznlnunk. Ha ezt tesszk,
akkor az adott metdus paramter-listjban a paramtertmbnek kell az utols helyen llnia, illetve egy
paramterlistban csak egyszer hasznlhat ez a szerkezet.
using System;
namespace TestApp
{
class Test
{
public void PrintElements(params object[] list)
{
foreach (var item in list)
{
Console.WriteLine(item);
}
}
}

class Program
{
static void Main(string[] args)
{
Test t = new Test();
t.PrintElements("alma", "krte", 4, 1, "di");
t.PrintElements(); // ez is mkdik
Console.ReadKey();
}
}
}

A paramtertmbt a params kulcsszval vezetjk be, ezutn a metdus belsejben pontosan gy viselkedik,
mint egy normlis tmb. Paramtertmbknt tadhatunk megfelel tpus tmbket is.
Alaprtelmezett paramterek
A C# 4.0 bevezeti az alaprtelmezett paramtereket, amelyek lehetv teszik, hogy 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 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; }
}

109

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. 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:
Person p1 = new Person("Istvn", "Reiter");
Person p2 = new Person("Istvn", "Reiter", "brtnr");

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");

VISSZATRSI RTK
Az objektumainkon nemcsak 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;
namespace TestApp
{
class Test
{
public int Add(int x, int y)
{
return x + y;
}
}

110

class Program
{
static void Main(string[] args)
{
Test t = new Test();
int result = t.Add(10, 11);
Console.ReadKey();
}
}
}

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 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, hiszen nem biztos,
hogy x s y is nulltl klnbz rtket kap.
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.).

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:
using System;
namespace TestApp
{
static public class StringHelper

111

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

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

TULAJDONSGOK
A tulajdonsgokat (property) a mezk kzvetlen mdostsra hasznljuk, anlkl, hogy megsrtennk az
egysgbe zrs 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;
namespace TestApp
{
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 void Main(string[] args)
{
Person p = new Person("Istvn");
Console.WriteLine(p.Name);
Console.ReadKey();
}
}
}

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

INDEXELK
Az indexelk hasonlak a tulajdonsgokhoz, azzal a klnbsggel, hogy nem nvvel, hanem egy indexszel
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;
namespace TestApp
{
class Names
{
private ArrayList nameList;
public Names()
{
nameList = new ArrayList();
nameList.Add("Istvn");
nameList.Add("Szandra");
nameList.Add("Bla");
nameList.Add("Balzs");
}
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 void Main(string[] args)
{
Names n = new Names();
for (int i = 0; i < n.Count; ++i)
{
Console.WriteLine(n[i]);
}
Console.ReadKey();
}
}
}

115

Ez gyakorlatilag egy nvtelen tulajdonsg, a this mutat az aktulis objektumra, amin az indexelt definiltuk.
Tbb indexet is megadhatunk, amelyek klnbz tpus indexszel vagy visszatrsi rtkkel rendelkezhetnek.
Nem csak egy paramterrel hivatkozhatunk egy indexelvel, pldul ha a bels adatszerkezet egy
ktdimenzis tmb, akkor ennek megfelel mennyisg paramterrel dolgozhatunk:
public int this[int idxx, int idxy]
{
get
{
/*...*/
}
}

116

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.

STATIKUS ADATTAG
Statikus tagot a static kulcssz segtsgvel hozhatunk ltre:
using System;
namespace TestApp
{
class Animal
{
static public int AnimalCounter = 0;
public Animal()
{
++Animal.AnimalCounter;
}
~Animal()
{
--Animal.AnimalCounter;
}
}
class Program
{
static void Main(string[] args)
{
Animal a = new Animal();
Console.WriteLine(Animal.AnimalCounter);
Console.ReadKey();
}
}
}

A pldban a statikus adattag rtkt minden alkalommal megnveljk eggyel, amikor meghvjuk a
konstruktort, s cskkentjk, 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 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
tetszleges.

117

STATIKUS KONSTRUKTOR
A statikus konstruktor a statikus tagok belltsrt felel. 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;
namespace TestApp
{
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 void Main(string[] args)
{
Console.WriteLine("Start...");
Test t = new Test();
Console.ReadKey();
}
}
}

Ha elindtjuk a programot, a kvetkez kimenetet kapjuk:


Start...
Var = 10
Statikus konstruktor
Konstruktor
A statikus konstruktoroknak van azonban egy hatalmas hibjuk, amelyet a kvetkez pldban lthatunk:
static class A1
{
static public int x = 10;
}
static class A2

118

{
static public int x;
static A2()
{
x = 10;
}
}

A kt osztly ltszlag ugyanazt teszi, mgis risi teljestmnyklnbsg van kztk:


class Program
{
static void Main(string[] args)
{
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);
Console.ReadKey();
}
}

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, ez
pedig a fenti teljestmnyvesztesget eredmnyezi.

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

119

A leghresebb statikus metdus a Main.

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).
class Math
{
static public double PI
{
get { return 3.14; }
}
}

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.
static class MathHelper
{
static public double PI
{
get { return 3.14; }
}
static public double Cos(double x)
{
return Math.Cos(x);
}
}

120

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

KONSTRUKTOR
Minden struktra alaprtelmezetten rendelkezik egy konstruktorszersggel (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;
namespace TestApp
{
struct Test
{
public int x;
}
class Program
{
static void Main(string[] args)
{
Test t = new Test();
Console.WriteLine(t.x); // x == 0
Console.ReadKey();
}
}
}

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;
namespace TestApp
{
struct Test
{
public int x;
}
class Program
{
static void Main(string[] args)
{
Test t;
Console.WriteLine(t.x); // nem j, x inicializlatlan

121

Console.ReadKey();
}
}
}

Kszthetnk sajt konstruktort, de ekkor minden mez rtkadsrl gondoskodnunk kell. Egy struktra
mezit nem inicializlhatjuk:
struct Test
{
int x = 10; // ez nem j
int y;
public Test(int x, int y)
{
this.y = y;// ez sem j, x nem kap rtket
}
}

Struktrban csakis paramteres konstruktort definilhatunk, paramter nlkli alaprtelmezettet nem.


Viszont ha ezt megtettk, attl az alaprtelmezett konstruktor mg hasznlhat marad:
using System;
namespace TestApp
{
struct Test
{
int x;
int y;
public Test(int x, int y)
{
this.y = y;
this.x = x;
}
}

class Program
{
static void Main(string[] args)
{
Test t1 = new Test(10, 11);
Test t2 = new Test(); //ez is mkdik
Console.ReadKey();
}
}
}

DESTRUKTOR
Struktrk nem rendelkezhetnek destruktorral. Egy struktra kt helyen lehet a memriban: a stack-ben s a
heap-ben (ha egy referenciatpus 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

122

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.

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).

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

123

Ha lefordtjuk ezt a kdot, azt fogjuk ltni, hogy t1.x rtke nem vltozott, teht nem referencit adtunk t t2
nek.
Most nzznk meg egy nagyon gyakori hibt, amibe belefuthatunk! Adott a kvetkez programkd:
using System;
namespace TestApp
{
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)
{
this.x = x;
this.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 void Main(string[] args)
{
Line l = new Line();
l.A = new Point(10, 10);
l.B = new Point(20, 20);
Console.ReadKey();
}
}
}

124

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:
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:
Line l = new Line();
l.A = new Point(10, 10);
l.B = new Point(20, 20);
l.A = new Point(5, 10);

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:
struct Test
{
public int x;
public override string ToString()
{
return "X == " + x.ToString();
}
}

125

OSZTLYKNYVTRAK
Eddig mindig egyetlen projektben dolgoztunk, ennek viszont megvan az a htrnya, hogy a program
nvekedsvel egytt a forrskd is tbb helyet foglal. Hozz kell tennnk azt is, hogy ez a fajta megkzelts a
kd-jrafelhasznlst is rontja, hiszen az ltalnosabb programrszeket minden j projektbe ismt be kellene
msolni. Sokkal egyszerbb lenne a dolgunk, ha az jrahasznostand forrskdot kln tudnnk vlasztani a
tnyleges programtl, ezzel idt s helyet takartva meg. Erre a clra talltk ki a shared library (~megosztott
knyvtr) fogalmt, amely a Windows alap rendszereken a DLL -t (Dynamic Link Library) jelenti.
A DLL knyvtrak felptse a hasznlt fejleszti platformtl fgg, teht egy COM DLL nem hasznlhat
kzvetlenl egy .NET programban. .Net krnyezetben egy ilyen knyvtrat assemblynek neveznk.
Ksztsk el az els osztlyknyvtrunkat! Feltesszk, hogy Visaul Studio-ban mr elksztettnk egy Console
Application tpus projektet. Kattintsunk jobb gombbal a Solution-n, s az Add menbl vlasszuk a New
Project elemet:

A megjelen ablakban a Class Library sablont kell hasznlnunk:

126

Az OK gombra kattintva elkszl az j projekt. Egyelre csak egy osztlyt tartalmaz, viszont Main fggvnyt
nem, gy ezt a projektet nem lehet futtatni, csakis egy msik projektbl hvhatjuk meg a szolgltatsait.
Ksztsnk egy egyszer osztlyt, akr tnevezve a mr ltezt, akr jat ltrehozva!
using System;
namespace MyLibrary
{
public class ClassLib
{
static public void PrintHello()
{
Console.WriteLine("n egy osztlyknyvtrban vagyok!");
}
}
}

A kvetkez lpsben tudatnunk kell a f projekttel, hogy van egy osztlyknyvtrunk. A projekten bell ott
van egy References nev knyvtr, amely a felhasznlt knyvtrakat trolja. Kattintsunk rajta jobb gombbal, s
vlasszuk az Add Reference pontot:

127

Az Add Reference ablakban, a Projects fln megjelennek a Solution-ben szerepl projektek, vlasszuk az elbb
elksztett knyvtrat:

Az OK gombra kattintva a projectben felhasznlhatjuk az osztlyknyvtr szolgltatsait. A hivatkozott knyvtr


a fordts utn a clknyvtr futtathat llomnya mell kerl .dll kiterjesztssel, ha tovbb akarjuk adni
programunkat, azt is mell kell msolnunk.
A forrskdban a nvtr megadsa utn azonnal kapunk Intellisense tmogatst is. Ksztsk egy programot,
amely felhasznlja az osztlyknyvtrunkat:

128

using System;
using MyLibrary; // az osztlyknyvtr nvtere
namespace TestApp
{
class Program
{
static public void Main(string[] args)
{
ClassLib.PrintHello();
Console.ReadKey();
}
}
}

129

GYAKORL FELADATOK III.


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;
namespace TestApp
{
class Program
{
static public double Pow(double x, int y)
{
if (y == 0) { return 1.0; }
else return x * Pow(x, y - 1);
}
static void Main(string[] args)
{
double result = Pow(2, 10);
Console.WriteLine(result); // 1024
Console.ReadKey();
}
}
}

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;
namespace TestApp
{
class Program
{
static public int Fact(int x)
{
if (x == 0) { return 1; }
else return x * Fact(x - 1);
}
static void Main(string[] args)
{
int result = Fact(10);

130

Console.WriteLine(result);
Console.ReadKey();
}
}
}

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:

131

private
{
int
int
int

void QuickSort(int left, int right)


pivot = array[left];
lhold = left;
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) kisebbet 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
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.

132

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 kisebb 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 tovbblpnk. 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 kisebb
most, mint a pivot s right pedig nagyobb nla, gy mindkt elgazs felttele teljesl, vagyis most mindkt
oldalra hvjuk a metdust.
Az ezutni esemnyek tgondolsa pedig az olvas feladata.

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 Node Previous { get; set; }
}

Most pedig jjjn a lncolt lista osztly:


class LinkedList
{
public LinkedList() { }
public LinkedList(int[] values)
{
foreach (int value in values)
{
this.Add(value);
}

133

}
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 Node Root { 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 utnafzni 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.

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:
A

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, jobb oldali rszfjban pedig a szlelemnl nagyobb elemek vannak,
ezltal egyrtelmen meghatrozhat, hogy egy elem benne van-e a fban vagy nincs (rtelemszeren a
rszfk is keresfk, vagyis rjuk is ugyanez vonatkozik). A binris keresfa minden elemnek egyedi kulccsal

134

kell rendelkeznie, vagyis ugyanaz az elem ktszer nem szerepelhet a fban. A keress mvelet O(logn)
nagysgrend.
Ahogyan az elz feladatban, most is kezdjk a fa cscsait jelkpez osztllyal:
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.
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);

135

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:
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,

136

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

137

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.Objectet), ugyanakkor megengedi tbb interfsz implementlst (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 rni, 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:
Dog d = new Dog();
Crocodile c = new Crocodile();

Ha ezt az j Animal osztllyal prbljuk 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:

138

class Dog : Animal


{
public Dog(string name)
: base(name)
{
}
}
class Crocodile : Animal
{
public Crocodile(string name)
: base(name)
{
}
}

Ezutn gy pldnyostunk:
Dog d = new Dog("Fli");
Crocodile c = new Crocodile("Aladr");

Ugyangy hasznlhatjuk az sosztly metdust is:


Dog d = new Dog("Fli");
Crocodile c = new Crocodile("Aladr");
d.Eat();
c.Eat();

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.

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;
namespace TestApp
{
class Animal
{
public virtual void Eat()
{
Console.WriteLine("Egy llat eszik...");
}
}
class Dog : Animal

139

{
public override void Eat()
{
Console.WriteLine("Kutya csontot rg...");
}
}
class Crocodile : Animal
{
public override void Eat()
{
Console.WriteLine("Krokodil embert rg...");
}
}
class Program
{
static void Main(string[] args)
{
Animal a = new Animal();
Dog d = new Dog();
Crocodile c = new Crocodile();
a.Eat();
d.Eat();
c.Eat();
Console.ReadKey();
}
}
}

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 override-dal jellt metdus automatikusan virtulis is lesz, gy az leszrmazottai is
tdefinilhatjk a mkdst:
class Crocodile : Animal
{
public override void Eat()
{
Console.WriteLine("Krokodil embert rg...");
}
}
class BigEvilCrocodile : Crocodile
{
public override void Eat()
{
Console.WriteLine("Krokodil cenjrt rg...");
}
}

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 figyelmeztet minket, hogy
eltakarjuk az rkltt metdust. s valban, ha meghvnnk, akkor az j metdus futna le. Ezt a jelensget
rnykolsnak (shadow) nevezik.

140

Termszetesen mi azt szeretnnk, ha 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("Egy llat eszik...");
}
}
class Dog : Animal
{
public new void Eat()
{
Console.WriteLine("Kutya csontot rg...");
}
}

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.
class Dog : Animal
{
public new virtual void Eat()
{
Console.WriteLine("Kutya csontot rg...");
}
}

Nem jellhetnk virtulisnak statikus, absztrakt s overridedal jellt tagokat (az utols kett egybknt
virtulis is lesz, de ezt nem kell kln jellni).

POLIMORFIZMUS
Korbban mr beszltnk arrl, hogy az s s leszrmazottak kzt az-egy (is-a) relci ll fenn. 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 (nyilvn) behelyettesthetek egy
specilisabb fajt). Pldul gond nlkl rhatom a kvetkezt:
Animal d = new Dog("Fli");

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:
Animal[] animalArray = new Animal[2];

141

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 virtulis metdus, radsul van leszrmazottbeli implementcija
is, amely tdefinilja az eredeti viselkedst, s ezt explicit jelltk is az override kulcsszval. gy a fordt fel
tudja ismerni a futsidej tpust, s ezltal 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 implementci egy virtulis metdus lesz, azaz a keress legksbb az
els virtulis vltozatnl megll.

LEZRT OSZTLYOK S METDUSOK


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

Ebben az esetben az IntelliSense eleve fel sem ajnlja az osztlyt.


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
{
}
}

ABSZTRAKT OSZTLYOK
Egy absztrakt osztlyt nem lehet pldnyostani. A ltrehozsnak clja az, hogy kzs felletet biztostsunk a
leszrmazottainak:

142

using System;
namespace TestApp
{
abstract class Animal
{
abstract public void Eat();
}
class Dog : Animal
{
public override void Eat()
{
Console.WriteLine("Kutya eszik...");
}
}
class Program
{
static void Main(string[] args)
{
// Animal a = new Animal(); //ez nem fordul le
Dog d = new Dog();
d.Eat();
Console.ReadKey();
}
}
}

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, amelyek 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();
}

143

class Dog : Animal


{
public Dog(string name) : base(name) { }
public override void Eat()
{
Console.WriteLine("Kutya eszik...");
}
}

Vajon hogyan mkdik a kvetkez pldban a polimorfizmus elve? :


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

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");

144

INTERFSZEK
Az interfszek hasonlak az absztrakt osztlyokhoz, abban az rtelemben, hogy meghatrozzk egy osztly
viselkedst, fellett. A nagy klnbsg a kett kzt az, hogy mg elbbi eleve meghatroz egy
osztlyhierarchit, egy interfsz nem kthet kzvetlenl egy osztlyhoz, mindssze elr egy mintt, amely
megvalstsra vr. Egy msik elnye az interfszek hasznlatnak, hogy mg egy osztlynak csak egy se lehet,
addig brmennyi interfszt megvalsthat. Ezenfell interfszt hasznlhatunk struktrk esetben is. A
kvetkez pldban trjuk az Animal sosztlyt interfszre:
using System;
namespace TestApp
{
interface IAnimal
{
void Eat();
}
class Dog : IAnimal
{
public void Eat()
{
Console.WriteLine("Kutya eszik...");
}
}
class Program
{
static void Main(string[] args)
{
Dog d = new Dog();
d.Eat();
Console.ReadKey();
}
}
}

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

145

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;
namespace TestApp
{
interface IAnimal
{
void Eat();
}
interface IDog : IAnimal
{
void Vau();
}
class Dog : IDog
{
public void Eat()
{
Console.WriteLine("Kutya eszik...");
}
public void Vau()
{
Console.WriteLine("Vau - Vau");
}
}
class Program
{
static void Main(string[] args)
{
Dog d = new Dog();
d.Eat();
d.Vau();
Console.ReadKey();
}
}
}

Ekkor termszetesen az sszes interfszt meg kell valstanunk.


Egy adott interfszt megvalst objektumot implicit mdon tkonvertlhatunk az interfsz tpusra:
Dog d = new Dog();
IAnimal ia = d;
IDog id = d;
ia.Eat();
id.Vau();

146

Az is s as opertorokkal pedig azt is megtudhatjuk, hogy egy adott osztly megvalste egy interfszt:
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");
}

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

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. Persze nem ez az elvrt viselkeds, ezrt ilyen
esetekben explicit mdon meg kell mondanunk, hogy melyik metdus/tulajdonsg/stb. melyik interfszhez
tartozik. rjuk t a fenti kdot:

147

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:
Test t = new Test();
((IOne)t).Method(); // ez mkdik
ITwo it = t;
it.Method(); // ez is mkdik

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("Kutya eszik...");
}
public virtual void Vau()
{
Console.WriteLine("Vau - Vau");
}
}
class BigDog : Dog
{
public override void Vau()
{
Console.WriteLine("VAU-VAU");
}
}

Egy leszrmazott jraimplementlhatja az adott interfszt, amennyiben nemcsak az snl, de az utdnl is


jelljk a megvalstst:
class BigDog : Dog, IAnimal
{
public new void Eat()
{

148

Console.WriteLine("Kutya nagyon eszik...");


}
public override void Vau()
{
Console.WriteLine("VAU-VAU");
}
}

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

149

OPERTOR KITERJESZTS
Nyilvn 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.
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 + opertor mkdst fogalmaztuk t. A paramterek (operandusok) nevei konvenci szerint lhs (left-handside) s rhs (right-hand-side), utalva a jobb s bal oldali operandusra.
Teht most mr nyugodtan rhatom a kvetkezt:
MyInt x = new MyInt(10);
MyInt y = new MyInt(20);
MyInt result = x + y;
Console.WriteLine(result.Value); // 30

150

Mivel definiltunk az osztlyunkon egy sajt opertort, gy a fordt tudni fogja, hogy azt hasznlja, s talaktja
a mveletet az albb lthat mdon. Ezt azonban csakis a fordt teheti meg, neknk a rendes formt kell
hasznlnunk.
MyInt result = MyInt.operator+(x, y);

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 a System.Object tpustl rklt virtulis Equals metdus is, ami a CLS
kompatibilitst hivatott 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:
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.

151

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;
}

A ++/-- OPERTOROK
Ez a kt opertor elg nagy fejfjst tud okozni, nzzk meg a kvetkez kdot:
using System;
namespace TestApp
{
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 void Main(string[] args)
{
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 (!)

Console.ReadKey();
}
}
}

Nzzk az utols sort! Mivel y a postfixes formban kapott rtket, ezrt 11et kellene tartalmaznia, ehelyett
x++ esetben pontosan ugyanaz trtnik, mint ha ++x-et rtunk volna.
A problma, hogy kifejezetten postfixes opertort nem tudunk definilni, az ltalunk ksztett kd minden
esetben a prefix opertort jelenti. Viszont meghvhatjuk az ltalunk definilt opertorokat postfixes alakban,
ekkor az adott objektumot eltrolja a rendszer, meghvja az rtket nvel kdrszletet, s visszadja az els
lpsben flrerakott rtket. Ugye ez rtktpusok esetben tkletesen mkdik, de referenciatpusoknl az
els lpsben nem az rtket, hanem a referencit troljuk, vagyis a ksbbi vltozs itt is rvnyben lesz.

152

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).

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 megismerkednk.

KONVERZIS OPERTOROK
A C# a szkebbrl tgabbra konverzikat implicit mdon (azaz klnsebb jells nlkl), mg a tgabbrl
szkebbre konvertlst explicite (ezt jellnnk kell) vgzi. Termszetesen szeretnnk, hogy a sajt tpusunk is
kpes legyen ilyesmire, 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));
}

Ezeket most gy hasznlhatjuk:


MyInt x = 10; // implicit konverzi
MyInt y = (MyInt)"20"; //explicit konverzi

Fontos, hogy a konverzis opertorok mindig statikusak.

153

KIVTELKEZELS
Vannak esetek, amikor az alkalmazsunk, gond nlkl lefordul, mgsem gy 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;
namespace TestApp
{
class Program
{
static void Main(string[] args)
{
int[] array = new int[2];
array[2] = 10;
}
}
}

Itt az utols rvnyes index az 1 lenne, gy kivtelt kapunk, mgpedig egy System.IndexOutOfRangeExceptio-t.
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 kezelni a hibt:
using System;
namespace TestApp
{
class Program
{
static void Main(string[] args)
{
int[] array = new int[2];
try
{
array[2] = 10;
}
catch (System.IndexOutOfRangeException e)
{
Console.WriteLine(e.Message);
}
Console.ReadKey();
}
}
}

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:
Index was outside the bounds of the array
Lthat, hogy a kivtel egy objektum formjban ltezik. Minden kivtel se a System.Exception osztly, gy ha
nem specilisan egy kivtelt akarunk elkapni, akkor rhattuk volna ezt is:

154

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

Ekkor minden kivtelt el fog kapni a catch blokk. A System.Exception tulajdonsgai kzl kettt kell
megemltennk:

Message: ez egy olvashatbb formja a kivtel oknak.


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

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

A catchnek nem ktelez megadni a kivtel tpust, ekkor minden kivtelt elkap:

155

try
{
throw new System.Exception();
}
catch
{
Console.WriteLine("Kivtel. Hurr!");
}

Ilyenkor viszont nem hasznlhatjuk a kivtelobjektumot.

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.
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.

KIVTEL KSZTSE
Mi magunk is kszthetnk kivtelt, a System.Exception-bl szrmaztatva:
using System;
namespace TestApp
{
class MyException : System.Exception
{
public MyException() { }
public MyException(string message)
: base(message)
{
}
public MyException(string message, Exception inner)
: base(message, inner)
{
}

156

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

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
}

Ilyenkor bellthatjuk az Exception InnerException tulajdonsgt is:


try
{
throw new Exception();
}
catch (System.ArgumentException e)
{
throw (new System.ArgumentNullException("Tovbb", e));
}

Itt az Exception osztly harmadik konstruktort hasznltuk, az j kivtel mr tartalmazni fogja a rgit is.

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 fjl, hlzati
kapcsolat stb.), illetve objektumok olyan formban maradnak meg a memriban, amely hibt okozhat.
Megoldst a finallyblokk hasznlata jelent, amely fggetlenl attl, hogy trtnt-e kivtel, mindig lefut:

157

using System;
namespace TestApp
{
class Program
{
static void Main(string[] args)
{
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);

Console.ReadKey();
}
}
}

Valdi erforrsok kezelsekor knyelmesebb a usingblokk hasznlata (ld. IDisposable fejezet), mivel az
automatikusan lezrja azokat. Lnyegben a using-blokkal hasznlt erforrsok fordts utn a megfelel trycatch-finally blokkokk alakulnak.

158

GYAKORL FELADATOK IV.


IENUMERATOR S IENUMERABLE
Ksztsnk osztlyt, amely megvalstja az IEnumerator s IEnumerable interfszeket!
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 foreach-nek fogja szolgltatni a megfelel felletet, ugyanis a ciklus meghvja a metdust, s annak vissza
kell adnia az osztlyt IEnumerator-knt (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 pozciban 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; }
}

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;

159

public AnimalContainer()
{
container.Add(new Animal("Fli"));
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:


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).

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 tpusnak is van rendez metdusa (Sort), amely ezzel a
metdussal dolgozik. Az IComparable egyetlen metdussal, a CompareTo-val rendelkezik, amely egy object
tpust kap paramterl:

160

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

A List<T> tpus hasznlathoz szksg van a System.Collections.Generic nvtrre, bvebben a Generikusok


cm fejezetben olvashatunk rla.
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 magt a List<T> -t.
Most is csak egy metdust kell elksztennk, ez a Compare, amely kt object tpust vr paramtereknt
(illetve felhasznltuk az elzleg elksztett ComparableClass osztlyt):
class ComparableClassComparer : IComparer
{

161

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 anlkl is megrhatjuk, hogy ismernnk a
bels szerkezett), gy tbbfle megvalsts is lehetsges.

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
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);

162

for (int i = 0; i < lhs.N; ++i)


{
for (int j = 0; j < lhs.M; ++j)
{
result[i, j] = lhs[i, j] + rhs[i, j];
}
}
return result;
}
}

Mtrixokat gy adunk ssze, hogy az azonos indexeken lv rtkeket sszeadjuk:


123 145 1+1 2+4 3+5
456 + 532 = (stb.)
789 211
Az sszeads mveletet csakis azonos nagysg dimenzik mellett 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

DELEGATE
A delegate olyan tpus, amely egy vagy tbb metdusra hivatkozik. 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);

Delegate nem deklarlhat blokkon bell, csakis osztlyon bell tagknt, illetve osztlyokon kvl (hasonlan az
enum tpusokhoz). Ez a delegate olyan metdusra mutathat, amelynek visszatrsi rtke int tpus s
egyetlen int paramtere van, pl.:
static public int Pow(int x)
{
return (x * x);
}

A hasznlata:
TestDelegate dlgt = Pow;
int result = dlgt(10);

A delegate-ekhez egynl tbb metdust is hozzadhatunk a += s + opertorokkal, valamint elvehetjk ket a = s opertorokkal. A delegate hvsakor a listjn lv sszes 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);
}
}

A delegateek legnagyobb haszna, hogy nem kell elre megadott metdusokat hasznlnunk, ehelyett ksbb
tetszs szerint adhatjuk meg az elvgzend mveletet:

164

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 void Main(string[] args)
{
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]);
}
Console.ReadKey();
}
}

Kt delegate szerkezetileg nem egyenl, mg akkor sem ha a szignatrjuk megegyezik:

165

using System;
namespace TestApp
{
class Program
{
public delegate void Dlgt1();
public delegate void Dlgt2();
static public void Method() { }
static void Main(string[] args)
{
Dlgt1 d1 = Program.Method;
Dlgt2 d2 = d1; // ez hibs
}
}
}

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;
namespace TestApp
{
class Test
{
public void Method() { }
}
class Program
{
public delegate void TestDelegate();
static public void Method1() { }
static public void Method2() { }
static void Main(string[] args)
{
TestDelegate t1 = null;
TestDelegate 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;
Test x = new Test();
Test y = new Test();
t1 += x.Method;
t2 += y.Method;

166

Console.WriteLine(t1 == t2); // False


Console.ReadKey();
}
}
}

PARAMTER S VISSZATRSI RTK


Egy delegatenek tadott metdus paramterei lehetnek olyan tpusok, amelyek az eredeti paramternl
ltalnosabbak:
using System;
namespace TestApp
{
class Animal { }
class Dog : Animal { }
class Cat : Animal { }
class Program
{
public delegate void DogDelegate(Dog d);
static public void AnimalMethod(Animal a) { }
static void Main(string[] args)
{
DogDelegate d = AnimalMethod;
Console.ReadKey();
}
}
}

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;
namespace TestApp
{
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(); }

167

static void Main(string[] args)


{
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());
Console.ReadKey();
}
}
}

Ezt pedig kovarins (covariant) viselkedsnek nevezzk.

NVTELEN METDUSOK
Egy delegate szmra 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;
namespace TestApp
{
class Program
{
public delegate void Test(int x);
static void Main(string[] args)
{
Test t = delegate(int x)
{
Console.WriteLine(x);
};
t(10);
Console.ReadKey();
}
}
}

Egy nvtelen metdus elri az t trol blokk loklis vltozit, s mdosthatja is ket:
using System;
namespace TestApp
{
class Program
{
public delegate void Test();

168

static void Main(string[] args)


{
int x = 10;
Test t = delegate()
{
x = 11;
Console.WriteLine(x);
};
t();
Console.ReadKey();
}
}
}

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.).

169

ESEMNYEK
Egy osztly esemnyeket (event) hasznlhat, hogy a sajt llapota megvltozsakor rtestsen ms osztlyokat.
Ehhez a megfigyel osztlyoknak fel kell iratkozni a megfigyelt osztly esemnyre azltal, hogy az elbbiek
rendelkeznek egy, az esemnynek megfelel szignatrj metdussal, n. esemnykezelvel. Az esemny
megtrtntekor ezek a metdusok fognak lefutni. Eddig ez nagyon gy hangzik, mintha a delegateekrl
beszltnk volna, s valban egy esemny tulajdonkppen egy specilis delegate, mindssze hrom dologban
klnbznek:

Esemny lehet rsze interfsznek, mg delegate nem.


Egy esemnyt csakis az az osztly hvhat meg, amely deklarlta.
Egy esemny rendelkezik add s remove metdusokkal, amelyek fellbrlhatak.

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:
class Program
{
static public void Handler(string message)
{
Console.WriteLine(message);
}
static void Main(string[] args)
{
Test t = new Test();

170

t.TestStatusChange += Program.Handler;
t.Data = 11;
Console.ReadKey();
}
}

A fenti kdban a TestStatusChange gyakorlatilag delegate-knt 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 rendelkezik 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);
public event EventHandlerDelegate TestStatusChange;
private void OnStatusChange()
{
if (TestStatusChange != null)
{
TestStatusChange(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:
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:

171

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));
}
}
}

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);
}
}

172

Vgl a Main:
static void Main(string[] args)
{
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);
Console.ReadKey();
}

173

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.

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, interfsznek 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):
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 hatkonyak, cserben sokkal biztonsgosabb
a hasznlatuk. Kt fontos klnbsg van a kett 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:

174

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.

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..."));
}
}

Ezt most a kvetkezkppen hasznlhatjuk:


static void Main(string[] args)
{
Stack s = new Stack(10);
for (int i = 0; i < 10; ++i)
{
s.Push(i);
}
for (int i = 0; i < 10; ++i)
{

175

Console.WriteLine((int)s.Pop());
}
Console.ReadKey();
}

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:


static void Main(string[] args)
{
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());
}

176

Console.ReadKey();
}

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.


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
{
}

177

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:
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.

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>
{
}

178

STATIKUS TAGOK
Generikus tpusok esetben minden tpushoz kln statikus tag tartozik:
using System;
namespace TestApp
{
class Test<T>
{
static public int Value;
}
class Program
{
static void Main(string[] args)
{
Test<int>.Value = 10;
Test<string>.Value = 20;
Console.WriteLine(Test<int>.Value); // 10
Console.WriteLine(Test<string>.Value); // 20
Console.ReadKey();
}
}
}

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.
List<T>
A List<T> az ArrayList generikus, ersen tpusos megfelelje. A legtbb esetben a List<T> hatkonyabb lesz az
ArrayListnl, emellett pedig tpusbiztos is.
Amennyiben rtktpussal hasznljuk a List<T>-t, az alaprtelmezetten nem ignyel bedobozolst, de
rendelkezik nhny olyan mvelettel, amely viszont igen, ezek fleg a keresssel kapcsolatosak. Azrt, hogy az
ebbl kvetkez teljestmnyromlst elkerljk, a hasznlt rtktpusnak meg kell valstania az IComparable
s az IEquatable interfszeket (a legtbb beptett egyszer (rtk)tpus ezt meg is teszi) (ezeket az
interfszeket az sszes tbbi gyjtemny is ignyli).
using System;
using System.Collections.Generic;
namespace TestApp
{
class Program
{
static void Main(string[] args)
{
List<int> list = new List<int>();
for (int i = 0; i < 10; ++i)
{
list.Add(i);

179

}
foreach (int item in list)
{
Console.WriteLine(item);
}
Console.ReadKey();
}
}
}

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;
namespace TestApp
{
class Program
{
static void Main(string[] args)
{
List<int> list = new List<int>();
Random r = new Random();
for (int i = 0; i < 10; ++i)
{
list.Add(r.Next(100));
Console.Write("{0}, ", list[i]); // rendezetlen elemek
}
Console.WriteLine();
list.Sort();
foreach (int item in list)
{
Console.Write("{0}, ", item); // rendezett elemek
}
Console.ReadKey();
}
}
}

Kereshetnk is az elemek kztt a BinarySearch metdussal, amely a keresett objektum indext adja vissza:
using System;
using System.Collections.Generic;
namespace TestApp
{
class Program
{

180

static void Main(string[] args)


{
List<string> list = new List<string>()
{
"alma", "di", "krte", "barack"
};
Console.WriteLine(list[list.BinarySearch("krte")]);
Console.ReadKey();
}
}
}

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;
namespace TestApp
{
class Program
{
static void Main(string[] args)
{
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);
});
Console.ReadKey();
}
}
}

A felttelek megadsnl s a pros szmok listjnak kiratsnl nvtelen metdusokat hasznltunk.


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.

181

Paramtereknt egy void Method(T item) szignatrj metdust (vagy nvtelen metdust) vr, ahol T a lista
elemeinek tpusa.
SortedList<T, U> s SortedDictionary<T, U>
A SortedList<T, U> kulcsrtk prokat trol el, s a kulcs alapjn rendezi is ket:
using System;
using System.Collections.Generic;
namespace TestApp
{
class Program
{
static void Main(string[] args)
{
SortedList<string, int> list = new SortedList<string, int>();
list.Add("egy", 1);
list.Add("kett", 2);
list.Add("hrom", 3);
Console.ReadKey();
}
}
}

A lista elemei tulajdonkppen nem a megadott rtkek, hanem a kulcsrtk 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 nincs gy, akkor mi magunk is definilhatunk ilyet, rszletekrt ld. az Interfszek
fejezetet.
A listban minden kulcsnak egyedinek kell lennie (ellenkez esetben kivtelt kapunk), illetve kulcs helyn nem
llhat null rtk (ugyanez viszont nem igaz az rtkekre).
A SortedDictionary<T, U> hasznlata gyakorlatilag megegyezik a SortedList<T, U>-val, a klnbsg a
teljestmnyben s a bels szerkezetben van.
A SD j (rendezetlen) elemek beszrst gyorsabban vgzi, mint a SL (O(Log n) s O(n)). Elre rendezett
elemek beszrsnl pont fordtott a helyzet. Az elemek kzti keress mindkt szerkezetben O(Log n).
Ezenkvl a SL kevesebb memrit hasznl fel.
Dictionary<T, U>
A SortedDictionary<T, U> rendezetlen prja a Dictionary<T, U>:
using System;
using System.Collections.Generic;
namespace TestApp
{
class Program
{
static void Main(string[] args)

182

{
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);
}
Console.ReadKey();
}
}
}

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!
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 LinkedListLNode<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;
namespace TestApp
{
class Program
{
static void Main(string[] args)
{
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)
{
Console.WriteLine(current.Value);
current = current.Next;
}
Console.ReadKey();
}
}
}

Ebben a pldban bejrunk egy lncolt listt, a kimeneten a narancs elemet ltjuk majd els helyen, mivel t
az AddFirst metdussal helyeztk be.

183

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!!
namespace TestApp
{
class Program
{
static void Main(string[] args)
{
List<string> list = new List<string>()
{
"alma", "krte", "di"
};
ReadOnlyCollection<string> roc = new ReadOnlyCollection<string>(list);
foreach (string item in roc)
{
Console.WriteLine(item);
}
Console.ReadKey();
}
}
}

GENERIKUS INTERFSZEK, DELEGATEEK 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.
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.

KOVARIANCIA S KONTRAVARIANCIA
Nzzk a kvetkez osztlyhierarchit:
class Person
{
public int Age;

184

}
class Student : Person
{
public string Name { get; set; }
}

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 nyilvn 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):
class Program
{
delegate void Method<T>();
static void Main(string[] args)
{
Method<Student> m1 = () => new Student();
Method<Person> m2 = m1;
Console.ReadKey();
}
}

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 kezeljnk. A .NET
beptett generikus tpusai mr fel vannak ksztve a fenti helyzetre, szval rhatjuk a kvetkezt:
IEnumerable<Student> sie = new List<Student>()
{
new Student() { Name = "Szandra", Age = 22 },
new Student() { Name = "Istvn", Age = 26 },
};
IEnumerable<Person> pie = sie;
foreach (Person person in pie)
{

185

Console.WriteLine(person.Age);
}

Most lssuk a kontravariancit! Itt mr lnyegesen termszetesebb dologrl van sz, hiszen hasonlt
lthattunk, amikor rkldssel foglalkoztunk.
class Program
{
delegate void Method<in T>(T t);
static void Main(string[] args)
{
Method<Person> m1 = (person) => Console.WriteLine(person.Age);
Method<Student> m2 = m1;
Student s = new Student() { Age = 22, Name = "Szandra" };
m1(s); // 22
m2(s); // 22
Console.ReadKey();
}
}

186

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 void Main(string[] args)
{
IntFunc func = (x) => (x * x);
Console.WriteLine(func(10)); // 100
Console.ReadKey();
}
}

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 visszadott rtkrl
gondoskod kifejezs (x * x) ll. A bemen paramternl nem kell (de lehet) explicit mdon jeleznnk a tpust,
azt a fordt magtl kitallja (a legtbb esetre ez igaz, de nha szksg lesz r, hogy jelljk a tpust).
Termszetesen nemcsak 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 void Main(string[] args)
{
IntFunc2 func = (x, y) => (x * y);
Console.WriteLine(func(10, 2)); // 20
Console.ReadKey();
}
}

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 generikus kifejezsek kaphatnak nvtelen metdusokat is rtkl). Ktfle generikus kifejezs

187

ltezik: a Func, amely adhat visszatrsi rtket s az Action, amely nem (void) (lsd: fggvny s eljrs).
Elsknt a Func-ot vizsgljuk meg:
using System;
class Program
{
static void Main(string[] args)
{
Func<int, int> func = (x) => (x * x);
Console.WriteLine(func(10)); // 100
Console.ReadKey();
}
}

A generikus paramterek kztt (balrl jobbra) utols helyen mindig a visszatrsi rtk ll, eltte pedig a
bemen paramterek (maximum ngy) kapnak helyet.
using System;
class Program
{
static void Main(string[] args)
{
Func<int, int, bool> func = (x, y) => (x > y);
Console.WriteLine(func(10, 5)); // True
Console.ReadKey();
}
}

Most megnztk, hogy az els paramter nagyobb-e a msodiknl. rtelemszeren a lambda opertor jobb
oldaln lv kifejezsnek megfelel tpust (bool) 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 void Main(string[] args)
{
Func<bool> func = () => true;
Console.WriteLine(func()); // True
Console.ReadKey();
}
}

A Func prja az Action, amely szintn maximum ngy bemen paramtert kaphat, de nem lehet visszatrsi
rtke:

188

using System;
class Program
{
static void Main(string[] args)
{
Action<int> act = (x) => Console.WriteLine(x);
act(10);
Console.ReadKey();
}
}

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 a nvtr kell
class Program
{
static void Main(string[] args)
{
Expression<Func<int, int, bool>> expression = (x, y) => (x > y);
Console.WriteLine(expression.Compile().Invoke(10, 2)); // True
Console.ReadKey();
}
}

A programban elszr IL kdra kell fordtani (Compile), csak azutn hvhatjuk meg.

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);
local = 100;
}

189

}
class Program
{
static void Main(string[] args)
{
Test t = new Test();
t.Method();
t.act(100);
Console.ReadKey();
}
}

Ez a program 10000-et fog kirni, vagyis valban a loklis vltoz legutols rtkt hasznlta a lambda kifejezs.
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.

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 void Main(string[] args)
{
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));
Console.ReadKey();
}
}

Esemnykezelt is rhatunk gy:

190

class Test
{
public event EventHandler TestEvent;
public void OnTestEvent()
{
if (TestEvent != null)
{
TestEvent(this, null);
}
}
}

Az EventHandler ltalnos delegate-et hasznltuk az esemny deklarcijnl. Az esemny elindtsnl nincs


szksgnk most EventArgs objektumra, ezrt itt nyugodtan hasznlhatunk null rtket.
Most nzzk a programot:
static void Main(string[] args)
{
Test t = new Test();
t.TestEvent += (sender, e) =>
{
Console.WriteLine("Esemnykezel!");
};
t.OnTestEvent();
Console.ReadKey();
}

Lambda kifejezs helyett n. lambda lltst (ezt blokkal jelljk ki) rtunk, gy akr tbbsoros utastsokat is
adhatunk.

191

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
keresztl 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 egsz sor hibalehetsg egsz egyszeren
eltnik. Ennek azonban 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 void Main(string[] args)
{
unsafe
{
Test t = new Test();
int y = 10;
t.x = &y;
Console.WriteLine(*t.x);
}
Console.ReadKey();
}
}

Elszr deklarltunk egy unsafe adattagot, mghozz egy int tpusra mutat pointert. A pointer tpus az rtks 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. Ebbl kvetkezen a fenti deklarciban nem
adhatok azonnal rtket az unsafe pointernek, mivel numerikus rtkads esetn nem fordul le a program
(hiszen nem egy int objektumrl van sz), ms objektum memriacmt viszont nem tudom. Sebaj, erre val az
n. cmeopertor (&), amellyel tadhatom egy hagyomnyos objektum cmt.
A programban ki akarjuk rni a memriaterleten lv objektum rtkt, ezt a dereference opertorral (*)
tehetjk meg. Ez visszaadja a mutatott rtket, mg ha magt a vltozt hasznljuk, az csak a memriacmet.
A memriacm a memria egy adott byte-jra mutat (vagyis a pointer nvelse/cskkentse a pointer
tpusnak megfelel mennyisg byte-tal rakja odbb a mutatt, teht egy int pointer esetn ez ngy byte

192

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 ablak, s a Build fln jelljk meg az Allow unsafe code ngyzetet.
csc /unsafe main.cs
A megfelel explicit konverzival a memriacmet is lekrhetjk:
using System;
class Program
{
static void Main(string[] args)
{
unsafe
{
int x = 10;
int* y = &x;
Console.WriteLine((int)y);
}
Console.ReadKey();
}
}

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 szkebb pointer tpusrl tgabb tpusra, fordtott eset fordtsi hibt okoz.
Ugyanakkor az elbbi eset elvileg definilatlan eredmnnyel jrhat, a gyakorlatban azrt mkdkpes.
int x = 10;
byte y = 20;
int* p1 = &x; // ez j
p1 = (int*)&y; // ez nem biztos, hogy j, de mkdik
p1 = (byte*)&x; // ez mr le sem fordul

Implicit konverzi van viszont brmely pointer tpusrl a void* univerzlis pointer tpusra. A void*-on nem
hasznlhat a dereference opertor:
int x = 10;
void* p1 = &x;
Console.WriteLine(*((int*)p1)); // 10

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;
}

193

class Program
{
static void Main(string[] args)
{
unsafe
{
Test t = new Test();
t.x = 10;
Test* p = &t;
Console.WriteLine((*p).x); // 10
Console.WriteLine(p->x); // 10
}
Console.ReadKey();
}
}

FIX OBJEKTUMOK
Normlis esetben a szemtgyjt a memria tredezettsgmentestse rdekben mozgatja az objektumokat a
memriban. Egy pointer azonban mindig egy fix helyre mutat, hiszen a mutatott objektum cmvel dolgozunk,
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 fixed-et, vegyk szemgyre a kvetkez forrskdot:
int[] array = new int[] { 1, 3, 4, 6, 7 };
int* p = &array;

Ez a kt sor fordtsi hibval jr. 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 void Main(string[] args)
{
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));
}
}
Console.ReadKey();
}
}

194

NATV DLL KEZELS


Elfordul, hogy olyan metdust akarunk hvni, amelyet egy natv (pl. C++ nyelven rt) kls knyvtr 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:
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
);

Ezenkvl mg tudnunk kell azt is, hogy melyik DLL trolja az adott fggvnyt, ez pedig 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; // ez a nvtr szksges
public class API
{
[DllImport("user32.dll")]
public static extern int MessageBox(int hWnd,
string text, string caption, uint type);
}
class Program
{
static void Main(string[] args)
{
API.MessageBox(0, "Hello!", "Nincs", 0);
}
}

Az attribtumnak meg kell adnunk a knyvtr nevt (user32.dll), 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:

195

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 program belpsi
pontja (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 felhasznltl rkez akcikra
(pl. billentylenyoms).
Az ilyen helyzetek elkerlsre a Windows (s a .NET) lehetv teszi msodlagos szlak (n. worker thread)
hozzadst a f szlhoz. Az egyes szlak (a processekhez 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 ma mr szinte csak tbbmagos rendszerek lteznek, de
gondoljunk bele, hogy amikor hozz sem nylunk a szmtgphez is tven-szz szl fut), gy el kell osztania az
egyes feladatok kzt a processzoridt (ezt a szlak prioritsa alapjn teszi) ez az n. idosztsos (time slicing)
rendszer. Amikor egy szlnak kiosztott id lejr, akkor a futsi adatait eltrolja az n. Thread Local Storageben
(ebbl minden szlnak van egy), s tadja a helyet egy msik szlnak, amely ha szksges betlti a sajt
adatait a TLS-bl, s elvgzi a feladatt.
A .NET szmos osztlyt s metdust bocst rendelkezsnkre, amelyekkel az egyes processeket felgyelhetjk,
ezek a System.Diagnostics nvtrben vannak. rjunk egy programot, amely kirja az sszes fut folyamatot s
azonostjukat:
using System;
using System.Diagnostics; // erre a nvtrre szksg van
class Program
{
static void Main(string[] args)
{
Process[] processList = Process.GetProcesses(".");
foreach (Process process in processList)
{
Console.WriteLine("PID: {0}, Process - nv: {1}",
process.Id, process.ProcessName);
}
Console.ReadKey();
}
}

Amennyiben tudjuk a process azonostjt, akkor hasznlhatjuk a Process.GetProcessById(azonost)


metdust is.
A kvetkez programunk az sszes fut process minden szlt s azoknak adatait fogja kilistzni:

196

using System;
using System.Diagnostics; // erre a nvtrre szksg van
class Program
{
static void Main(string[] args)
{
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);
}
}
Console.ReadKey();
}
}

Lehetsges, 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 kivtelbiztoss 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 process-ek irnytst szemllteti. Indtsuk el az Internet Explorert, vrjunk t
msodpercet, s lltsuk le:
using System;
using System.Diagnostics;
using System.Threading; // erre a nvtrre szksg van
class Program
{
static void Main(string[] args)
{
Process explorer = Process.Start("iexplore.exe");
Thread.Sleep(5000);
explorer.Kill();
Console.ReadKey();
}
}

Egyttal felhasznltuk az els igazi szlkezelshez tartoz metdusunkat is, a Thread osztly statikus Sleep
metdust (a Thread osztly a System.Threading nvtrben tallhat).

197

APPLICATION DOMAIN -EK


Egy .NET program nem direkt mdon processknt fut, hanem be van gyazva egy n. application domain-be a
processen bell (egy process tbb AD-t 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 AD-ben, 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 AD-ket hoz ltre.
A kvetkez program kirja az aktulis AD nevt:
using System;
class Program
{
static void Main(string[] args)
{
AppDomain currAD = AppDomain.CurrentDomain;
Console.WriteLine(currAD.FriendlyName);
Console.ReadKey();
}
}

Az alkalmazs neve fog megjelenni, hiszen az alaprtelmezett AD, s egyelre nincs is tbb. Hozzunk ltre egy
msodik AppDomain objektumot:
using System;
class Program
{
static void Main(string[] args)
{
AppDomain secondAD = AppDomain.CreateDomain("second");
Console.WriteLine(secondAD.FriendlyName);
AppDomain.Unload(secondAD); // megszntetjk
Console.ReadKey();
}
}

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:
using System;
using System.Threading;
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Szl-Id: {0}", Thread.CurrentThread.ManagedThreadId);

198

Console.ReadKey();
}
}

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.

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 BeginInvoke-val fogjuk
meghvni a delegate-et, ennek els paramtere megegyezik a delegate paramtervel (vagy paramtereivel).
Az EndInvoke fogja majd az eredmnyt szolgltatni, ennek visszatrsi rtke megegyezik a delegateval. Az
IAsyncResult objektum, amit a BeginInvoke visszatrt, segt elrni az eredmnyt, s az EndInvoke is ezt kapja
majd paramterl.
A BeginInvoke msik kt paramtervel most nem foglalkozunk, ksztsnk egy egyszer metdust a delegatehez, s hvjuk meg aszinkron:
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 void Main(string[] args)
{
MyDelegate d = Square;
Console.WriteLine("Szl-ID: {0}",

199

Thread.CurrentThread.ManagedThreadId);
IAsyncResult iar = d.BeginInvoke(12, null, null);
Console.WriteLine("BlaBla...");
int result = d.EndInvoke(iar);
Console.WriteLine(result);
Console.ReadKey();
}
}

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):
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 void Main(string[] args)
{
MyDelegate d = Square;
Console.WriteLine("Szl-ID: {0}",
Thread.CurrentThread.ManagedThreadId);
IAsyncResult iar = d.BeginInvoke(12, null, null);
while (!iar.IsCompleted)
{
Console.WriteLine("BlaBla...");
}

200

int result = d.EndInvoke(iar);


Console.WriteLine(result);
Console.ReadKey();
}
}

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, gy felmerlhet a krds, hogy nem lehetne-e
valahogy automatizlni az egszet. Nos, pp ezt a gondot 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; // erre a nvtrre szksg van
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));
}

201

static void Main(string[] args)


{
MyDelegate d = Square;
Console.WriteLine("Szl-ID {0}",
Thread.CurrentThread.ManagedThreadId);
IAsyncResult iar = d.BeginInvoke(12,
new AsyncCallback(AsyncMethodComplete), null);
Console.WriteLine("BlaBla...");
Console.ReadKey();
}
}

Ha Console.ReadKey nlkl futtatnnk a programot, akkor azt ltnnk, 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
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));
}

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

202

{
public void ThreadInfo()
{
Console.WriteLine("Szl-nv: {0}", Thread.CurrentThread.Name);
}
}
class Program
{
static void Main(string[] args)
{
Thread current = Thread.CurrentThread;
current.Name = "Current-Thread";
Test t = new Test();
t.ThreadInfo();
Console.ReadKey();
}
}

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 void Main(string[] args)
{
Test t = new Test();
Thread backgroundThread = new Thread(
new ThreadStart(t.ThreadInfo));
backgroundThread.Name = "Background-Thread";
backgroundThread.Start();
}
}

A Thread konstruktorban szerepl ThreadStart delegate-nek kell megadnunk azt a metdust, amelyet a
msodlagos szl majd futtat. A programot rdemes Console.ReadKey nlkl futtatni, mivel azon fennakadna a
futs. gy is csak egy villanst lehet ltni belle, parancssorbl indtva ltvnyosabb lehet.
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:

203

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 void Main(string[] args)
{
Test t = new Test();
Thread backgroundThread = new Thread(new
ParameterizedThreadStart(t.ThreadInfo));
backgroundThread.Name = "Background-Thread";
backgroundThread.Start("Hello");
Console.ReadKey();
}
}

Nylvn a metdusban nem rt ellenrizni a paramter tpust, mieltt brmit csinlunk vele.

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:
using System;
using System.Threading;
class Test
{
public void ThreadInfo()
{
Thread.Sleep(5000);
Console.WriteLine("Szl-nv: {0}", Thread.CurrentThread.Name);
}
}
class Program
{
static void Main(string[] args)

204

{
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, hogy vgezzen a dolgval.

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 void Main(string[] args)
{
Console.WriteLine("Start...");
Thread.Sleep(2000);
Console.WriteLine("Stop...");
Console.ReadKey();
}
}

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.
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 void Main(string[] args)
{
Thread t = new Thread(delegate() { Thread.Sleep(2000); });
t.Start();
t.Join();
Console.WriteLine("Vge");
Console.ReadKey();
}
}

205

A Join-nak 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 void Main(string[] args)
{
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
}
Console.ReadKey();
}
}

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)
{
Thread.Sleep(2);
Console.WriteLine(Test.y / Test.x);
Test.x = 0;
}
}
}
class Program
{
static void Main(string[] args)
{
Thread t1 = new Thread(new ThreadStart(Test.Divide));
Thread t2 = new Thread(new ThreadStart(Test.Divide));
t1.Start();
t2.Start();
}
}

206

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
kell jellnnk egy n. tokent, amelyet lezrhat. A tokennek minden esetben referenciatpusnak kell lennie.
A lock-hoz 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();

207

static public void ResourceUserMethod()


{
for (int i = 0; i < 10; ++i)
{
t.ResourceMetod();
}
}
static void Main(string[] args)
{
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.

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!

208

Ha gy ksztjk el a programot, hogy a bejv kapcsolatoknak mindig j szlat ksztnk, akkor nagyon
gyorsan teljestmnyproblmkba fogunk tkzni:

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 thread-pooling-nak 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 void Main(string[] args)
{
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.
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).

209

PRHUZAMOS PROGRAMOZS TASK PARALLEL LIBRARY


Ma mr a legtbb szmtgp tbb processzormaggal rendelkezik, amelyek fizikailag is lehetv teszik tbb szl
prhuzamos futst. Ahhoz, hogy az ebben rejl lehetsgeket kihasznljuk, a mltban alacsony szinten,
kzvetlenl a szlakkal kellett dolgoznunk. A .NET Framework 4 (s a C# 4.0) olyan j osztlyokat s
keretrendszert ad a keznkbe, amelyekkel ezeket a feladatokat leegyszersthetjk.

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 nyjtja, de kpes a
processzorok szmnak fggvnyben sztosztani a munkt a CPUk kztt, ezzel pedig
teljestmnynvekedst r el. Ennek a mdszernek a nagy htrnya, hogy olyan algoritmust kell talljunk,
amely minden helyzetben a lehet leghatkonyabban tudja elosztani a munkt, ankl, hogy brmely
processzor resjratban llna.

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:
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:

1
+

Ahol P a program azon rsze, amely prhuzamosthat, (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):
1
1 0,9 + 0,9/2
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

210

a hagyomnyos futsid tzszerese lehet (1 / (1 0,9), vagyis pontosan az az egy ra, amelyet a nem
prhuzamosthat programrsz hasznl fel!

PRHUZAMOS CIKLUSOK
A TPL tartalmazza a for s foreach ciklusok prhuzamostott vltozatt. Nzznk meg egyszer pldt,
hasonltsuk ssze a sima for s a prhuzamos for teljestmnyt! Ksztsnk egy tmbt, tltsk fel
szmokkal, s rjuk ki a tartalmt! Nem valami okos plda, de arra elg, hogy lssuk a prhuzamos vltozatok
szintaxist.
using
using
using
using

System;
System.Diagnostics;
System.Threading.Tasks;
System.Linq;

namespace TestApp
{
class Program
{
static void Main(string[] args)
{
int[] array = Enumerable.Range(0, 10000).ToArray<int>();
Stopwatch sw = new Stopwatch();
long result1 = 0;
long result2 = 0;
sw.Start();
Parallel.For(
0, //honnan
array.Length, //meddig
(i) => Console.WriteLine(array[i])); //ciklustrzs
sw.Stop();
result1 = sw.ElapsedMilliseconds;
sw.Reset();
sw.Start();
for (int i = 0; i < array.Length; ++i)
{
Console.WriteLine(array[i]);
}
sw.Stop();
result2 = sw.ElapsedMilliseconds;
Console.WriteLine("Az els ciklus ltal felhasznlt id: {0} ms",
result1);
Console.WriteLine("A msodik ciklus ltal felhasznlt id: {0} ms",
result2);
Console.ReadKey();
}
}
}

211

A Parallel.For ebben a legegyszerbb vltozatban hrom paramtert vr, akrcsak a nem prhuzamos trsa: a
ciklusvltoz kiindulsi rtkt, a maximlis rtket (amely mr nem szmt bele az itercikba), illetve a
ciklustrzset. A ciklusvltoz int vagy long tpus lehet, s minden esetben eggyel lp elre.
Az Enumerable.Range a paramtereiknt megadott intervallumban generl szmokat, ezen meghva a ToArray
fggvnyt visszakapjuk ennek a sorozatnak a tmbalakjt.
A Stopwatch osztlyt hasznljuk idmrsre? a Start s Stop metdusval elindtjuk, illetve meglltjuk az rt,
a Reset pedig alaphelyzetbe rakja.
Ha megnzzk a program futsa utn az eredmnyeket, akkor azt ltjuk, hogy a ktfle ciklus nagyjbl
ugyanannyi id alatt rta ki a 10000 szmot. Azt is vegyk szre, hogy mg a rendes vltozat sorrendben veszi
el a szmokat a tmbbl, addig a prhuzamos meglehets sszevisszasgban! Ennek oka, hogy utbbi esetben
a prhuzamos vgrehajts csoportokba rendezi az elemeket (particionlja) s az egyes csoportokat kldi
feldolgozsra. Ezek aztn nem sorrendben rkeznek a processzorhoz, gy a kimenet nem rendezett.
Nzznk egy sszetettebb pldt: adjuk ssze az els egymilli szmot! A forrskd a kvetkez lesz:
using
using
using
using
using
using

System;
System.Diagnostics;
System.Threading.Tasks;
System.Linq;
System.Collections.Concurrent;
System.Threading;

namespace TestApp
{
class Program
{
static void Main(string[] args)
{
int[] array = Enumerable.Range(1, 1000000).ToArray<int>();
Stopwatch sw = new Stopwatch();
long sum = 0;
sw.Start();
for (int i = 0; i < array.Length; ++i)
{
sum += array[i];
}
sw.Stop();
Console.WriteLine("Az eredmny: {0}", sum);
Console.WriteLine("A ciklus ltal felhasznlt id: {0} ms",
sw.ElapsedMilliseconds);
sum = 0;
sw.Reset();
sw.Start();
Parallel.For(0,
array.Length,
(i) => sum += array[i]);
sw.Stop();
Console.WriteLine("Az eredmny: {0}", sum);
Console.WriteLine("A ciklus ltal felhasznlt id: {0} ms",
sw.ElapsedMilliseconds);
Console.ReadKey();

212

}
}
}

Ha elindtjuk a programot, kt nagyon rdekes dolgot tapasztalunk: a prhuzamos vltozat jelentsen lassabb
(akr ngy-tszr is), illetve rossz eredmnyt is ad vissza.
Elssorban kezeljk az utbbi problmt: a Parallel.For ha mst nem adtunk meg, automatikusan int tpus
ciklusvltozt hasznl, amelyben a vrt eredmny nem fr el. rjuk t long tpusra:
Parallel.For(0,
array.Length,
(long i) => sum += array[i]);

Ezutn is fennll azonban, hogy sok esetben rossz az eredmny (nem rt nhnyszor futtatni). Mirt van ez gy?
A TPL ugyan egyszersti a dolgunkat, de nem lehet sz nlkl hasznlni. Ebben az esetben az alapproblma,
hogy egy cikluson kvli vltozt hasznlunk, radsul olyan mvelettel, amely nem szlbiztos.
A += opertor itt sum = sum + array[i] alakban rtkeldik ki. Kpzeljk el, hogy megrkezik az egyik szl,
elkezd szmolni, ebben a pillanatban pedig egy jabb szl bukkan fel! Az els mr kisajttotta magnak a sum
vltozt, a msodik nem jut hozz, az adott mvelet egyszeren kimarad. gy mr nem csoda, hogy a
vgeredmny nem megfelel.
rjuk t ismt a prhuzamos ciklust:
Parallel.For(0,
array.Length,
() => 0,
(long i, ParallelLoopState loop, long total) =>
{
total += array[i]; return total;
},
(x) => Interlocked.Add(ref sum, x));

A paramterek sorban: a ciklusvltoz kiindulsi rtke, a maximlis rtk, utna a programrsz, amellyel az
egyes szlaknak adhatunk informcit. Az utols kt paramter a lnyeg: az els maga is kap hrom
paramtert, ezek a ciklusvltoz, egy objektum, amelynek segtsgvel id eltt lellthatjuk a ciklus futst,
illetve egy vltoz, amelyet tovbbadhatunk a tovbbi iterciknak. A ciklustrzs ennek fnyben egyszer: a
helyi vltoznkat hasznljuk az sszeadshoz.
Vgl a For utols paramtere jn, amely a szlbiztos mveleteket segti: az Interlocked osztllyal elemi
mveleteket vgezhetnk (ebben az esetben sszeadst). Ez a fggvny nem fut le minden egyes iterciban,
helyette az egyes szlak mkdse vgn hvja meg csak a rendszer, ezltal jelents mennyisg teljestmnyt
nyerhetnk.
Ha jra futtatjuk a programunk, most mr minden esetben helyes vgeredmnyt kapunk, viszont a sebessg
nem vltozott.
Ezzel el is rkeztnk a fejezet lnyeghez: a prhuzamos vgrehajts meglehetsen nagy odafigyelst s
specilis krlmnyeket ignyel. Amennyiben a ciklustrzs rvid (rtsd: kevs vagy olcs mvelet) magnak a
prhuzamossgnak a bezemelse megli a teljestmnyt, gyakorlatilag tbb erforrs szksges hozz mint a
mveletek vgrehajtshoz.
Ugyangy a lehetsges mveletek is behatroltak, ahogy lttuk, nem biztos, hogy j tlet kls vltozkat,
objektumokat hasznlni (viszont tmbk esetn ha az egyes elemekkel dolgozunk amg nem mdostunk
tbbszr egy elemet, addig szlbiztos a program). Nem rt azt is tudni, hogy rdemes olyan mveletek et
hasznlni, ahol az egyes rszeredmnyek nem fggenek egymstl (persze vannak mdszerek a kevsb
egyrtelm esetek kezelsre is).
Nem kell azonban elkeseredni, a TPL ettl fggetlenl is rendkvl hasznos. Nzznk meg egy olyan pldt, ahol
tnyleges elnyhz jutunk hasznlatval! A kvetkez programunkban mtrixokat szorzunk ssze, ez egy
kellen komplex mveletsorozatot ignyel. Lssuk, hogy teljest a prhuzamos ciklus. Ksztsnk elszr egy
fggvnyt, amellyel tetszleges mret mtrixot generlhatunk:

213

static public double[,] GenerateMatrix(int size)


{
double[,] matrix = new double[size, size];
Random r = new Random();
for (int i = 0; i < size; ++i)
{
for (int j = 0; j < size; ++j)
{
matrix[i, j] = r.Next(1000, 100000);
}
}
return matrix;
}

Most jjjn a szorzst vgz programrsz a tesztcsomaggal:


static void Main(string[] args)
{
int size = 1000;
double[,] a = GenerateMatrix(size);
double[,] b = GenerateMatrix(size);
double[,] c = new double[size, size];
Stopwatch sw = new Stopwatch();
sw.Start();
for (int i = 0; i < size; ++i)
{
for (int j = 0; j < size; ++j)
{
double v = 0;
for (int k = 0; k < size; ++k)
{
v += a[i, k] * b[k, j];
}
c[i, j] = v;
}
}
sw.Stop();
Console.WriteLine("A ciklus ltal felhasznlt id: {0} ms",
sw.ElapsedMilliseconds);
c = new double[size, size];
sw.Reset();
sw.Start();
Parallel.For(0, size, (i) =>
{
for (int j = 0; j < size; ++j)
{
double v = 0;
for (int k = 0; k < size; ++k)
{

214

v += a[i, k] * b[k, j];


}
c[i, j] = v;
}
});

sw.Stop();
Console.WriteLine("A ciklus ltal felhasznlt id: {0} ms",
sw.ElapsedMilliseconds);
Console.ReadKey();
}

A 100x100-as mtrixok sszeszorzsra a tesztgpen (AMD 255 X2, kt mag) a hagyomnyos ciklusnak 14
msodpercre volt szksge, a prhuzamos 10 alatt vgzett. 1000x1000-es mtrixokkal hasonl az elny: 162
msodperc 108 ellen. Lthat, hogy egy elavult processzor felett is kzel 50% plusz teljestmnyt lehet
kifacsarni a rendszerbl.
A Parallel.For-nl jval rugalmasabb a Paralell.ForEach. Amg elbbinl csak egy szmsorozaton iterltunk t s
kls forrsbl szedtk el az adatokat, megkzdve a szlbiztossg rmvel, addig utbbi kzvetlenl egy
gyjtemny elemein megy vgig.
rjunk egy programot, amellyel egy knyvtr fjljait tudjuk feldolgozni:
using System;
using System.Threading.Tasks;
using System.IO;
namespace TestApp
{
class Program
{
static public void Main(string[] args)
{
DirectoryInfo di = new DirectoryInfo("D:\\");
Parallel.ForEach(di.GetFiles(), (file) =>
{
Console.WriteLine(file.FullName);
});
Console.ReadKey();
}
}
}

A For-hoz hasonl a szintaxis, de ebben az esetben a ciklustrzs szmra az aktulis elemet adjuk t.

PARALLEL.INVOKE
Ciklusokkal egy adott feladatot hajthatunk vgre tbbszr. Ha viszont tbb klnbz akr egymstl
fggetlen akcit szeretnnk elvgezni, akkor ms eszkzt kell vlasztanunk. A Parallel.Invoke pp ennek a
felttelnek tesz eleget: paramtereknt tetszleges szm tevkenysget adhatunk meg, amelyeket aztn
prhuzamosan hajt vgre:

215

using System;
using System.Threading.Tasks;
using System.Net;
namespace TestApp
{
class Program
{
static public void Main(string[] args)
{
Parallel.Invoke(
() => Console.WriteLine("Task 1"),
() => Console.WriteLine("Task 2"),
() => Console.WriteLine("Task 3")
);
Console.ReadKey();
}
}
}

Az Invoke Action delegate-ek paramtertmbjt vrja paramterknt, ezeknek nem lehet bemen paramtere.
A fenti program egyszeren kirja a hrom mondatot, csinljunk valami bonyolultabbat. Pldul egy hosszabb
szvegbl keressk meg a leghosszabb szt, s hogy sszesen hny sz szerepel az rsban!
using
using
using
using

System;
System.Threading.Tasks;
System.Net;
System.Linq;

namespace TestApp
{
class Program
{
static public void Main(string[] args)
{
WebClient wc = new WebClient();
string[] s = wc.DownloadString(
"http://www.gutenberg.org/cache/epub/103/pg103.txt")
.Split(
new char[] { ' ', '\u000A', ',', '.', ';', ':', '-', '_', '/' },
StringSplitOptions.RemoveEmptyEntries
);
Parallel.Invoke(
() => Console.WriteLine(
s.OrderByDescending((item) => item.Length).First()),
() => Console.WriteLine(s.Length)
);
Console.ReadKey();
}
}
}

Ha futtatjuk a programot, akkor szrevehetjk, hogy nem biztos, hogy az ltalunk megadott sorrendben futnak
le az egyes mveletek. Illetve vegyk szre azt is, hogy ugyanazt az erforrst hasznlja a kt Action objektum,
de mivel nem vgeznek rajta mdostst, ezrt a mveletek szlbiztosak.

216

TASK
A TPL egyik alapkve a Task osztly. Ne keverjk ssze azonban t a szlakkal! Egy Task objektum egy komplett
prhuzamos feladatot reprezentl, mg egy szl csak a munkafolyamat rsze. Ugyanakkor ennek az osztlynak a
kezelse elgg hasonlt a szlakhoz. Nzznk is egy egyszer pldt:
using System;
using System.Threading.Tasks;
namespace TestApp
{
class Program
{
static public void Main(string[] args)
{
Task task1 = Task.Factory.StartNew(
() => Console.WriteLine("n egy Task vagyok!")
);
Task task2 = Task.Factory.StartNew(
() => Console.WriteLine("n is egy Task vagyok!")
);
task2.Wait();
Console.ReadKey();
}
}
}

A Task.Factory.StartNew egy j Task objektumot ad vissza, amelyet egyttal el is indt. A Wait metdus
megvrja, amg a hozz tartoz objektum befejezi a futst. Ha nem hvtuk volna meg, a program indtsakor
semmit nem ltnnk, hiszen a Console.ReadKey blokkolja a fszlat. Egy billentylets utn megjelenne a kt
mondat, de ugye csak egy villansnyi idre.
Ugyanez a helyzet, ha kzzel gyrtunk egy Task objektumot:
using System;
using System.Threading.Tasks;
namespace TestApp
{
class Program
{
static public void Main(string[] args)
{
Task task = new Task(
() => Console.WriteLine("n egy Task vagyok!")
);
task.Start();
task.Wait();
Console.ReadKey();
}
}
}

Egy Task objektum eredmnyt is adhat vissza a kvetkezkppen:

217

using System;
using System.Threading.Tasks;
namespace TestApp
{
class Program
{
static public void Main(string[] args)
{
Task<int> task = new Task<int>(
() => { return 10; }
);
task.Start();
Console.WriteLine(task.Result); // 10
Console.ReadKey();
}
}
}

A Task paramterestett vltozatn megjelenik a Result tulajdonsg. Ilyenkor a paramter tpusa lesz a
visszatrsi rtk tpusa. Hasonlkppen jrunk el a Task.Factory.StartNew esetben is. Egy Taskhoz
rendelhetnk paramtert is:
using System;
using System.Threading.Tasks;
namespace TestApp
{
class Program
{
static public void Main(string[] args)
{
string[] array = new string[]
{
"n egy Task vagyok!",
"n is egy Task vagyok!",
"n nem vagyok egy Task!"
};
foreach (string item in array)
{
Task task = new Task(
(obj) => Console.WriteLine(obj),
item);
task.Start();
}
Console.ReadKey();
}
}
}

A paramtert a Taskon bell object tpusnak ltjuk, ha szksges, el kell vgezni a megfelel konverzit.

218

ASYNC/AWAIT
Amikor egy aszinkron programrszt hvunk meg, meglehetsen hossz forrskdot kell rnunk. El kell indtani a
folyamatot, majd ha vgzett, egy msik fggvnynek t kell adnunk az eredmnyt, ahonnan ki tudja, hov
utazik majd tovbb. A C# 5.0 s a .NET 4.5 bevezeti az async/await kulcsszavakat, amelyek segtsgvel
igencsak leegyszersdik ez a folyamat. Az albbi forrskdok lefordtshoz Visual Studio 2011-re van szksg!
Nzzk meg, hogy mkdik ez az j konstrukci:
using System;
using System.Threading;
using System.Threading.Tasks;
namespace AsyncDemo
{
class Program
{
static async void DoOperations()
{
var result = await Task<string>.Factory.StartNew(
() => ReallyLongOperation()
);
Console.WriteLine("Az eredmny: {0}", result);
}
static string ReallyLongOperation()
{
Thread.Sleep(2000);
return "Siker";
}
static void Main(string[] args)
{
Console.WriteLine("A mvelet eltt...");
DoOperations();
Console.WriteLine("A mvelet utn...");
Console.ReadKey();
}
}
}

Ha futtatjuk a programot, akkor ltni fogjuk, hogy az eltt s utn zenetek jelennek meg elszr, s csak
utnuk az eredmny, ami arra utal, hogy a ReallyLongOperation fggvny aszinkron mdon futott le. Hogyan
rtk ezt el? A metdus szignatrja el helyezett async sz azt jelzi a fordtnak, hogy a blokkjban valahol
hasznlni szeretnnk az await utastst. Amikor ahhoz a bizonyos helyhez r a program futsa, az trtnik, hogy
abban a pillanatban a metdus futsa megszakad, s httrbe vonul, amg az adott mvelet vget nem r.
Azutn felbred s befejezi a feladatt.
Az async kulcsszt csakis olyan metdusok/fggvnyek esetben hasznlhatjuk, ahol a visszatrsi rtk tpusa
void, Task vagy Task<T> (nhny kivtellel, pldul a Main-en nem lehet). Az async meglte nem ktelez
minket az await hasznlatra, ez fordtva viszont nem igaz.
Az await utastst hasznlhatjuk minden olyan fggvnyen, amelynek visszatrsi tpusa Task vagy Task<T>,
illetve ha megvalstja a fggvnyt tartalmaz osztly a GetAwaiter metdust. A .NET 4.5 beptve tartalmaz
olyan mveleteket, amelyek hasznlhatak ezzel az utastssal, pldul a klnbz Stream osztlyok tbb
metdusa is ilyen. Jellemzen Async uttagot kapnak, illetve az IntelliSense a fggvny/metdus
paramterlistja mellett megjelenti az awaitable szt is.

219

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 r (ugyanakkor bizonyos
esetekben nagy hatkonysggal jrhat a hasznlata). Vegyk a kvetkez pldt:
using System;
class Test
{
}
class Program
{
static public void Main(string[] args)
{
Test t = new Test();
Type type = t.GetType();
Console.WriteLine(type); // Test
Console.ReadKey();
}
}

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; // erre a nvtrre szksg van
class Test
{
}
class Program
{
static public void Main(string[] args)
{
Type type = Assembly.GetCallingAssembly().GetType("Test");
var t = Activator.CreateInstance(type);
Console.WriteLine(t.GetType()); // Test
Console.ReadKey();
}
}

Megjegyzend, hogy fordtsi idben semmilyen ellenrzs sem trtnik a pldnyostand osztlyra nzve, gy
ha elgpeltnk valamit, az bizony kivtelt fog dobni futskor (System.ArgumentNullException).

220

LLOMNYKEZELS
Ebben a fejezetben megtanulunk fjlokat rni/olvasni s megtanuljuk a knyvtrstruktra llapotnak
lekrdezst illetve mdostst is.

OLVASS/RS FJLBL/FJLBA
A .NET szmos osztlyt biztost szmunkra, amelyekkel fjlokat tudunk kezelni. Ebben a fejezetben a
leggyakrabban hasznltakkal ismerkednk meg. Kezdjk egy fjl megnyitsval s tartalmnak a kpernyre
rsval. Legyen pl. a szveges fjl tartalma a kvetkez:
alma
krte
di
cskny
pnz
knyv
A fjlnak a futtathat programunk mellett kell lennie, vagy teljes elrsi utat kell megadnunk. Visual Studio-ban
pldul hozzadhatunk egy Text File-t a projekthez (jobb klikk, majd Add New Item), ezutn a fjlon jobb
gombbal kattintva Properties, s a Copy To Output Directory sorban vlasszuk a Copy Always lehetsget! A
program pedig:
using System;
using System.IO;
class Program
{
static public void Main(string[] args)
{
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();
Console.ReadKey();
}
}

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 fjl neve (ha nem tall ilyen nev fjlt, 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:

221

FileStream fs = new FileStream("C:\\Dir1\\Dir2\\test.txt", FileMode.Open);

Azrt hasznlunk dupla backslasht (\), mert ez egy n. escape karakter, nmagban nem lehetne hasznlni
(persze ez nem azt jelenti, hogy minden ilyen karaktert kettzve kellene rni, minden specilis karakter eltt a
backslasht kell hasznlnunk).
Egy msik lehetsg, hogy az at jelet (@) hasznljuk az elrsi t eltt, ekkor nincs szksg dupla
karakterekre, mivel mindent normlis karakterknt fog rtelmezni:
FileStream fs = new FileStream(@"C:\Dir1\Dir2\test.txt", FileMode.Open);

A FileMode enumnak a kvetkez rtkei lehetnek:


Create
CreateNew
Open
OpenOrCreate
Append
Truncate

Ltrehoz egy j fjlt, ha mr ltezik, a tartalmt kitrli.


Ugyanaz, mint az elz, de ha mr ltezik a fjl, akkor
kivtelt dob.
Megnyit egy fjl, ha nem ltezik, kivtelt dob.
Ugyanaz, mint az elz, de ha nem ltezik, akkor
ltrehozza a fjlt.
Megnyit egy fjlt, s automatikusan a vgre
pozicionl. Ha nem ltezik, ltrehozza.
Megnyit egy ltez fjlt, s trli a tartalmt. Ebben a
mdban a fjl tartalmt nem lehet olvasni (egybknt
kivtelt dob).

A FileStream konstruktornak tovbbi kt paramtere is lehet, amelyek rdekesek szmunkra (tulajdonkppen


15 klnbz konstruktora van), mindkett felsorolt tpus. Az els a FileAccess, amellyel belltjuk, hogy
pontosan mit akarunk csinlni az llomnnyal:
Read
Write
ReadWrite

Olvassra nyitja meg.


rsra nyitja meg.
Olvassra s rsra nyitja meg

A fenti pldt gy is rhattuk volna:


FileStream fs = new FileStream("test.txt", FileMode.Open, FileAccess.Read);

Vgl a FileSharerel azt lltjuk be, ahogy ms folyamatok frnek hozz a fjlhoz:
None
Read
Write
ReadWrite
Delete
Inheritable

Ms folyamat nem frhet hozz a fjlhoz, amg azt be


nem zrjuk.
Ms folyamat olvashatja a fjlt.
Ms folyamat rhatja a fjlt.
Ms folyamat rhatja s olvashatja is a fjlt.
Ms folyamat trlhet a fjlbl (de nem magt a fjlt).
A gyermek processzek is hozzfrhetnek a fjlhoz.

Ha a fenti programot futtatjuk, akkor elfordulhat, hogy az kezetes karakterek helyett krdjel jelenik meg. Ez
azrt van, mert az ppen aktulis karaktertbla nem tartalmazza ezeket a karaktereket, ez tipikusan nem
magyar nyelv opercis rendszer esetn fordul el. A megolds, hogy kzzel lltjuk be a megfelel tblt, ezt
a StreamReader konstruktorban tehetjk meg (a tbla pedig iso-8859-2 nven szerepel):
StreamReader rs = new StreamReader(fs, Encoding.GetEncoding("iso-8859-2"), false);

Ehhez szksg lesz mg a System.Text nvtrre is.

222

Most rjunk is a fjlba. Erre a feladatra a StreamReader helyett a StreamWriter osztlyt fogjuk hasznlni:
using System;
using System.IO;
using System.Text;
class Program
{
static public void Main(string[] args)
{
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();
Console.ReadKey();
}
}

Az Environment.NewLine egy jsor karaktert (Carriage Return s Line Feed) ad vissza. Binris fjlok
kezelshez a BinaryReader/BinaryWriter osztlyokat hasznlhatjuk:
using System;
using System.IO;
using System.Text;
class Program
{
static public void Main(string[] args)
{
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();
Console.ReadKey();
}
}

223

Ksztettnk egy binris fjlt, 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);
}
}

KNYVTRSTRUKTRA KEZELSE
A fjlok mellett a .NET a knyvtrstruktra kezelst is tmogatja. A System.IO nvtr ebbl a szempontbl kt
rszre oszlik: informcis s opercis eszkzkre. Elbbiek (ahogyan a nevk is sugallja) informcit
szolgltatnak, mg az utbbiak (tbbsgkben statikus metdusok) bizonyos mveleteket (j knyvtr
ltrehozsa, trlse stb.) vgeznek a fjlrendszeren. Els pldnkban rjuk ki mondjuk a C meghajt gykernek
knyvtrait:
using System;
using System.IO;
class Program
{
static public void Main(string[] args)
{
foreach (string s in Directory.GetDirectories("C:\\"))
{
Console.WriteLine(s);
}
Console.ReadKey();
}
}

Termszetesen nemcsak a knyvtrakra, de a fjlokra 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;

224

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(string[] args)
{
foreach (string s in Directory.GetDirectories("C:\\"))
{
PrintFileSystemInfo(new DirectoryInfo(s));
}
foreach (string s in Directory.GetFiles("C:\\"))
{
PrintFileSystemInfo(new FileInfo(s));
}
Console.ReadKey();
}
}

Elszr a mappkon, majd a fjlokon megynk vgig. Ugyanazzal a metdussal rjuk ki az informcikat
kihasznlva azt, hogy a DirectoryInfo s a FileInfo is egy kzs stl, a FileSystemInfo-bl szrmazik (mindkett
konstruktora a vizsglt alany elrsi 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 krtnk le, most megtanuljuk mdostani is a knyvtrstruktrt. Arra figyeljnk, hogy
olyan knyvtrat vagy meghajtt hasznljunk a program tesztelsre, amelybe joga van rni a programunknak,
ellenkez esetben kivtelt kaphatunk!
using System;
using System.IO;
class Program
{
static public void Main(string[] args)
{
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");

225

sw.WriteLine("Alma");
sw.Close();
}
}
}

A FileInfo CreateText metdusa egy StreamWriter objektumot ad vissza, amellyel rhatunk egy szveges fjlba.

IN-MEMORY 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. 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 fjlba rni a tartalmt. A kvetkez program erre mutat pldt:
using System;
using System.IO;
class Program
{
static public void Main(string[] args)
{
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("D:\\inmem.txt");
mstream.WriteTo(fs);
sw.Close();
fs.Close();
mstream.Close();
}
}

A MemoryStream-re rlltottunk egy StreamWriter objektumot. Miutn minden adatot a memriba rtunk,
a Flush metdussal (amely egyttal kirti a StreamWriter-t is) ltrehoztunk egy fjlt s a MemoryStream
WriteTo metdusval kirtuk bel az adatokat.

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>

226

<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. A .NET Framework ersen pt az XML-re, mind offline (konfigurcis fjlok, inmemory adatszerkezetek), mind online (SOAP alap informcicsere stb.) 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 fjlt. A megnyitshoz az XmlReader egy statikus
metdust, a Create-et fogjuk hasznlni, ez egy streamtl kezdve egy szimpla fjlnvig 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 fjlt,
vgig tudunk iterlni a tartalmn:
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
legegyszerbb tpusokat vizsgltuk, s ezek fggvnyben rtuk ki a fjl tartalmt. A kvetkez pldban
hasznlni fogjuk az XmlWriter osztly egy leszrmazottjt, az XmlTextWritert, ugyanis a fjlt kdbl fogjuk
ltrehozni:
XmlTextWriter writer = new XmlTextWriter("newxml.xml", Encoding.UTF8);
writer.Formatting = Formatting.Indented;

Az Encoding felsorols hasznlathoz szksges a System.Text. A Formatting tulajdonsg az Xml fjloktl


megszokott hierarchikus szerkezetet fogja megteremteni (ennek az rtkt is bellthatjuk).

227

writer.WriteStartDocument();
writer.WriteComment(DateTime.Now.ToString());

Elkezdtk az adatok feltltst, s beszrtunk egy kommentet is. A file tartalma most a kvetkez:
<?xml version="1.0" encoding="utf-8"?>
<!--2012.07.24. 7:05:24-->

A fjl ebben a pldban szemlyek adatait fogja tartalmazni:


writer.WriteStartElement("PersonsList");
writer.WriteStartElement("Person");
writer.WriteElementString("Name", "Reiter Istvan");
writer.WriteElementString("Age", "26");
writer.WriteEndElement();

A StartElement egy j tagcsoportot kezd a paramtereknt megadott nvvel, a WriteElementString pedig


feltlti azt rtkekkel, kulcs-rtk prokkal. A fjl:
<?xml version="1.0" encoding="utf-8"?>
<!--2012.07.24. 7:07:04-->
<PersonsList>
<Person>
<Name>Reiter Istvan</Name>
<Age>22</Age>
</Person>
</PersonsList>

Az egyes tagekhez attribtumokat is rendelhetnk:


writer.WriteStartElement("PersonsList");
writer.WriteAttributeString("Note", "List of persons");

Els paramter az attribtum neve, utna pedig a hozz rendelt rtk jn. Ekkor a fjl gy alakul:
<?xml version="1.0" encoding="utf-8"?>
<!--2012.07.24. 7:08:03-->
<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 fjl 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 objektumot, s igaz rtkkel tr vissza,
ha ltezik az attribtum. Ellenkez esetben a pozci nem vltozik, s a visszatrsi rtk hamis lesz.
reader.MoveToContent();

Ez a metdus a legkzelebbi olyan csompontra ugrik, amely tartalmaz adatot is, ekkor az XmlReader (vagy
leszrmazottainak) Value tulajdonsga ezt az rtket fogja tartalmazni.

228

A MoveToElement metdus visszalp arra a csompontra, amely azt az attribtumot tartalmazza, amelyen a
reader ll (rtelemszeren kvetkezik, hogy az elz kett metdust gyakran hasznljuk egytt):
while (reader.Read())
{
if (reader.HasAttributes)
{
for (int i = 0; i < reader.AttributeCount; ++i)
{
reader.MoveToAttribute(i);
Console.WriteLine("{0} {1}", reader.Name, reader.Value);
}
reader.MoveToElement();
}
}

A HasAttribute metdussal megtudakoljuk, hogy vane a csomponton attribtum, utna pedig az


AttributeCount tulajdonsg segtsgvel (amely azoknak a szmt adja vissza) vgigiterlunk rajtuk. Miutn
vgeztnk, visszatrnk a kiindulsi pontra (hogy a Read metdus tudja vgezni a dolgt).
Emltst rdemelnek mg a MoveToFirstAttribute s a MovetoNextAttribute metdusok, amelyek nevkhz
mltan az els illetve a kvetkez attribtumra pozicionlnak. 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.

XML DOM
Eddig az XML llomnyokat hagyomnyos fjlknt kezeltk, ennl azonban van egy nmileg knyelmesebb
lehetsgnk is. Az XML DOM (Document Object Model) a fjlokat hierarchikus felptsk szerint kezeli. Az
osztly amelyen keresztl elrjk a DOM-ot az XmlDocument lesz. Egy msik fontos osztly, amelybl
egybknt maga az XmlDocument is szrmazik, az XmlNode. Egy XML fjl egyes csompontjait egy-egy
XmlNode objektum fogja jelkpezni.
Ahogy azt mr megszokhattuk, az XmlDocument szmos forrsbl tpllkozhat, pl. stream vagy egyszer
fjlnv. A forrs betltst az XmlDocument Load illetve LoadXml metdusaival vgezzk:
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:
using System;
using System.Xml;
class Program
{
static public void Main(string[] args)

229

{
XmlDocument xdoc = new XmlDocument();
xdoc.Load("test.xml");
foreach (XmlNode node in xdoc.ChildNodes)
{
Console.WriteLine(node.Name);
}
Console.ReadKey();
}
}

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 tudunk manipullni egy dokumentumot! Elszr hozzunk ltre egy j fjlt:
using System;
using System.Xml;
class Program
{
static public void Main(string[] args)
{
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");
Console.ReadKey();
}
}

Az XmlText illetve XmlElement osztlyok is az XmlNode leszrmazottai. A Save metdussal el tudjuk menteni a
dokumentumot, akr fjlnevet, 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:
XmlDocument xdoc = new XmlDocument();
XmlNodeChangedEventHandler handler = null;
handler = (sender, e) =>

230

{
Console.WriteLine(e.Node.Value);
};
xdoc.NodeInserting += handler;

XML SZERIALIZCI
Mi is az a szrializls? A legegyszerbben egy pldn keresztl lehet megrteni a fogalmat. Kpzeljk el, hogy
rtunk egy jtkot, s a jtkosok pontszmt szeretnnk eltrolni! Az elrt pontszm mell jn a jtkos neve
s a teljests ideje is. Ez elsre semmi klns, hiszen simn kirhatjuk az adatokat egy fjlba, majd
visszaolvashatjuk onnan. Egy apr gond mgis van, mgpedig az, hogy ez elg bonyolult feladat: figyelni kell a
beolvasott adatok tpusra, formtumra stb.
Nem lenne egyszerbb, ha a kirt adatokat meg tudnnk feleltetni egy osztlynak? Ezt megtehetjk, ha
ksztnk egy megfelel osztlyt, amelyet szrializlva kirhatunk XML formtumba, s onnan vissza is
olvashatjuk (ezt deszrializlsnak hvjk). Termszetesen a szrializls jelentsge ennl sokkal nagyobb, s
nemcsak XML segtsgvel tehetjk meg. Tulajdonkppen a szrializls annyit tesz, hogy a memriabeli
objektumainkat egy olyan szekvencilis formtumba (pl. binris vagy most XML) konvertljuk, amelybl vissza
tudjuk alaktani az adatainkat.
Mr emltettk, hogy tbb lehetsgnk is van a szerializcira, ezek kzl az XML a legltalnosabb, hiszen ezt
ms krnyezetben is felhasznlhatjuk.
Kezdjk egy egyszer pldval (hamarosan megvalstjuk a pontszmos pldt is), szrializljunk egy
hagyomnyos beptett objektumot:
FileStream fstream = new FileStream("serxml.xml", FileMode.Create);
XmlSerializer ser = new XmlSerializer(typeof(DateTime));
ser.Serialize(fstream, DateTime.Now);

Ezutn ltrejn egy XML file, benne a szrializlt objektummal:


<?xml version="1.0"?>
<dateTime>2008-10-23T16:19:44.53125+02:00</dateTime>

Az XmlSerializer a System.Xml.Serialization nvtrben tallhat. A deszrializls hasonlkppen mkdik:


FileStream fstream = new FileStream("serxml.xml", FileMode.Open);
XmlSerializer ser = new XmlSerializer(typeof(DateTime));
DateTime deser = (DateTime)ser.Deserialize(fstream);

Most mr eleget tudunk ahhoz, hogy ksztsnk egy szrializlhat osztlyt. Egy nagyon fontos dolgot kell
megjegyeznnk: az osztlynak csakis a publikus tagjai szrializlhatak, a private vagy protected elrsek
automatikusan kimaradnak (emellett az osztlynak magnak is publikus elrsnek kell lennie). Ezeken kvl
mg szksg lesz egy alaprtelmezett konstruktorra is (a deszrializlshoz, hiszen ott mg nem tudjuk, hogy
milyen objektumrl van sz).
public class ScoreObject
{
public ScoreObject()
{
}
private string playername;
[XmlElement("PlayerName")]
public string PlayerName

231

{
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 fjlban (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>2012-07-24T10:31:43.8922442+02:00</Date>
</ScoreObject>

232

KONFIGURCIS FJL 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 fjlban trolnnk. Termszetesen ezt megtehetjk gy is, hogy
egy sima szveges fjlt ksztnk, s azt olvassuk/rjuk, de a .NET ezt is megoldja helyettnk a konfigurcis
fjlok bevezetsvel. Ezek tulajdonkppen XML dokumentumok, amelyek a kvetkezkppen plnek fel:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<appSettings>
<add key="1" value="alma" />
<add key="2" value="korte" />
</appSettings>
</configuration>

Az els sor egy szabvnyos XML file fejlce, evvel 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).
Visual Studio-ban knnyen hozzadhatunk egy konfigurcis fjlt a projektnkhz: jobb gombbal kattintsunk a
projekten, majd az Add menbl vlasszuk a New Item elemet, a listban pedig keressk meg az
Application Configuration File sort! Az OK gombra kattintva ltrejn egy App.config nev fjl a projekten
bell. Az res <configuration> szekciba msoljuk bele a fenti sorokat!
Mg valamit meg kell tennnk: jobb gombbal kattintsunk a projekten bell a References mappn, majd
vlasszuk az Add Reference elemet, majd a .NET fln a listbl keressk ki a System.Configuration
assemblyt, s az Add gombbal adjuk hozz a projekthez! Most mr rhatunk egy programot, amely felhasznlja
ezt a fjlt:
using System;
using System.Configuration;
class Program
{
static public void Main(string[] args)
{
string s = ConfigurationManager.AppSettings["1"];
Console.WriteLine(s); // alma
Console.ReadKey();
}
}

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, ha hasznlni akarjuk , 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-fjlba egy j kulcs-rtk prost:
<?xml version="1.0"?>
<configuration>

233

<appSettings>
<add key="1" value="alma"/>
<add key="2" value="korte"/>
<add key="3" value="10"/>
</appSettings>
</configuration>

s hasznljuk is fel a forrsban:


using System;
using System.Configuration;
class Program
{
static public void Main(string[] args)
{
int x = int.Parse(ConfigurationManager.AppSettings["3"]);
Console.WriteLine(x); // 10
Console.ReadKey();
}
}

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 fjlban is megjelenik.
A kvetkez pldban elksztnk egy programot, amely konfigurcis fjlbl kiolvas egy fjlnevet s a
kdtblt, s ezek alapjn kirja az llomny tartalmt. Els lpsknt ksztsnk egy j osztlyt (Add/Class)
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;
}
}

234

[ConfigurationProperty("encodeType", IsRequired = true)]


public string EncodeType
{
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 a konfig-fjlban szerepl nevet, illetve, hogy muszj megadnunk
ezeket az rtkeket (a DefaultValue segtsgvel kezdrtket is megadhatunk). Most jn a konfigurcis
llomny, 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 most a projekt neve lesz.
Most jjjn az osztly, amely hasznlja az j szekcit! Ksztsnk egy DataHandlerClass.cs nev fjlt 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();
}
}

235

}
}
}

Most mr knnyen hasznlhatjuk az osztlyt:


using System;
using System.Configuration;
class Program
{
static public void Main(string[] args)
{
DataHandlerClass handler = new DataHandlerClass();
handler.PrintData();
Console.ReadKey();
}
}

236

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.

SOCKET
A socketek szmtgpes hlzatok (pl. az Internet) kztti kommunikcis vgpontok. Minden socket
rendelkezik kt adattal, amelyek ltal egyrtelmen azonosthatak s elrhetek: ezek az IP cm s a port
szm.
Mi az az IP cm? Az Internet az n. Internet Protocol (IP) szabvnya szerint mkdik. Eszerint a hlzaton lv
minden szmtgp egyedi azonostval IP cmmel rendelkezik (ugyanakkor egy szmtgp tbb cmmel is
rendelkezhet, ha tbb hlzati hardvert hasznl). Egy IP cm egy 32 bites egsz szm, amelyet 8 bites rszekre
osztunk (pl.: 123.255.0.45), ezltal az egyes szekcik legmagasabb rtke 255 lesz (ez a jellemz az IP negyedik
genercijra vonatkozik, az j hatos generci mr 128 bites cmeket hasznl, igaz ez egyelre kevsb
elterjedt (a .NET tmogatja az IPv4 s IPv6 verzikat is)).
Az IP cmet tekinthetjk az orszg/vros/utca/hzszm ngyesnek, mg a portszmmal egy szobaszmra
hivatkozunk. A portszm egy 16 bites eljel nlkli szm 1 s 65535 kztt. A portszmokat ki lehet sajttani,
vagyis ezltal biztostjk a nagyobb szoftvergyrtk, hogy ne legyen semmilyen tkzs a termk hasznlatakor.
A portszmok hivatalos regisztrcijt az Internet Assigned Numbers Authority (IANA) vgzi.
Az 1 s 1023 kztti portokat n. well-knowed portknt ismerjk, ezeken olyan szles krben 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 e felettieket 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 domain-nv vagy tartomnynv intzmnyt, amely rl egy adott IP cmre, vagyis ahelyett,
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;
class Program
{
static public void Main(string[] args)
{
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);

237

Console.ReadKey();
}
}

Most mr ideje nagyobb kihvs utn nzni, 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;
class Program
{
static public void Main(string[] args)
{
IPAddress ip = IPAddress.Parse("127.0.0.1");
int port = 5000;
IPEndPoint endPoint = new IPEndPoint(ip, port);
TcpListener server = new TcpListener(endPoint);
server.Start();
Console.WriteLine("A szerver elindult!");
Console.ReadKey();
}
}

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 (rtelemszeren 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).
A szervert a Stop metdussal llthatjuk le.
using System;
using System.Net.Sockets;
using System.Net;
namespace Server
{
class Program
{
static void Main(string[] args)
{
TcpListener server = null;
try
{
IPAddress ipAddr = IPAddress.Parse("127.0.0.1");
int portNum = 5000;
IPEndPoint endPoint = new IPEndPoint(ipAddr, portNum);
server = new TcpListener(endPoint);
server.Start();
Console.WriteLine("A szerver elindult!");

238

Console.ReadKey(); // vrunk bejv kapcsolatra


}
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 Console.ReadKey azrt kell, mert klnben ugrannk is tovbb a finally gra, ahol lezrjuk a szervert.
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("127.0.0.1", 5000);
Console.WriteLine(client.Connected); // true
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
finally
{
client.Close();
}
Console.ReadKey();
}
}

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 szervert s klienst is egyszerre elindthassuk anlkl, hogy kt Visual Studio-t nyitnnk, elszr
ksztsnk egy kln projektet a kliens vagy szerver szmra! Ezutn kattintsunk jobb gombbal a Solution-n

239

(nem a projekten), s vlasszuk a Set StartUp Projects menpontot! Jelljk be a Multiple StartUp Projects
kapcsolt s a kvnt projektek Action oszlopban lltsuk a megadott rtket Start-ra! Figyeljnk arra, hogy a
szervert tartalmaz projekt legyen ell!
Most mr elindthatjuk a programjainkat, s a kliens ablakban a True feliratot fogjuk ltni, hiszen a Connected
tulajdonsg azt mondja meg, hogy van-e aktv kapcsolat vagy sem.
Ahhoz, hogy a szerver kezelni is tudja a kliensnket (eddig csak kapcsolatot ltestettnk), 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 a szerverrl a kliensnek.
Nzzk az j szervert:
using
using
using
using

System;
System.Text;
System.Net.Sockets;
System.Net;

namespace Server
{
class Program
{
static void Main(string[] args)
{
TcpListener server = null;
NetworkStream stream = null;
try
{
IPAddress ipAddr = IPAddress.Parse("127.0.0.1");
int portNum = 5000;
IPEndPoint endPoint = new IPEndPoint(ipAddr, portNum);
server = new TcpListener(endPoint);
server.Start();
Console.WriteLine("A szerver elindult!");
TcpClient client = server.AcceptTcpClient();
byte[] data = Encoding.ASCII.GetBytes("Hello kliens...");
stream = client.GetStream();
stream.Write(data, 0, data.Length);
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
finally
{
stream.Close();
server.Stop();
}
}
}
}

240

Az elkldeni kvnt zenetet bytetmb formjban kell tovbbtanunk, mivel a TCP/IP byet-ok sorozatt
tovbbtja, ezrt szmra emszthet formba kell hoznunk az zenetet. A fenti pldban a legegyszerbb,
ASCII kdolst vlasztottuk, de mst is hasznlhatunk; a lnyeg, hogy tudjuk byte-onknt kldeni
(rtelemszeren mind a kliens, mind a szerver ugyanazt a kdolst kell hasznlja).
A NetworkStream ugyanolyan adatfolyam, amit a fjlkezelssel foglalkoz fejezetben megismertnk, csak
ppen az adatok most a hlzaton keresztl folynak t. A Write metdus hrom paramtere kzl az els a
kldeni kvnt adat (byte-tmb formban), utna a kezdpozici (ebben azesetben az elejtl kezdve runk,
vagyis nulla), majd a hossz kvetkezik.
A szerver az zenet elkldse utn lell, a program pedig befejezi futst. Fontos, hogy az erforrsokat
megfelel sorrendben zrjuk le, elszr a streamet s csak utna kvetkezik maga szerver. Lssuk a kliens
oldalt:
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("127.0.0.1", 5000);
byte[] data = new byte[128];
stream = client.GetStream();
int length = stream.Read(data, 0, data.Length);
Console.WriteLine("A szerver zenete: {0}",
Encoding.ASCII.GetString(data));
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
finally
{
stream.Close();
client.Close();
}
Console.ReadKey();
}
}

Az alapelv ugyanaz, egyedli jdonsg a byte-tmb inicializlsa. Most nem tudjuk pontosan, hogy milyen
hossz adatot kapunk a szervertl, ezrt tippelnk (igazbl tudjuk, de ez most nem szmt). Ha nincs elg
hely akkor nem kapunk kivtelt, de a bejv adatbl csak annyit ltunk viszont, amennyi a tmbben elfr. A
NetworkStream Read metdusa hasonlan mkdik a Write-hoz, viszont visszaadja a bejv zenet hosszt
(byte-ban).
Mr ismerjk az alapokat, eljtt az ideje, hogy egy szinttel lejjebb lpjnk a socketek vilgba.

241

using
using
using
using

System;
System.Text;
System.Net.Sockets;
System.Net;

namespace Server
{
class Program
{
static 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("127.0.0.1"),
5000);
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();
}
Console.ReadKey();
}
}
}

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.

242

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

System;
System.Net;
System.Net.Sockets;
System.Text;

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("127.0.0.1"),
5000); ;
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();
}
Console.ReadKey();
}
}

A klnbsget a Connect metdus jelenti, mivel most kapcsoldni akarunk, nem hallgatzni.

243

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);
}
}

A ciklus egszen addig fut, amg nincs bejv kapcsolat, ezt az l vltozval jelezzk.
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);
}
}

Itt egy msik mdszert, egy vgtelen ciklust vlasztottunk a ciklus kezelsre, amikor rkezik bejv adat, a
break utastssal kitrnk belle. A vrakozs idszakban pedig 200 ezredmsodpercre meglltjuk a szlat.
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.

244

Nzzk elsknt a TcpListener -t:


while (true)
{
if (server.Pending())
{
client = server.AcceptTcpClient();
Console.WriteLine("Kliens kapcsoldott...");
stream = client.GetStream();
byte[] data = new byte[256];
int length = stream.Read(data, 0, data.Length);
Console.WriteLine("A kliens zenete: {0}",
Encoding.ASCII.GetString(data, 0, length));
data = Encoding.ASCII.GetBytes("Hello kliens!");
stream.Write(data, 0, data.Length);
}
else
{
Console.WriteLine("Most valami mst csinlunk");
System.Threading.Thread.Sleep(500);
}
}

A Pending azt az informcit osztja meg velnk, hogy vrakozik-e bejv kapcsolat. Tulajdonkppen ez a
metdus a kvetkez forrsban szerepl (a Socket osztlyhoz tartoz) Poll metdust hasznlja:
while (true)
{
if (server.Poll(0, SelectMode.SelectRead))
{
client = server.Accept();
/* itt pedig kommuniklunk a klienssel */
}
else
{
Console.WriteLine("A szerver bejv kapcsolatra vr!");
System.Threading.Thread.Sleep(500);
}
}

A Poll metdus els paramtere egy egsz szm, amely mikromsodpercben (nem milli-, itt valban a
msodperc milliomod rszrl van sz, vagyis ha egy msodpercig akarunk vrni, akkor 1000000ot kell
megadnunk) adja meg azt az idt, amg vrunk bejv kapcsolatra/adatra. Amennyiben az els paramter
negatv szm, akkor addig vrunk, amg nincs kapcsolat (vagyis blokkoljuk a programot), ha pedig nullt adunk
meg, akkor hasznlhatjuk prechecking-re is a metdust.
A msodik paramterrel azt mondjuk meg, hogy mire vrunk. A SelectMode felsorolt tpus hrom taggal
rendelkezik:

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.
SelectWrite: igaz rtket kapunk vissza, ha a Connect metdus hvsa sikeres volt, azaz csatlakoztunk
a tvoli llomshoz, illetve ha lehetsges adat kldse.
SelectError: true rtket ad vissza, ha a Connect metdus hvsa sikertelen volt.

245

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 sokkal hatkonyabb mdszer(eke)t
fogunk megvizsglni.

TBB KLIENS KEZELSE


Az eddigi programjainkban csak egy klienst kezeltnk, ami nagyon knyelmes volt, mivel egy sor dolgot
figyelmen kvl hagyhattunk:

A kliensekre mutat referencia trolsa.


A szerver akkor is tudjon kapcsolatot fogadni, amg a bejelentkezett kliensekkel foglalkozunk.
Minden kliens zavartalanul (lehetleg blokk nlkl) tudjon kommuniklni a szerverrel s viszont.

A kvetkezkben hromflekppen fogjuk krljrni a problmt.


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:
static public void Select(
IList checkRead,
IList checkWrite,
IList checkError,
int microSeconds
)

Az els hrom paramter olyan IList interfszt implementl gyjtemnyek (lnyegben az sszes beptett
gyjtemny ilyen, belertve a sima tmbket is), amelyek Socket pldnyokat tartalmaznak. Az els
paramternek megadott listt olvashatsgra, a msodikat rhatsgra, mg a harmadikat hibkra ellenrzi a
Select, majd a felttelnek megfelel listaelemeket megtartja a listban, a tbbit eltvoltja (vagyis ha
szksgnk van a tbbi elemre is, akkor clszer egy msolatot hasznlni az eredeti lista helyett). Az utols
paramterrel azt adjuk meg, hogy mennyi ideig vrjunk a kliensek vlaszra (mikroszekundum).
Ksztsnk egy Selectet hasznl kliens-szerver alkalmazst! A kliens oldalon lnyegben semmit nem
vltoztatunk azt leszmtva, hogy folyamatosan zenetet kldnk a szervernek (most csak egyirny lesz a
kommunikci):
Random r = new Random();
while (true)
{
if (r.Next(1000) % 2 == 0)
{
byte[] data = new byte[256];
data = Encoding.ASCII.GetBytes("Hello szerver!");
client.Send(data, data.Length, SocketFlags.None);
}
System.Threading.Thread.Sleep(500);
}

246

Most nzzk a szervert:


int i = 0;
while (i < MAXCLIENT)
{
Socket client = server.Accept();
clientList.Add(client);
++i;
}
while (true)
{
ArrayList copyList = new ArrayList(clientList);
Socket.Select(copyList, null, null, 1000);
foreach (Socket client in clientList)
{
Program.CommunicateWithClient(client);
}
}

A MAXCLIENT vltoz egy egyszer egsz szm, meghatrozzuk vele, hogy maximum hny klienst fogunk
kezelni. Miutn megfelel szm kapcsolatot hoztunk ltre, elkezdnk beszlgetni a kliensekkel. A ciklus
minden itercijban meghvja a Select metdust, vagyis kivlasztjuk, hogy melyik kliensnek van mondandja.
A CommunicateWithClient statikus metdus a mr ismert mdon olvassa a kliens zenett:
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));
}

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 kapnak, 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 BeginAccept-et, ezutn pedig vrunk, hogy az AcceptCallback metdus
visszajelezzen a fszlnak, hogy a kliens csatlakozott, s folytathatja a figyelst. Nzzk az AcceptCallback-ot:

247

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).
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.

248

Szlakkal megvalstott szerver


Ez a mdszer nagyon hasonl az aszinkron vltozathoz, azzal a klnbsggel, hogy most manulisan hozzuk
ltre a szlakat, illetve nemcsak 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: -1-et, ha a gondolt szm nagyobb, 1et, ha a szm kisebb, 0-t, ha eltallta a szmot s 2-t, ha valaki mr 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, a kvetkez IP cmrl: {0}",
((IPEndPoint)client.RemoteEndPoint).Address.ToString());
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();
}

249

++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;
bool win =
while (i <
{
data =
length

false;
5 && win == false)
new byte[128];
= 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;
}

250

else if (NUMBER < number)


{
result = 1;
}
else
{
result = -1;
}
}
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 a 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.

TCP S UDP
Ez a fejezet elsdlegesen a TCP protokollt hasznlta, de emltst kell tennnk az Internet msik
alapprotokolljrl, az UDP-rl is.
A TCP megbzhat, de egyttal 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. Ezenkvl 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 alkalmaz trst.

251

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 XML-hez 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.

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 egy idben
bellthassuk a tulajdonsgaikat, illetve gyjtemnyek esetben az elemeket:
using System;
using System.Collections.Generic;
namespace Server
{
class Program
{
static void Main(string[] args)
{
/*Objektum inicializl*/
MyObject mo = new MyObject()
{
Property1 = "value1",
Property2 = "value2",
};
/*Gyjtemny inicializl*/

252

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 rendkvl nehz elre megadni az eredmnylista tpust, ezrt ilyenkor a
var-t fogjuk hasznlni, bzzuk magunkat a fordtra.
Nvtelen tpusok: sok esetben nincs szksgnk egy objektum minden adatra, ilyenkor feleslegesen foglaln
egy lekrdezs eredmnye a memrit. A nvtelen tpusok bevezetse lehetv teszi, hogy helyben
deklarljunk egy nvtelen osztlyt:
using System;
namespace Server
{
class Program
{
static void Main(string[] args)
{
var type1 = new { Value1 = "alma", Value2 = "krte" };
Console.WriteLine(type1.Value1); //alma
Console.ReadKey();
}
}
}

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.
using System;
using System.Collections.Generic;
using System.Linq;
namespace Server
{
class Program
{
static void Main(string[] args)
{
List<int> list = new List<int>()
{
10, 2, 4, 55, 22, 75, 30, 11, 12, 89
};
var result = from number in list select number;

253

foreach (var item in result)


{
Console.WriteLine("{0}", item);
}
Console.ReadKey();
}
}
}

Elssorban vegyk szre az j nvteret, a System.Linq-t. R lesz szksgnk mostantl az sszes lekrdezst
tartalmaz programban, teht ne felejtsk le! Nem mintha lenne vlasztsunk, hiszen ebben az esetben le sem
fordulna a program. 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 adja meg 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.


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:

254

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;
namespace Server
{
class Program
{
static void Main(string[] args)
{
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);
}
Console.ReadKey();
}
}
}

A jegyzet ezutn mindkt vltozatot bemutatja a forrskdokban.


Projekci
Vegyk a kvetkez egyszer osztly-hierarchit:
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

255

{
public
public
public
public
public
public

int ID { get; set; }


string FirstName { get; set; }
string LastName { get; set; }
string Email { get; set; }
string PhoneNumber { get; set; }
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, e-mail cmt s telefonszmt. Ez nem 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.
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.
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

256

select word;

A let segtsgvel minden sorbl egy jabb string-tmbt ksztettnk, amelyeken egy bels lekrdezst
futtattunk le (teht az eredmnylistban az eredeti stringek szavai lesznek).

SZRS
Nyilvn 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;
namespace Server
{
class Program
{
static void Main(string[] args)
{
List<int> list = new List<int>()
{
12, 4, 56, 72, 34, 0, 89, 22
};
var result1 = from number in list
where number > 30
select number;
var result2 = list.Where(number => number > 30);
var result3 = (from number in list select number)
.Where(number => number > 30);
foreach (var item in result1)
{
Console.WriteLine(item);
}
Console.ReadKey();
}
}
}

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)

257

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;
namespace Server
{
class Program
{
static void Main(string[] args)
{
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)
{
Console.WriteLine("{0}", item);
}
Console.ReadKey();
}
}
}

A kimenet pedig ez lesz:

Lekrdezs eltt...
Lekrdezs utn...
Szrs...
Szrs...
Szrs...
56
Szrs...

258

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.
Ne hasznljuk a kvetkez formt:
var result = list.Select(x => x).Where(x => x > 10);

Ltszlag szablyos a lekrdezs, azonban a fordt a Select s Where szmra is elkszt egy fggvnyt, holott
a munkt egyedl a Where is elvgezn. gy viszont egy plusz felesleges fggvnyhvs az eredmny.

RENDEZS
A lekrdezsek eredmnyt knnyen rendezhetjk az orderby utastssal, a lekrdezs sablonja ebben az
esetben gy alakul:
from azonost in kifejezs where kifejezs orderby tulajdonsg ascending/descending select kifejezs
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

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;
namespace Server
{
class Program
{

259

static void Main(string[] args)


{
List<string> names = new List<string>()
{
"Szandra", "Istvn", "Ivn", "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);
}
Console.ReadKey();
}
}
}

Az Extension Method formban a ThenBy volt segtsgnkre, mg a Query szintaxissal csak annyi a dolgunk,
hogy az alapszably mg rjuk a tovbbi kritriumokat. Egy lekrdezs pontosan egy orderby/OrderByt s
brmennyi ThenByt tartalmazhat.
Az OrderBy metdus egy msik vltozata kt paramtert fogad, az els a rendezs alapszablya, mg msodik
paramterknt megadhatunk egy tetszleges IComparer<T> interfszt megvalst osztlyt.

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:
using System;
using System.Collections.Generic;
using System.Linq;
namespace Server
{
class Program
{
static void Main(string[] args)
{
List<string> names = new List<string>()
{
"Szandra", "Istvn", "Ivn", "Joln", "Jen", "Bla",
"Balzs", "Viktria", "Vazul", "Thtm", "Tams"
};

260

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);
}
}
Console.ReadKey();
}
}
}

A kimenet a kvetkez lesz:


B
-- Bla
-- Balzs
I
-- Istvn
-- Ivn
J
-- Joln
-- Jen
S
-- Szandra
T
-- Thtm
-- Tams
V
-- Viktria
-- Vazul
A csoportostshoz meg kell adnunk egy kulcsot, ez lesz az OrderBy paramtere. Az eredmny tpusa a
kvetkez lesz:
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.

Null rtkek kezelse

261

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.
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.
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:

262

using System;
using System.Collections.Generic;
using System.Linq;
namespace Server
{
class Program
{
static void Main(string[] args)
{
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);
}
}
Console.ReadKey();
}
}
}

LISTK SSZEKAPCSOLSA
A relcis adatbzisok egyik alapkve, hogy egy lekrdezsben tbb tblt sszekapcsolhatunk (join) egy
lekrdezssel, pl. egy webruhzban a vev-ru-rendels adatokat. Memriban lv gyjtemnyek esetben
ez viszonylag ritkn szksges, de a LINQ To Objects tmogatja ezt is.
A join-mveletek azon az egyszer felttelen alapulnak, hogy az sszekapcsoland objektumlistk 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 int CustomerID { get; set; }

263

public
public
public
public

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 join-nal 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):
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
-- Paprzsebkend
Jzsef Fekete
-- Elektromos vzforral

264

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.:
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);
}

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.Generic;
System.Linq;
System.Collections;

namespace Server
{
class Program
{
static void Main(string[] args)
{
ArrayList list = new ArrayList();
list.Add("alma");
list.Add("di");
list.Add(12345);

265

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
}
Console.ReadKey();
}
}
}

A program ugyan kirja a lista els kt elemt, de a harmadiknl mr kivtelt dob, teht a konverzi elemenknt
trtnik, mgpedig akkor, amikor az eredmnylistt tnylegesen felhasznljuk, nem pedig a lekrdezsnl.
ToArray, ToList, ToLookup, ToDictionary: ezek a metdusok ahogyan a nevkbl is ltszik az IEnumerable<T>
eredmnylistt tmbb vagy generikus gyjtemnny alaktjk. A kvetkez pldban a ToList metdus
lthat:
using
using
using
using

System;
System.Collections.Generic;
System.Linq;
System.Collections;

namespace Server
{
class Program
{
static void Main(string[] args)
{
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));
Console.ReadKey();
}
}
}

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:

266

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);
}
}

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.

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;
namespace Server
{
class Program
{
static void Main(string[] args)
{
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);

267

}
var result5 = list.FirstOrDefault((item) => item < 3); // 0
Console.WriteLine("{0}, {1}, {2}, {3}",
result1, result2, result3, result5);
Console.ReadKey();
}
}
}

Single/SingleOrDefault: ugyanaz, mint a First/FirstOrDefault pros, de mindkett kivtelt dob, ha a felttelnek


tbb elem is megfelel.
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.

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>()
{

268

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
*/

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

269

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:
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.

PLINQ PRHUZAMOS VGREHAJTS


Egy korbbi fejezetben mr megismerkedtnk a prhuzamos programozst tmogat Task Parallel Library-val.
Azonban a Microsoft nemcsak a hagyomnyos programok eltt nyitotta meg a prhuzamossg kapujt,
hanem a LINQ lekrdezseinket is felgyorsthatjuk ezen a mdon.
PLINQ a gyakorlatban
Hasonltsuk ssze a hagyomnyos s prhuzamos LINQ lekrdezsek teljestmnyt! Elssorban szksgnk
lesz valamilyen megfelelen nagy adatforrsra, amely jelen esetben egy szveges fjl lesz. A knyvhz mellkelt
forrskdok kztt megtalljuk a DataGen.cs nevt, amely az els paramterknt megadott fjlba a msodik
paramterknt megadott mennyisg vezetknv-keresztnv-kor-foglalkozs-megye adatokat r. A kvetkez
pldban tzmilli szemllyel fogunk dolgozni, teht gy futtassuk a programot:
DataGen.exe E:\Data.txt 10000000
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;

270

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 eredmnyt 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:

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:

271

1.

2.

3.
4.

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
a WithExecutionMode metdussal.
Ha az tlet a prhuzamossgra nzve kedvez, akkor a kvetkez lpsben a feldolgozand adatot a
rendszer a rendelkezsre ll processzorok szma alapjn elosztja. Ez meglehetsen bonyolult tma,
az adatforrstl fggen tbb stratgia is ltezik, ezt itt nem rszletezzk.
Vgrehajts.
A rszeredmnyek sszeillesztse. Erre a rszre is lehet rhatsunk, miszerint egyszerre szeretnnk a
vgeredmnyt megkapni, vagy megfelelnek a rszeredmnyek is. Erre a clra a WithMergeOptions
metdust kell meghvnunk a lekrdezsen. A paramtereknt hasznlatos ParallelMergeOptions
felsorols hrom taggal rendelkezik: a NotBuffered hatsra azonnal visszakapunk minden egyes
elemet a lekrdezsbl, az AutoBuffered periodikusan ad vissza tbb elemet, mg a FullyBuffered csak
akkor kldi vissza az eredmnyt, ha a lekrdezs teljesen ksz van.

Lthatjuk, hogy komoly szmtsi kapacitst ignyel a rendszertl egy prhuzamos lekrdezs vgrehajtsa, pp
ezrt a nem megfelel lekrdezsek parallel mdon val indtsa nem fogja az elvrt teljestmnyt hozni,
tulajdonkppen mg teljestmnyromls is lehet az eredmnye. Tipikusan olyan helyzetekben akarunk ilyen
lekrdezseket hasznlni, ahol nagy mennyisg elemen sok vagy drga mveletet vgznk el, mivel itt
nyerhetnk a legtbbet.
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 nhny ezer elemre is tesztelni).

272

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!
AsSequential
Az AsSequential metdus pp ellenkezje az AsParallelnek, vagyis szablyozhatjuk, hogy egy lekrdezs mely
rsze legyen szekvencilis s melyik prhuzamos. A kvetkez pldban megnzzk, hogy ez mirt j neknk. A
PLINQ egyelre mg nem teljesen kiforrott, annak a szablyai, hogy mely opertorok lesznek mindig
szekvencilisan kezelve a jvben valsznleg vltozni fognak, ezrt a pldaprogramot illik fenntartssal
kezelni:
var result = (from x in list.AsParallel() select x).Take(10);

Kivlasztjuk az eredmnylistbl az els tz elemet. A problma az, hogy a Take opertor szekvencilisan fut
majd le fggetlenl attl, hogy mennyire bonyolult a bels lekrdezs, pp ezrt nem kapunk semmifle
teljestmnynvekedst. rjuk t egy kicsit:
var result2 = (from x in list.AsParallel() select x)
.AsSequential().Take(10);

Most pontosan azt kapjuk majd, amire vrunk: a bels prhuzamos mveletsor vgeztvel visszavltottunk
szekvencilis mdba, vagyis a Take nem fogja vissza a sebessget.

273

GRAFIKUS FELLET ALKALMAZSOK WINDOWS FORMS


Az eddigi fejezetekben parancssoros alkalmazsokat ksztettnk, hogy a nyelv elemeinek megrtsre
koncentrlhassunk. Azonban a modern alkalmazsok nagy rsze rendelkezik grafikus fellettel, amellyel
kezelsk knyelmesebb vlik az tlagfelhasznlk szmra is. Szmos lehetsgnk van ilyen programok
ksztsre, ebben a knyvben a Windows Forms knyvtrral ismerkednk meg, ami meglehetsen egyszeren
elsajtthat! Ez a rendszer a .NET-tel szlltott els grafikus interfsz, tulajdonkppen a .Net eltti COM
osztlyokat burkolja be, hogy azok menedzselt krnyezetben legyenek hasznlhatak.

HELLO WINDOWS FORMS


Ksztsnk egy j projektet, Windows Forms Application sablonnal:

A projekt elkszlte utn az n. Design nzetbe kerlnk, ahol az alkalmazsunk lthat, egyelre mg resen.
Ugyanakkor mris elindthatjuk, br sok haszna mg nincsen. rdemes ilyen projekt tpusnl a kpen lthat
mdon megnyitni a Properties ablakot, hiszen az egyes vezrlk tulajdonsgait itt knnyen bellthatjuk.

274

Mieltt csinlnnk valami hasznosat, rdemes megnzni, hogyan pl fel egy ilyen projekt. Kattintsunk jobb
gombbal a Design nzeten, s vlasszuk a View Code menpontot! Ekkor megjelenik az n. code-behind fjl
tartalma:
using
using
using
using
using
using
using
using

System;
System.Collections.Generic;
System.ComponentModel;
System.Data;
System.Drawing;
System.Linq;
System.Text;
System.Windows.Forms;

namespace WFDemo
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
}
}

Kt dolgot kell szrevennnk: az egyik, hogy ez egy parcilis osztly, a msik pedig az, hogy a konstruktorban
meghvtunk egy InitializeComponent nev metdust, aminek a defincijt viszont nem ltjuk. Ezt a metdust a
fordt automatikusan generlja, s az ablakon szerepl vezrlk s az alkalmazs alapbelltsrt felels. Ha a
Solution Explorerben lenyitjuk a Form1.cs fjlt, lthatunk egy Form1.Designer.cs nev llomnyt is, amely
nem ms, mint a fent lthat osztlyunk msik fele. Ebbe a fjlba semmikppen se rjunk, minden egyes
fordtskor jragenerldik, vagyis az ltalunk rt kd elveszne. Erre egybknt szksg sincsen, hiszen az
ltalunk mdosthat felbl ugyangy elrnk mindent, mint innen.
Amennyiben a konstruktorban szeretnnk a program valamely tulajdonsgn lltani azt mindenkppen az
InitializeComponent hvs utn tegyk meg, ellenkez esetben Nullreference kivtelt kapunk, hiszen a vezrlk
csak a futsa utn lesznek pldnyostva.
Nemcsak ennyibl ll azonban a projektnk: ha emlksznk, minden .NET program tartalmaz egy Main
fggvnyt is. Ezt a Program.cs fjlban leljk meg:

275

using
using
using
using

System;
System.Collections.Generic;
System.Linq;
System.Windows.Forms;

namespace WFDemo
{
static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
}

Most mr elkszthetjk az els igazi Windows Forms programunkat. Trjnk vissza a Design nzetbe, s
hozzuk el a ToolBox-ot, ez alaprtelmezs szerint bal oldalt kell legyen, ha mgsem, akkor a Ctrl+W
billentykombincival csalogathatjuk el. Az Auto Hide kikapcsolsval (kis rajzszg az ablak jobb fls
sarkban) rgzthetjk is. Ebben az ablakban megtallhatjuk az sszes vezrlt, amelyet felhasznlhatunk,
egyelre elg lesz a Common Controls csoportban lv gyjtemny is:

276

Hzzunk egy Button, vagyis gomb vezrlt az alkalmazsunkra:

A gombra kattintva a Properties ablakban a Text mezben lltsuk be a feliratt:

Helyezznk el az ablakon mg egy TextBox vezrlt is, tetszleges mdon! Most pedig programozzunk egy
kicsit! Azt szeretnnk elrni, hogy a gomb megnyomsakor valamilyen szveg jelenjen meg a szvegmezben.
Valjban minden tudssal rendelkeznk, amely ehhez a feladathoz szksges, legalbbis ha mr elolvastuk az
Esemnyekrl szl fejezetet. Ugyanis minden vezrlhz tartoznak esemnyek, amelyekre feliratkozva
tetszleges mveletet hajthatunk vgre. Ha dupln kattintunk a gombon, a code-behind fjlba kerlnk, s
megjelenik ott egy esemnykezel metdus is button1_Click nven (minden vezrl automatikusan nevet kap
a vezrl tpusa+sorszm formban, ezt mdosthatjuk a Properties ablak Name mezjben).
A duplakattints az adott vezrl alaprtelmezett esemnyhez kszt esemnykezelt, ez egy gomb esetn
rtelemszeren a kattints lesz. Ms esemnyekhez a Properties ablakban rendelhetnk esemnykezelket, a
kis villm gombra kattintva.
Mr csak annyi a dolgunk, hogy runk valamit a TextBox-ba, ezt a Text tulajdonsgn keresztl tudjuk
megtenni:
private void button1_Click(object sender, EventArgs e)
{
textBox1.Text = "Hello";
}

277

VEZRLK
Vezrlk alatt azokat az elemeket rtjk, amelyekbl egy grafikus fellet felpl. A legtbb esetben valamilyen
akcit hajthatunk vgre rajtuk, pldul megnyomunk egy gombot, vagy runk egy szvegmezbe. Ebben a
fejezetben megismerkednk a leggyakrabban hasznlt elemekkel.
Form
Minden Windows Forms alkalmazs alapja. Mivel a ContainerClass osztlybl szrmazik, kpes magban ms
vezrlket trolni. A Form alaprtelmezett esemnye a Load, amely azeltt fut le, hogy a vezrl megjelenne.
Egy Formhoz ktflekppen adhatunk vezrlt, igaz a kt mdszer tulajdonkppen egy s ugyanaz. A
legegyszerbb, hogy a tervez nzetben egyszeren rhzzuk a ToolBox-bl. A msik eset, amikor futsidben
forrskdbl szeretnnk ezt megtenni.
Ez utbbihoz tudni kell, hogy minden ContainerClass-bl szrmaz osztly rendelkezik egy Controls nev
tulajdonsggal, amely valjban az objektum vezrlinek listja. Egy elemet a kvetkezkppen adhatunk egy
Form-hoz, pozci s mret megadsval:
Button button = new Button()
{
Width = 100,
Height = 30,
Location = new Point(30, 40),
Text = "Click"
};
this.Controls.Add(button);

A this itt a fablakra, vagyis az aktulis Form objektumra mutat. A Controls.Add egy Control objektumot vr,
amely osztlybl minden vezrl szrmazik.
Button
A gomb vezrlt mr ismerjk, elsdlegesen arra hasznljuk, hogy elindtsunk vele egy folyamatot, pldul
adatokat mentsnk el. Alaprtelmezett esemnye a kattints.
Label
A felhasznl ltal nem mdosthat szveges tartalmat jelenthetnk meg vele. rtkt a TextBox-hoz
hasonlan a Text tulajdonsgn keresztl adhatjuk meg.

A Label specilis vltozata a LinkLabel, amellyel hivatkozst adhatunk meg. Alaprtelmezett esemnye a
LinkClicked, ahol tirnythatjuk a felhasznlt a kivlasztott helyre.

278

TextBox
Szintn szveges vezrl, de kpes fogadni a felhasznl billentyletseit. MultiLine tulajdonsgt true
rtkre lltva tbb sort is megjelent. A PasswordChar tulajdonsgnak rtkl adott karaktert jelenti meg
minden ms karakter helyett, gy jelszmezknt funkcionl.

Alaprtelmezett esemnye a TextChanged, amely a vezrl tartalmnak megvltozsakor fut le.


ListBox
Ez olyan vezrl, amelyben jellemzen listkat trolunk. Egy ListBox-ot feltlthetnk az Items tulajdonsgn
keresztl (akr a Properties ablakban), vagy pedig n. adatktssel (data binding). Ehhez a DataSource
tulajdonsgot hasznljuk fel. Generljuk le a Form Load esemnyt, majd rjuk a kvetkezt:
private void Form1_Load(object sender, EventArgs e)
{
string[] items = new string[]
{
"Istvn", "Szandra", "Bla", "Balzs"
};
listBox1.DataSource = items;
}

Amikor a DataSource tulajdonsgot hasznljuk, a ListBox az elemek ToString metdust hvja meg, hogy elkrje
a megjelentend informcit. Amennyiben a felhasznlt osztlyunk nem valstja ezt meg (override
hasznlatval), akkor az elemek tpust adja vissza.

A ListBox alaprtelmezett esemnye a SelectedIndexChanged, amely akkor fut le, ha megvltozott a


vezrlben a kijellt elem. Az aktulis kivlasztott elemet a SelectedItem segtsgvel rjk el, ez object
tpussal tr vissza, teht konvertlni kell, ha tovbbi terveink vannak vele. A kivlasztott elem indext (nulltl
kezdve) a SelectedIndex tulajdonsg trolja.
Az Items tulajdonsga hagyomnyos listaknt viselkedik, hasznlhatjuk rajta pldul a foreach ciklust is. A
SelectionMode tulajdonsgt megvltoztatva tbb elemet is kijellhetnk egyszerre. Ez egy enum tpus,
rtkei lehetnek None (nem lehet kivlasztani egy elemet sem), MultiSimple (a Ctrl billenty nyomva

279

tartsval lehet tbb elemet kivlasztani) illetve MultiExtended (az egr nyomva tartsa mellett lehet
kijellni az elemeket).
CheckBox
Ez a vezrl a vlaszts lehetsgt knlja. Tulajdonkppen egy jellngyzetrl van sz, amely segtsgvel
egy vagy tbb opci kzl vlaszthatunk. Alaprtelmezett esemnye a CheckedChanged, amelyet a
bejells/visszavons vlt ki. A Checked tulajdonsgn keresztl krdezhetjk le, hogy be van-e jellve vagy
sem.

Helyezznk el a programunkban mg egy Label-t is, amelynek feliratt annak fggvnyben vltoztassuk, hogy
a CheckBox be van-e jellve vagy sem (a Label Text tulajdonsgt lltsuk res stringre a program futsnak
elejn)! Ezutn kezeljk a CheckedChanged esemnyt:
private void checkBox1_CheckedChanged(object sender, EventArgs e)
{
if (checkBox1.Checked)
label1.Text = "A CheckBox be van jellve!";
else
label1.Text = "A CheckBox nincs bejellve!";
}

RadioButton
Hasonl funkcionalitst knl, mint a CheckBox, de egy adott troln bell (pl. Form) tbb ilyen vezrl egy
csoportot alkot, s egy idben csak egy darab lehet bejellve. Alaprtelmezett esemnye a CheckedChanged.

Egy Formon bell elfordulhatnak nll RadioButton csoportok, amelyeket valahogy el kell klnteni
egymstl. Erre a clra Panel vagy GroupBox vezrlket hasznlhatunk, elbbi grgethet, de nem
rendelhetnk hozz feliratot, utbbi fix s lehet fejlcet megadni hozz. Ezeket a vezrlket a ToolBox
Containers fln talljuk meg.
Az albbi kpen GroupBox s Panel vezrlkben helyeztnk el kt-kt RadioButton-t. Lthat, hogy elbbi eset
ltvnyosabb, hiszen a GroupBox kerettel is rendelkezik, mg a Panel gyakorlatilag lthatatlan.

280

Ha szeretnnk lekrdezni a RadioButton-t trol elemet (ez lehet GroupBox, Form, brmely vezrl, amely
kpes trolni ms vezrlket), azt a Parent tulajdonsgon tehetjk meg. Egy csoporton bell rdemes ugyanazt
az esemnykezelt rendelni az sszes RadioButton-hoz, ezt gy tehetjk meg, hogy kijelljk egyesvel a
vezrlket (Ctrl nyomva tartva s bal egrgomb), majd a Properties ablakban a villm gombbal elhozzuk a
vlaszthat esemnyek listjt, s a kvnt elemen dupln kattintunk:

Ezutn a sender paramteren elvgezve a megfelel tpuskonverzit visszakapjuk azt a vezrlt, amely az
esemnyrl rtestt kldtt:
private void radioButton1_CheckedChanged(object sender, EventArgs e)
{
RadioButton radioButton = (RadioButton)sender;
}

ComboBox
Ez a vezrl a TextBox s a ListBox egyfajta kombincijnak tekinthet, a bert szveg alapjn kivlaszthatunk
egy elemet egy elre megadott listbl. Az adatok feltltse ugyangy zajlik, mint a ListBox esetn (Properties
ablak vagy DataSource tulajdonsg).
private void Form1_Load(object sender, EventArgs e)
{
string[] items = new string[]
{
"Szandra", "Istvn", "Bla", "Balzs"
};

281

comboBox1.DataSource = items;
}

Alaprtelmezett esemnye a SelectedIndexChanged, amely az aktulis elem vltozst jelli. A kivlasztott


elemet illetve indext a ListBox-szal megegyez mdon krdezhetjk le.
NumericUpDown
Ezzel a vezrlvel egy adott szmtartomnybl vlaszthatunk ki egy rtket. A Minimum s Maximum
tulajdonsgaival a legkisebb illetve legnagyobb lehetsges szmot, mg az Increment propertyvel a lptket
llthatjuk be.

Alaprtelmezett esemnye a ValueChanged, amelyet a mutatott rtk vltozsa vlt ki.


ProgressBar
A ProgressBar egy folyamat jellsre szolgl. A NumericUpDown-hoz hasonlan a Maximum, Minimum s
Step tulajdonsgokkal llthatjuk rtkeit. Helyezznk el az alkalmazsunkban egyet, lltsuk a Step rtket 1re, s tegynk mell egy gombot is! A gomb esemnykezeljbe rjuk a kvetkezt:
private void button1_Click(object sender, EventArgs e)
{
for (int i = 0; i < 100; ++i)
{
progressBar1.PerformStep();
System.Threading.Thread.Sleep(100);
}
}

A PerformStep metdus a Step rtk szerint lpteti a vezrlt.

Minden lpsben pihentetjk kicsit a fszlat, hogy lelasstsuk a folyamatot.

282

PictureBox
Ezt a vezrlt kpek megjelentsre hasznljuk. Ktflekppen adhatjuk meg a kvnt elemet: a
terveznzetben a vezrln megjelen kis nyilat kinyitva a Choose Image menpontban az Import gombokkal
vlaszthatunk begyazott illetve kls erforrsbl. Van egy msik lehetsgnk is: forrskdbl egy Image
objektumot rendelhetnk az Image tulajdonsghoz.
private void Form1_Load(object sender, EventArgs e)
{
Image img = new Bitmap("Desert.jpg");
pictureBox1.Image = img;
}

Alaprtelmezett esemnye a kattints.


RichTextBox
A TextBox-hoz hasonlan szveges bevitelre alkalmas, m annl lnyegesen rugalmasabb s tbb lehetsget
is nyjt. Szveget rendelhetnk hozz a Text tulajdonsgn keresztl, txt vagy rtf fjlokbl. Ez utbbi esetben a
klnfle formzsokat (betszn, bettpus stb.) is tmogatja. Alaprtelmezett esemnye a TexChanged.
Az AppendText metdussal a mr ltez tartalomhoz tudunk hozzfzni karaktereket. Fjlokat a LoadFile
metdussal tlthetnk be. Ennek prja a SaveFile, amellyel tbb formtumba is menthetnk:
// szveges llomny
richTextBox1.SaveFile("test.txt", RichTextBoxStreamType.PlainText);
//rtf ffl
richTextBox1.SaveFile("test.rtf", RichTextBoxStreamType.RichText);

A vezrl lehetsget ad a szveg formzshoz is, ehhez a Font tulajdonsgt llthatjuk, ekkor az sszes
ltez szveg formzsa megvltozik. Ha csak a kijellt szveget szeretnnk lltani, akkor a SelectionFont
tulajdonsgon keresztl tehetjk ezt meg.
Font font = new Font("Arial", 12, FontStyle.Bold);
richTextBox1.SelectionFont = font;

Ugyangy dolgozhatunk a tbbi Selection eltag tulajdonsggal.

DateTimePicke
Ezzel a vezrlvel lehetsgnk van egy adott dtum kivlasztsra.

283

Tulajdonkppen kt rszbl ll, egy TextBox s egy gomb, amellyel megjelenthetjk a naptrat, illetve maga a
naptr vezrl. Alaprtelmezett esemnye a ValueChanged:
private void dateTimePicker1_ValueChanged(object sender, EventArgs e)
{
MessageBox.Show(dateTimePicker1.Value.ToString());
}

A Value tulajdonsga DateTime tpussal tr vissza.


MenuStrip
Mensort kszthetnk vele, a Toolbox-ban a Menus & ToolBars szekciban talljuk meg. A tervez nzetben
azonnal fel is tlthetjk elemekkel, illetve a helyi ment kinyitva bellthatjuk a megjelenst, illetve hogy hol
helyezkedjen el. Az egyes menpontokhoz (amelyek ToolStripMenuItem tpusak) esemnykezelt
rendelhetnk (az alaprtelmezett esemnyk a kattints).
private void subMenu1ToolStripMenuItem_Click(object sender, EventArgs e)
{
ToolStripMenuItem menu = (ToolStripMenuItem) sender;
MessageBox.Show(menu.Text); // kirjuk a mne feliratt
}

Prbeszdablakok
Hasznlhatunk elre megrt dialgusablakokat, mint pldul a fjl megnyitsa/elmentse. A lehetsges
ablakokat megtalljuk a ToolBox-ban is a Dialogs szekciban, s rhzhatjuk ket az alkalmazsunkra, ekkor
azok osztlytagknt elrhetv vlnak. Azonban clszerbb ket helyben pldnyostani, csakis akkor, ha

284

szksg van rjuk. Ezt a kvetkezkppen tehetjk meg (feltesszk, hogy egy gomb megnyomsakor megnylik
az adott ablak):
private void button1_Click(object sender, EventArgs e)
{
using (OpenFileDialog dialog = new OpenFileDialog())
{
if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
MessageBox.Show(dialog.FileName);
}
}
}

Az OpenFileDialog lehetsget ad kivlasztani egy fjlt. A ShowDialog hvsakor megjelenik a fjlvlaszt ablak.
Minden mvelet (OK gomb, ablak bezrsa stb.) egy DialogResult felsorols-tpus eredmnyt ad vissza, ebben
az esetben csak arra vagyunk kvncsiak, hogy a felhasznl megnyit egy fjlt. A FileName illetve FileNames (ha
tbb fjlt nyitunk meg) a fjlok nevt adja vissza teljes elrsi ttal.

285

GYAKORL FELADATOK V.
SZMOLGP
Ksztsnk szmolgp alkalmazst, amely Windows Forms alatt fut! A programnak kt szmon kell elvgeznie
az alapmveleteket.
Megolds
Ksztsnk egy j projektet, s vltsunk tervez nzetbe! Hzzunk 15 gombot s egy TextBox-ot az ablakra, 10et a szmoknak, ngyet az alapmveletek s egyet az egyenlsgjel rszre, a szvegmezben pedig a kpletet
s az eredmnyt jelentjk majd meg. Valahogy gy kell most kinznie a programunknak:

Jelljk ki az sszes szmot tartalmaz gombot, kivve az egyenlsgjelet tartalmazt, s rendeljk hozzjuk
ugyanazt az esemnykezelt (Click):
private void button13_Click(object sender, EventArgs e)
{
textBox1.Text += ((Button)sender).Text;
}

Nem csinlunk itt mst mint, hogy hozzfzzk a szvegdobozhoz a soron kvetkez karaktert. Egy
hibalehetsg, hogy bizony tbb opertor karaktert is berhatunk gy, ezt az olvasnak kell megoldania,
egyszer felttellel.
Az eredmnyt egy kln esemnykezelben szmoljuk ki, amit az egyenlsg gombhoz rendelnk:
private
{
int
int
int

void button15_Click(object sender, EventArgs e)


opIdx = textBox1.Text.IndexOfAny(new char[] { '+', '-', '/', '*' });
x = int.Parse(textBox1.Text.Substring(0, opIdx));
y = int.Parse(textBox1.Text.Substring(opIdx + 1));

int result = 0;
switch(textBox1.Text[opIdx])
{
case '+':
result = x + y;
break;
case '-':
result = x - y;
break;
case '/':

286

result = x / y;
break;
case '*':
result = x * y;
break;
}
textBox1.Text += "=" + result;
}

Az els sorban meghatrozzuk az opertor indext a stringben, hogy sztvlaszthassuk a szmokat, amit a
msodik s harmadik sorban tesznk meg a Substring fggvnnyel. Figyeljnk az indexekre, a msodik szm az
opertor indexe utn kvetkezik! Vgl egy switch szerkezettel kiszmoljuk az eredmnyt.

SZVEGSZERKESZT
Ksztsnk egyszer szvegszerkeszt programot, amellyel
dokumentumokat, illetve legyen lehetsg alapvet formzsokra is!

betlthetnk,

illetve

elmenthetnk

Megolds
Helyezznk el a tervez nzetben egy MenuStrip illetve egy RichTextBox vezrlt, hogy az albbi kphez
hasonl eredmnyt kapjunk:

Ksztsk el az egyes menpontokhoz az esemnykezeljket (Click)! A Megnyits menpont:

287

private void openToolStripMenuItem_Click(object sender, EventArgs e)


{
using (OpenFileDialog dialog = new OpenFileDialog())
{
dialog.Filter = "Szveges Fjlok| *.txt|RTF Fjlok|*.rtf|All Files|*.*";
if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
richTextBox1.LoadFile(dialog.FileName);
}
}
}

A Filter tulajdonsg segtsgvel megadhatjuk, milyen fjltpusok jelenjenek meg a dialgusablakban. Jjjn a
Ments:
private void saveToolStripMenuItem_Click(object sender, EventArgs e)
{
using (SaveFileDialog dialog = new SaveFileDialog())
{
if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
richTextBox1.SaveFile(dialog.FileName);
}
}
}

Vgl a kt formz menpont:


private void italicToolStripMenuItem_Click(object sender, EventArgs e)
{
FontStyle style = FontStyle.Italic;
richTextBox1.SelectionFont = new Font(richTextBox1.Font, style);
}
private void boldToolStripMenuItem_Click(object sender, EventArgs e)
{
FontStyle style = FontStyle.Bold;
richTextBox1.SelectionFont = new Font(richTextBox1.Font, style);
}

Az alkalmazs tetszlegesen kiegszthet a fentiek mintjra. Javasolt fejlesztsek: bettpus megadsa,


betmret lltsa, nyomtats stb.).

288

You might also like