Amit a C# tudni érdemes

216
C# Amit a C# tudni érdemes! Tartalomjegyzék A C#-ról röviden ........................................................................................................................ 7 A C# története ................................................................................................................................... 7 Szerkesztő használata ...................................................................................................................... 7 Forrásfájlok elnevezése .................................................................................................................... 8 A C# program végrehajtásáról ........................................................................................................ 8 A C# forráskód fordítása köztes nyelvre ........................................................................................ 9 C# objektumközpontú nyelv .................................................................................................... 10 Első C# programunk ...................................................................................................................... 10 A C# programok típusai ................................................................................................................ 11 A C#, mint objektum orientált programozási nyelv .................................................................... 11 Egységbe zárás ............................................................................................................................................... 11 Többalakúság ................................................................................................................................................. 12 Öröklés ........................................................................................................................................................... 12 Újrahasznosíthatóság ..................................................................................................................................... 12 Objektumok és osztályok ............................................................................................................................... 13 A C# moduláris .............................................................................................................................................. 13 .NET madártávlatból ..................................................................................................................... 13 A C# programok ....................................................................................................................... 14 A C# alkalmazások felépítése ........................................................................................................ 14 A megjegyzésekről ........................................................................................................................................ 15 A C# alkalmazások alapelemei ...................................................................................................... 17 Formázás térközökkel .................................................................................................................................... 17 Kulcsszavak – a C# nyelv alapkövei ............................................................................................................. 18 Literálok ......................................................................................................................................................... 18 Azonosítók ..................................................................................................................................................... 18 A C# alkalmazások szerkezete ....................................................................................................... 19 Utasítások és kifejezések a C#-ban ................................................................................................................ 19 Az üres utasítás .............................................................................................................................................. 19 Adattárolás változókkal ................................................................................................................. 19 A változók használata .................................................................................................................................... 20 Értékadás a változóknak ................................................................................................................................ 20 Változók kezdőérték nélkül ........................................................................................................................... 21 A C# adattípusai ............................................................................................................................. 21 Lebegőpontos értékek .................................................................................................................................... 22 Logikai értékek .............................................................................................................................................. 22 Adattípusok a .NET környezetben ................................................................................................................. 22 Literálok vagy változók? ............................................................................................................................... 23 Egész literálok ............................................................................................................................................... 23 Lebegőpontos literálok .................................................................................................................................. 24 Logikai literálok ............................................................................................................................................. 24 Karakterlánc literálok .................................................................................................................................... 24 Állandók létrehozása ..................................................................................................................................... 24 Hivatkozási típusok ....................................................................................................................................... 25 Készítette: Zsótér Csaba III. éves informatikus hallgató 1

Transcript of Amit a C# tudni érdemes

Page 1: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

Tartalomjegyzék

A C#-ról röviden ........................................................................................................................ 7

A C# története ................................................................................................................................... 7

Szerkesztő használata ...................................................................................................................... 7

Forrásfájlok elnevezése .................................................................................................................... 8

A C# program végrehajtásáról ........................................................................................................ 8

A C# forráskód fordítása köztes nyelvre ........................................................................................ 9

C# objektumközpontú nyelv .................................................................................................... 10

Első C# programunk ...................................................................................................................... 10

A C# programok típusai ................................................................................................................ 11

A C#, mint objektum orientált programozási nyelv .................................................................... 11 Egységbe zárás ............................................................................................................................................... 11 Többalakúság ................................................................................................................................................. 12 Öröklés ........................................................................................................................................................... 12 Újrahasznosíthatóság ..................................................................................................................................... 12 Objektumok és osztályok ............................................................................................................................... 13 A C# moduláris .............................................................................................................................................. 13

.NET madártávlatból ..................................................................................................................... 13

A C# programok ....................................................................................................................... 14

A C# alkalmazások felépítése ........................................................................................................ 14 A megjegyzésekről ........................................................................................................................................ 15

A C# alkalmazások alapelemei ...................................................................................................... 17 Formázás térközökkel .................................................................................................................................... 17 Kulcsszavak – a C# nyelv alapkövei ............................................................................................................. 18 Literálok ......................................................................................................................................................... 18 Azonosítók ..................................................................................................................................................... 18

A C# alkalmazások szerkezete ....................................................................................................... 19 Utasítások és kifejezések a C#-ban ................................................................................................................ 19 Az üres utasítás .............................................................................................................................................. 19

Adattárolás változókkal ................................................................................................................. 19 A változók használata .................................................................................................................................... 20 Értékadás a változóknak ................................................................................................................................ 20 Változók kezdőérték nélkül ........................................................................................................................... 21

A C# adattípusai ............................................................................................................................. 21 Lebegőpontos értékek .................................................................................................................................... 22 Logikai értékek .............................................................................................................................................. 22 Adattípusok a .NET környezetben ................................................................................................................. 22 Literálok vagy változók? ............................................................................................................................... 23 Egész literálok ............................................................................................................................................... 23 Lebegőpontos literálok .................................................................................................................................. 24 Logikai literálok ............................................................................................................................................. 24 Karakterlánc literálok .................................................................................................................................... 24 Állandók létrehozása ..................................................................................................................................... 24 Hivatkozási típusok ....................................................................................................................................... 25

Készítette: Zsótér Csaba III. éves informatikus hallgató

1

Page 2: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

Műveletek a C#-ban ....................................................................................................................... 25

Alapvető adatok megjelenítése ...................................................................................................................... 25 Változók értékének módosítása műveletekkel ............................................................................................... 26 Egyváltozós műveletek .................................................................................................................................. 26 Kétváltozós műveletek ................................................................................................................................... 26 Háromváltozós műveletek ............................................................................................................................. 27 Egyváltozós aritmetikai műveletek ................................................................................................................ 27 Összehasonlítás viszonyító műveletek .......................................................................................................... 28 Az if utasítás .................................................................................................................................................. 28 A műveletek kiértékelési sorrendje ............................................................................................................... 29 Adattípusok átalakítása .................................................................................................................................. 30 Típusléptetés .................................................................................................................................................. 31

A program működésének szabályozása ........................................................................................ 31 Kiválasztó utasítások ..................................................................................................................................... 31 Ismét az if ...................................................................................................................................................... 31 Az if utasítások beágyazása ........................................................................................................................... 32 Az if utasítások felhalmozása ........................................................................................................................ 33 A switch utasítás ............................................................................................................................................ 33 Egy kód – több eset ........................................................................................................................................ 35 A switch utasítás vezérlő típusai .................................................................................................................... 36 Bejáró utasítások ............................................................................................................................................ 36 A while utasítás .............................................................................................................................................. 36 Kiugrás a while utasításból és a ciklus folytatása .......................................................................................... 37 A do utasítás .................................................................................................................................................. 37 A for utasítás .................................................................................................................................................. 38 A foreach utasítás .......................................................................................................................................... 38 Az „átkos” goto .............................................................................................................................................. 39 Utasítások címkézése ..................................................................................................................................... 40

A C# programok lelke: az osztályok ............................................................................................. 40 Osztályok bevezetése ..................................................................................................................................... 41

Az osztály tagjai ............................................................................................................................................ 41 Az adattagok, vagy mezők ............................................................................................................................. 42 Az adattagok elérése ...................................................................................................................................... 42 Statikus változók használata .......................................................................................................................... 44 Az alkalmazás osztály .................................................................................................................................... 45 Tulajdonságok létrehozása ............................................................................................................................. 45 A névterekről ................................................................................................................................................. 46 Beágyazott névterek ....................................................................................................................................... 47

Műveletek egységbe zárása: tagfüggvények ................................................................................. 47 A tagfüggvény felépítése ............................................................................................................................... 48 Értékek visszaadása tagfüggvényekből ......................................................................................................... 48 A tagfüggvények törzsének felépítése ........................................................................................................... 49 A tagfüggvények meghívása .......................................................................................................................... 49 Adattagok elérése tagfüggvényekből ............................................................................................................. 50 Értékadás tagfüggvények számára ................................................................................................................. 51 Statikus tagfüggvények .................................................................................................................................. 52 Paraméterek elérési jellemzői ........................................................................................................................ 52 Érték szerinti paraméterátadás ....................................................................................................................... 52 Hivatkozás szerinti paraméterátadás .............................................................................................................. 52 A kimeneti elérés használata ......................................................................................................................... 54 Az osztály-tagfüggvények típusai .................................................................................................................. 55 Tulajdonságelérési tagfüggvények ................................................................................................................ 55 Konstruktorok ................................................................................................................................................ 55 Példánykonstruktor ........................................................................................................................................ 55 Statikus konstruktorok ................................................................................................................................... 56

Készítette: Zsótér Csaba III. éves informatikus hallgató

2

Page 3: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

Destruktorok .................................................................................................................................................. 57 A destruktor használata .................................................................................................................................. 58

Összetett adatszerkezetek: struktúrák, felsorolások és tömbök ................................................. 59 Struktúrák ...................................................................................................................................................... 59 Az osztályok és struktúrák közti különbség .................................................................................................. 59 A struktúrák tagjai ......................................................................................................................................... 59 A struktúrák tagfüggvényei ........................................................................................................................... 60 A struktúrák konstruktorai ............................................................................................................................. 60 A struktúrák destruktorai ............................................................................................................................... 61 Felsorolások ................................................................................................................................................... 61 A felsorolások alapértelmezett értékeinek módosítása .................................................................................. 62 A felsorolás alapjául szolgáló típus módosítása ............................................................................................ 63 Tömbök .......................................................................................................................................................... 63 A tömbelemek kezdeti beállítása ................................................................................................................... 65 Többdimenziós tömbök ................................................................................................................................. 65 Különböző méretű tömböket tartalmazó tömbök .......................................................................................... 66 A tömbök hosszának és határainak vizsgálata ............................................................................................... 66 Tömbök használata osztályokban és struktúrákban ....................................................................................... 67

A tagfüggvényekhez való hozzáférés kifinomult módozatai ........................................................ 67 A tagfüggvények túlterhelése ........................................................................................................................ 67 Konstruktor túlterhelése ................................................................................................................................. 69 A tagfüggvények aláírása .............................................................................................................................. 71 Változó számú paraméter használata ............................................................................................................. 72 A params kulcsszó használata több különböző adattípussal .......................................................................... 73 A params kulcsszó működésének még részletesebb vizsgálata ..................................................................... 74 A Main tagfüggvény használata és a parancssori paraméterek kezelése ....................................................... 75 Hatókörök ...................................................................................................................................................... 76 Munka, helyi hatókörrel rendelkező változókkal .......................................................................................... 76 Az osztályváltozók és a helyi változók megkülönböztetése ......................................................................... 77 Osztály hatókörének megváltoztatása módosítókkal ..................................................................................... 77 Objektumok nélküli osztályok létrehozása ................................................................................................... 77 Privát konstruktor használata ......................................................................................................................... 78 A névterek használatának átismétlése ........................................................................................................... 79 A névtér elnevezése ....................................................................................................................................... 80 A névtér bevezetése ....................................................................................................................................... 80 A using kulcsszó és a névterek ...................................................................................................................... 81 A teljesen meghatározott névtereknek rövidítése .......................................................................................... 81 Álnevek használata a using segítségével ....................................................................................................... 82

Problémák kezelése a programon belül: kivételek és hibák ........................................................ 82 A programok kezelésével kapcsolatos alapfogalmak .................................................................................... 82 A hibák kiküszöbölése logikus kódolással .................................................................................................... 83 Mi okozhat kivételt? ...................................................................................................................................... 83 Kivételkezelés ................................................................................................................................................ 84 A try és a catch használata ............................................................................................................................. 85 A kivétellel kapcsolatos információ elfogása ................................................................................................ 85 Több catch használata egyetlen try paranccsal .............................................................................................. 86 A kivételek kezelésének sorrendje ................................................................................................................. 86 A végső megoldás: a finally kulcsszó ............................................................................................................ 87 A leggyakoribb kivételek ............................................................................................................................... 88 Saját kivételosztályok megadása ................................................................................................................... 90 Saját kivételek kiváltása ................................................................................................................................ 91 Kivételek továbbdobása ................................................................................................................................. 92 Ellenőrzött és ellenőrizetlen parancsok használata ........................................................................................ 92 Az előfeldolgozó utasításainak használata .................................................................................................... 93 Az előfeldolgozással kapcsolatos deklarációk ............................................................................................... 94 Értékek megadása a parancssorban ............................................................................................................... 96

Készítette: Zsótér Csaba III. éves informatikus hallgató

3

Page 4: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

A #define és az #undef hatása ....................................................................................................................... 96 Feltételes feldolgozás (#if, #elif, #endif) ....................................................................................................... 96 Előfeldolgozási kifejezések (!, ==,!=, &&, ||) .............................................................................................. 97 Hibás és figyelmeztetések kezelésének előírása a kódban (#error, #warning) .............................................. 97 A sorszámok megváltoztatása ........................................................................................................................ 97 A régiók rövid áttekintése .............................................................................................................................. 98

Már meglévő kódrészletek újrahasznosítása öröklés segítségével .............................................. 98 Az öröklés alapja ........................................................................................................................................... 98 Az egyszeres és a többszörös öröklés ............................................................................................................ 99 Az öröklés egyszerű formája ......................................................................................................................... 99 Alaptagfüggvények használata öröklött tagfüggvényekben ........................................................................ 103 A többalakúság felfedezése és a származott osztályok ................................................................................ 103 Munka virtuális tagfüggvényekkel .............................................................................................................. 105 Munka elvont osztályokkal .......................................................................................................................... 107 Osztályok lezárása ....................................................................................................................................... 109 Az Object, avagy a legvégső alaposztály .................................................................................................... 110 Az Object osztály tagfüggvényeinek áttekintése ......................................................................................... 110 Becsomagolás és kicsomagolás ................................................................................................................... 111 Az is és as kulcsszavak használata – osztályok típusának átalakítása ......................................................... 112 Az is kulcsszó használata ............................................................................................................................. 112 Az as kulcsszó használata ............................................................................................................................ 114 Különböző objektumtípusok tömbjei .......................................................................................................... 114

Információ formázása és bekérése .............................................................................................. 117 A konzolon keresztül megvalósított bemenet és kimenet ............................................................................ 117 Az információ formázása ............................................................................................................................. 117 Számok formázása ....................................................................................................................................... 118 Egyedi formátumok létrehozása képleírók segítségével .............................................................................. 119 A dátum és idő formázása ............................................................................................................................ 120 A dátum és idő lekérdezése ......................................................................................................................... 120 A dátum és idő formázása ............................................................................................................................ 121 Felsorolások tagjainak megjelenítése .......................................................................................................... 122 A karakterláncokkal végezhető kifinomultabb műveletek .......................................................................... 123 Karakterláncokkal kapcsolatos tagfüggvények ........................................................................................... 124 @ - a különleges karakterlánc formázó ....................................................................................................... 126 Karakterláncok építése ................................................................................................................................. 126 Információ bekérése a konzolról ................................................................................................................. 128 A Read tagfüggvény használata ................................................................................................................... 128 A ReadLine tagfüggvény használata ........................................................................................................... 129 A Convert osztály használata ....................................................................................................................... 129

Bevezetés az objektumközpontú programozásba: felületek ...................................................... 131 Felületek: előzetes ....................................................................................................................................... 131 Az osztályok és a felületek kapcsolata ........................................................................................................ 132 A felületek használata .................................................................................................................................. 132 Miért használjunk felületeket? ..................................................................................................................... 133 Felületek meghatározása .............................................................................................................................. 133 Felület meghatározása tagfüggvényekkel .................................................................................................... 133 Tulajdonságok megadása a felületeken belül .............................................................................................. 136 Több felület használata ................................................................................................................................ 137 Kifejezett felülettagok használata ................................................................................................................ 138 Új felületek levezetése már létező felületekből ........................................................................................... 140 Felületek tagjainak elrejtése ........................................................................................................................ 140

A program válaszoló képességének kialakítása képviselők, események és indexelők segítségével .................................................................................................................................... 142

Hogyan használjunk egy indexelőt? ............................................................................................................ 142 A képviselők felfedezése ............................................................................................................................. 143

Készítette: Zsótér Csaba III. éves informatikus hallgató

4

Page 5: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

Események használata ................................................................................................................................. 147 Események létrehozása ................................................................................................................................ 147 Az eseményképviselők működése ............................................................................................................... 148 Származtatás az EventArgs osztályból ........................................................................................................ 148 Az Event osztály kódjának használata ......................................................................................................... 149 Eseménykezelők létrehozása ....................................................................................................................... 150 Az események és eseménykezelők egymáshoz rendelése ........................................................................... 150 Több eseménykezelő egyetlen eseményhez ................................................................................................ 152 Eseménykezelők eltávolítása ....................................................................................................................... 154

Parancsaink végrehajtatása műveletekkel: túlterhelés ............................................................. 155 Ismétlés: függvények túlterhelése ................................................................................................................ 155 Műveletek túlterhelése ................................................................................................................................. 156 Az alapvető kéttényezős matematikai műveletek túlterhelése .................................................................... 156 Az alapvető egytényezős matematikai műveletek túlterhelése ................................................................... 158 Az összehasonlító és logikai műveletek túlterhelése ................................................................................... 159 A logikai műveletek túlterhelése ................................................................................................................. 162 Összefoglalás: A C# túlterhelhető műveletei .............................................................................................. 164 Összefoglalás: A C# nem túlterhelhető műveletei ....................................................................................... 164

A .NET alaposztályok eljárásai ................................................................................................... 164 Osztályok a .NET környezetben .................................................................................................................. 164 A közös nyelvleírás (CLS) ........................................................................................................................... 164 A típusok szervezése névterekkel ................................................................................................................ 165 Az ECMA szabványok használata ............................................................................................................... 165 Tájékozódás a környezet osztályairól .......................................................................................................... 166 Könyvtár- és rendszeradatok kezelése ......................................................................................................... 166 Matematikai eljárások használata ................................................................................................................ 166 Fájlkezelés ................................................................................................................................................... 167 A fájlok adatai .............................................................................................................................................. 169 Egyszerű adatfájlok kezelése ....................................................................................................................... 170 Ismerkedés a folyamokkal ........................................................................................................................... 170 A fájlolvasás menete .................................................................................................................................... 170 Fájlok létrehozása és megnyitása ................................................................................................................. 171 Szöveg fájlba írása ....................................................................................................................................... 171 Szöveg olvasása fájlból ................................................................................................................................ 172 Bináris adatok fájlba írása ........................................................................................................................... 172 Bináris adatok olvasása fájlokból ................................................................................................................ 173

Windows alkalmazások készítése .......................................................................................... 174

Windows ablakok készítése ......................................................................................................... 174 Fordítási beállítások ..................................................................................................................................... 175 Az Application.Run tagfüggvény működése ............................................................................................... 176 Ablakok testreszabása .................................................................................................................................. 177 Ablakok átméretezése .................................................................................................................................. 180 Az ablak színeinek és hátterének megváltoztatása ...................................................................................... 182 Az ablak szegélyének módosítása ............................................................................................................... 183 Vezérlők elhelyezése az ablakon ................................................................................................................. 184 Címkék és szövegmegjelenítés .................................................................................................................... 185 Gombok használata ...................................................................................................................................... 186 Gombesemények .......................................................................................................................................... 187 OK gomb készítése ...................................................................................................................................... 189 Szövegmezők használata ............................................................................................................................. 190

Windows alkalmazások készítése ................................................................................................ 193 Választógombok használata ......................................................................................................................... 193 Tárolók használata ....................................................................................................................................... 195 Listamezők használata ................................................................................................................................. 198 Menük az ablakokon .................................................................................................................................... 201

Készítette: Zsótér Csaba III. éves informatikus hallgató

5

Page 6: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

Bejelölhető menüelemek ............................................................................................................................. 204 Helyi menü készítése ................................................................................................................................... 207 A MessageBox osztály ................................................................................................................................ 209 A Microsoft Windows beépített párbeszédablakainak használata ............................................................... 211 Saját párbeszédablak készítése .................................................................................................................... 213

Felhasznált irodalom ............................................................................................................. 216

Készítette: Zsótér Csaba III. éves informatikus hallgató

6

Page 7: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

A C#-ról röviden

A C# története

A C# béta változatához először 2000 júniusában juthatott hozzá a nagyközönség, a hivatalos kiadás pedig 2002 tavaszán látott napvilágot, így a nyelv valóban nincs túl régen jelen a köztudatban.

A C# („cé kereszt” vagy „C sharp”) szülőatyja a Microsoft, szabványosításáról pedig az ECMA gondoskodott. Készítői között vezetőkent feltűnik az az Andres Hejlsberg is, aki a korábbiakban már más programozási nyelek készítésénél – Borland C++, Borland Delphi – megmutatta oroszlánkörmeit. A C# fejlesztésénél a Microsoft programozói csoportja megpróbálta átvenni a már létező programnyelvek jó tulajdonságait, itt-ott további javításokat, fejlesztéseket eszközölve.

Jóllehet a C# a Microsoft fejlesztésének eredménye, használata mégsem korlátozható Microsoft rendszerekre. Léteznek C# fordítók a FreeBSD, a Linux és a Macintosh rendszereken, továbbá számos Microsoft felületen.

A C# igen hatékony és rugalmas programozási nyelv, mellyel – számos nyelvekhez hasonlóan – alkalmazások széles körét készíthetjük el. Maga a nyelv nem korlátozza a programozót tevékenységében, így fantáziánk határtalanul szárnyalhat. Mindezt mi sem mutatja jobban, mint az, hogy a C#-ot használták már dinamikus webhelyek, fejlesztőeszközök, sőt fordítóprogramok készítéséhez is.

Szerkesztő használata

A Microsoft a C# lehetőségeit elérhetővé tette a Microsoft Visual Studio .NET-ben, amely így tartalmazza a Microsoft Visual Studio C# .NET-et is. Ezzel a legismertebb szerkesztő a C# programozásához – mindazonáltal ahhoz, hogy C# programokat készítsünk, nincs feltétlenül szükségünk a Visual Studio .NET-re vagy a Visual C# .NET-re.

Léteznek ugyanis más szerkesztők is, melyek némelyike a Visual Studio .NET-hez hasonlóan lehetővé teszi, hogy a szerkesztő elhagyása nélkül végezzük el a fejlesztés folyamatának összes lépését. Többségük emellett más szolgáltatásokat is nyújt, így például a beírt szövegek színkódolását – ezzel, sokkal könnyebben rábukkanhatunk az esetleges hibákra. Számos szerkesztő még abban is segít, mit írjunk be egy adott helyen, továbbá sokoldalú súgóval kedveskedik számunkra.

Azonban C# programot lehet készíteni akár a Microsoft Windows rendszereken található Jegyzettömb vagy a WordPad nevű alkalmazás, Linux és Unix rendszereken pedig a jól használható az ed, az ex, az edit, az emacs, vagy a vi.

Készítette: Zsótér Csaba III. éves informatikus hallgató

7

Page 8: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

Ha más szerkesztőt szeretnénk használni, arra is van lehetőség. Ilyen szerkesztők akár ingyen is beszerezhetők:

• SharpDevelop – A SharpDevelop egy ingyenes szerkesztő, melyet a C# és a VB.NET projektekhez használhatunk a Microsoft .NET felületen. Mivel a szerkesztő nyílt forrású (GPL), a www.icsharpcode.net címről a futtatható fájlok mellett a forráskódot is letölthetjük.

• CodeWright – A CodeWright olyan szerkesztő, amely külön támogatást biztosít az ASP, az XML, a HTML, a C#, a Perl, a Python és más formátumokhoz. A 30 napos próbaváltozatát letölthetjük a www.premia.com címről. A CodeWright jelenleg a Borlandhoz tartozik.

• JEdit – A JEdit egy nyílt forrású Java szerkesztő, amely azonban egyúttal használható C# programok szerkesztésére is, a kód színezésének lehetőségével. A programot a http://jedit.sourceforge.net címen találhatjuk meg.

Forrásfájlok elnevezése

Ha elkészült a forrásfájl, természetesen nevet kell adnunk neki – ennek a névnek pedig le kell írnia, mit is tesz a program. Forrásfájljaink kiterjesztését szabadon megválaszthatjuk, de jobban járunk, ha megmaradunk az általánosan elismert, szabványos .cs-nél.

A névnek le kell írnia a program működését. Sokan vélik úgy, hogy ez a név jó, ha megegyezik az adott C# osztály nevével.

A C# program végrehajtásáról

Fontos, hogy némiképp tisztában legyünk azzal, miként történik a C# programok végrehajtása – ezek a programok ugyanis e téren eltérnek a szokásos programnyelveken írt társaiktól.

A C# programokat a .NET Common Language Runtime (CLR) környezetében futtathatjuk. Ez azt jelenti, hogy ha elkészítünk egy futtatható C# programot, és megkíséreljük elindítani egy olyan gépen, melyen nincs jelen a CLR, vagy más, vele összeegyeztethető futásidejű környezet, a futás nem sikerül.

A futásidejű környezet használatának előnye a hordozhatóság. Ha korábban olyan programot szerettünk volna készíteni valamely régebbi programnyelven – mondjuk C-ben vagy C++-ban -, ami képes különböző operációs rendszereken és felületeken futni, mindegyikük esetében külön futtatható programot kellett készítenünk a fordítóval. Ha például, készítünk egy C alkalmazást, és egy Linux és egy Windows gépen is futtatni akartuk, két futtatható programot kellett készítenünk – egyet a Linuxhoz, egyet a Windowshoz. A C# esetében csak egyre van szükségünk, és ez működik mindkét rendszeren.

Ha programunkat a lehető leggyorsabban szeretnénk futtatni, valódi futtatható fájlt kell belőle készítenünk. Ehhez a forráskódot gépi kódra kell fordítanunk egy fordítóprogrammal. A fordítóprogram tehát fogja a forrásfájlunkat, és utasításait gépi kódú utasításokká alakítja át.

Készítette: Zsótér Csaba III. éves informatikus hallgató

8

Page 9: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

A C, C++ és hasonló programnyelveken készült programok esetében a fordítóprogram olyan fájlt ad, amely minden további erőfeszítés nélkül egyszerűen futtatható.

A C# esetében fordítóprogramunk nem közvetlenül gépi kódra fordít, hanem egy köztes nyelvre (Intermediate Language - IL). Az így kapott IL fájlt ezután átmásolhatjuk bármely .NET CLR környezettel rendelkező gépre. Mivel ez az IL fájl közvetlenül nem futtatható, további fordításra van szükség a működéséhez – ezt teszi meg a CLR, vagy más C# futásidejű környezet.

A CLR, ha IL fájlt lát, első dolga, hogy lefordítsa. Ilyenkor a hordozható IL kódot átalakítja gépi kóddá, amit a számítógép már képes értelmezni és futtatni. A dolog persze ennél egy kicsit összetettebb, a CLR ugyanis valójában csak a program éppen használatban lévő részét fordítja le, hogy időt takarítson meg. Ezt a végső fordítást nevezik „Just In Time” (JIT) fordításnak.

Mivel tehát az IL fájlokat futás időben kell fordítani, a program kezdetben lassabban fut, mintha egy teljes fordítású nyelven, például C++-ban írták volna. Ha azonban egy adott részt már futtattunk, a különbség eltűnik, ugyanis innentől kezdve a rendszer a teljes lefordított kódot használja. Az esetek többségében ez a kezdeti időkiesés elhanyagolható, sőt lehetőségünk van arra is, hogy C# programunkat rögtön lefordítsuk a JIT-tel, mihelyt telepítjük egy rendszerbe.

A C# forráskód fordítása köztes nyelvre

Az IL fájlok készítéséhez használjuk a C# fordítót. Amennyiben a Microsoft :NET Framework SDK-t használjuk, alkalmazhatjuk a csc parancsot, feltüntetve utána a forrásfájl nevét. Így, ha például a Radius.cs forrásfájlt szeretnénk lefordítani, az alábbiakat kell a parancssorba írnunk:

csc Radius.cs

Ha nem a Microsoft .NET környezetét használjuk, esetleg más parancs használatára szorulunk. Ez a mono esetében a mcs, így ha itt szeretnénk az előző fordítási műveletet elvégezni, a következőt kell beírnunk:

mcs Radius.cs

Amennyiben grafikus fejlesztőkörnyezetet használunk, mint a Microsoft Visual C# .NET, a fordítás még egyszerűbb. A legtöbb grafikus környezetben elég a Compile gombra kattintani vagy a megfelelő menüpontot választani. Ha a fordítás megtörtént, akkor a Run gombbal, vagy a megfelelő menüpont kiválasztásával futtathatjuk a programot.

A fordítás végeztével egy IL fájlt kapunk. Ha körülnézünk a fordítás helyéül szolgáló könyvtárban, illetve mappában, biztosan találunk egy új fájlt, melynek neve megegyezik a forrásfájléval, kiterjesztése azonban nem .cs, hanem .exe. Ez az .exe kiterjesztésű fájl a lefordított programunk, melyet futtathatunk a CLR-ben. E fájl tartalmaz minden adatot, amire

Készítette: Zsótér Csaba III. éves informatikus hallgató

9

Page 10: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

a CLR-nak szüksége van a futtatáshoz. A .NET szóhasználata szerint az e fájlban található kód neve kezelt kód (managed code).

Miután programunkat IL fájllá fordítottuk, futtathatjuk úgy, hogy beírjuk a nevét a parancssorba, vagy bárhogy, ahogyan más programot szoktunk. Azt viszont nem szabad elfelejtenünk, hogy a programnak a futáshoz szüksége van a .NET CLR-re – ha nem telepítjük ezt a környezetet, futtatáskor csak egy hibaüzenetet kapunk. Ha ellenben telepítjük a Microsoft .NET környezetét, a futás pontosan úgy zajlik, mint bármely más program esetében.

C# objektumközpontú nyelv

Első C# programunk

Példánkban egy Hello.cs nevű programot használunk, mely mindössze a Hello, World! feliratot írja ki a képernyőre:

2.1 kódszöveg Hello.cs

class Hello{

public static void Main(){

System.Console.WriteLine("Hello, World!");}

}

Gépeljük be a fenti kódszöveget a szerkesztőnkbe és mentsük Hello.cs néven. Ügyeljünk arra, hogy a C#-nál a Main() nagybetűvel íródik, míg más programnyelveken – ilyen a C, vagy a C++ - kisbetűvel. Ha nem nagy M-et használunk fordítási hibát kapunk.

Ha ezen kis programocskát futtatni szeretnénk, nincs más dolgunk, mint a lefordítani a Hello.cs fájl. Ehhez írjuk be a következőt a parancssorba:

csc Hello.cs

Ha egyesített fejlesztői környezetet használunk (IDE), válasszuk a megfelelő gombot (ikont), gyorsbillentyűt vagy menüpontot. Ha minden jól végeztünk, egy üzenetet kell kapjunk arról, hogy nincs figyelmeztetés, vagy fordítási hiba.

Ha lefordítottuk, akkor a fordítás helyéül szolgáló könyvtárban, illetve mappában találunk egy Hello.exe nevű fájlt. Futtatásához egyszerűen írjuk be a Hello szót a parancssorba; ekkor képernyőnkön megjelenik a Hello, World! felírat.

Készítette: Zsótér Csaba III. éves informatikus hallgató

10

Page 11: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

A C# programok típusai

Mielőtt tovább folytatnánk utunkat a C# rejtelmei közt, fontos dolog, hogy megismerkedjünk, hogy pontosan milyen programokat készíthetünk a C#-al:

• Konzolalkalmazások – A konzolalkalmazásokat a parancssorból indíthatók. • Ablakos alkalmazások – Készíthetünk olyan Windows-alkalmazásokat is,

melyek kihasználják a rendszer nyújtotta grafikus felhasználói felületet (GUI).• Webszolgáltatások – Ezek olyan eljárások, melyeket a Világhálón keresztül

lehet meghívni.• Webes formok / ASP.NET alkalmazások – Az ASP.NET alkalmazásokat

webkiszolgálókon futtathatjuk, segítségükkel dinamikus weblapokat készíthetünk.

Az alábbi típusú programok készítése mellett a C# nyelvet számos más feladatra is használhatjuk, így készíthetünk a segítségével könyvtárakat, vezérlőket és más egyebeket.

A C#, mint objektum orientált programozási nyelv

A C# eleve objektumközpontú programozási nyelvnek készült. Más nyelvekben is találhatunk objektumközpontú lehetőségeket, de csak kevés épül valóban objektumközpontú alapokra. A C# ősei a C és a C++, de összeállítását készítőik valójában az alapoktól kezdték. A Microsoft azokból az elemekből indult ki, melyek e két nyelvben sikeresen működtek, és olyan lehetőségekkel egészítették ki ezeket, melyek megkönnyítették a használatukat. – sokuk a Javából már ismerős lehet. A Microsoftot a C# megtervezésénél határozott célkitűzések vezérelték – olyan programnyelvet szerettek volna létrehozni, ami egyszerű, emellett azonban modern és objektumorientált is.

A C#-ban eltűnik a Java és a C++ néhány bonyolultabb és sokszor csapdákat rejtő eleme, mint a makrók, a többszörös öröklődés és a virtuális alaposztályok. Ezek mind olyan pontok, melyek folyamatosan zavart és hibalehetőségeket okoznak a C++ fejlesztés során. Az újítások között találjuk a felesleges ismétlések visszanyesését, vagy bizonyos nyelvtani váloztatásokat. A C++ például különböző műveleti jeleket (::, . és ->) használ, ha struktúrák tagjaival dolgozunk. Nos a C#-ban csak egyetlen műveleti jel létezik e célra , a „pont”.

A C# mindemellett korszerű programnyelv. Kivételkezelés, szemétgyűjtés, bővíthető adattípusok és kódbiztonság – e lehetőségek biztosítását mindenki elvárja egy modern programnyelvtől, és a C# esetében rendelkezésre is állnak.

A C# objektumközpontú – Amint az már említettük a C# objektumközpontú nyelv. Ennek alapjai, pedig az egységbe zárás (betokozás, encapsulation), az öröklés és a többalakúság, melyet a C# természetesen megvalósít.

Egységbe zárásAz egységbe zárás gyakorlatilag annyit jelent, hogy „csomagokat” készítünk, melyekbe mindent beleteszünk, ami az adott feladathoz szükséges. Az objektumközpontú programozás

Készítette: Zsótér Csaba III. éves informatikus hallgató

11

Page 12: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

esetében objektumokat (csomagokat) készítünk, például egy kört, amely maga képes elvégezni mindent, amit egy körrel megtehetünk. Ebbe beletartozik a kör adatainak – sugár és középpont – nyilvántartása, de ki kell tudnia számítani például más adatokból a sugarat, vagy éppen kirajzolni a kört.

A kör egységbe zárásával tehát elérhető, hogy a felhasználónak ne kelljen ismernie a kör „működését”, elég legyen tudnia, miként bánjon vele. Így elrejthetjük előle a belső műveleteket.

TöbbalakúságA többalakúság az objektumközpontú programozás két (ha nem több) különböző területén is alkalmazható. Először is, segítségével egy objektumot vagy eljárást többféle módon meghívhatunk, mégis azonos eredményt kapunk. A kör példájánál maradva, ez azt jelenti, hogy he meg szeretnénk határozni a kör területét, megadhatjuk három pontját, vagy a középpontját és a sugarát. Természetesen minkét esetben ugyanazt az eredményt kapjuk. Eljárás alapú nyelvekben, mint a C, két, eltérő nevű eljárásra van szükség a terület e két módon történő kiszámításához – C# is kettőre van szükség, de ezek neve megegyezhet. Ha ezután egy program a területet szeretné kiszámolni, akkor csak meghívja ezt az eljárást, átadva a paramétereket, és program automatikusan eldönti, hogy melyik módszerrel számítsa ki az eredményt. A felhasználónak így nem kell foglalkoznia azzal, hogy melyik eljárást hívja meg – az a program feladata.

A többalakúság másik – és talán fontosabb – felhasználása az alkalmazkodást teszi lehetővé, olyan adatok kezelését, melyekről esetleg előzőleg semmit sem tudunk. Tegyük fel, hogy különböző alakzataink vannak – háromszögek, négyzetek és körök. Programunk a többalakúság segítségével képes lehet általában az alakzatokkal dolgozni. Mivel háromszögek, négyzetek és körök alakzatok, programunk képes mindegyikük használatához alkalmazkodni. Az itt használt programozási módszerek bonyolultabbak a szokásosnál, de használatuk hihetetlen lehetőségeket adhat.

ÖröklésAz öröklés az objektumközpontú programozás legösszetettebb fogalma. Körökkel már dolgoztunk – mi legyen, ha gömbökkel kell foglalkoznunk? Nos, a gömb bizonyos értelemben a kör továbbfejlesztése, hiszen rendelkezik a kör jellemzőivel, csak eggyel több dimenziója van. Azt is mondhatnánk, hogy a gömb egy különleges körféleség, egy újabb dimenzióval kiegészítve. Ha tehát a kör segítségével készítjük el a gömbünket, a gömb örökölheti a kör tulajdonságait. Az itt leírtakat nevezzük általánosságban öröklésnek.

ÚjrahasznosíthatóságAz objektumközpontú programozás használatának egyik legfontosabb célja az újrahasznosíthatóság biztosítása. Ha készítünk egy osztályt, újrafelhasználva létrehozhatunk belőle több objektumot is. Az öröklés és más korábban bemutatott lehetőségek használatával eljárásokat készíthetünk, melyeket később más programokban, különféleképpen használhatunk fel. Az egyes szolgáltatások egységbe zárásával olyan eljárásokat hozhatunk létre, melyek helyességét jó előre ellenőrizhetjük, így a későbbiekben nem kell törődnünk a

Készítette: Zsótér Csaba III. éves informatikus hallgató

12

Page 13: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

működés részleteivel – csak a helyes használatra kell ügyelni. Így ezen eljárások újrafelhasználása egyszerű és gyors lesz.

Objektumok és osztályokAz osztály valójában egy később készítendő elem meghatározása, szóban forgó elem pedig az objektum.

Gyakran használt párhuzam a süteményforma. Ez a forma meghatározza a süti alakját, de önmaga nem sütemény – még csak nem is ehető. A süteményforma csak egy eszköz, mellyel süteményeket készíthetünk. Használatánál biztosak lehetünk abban, hogy a sütik egyformák lesznek, és abban hogy nem fog elkopni – akármennyi süteményt is készítsünk.

A süteményformához hasonlóan az osztályból is több objektumot készíthetünk. Így, ha van például egy kör osztályunk, ezzel számos kört létrehozhatunk. Amennyiben egy rajzprogramot készítünk, elég, ha csak egyetlen kör osztállyal rendelkezünk – a kör objektumok sokasága ennek segítségével létrehozható.

A C# modulárisA moduláris azt jelenti, hogy a C# kódot, osztályoknak nevezett kódrészletekből kell összeállítanunk, melyek eljárásokat, úgynevezett tagfüggvényeket (metódusokat) tartalmaznak. Ezeket az osztályokat és tagfüggvényeket több helyütt is felhasználhatjuk, így elkerülhetjük a felesleges, többszöri kódolást.

Egy másik, a C#-hoz kapcsolódó kifejezés a komponens. A C# segítségével ugyanis készíthetünk komponenseket (újrahasznosítható programelemeket) is, melyek más alkalmazásokba ágyazható programok. A komponensek – melyek nem feltétlenül C# kódból állnak – összetett programok építőelemeiként szerepelhetnek.

.NET madártávlatból

A C# nyelv tervezésekor a .NET környezettel való együttműködést tartották szem előtt. Ez a környezet több részből áll, köztük egy futásidejű környezetből, néhány előre meghatározott eljárásból, valamint az adatok tárolásának meghatározott módjaiból. A C# programok képesek kihasználni mindezek előnyeit.

A futásidejű környezetről (CLR) már volt szó a korábbiakban – röviden róla csak annyit, hogy feladata a lefordított C# program és az adott operációs rendszer összekötése.

Az adatok szabványosított tárolásáért a Common Type System (CTS), közös típusrendszer felel. Ez valójában tárolási típusok egy családja, melyeket bármely használhat. Pontosabban szólva, mindent, a .NET környezettel együttműködő programnyelv ezeket a közös típusokat használja. A közös típusok alkalmazása lehetővé teszi, hogy különböző programok megosszák adataikat.

Készítette: Zsótér Csaba III. éves informatikus hallgató

13

Page 14: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

A .NET környezet másik lényeges eleme az előre meghatározott eljárások családja. Ezeket a .NET alaposztály-könyvtár (BCL) részeiként érhetjük el. Itt eljárások ezrei segítenek C# programozási feladataink megvalósításában – így tettek „Hello, World!” programunkban is, ahol agy ilyen eljárás végezte a kiíratást a konzolablakra, de az ablakok és vezérlők készítésénél is ezek kapnak szerepet. További eljárások állnak rendelkezésre a fájlkezelés feladataihoz, az XML kezeléséhez, a többfeladatossághoz és más egyéb feladatokhoz.

A CTS, a BCL eljárásai, valamint a .NET környezet más elemei ugyanúgy elérhetők más .NET programozási nyelvekben, mint a C#-ban. Így például a BCL eljárásai ugyanazok, mint a Microsoft VisualBasic.NET, a Microsoft J#.NET, vagy a JScript.NET által használtak.

A C# programok

A C# alkalmazások felépítése

3.1. kódszöveg App.cs

// App.cs – Bemutató C# alkalmazás// Nem baj, ha még nem értünk mindent,// később választ kapunk kérdéseinkre//-----------------------------------------------

using System;

class App{

public static void Main() {

int radius = 4; const double PI = 3.14159; double area; area = PI * radius * radius; Console.WriteLine("Radius = {0}, PI = {1}", radius, PI ); Console.WriteLine("The area is {0}", area); }}

Kimenet:

Radius = 4, PI = 3.14159The area is 50.26544

Ismerkedjünk meg a 3.1.-es kódszöveg részleteivel:

Készítette: Zsótér Csaba III. éves informatikus hallgató

14

Page 15: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

A megjegyzésekrőlA 3.1. kódszöveg első négy sora megjegyzés, vagyis olyan tájékoztató szöveg, melyet a fordító figyelmen kívül hagy. Haszna a megjegyzésnek, hogy ezáltal dokumentálni tudjuk a programunkat, ezáltal könnyebb lesz majd a későbbi fejlesztés, hibajavítás, és bővítés, illetve mások számára megjegyzéseket tehetünk, hogy eligazodjanak a programunkban. A C#-ban háromféle megjegyzés van:

Egysoros megjegyzésIlyeneket találunk a 3.1. kódszöveg 1-4., valamint 12., 18. és 22. sorában. Alakjuk a következő:

// megjegyzés

A két perjel adja a megjegyzés kezdetét – innen a sor végéig a fordító mindent megjegyzésként kezel. Az egysoros megjegyzéseknek nem kell a sor elején kezdődniük, sőt állhat előttük C# kód is. A két perjel után azonban minden megjegyzésnek számít.

Többsoros megjegyzésA fenti példában nem található ilyen, de használatára szükség lehet, ha azt szeretnénk, hogy egy megjegyzés több sorra terjedjen ki. Ilyenkor persze megtehetjük, hogy minden sor elejére kitesszük a két perjelet, de használhatunk valódi többsoros megjegyzéseket is.

A többsoros megjegyzéseknek egy nyitó és egy záró jelző határolja. Megjegyzés kezdetén egy perjelet, majd egy csillagot kell beírnunk:

/*

Minden, amit ezután következik, a megjegyzéshez tartozik, egészen a vég jelzőjéig – ez pedig egy csillag és egy perjel egymásutánja:

*/

Megjegyzés például a következő:

/* Ez egy megjegyzés */

vagy

/* Ez szintén egy megjegyzés csak több sorban */

A többsoros megjegyzések nem ágyazhatók egymásba. Ez azt jelenti, hogy egy többsoros megjegyzés nem helyezhető el egy másik belsejében.

Dokumentációs megjegyzésA C# rendelkezik egy különleges megjegyzéstípussal, ami lehetővé teszi az automatikus külső dokumentálást.

Készítette: Zsótér Csaba III. éves informatikus hallgató

15

Page 16: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

Ezeket a megjegyzéseket három perjellel vezeti be, és bennük használhatjuk az XML stílusú kódokat. Az XML egy adatok jelölésére szolgáló szabvány. Jóllehet bármely XML kódot használhatjuk, a C#-ban jellemzően a következők fordulnak elő: <c>,<code>,<example>,<exception>,<list>,<para>,<param>,<paramref>,<permission>,<remarks>,<returns>,<see>,<seealso>,<summary> és <value>.

Ezeket megjegyzéseket a 3.2 kódszöveghez hasonlóan helyezhetjük el kódunkban:

3.2 kódszöveg Az XML megjegyzések használata – Xmlapp.cs __________________________ // Xmlapp.cs – Bemutató C# alkalmazás// XML dokumentációval//-----------------------------------------------

/// <summary>/// Összefoglaló leírás az osztályról.</summary>/// <remarks>/// Ez egy hosszabb megjegyzés melyben/// részletesebben leírhatjuk az osztályt. </remarks>class Xmlapp{

/// <summary> /// Az alkalmazás kezdőpontja. /// </summary> /// <param name="args"> paracssori paramáterek felsorolása</param> public static void Main(string[] args) { System.Console.WriteLine("An XML Documented Program"); } } ____________________________________________________________________

Ha lefordítjuk és futtatjuk ezt a kódot, az alábbi eredményt kapjuk:

An XML Documented Program

Ha hozzá akarunk jutni az XML dokumentációhoz, a fordítást a korábbiaktól eltérően kell elvégezni. A korábbi parancssorban el kell helyezni a /doc kapcsolót. Tehát a következőt kell begépelni:

csc /doc:xmlfile Xmlapp.cs

A fordítás után az előzővel megegyező kimenetet kapunk, de emellett hozzájutunk egy xmlfile nevű fájlhoz, amely XML dokumentációt tartalmaz. A 3.2 kódszöveg esetén a kapott file tartalma a következő:<?xml version="1.0"?><doc> <assembly> <name>Xmlapp</name> </assembly> <members> <member name="T:Xmlapp"> <summary>

Készítette: Zsótér Csaba III. éves informatikus hallgató

16

Page 17: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

Összefoglaló leírás az osztályról.</summary> <remarks> Ez egy hosszabb megjegyzés melyben részletesebben leírhatjuk az osztályt. </remarks> </member> <member name="M:Xmlapp.Main(System.String[])"> <summary>

Az alkalmazás kezdőpontja. </summary> <param name="args"> paracssori paramáterek felsorolása </param> </member> </members></doc>

A C# alkalmazások alapelemei

A programnyelvek alapja egy csokrnyi kulcsszó, melyek különleges jelentéssel bírnak. A számítógépprogramok e kulcsszavak rendezett halmazából állnak, kiegészítve néhány további szóval és jellel. A C# nyelv kulcsfontosságú részei a következők:

• Térközök• C# kulcsszavak• Literálok• Azonosítók

Formázás térközökkelA 3.1 kódszöveget úgy formáztuk, hogy a kód sorai igazodjanak egymáshoz, megkönnyítve az olvasást. A formázáshoz beillesztett üres helyeket hívjuk térközöknek. Ezek lehetnek szóközök, tabulátorok, újsor karakterek és kocsivisszák.

A fordító szinte minden esetben figyelmen kívül hagyja a térközöket, így akármennyi szóközt, tabulátort, vagy újsor karaktert beilleszthetünk a kódba. Nézzük például a 3.1 kódszöveg 14. sorát:

int radius = 4;

Ez egy jól formázott sor az elemek között egy-egy szóközzel. Ezeket a szóközöket azonban továbbiakkal is megtoldhatjuk:

int Radius = 4 ;

Szét is tördelhetjük a sorokat:

intradius=4;

Készítette: Zsótér Csaba III. éves informatikus hallgató

17

Page 18: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

Nos ez nem valami olvasható, de működik.

A térközöket egyetlen esetben kell figyelembe vennünk – az idézőjelek között álló szövegekben. Ilyenkor ugyanis pontosan az jelenik meg a képernyőn, amit beírtunk.

Kulcsszavak – a C# nyelv alapköveiA kulcsszavak különleges kifejezések egyedi jelentéssel, melyek a programnyelvek alapját adják. A C# kulcsszavait a 3.1. táblázatban soroljuk fel:

3.1. táblázat A C# kulcsszavai .

abstractbyteclassdelegateeventfixedifinternalnewoverridereadonlyshortstructtryunsafewhile

ascaseconstdoexplicitfloatimplicitisnullparamsrefsizeofswitchtypeofushort

basecatchcontinuedoubleexternforinlockobjectprivatereturnstackallocthisuintusing

boolchardecimalelsefalseforeachintlongoperatorprotectedsbytestaticthrowulongvirtual

breakcheckeddefaultenumfinallygotointerfacenamespaceoutpublicsealedstringtrueuncheckedvoid

Vannak ezen kívül a C# programokban néhány szó – get, set és a value -, melyek nem kulcsszavak, csak foglaltak. Kezeljük őket kulcsszavakként. A C# jövőbeni változataiban várható, hogy a partial, a yield és a where is kulcsszavakká válnak.

Mivel mindegyiknek jelentése van a nyelv szempontjából, természetesen foglaltak, vagyis nem használhatjuk őket saját céljainkra.

LiterálokA literálok egyszerű, rögzített értékek. Jelentésük pontosan az, amit leírva látunk belőlük. Így a 4 és a 3.14159 egyaránt literálok, de literál az idézőjelek között elhelyezett szöveg is.

AzonosítókA C# kulcsszavai és a literálok mellett vannak más szavak is, melyeket használhatunk C# programjainkban. E szavakat a fordító azonosítóknak tekinti. A 3.1. kódszöveg számos ilyet tartalmaz. Példa erre a 6. sorban a System, a 8. sorban a sample, a 14. sorban a radius, a 15. sorban a PI, a 16. sorban az area.

Készítette: Zsótér Csaba III. éves informatikus hallgató

18

Page 19: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

A C# alkalmazások szerkezete

A szavakból és kifejezésekből mondatokat állítunk össze, a mondatokból pedig bekezdéseket szerkesztünk. A térközök, kulcsszavak, literálok és azonosítók ugyanígy az utasítások és kifejezések alapjául szolgálnak. Ezekből pedig összeáll maga a program.

Utasítások és kifejezések a C#-banA kifejezés olyan kódrészlet, amelyek kulcsszavakból állnak. A következők például egyszerű kifejezések:

PI = 3.14159PI * radius * radius

Az utasítások a mondatokhoz hasonlíthatók – egy teljes gondolatot írnak le. Az utasítások végét rendszerint egy írásjel jelzi – nevezetesen egy pontosvessző ( ; ).

Az üres utasításVan egy utasítás, amelyet külön kell említenünk: ez az üres utasítás. Amint korábbam mondtuk, az utasítások pontosvesszővel végződnek – megtehetjük azonban azt is, hogy egy sorba egyetlen, önmagában álló pontosvesszőt teszünk. A kapott utasítás nem csinál semmit – hiszen nincs benne semmilyen végrehajtható kifejezés.

Adattárolás változókkal

A változók voltaképpen számítógépünk memóriájának névvel ellátott részei, így ha programunk egy változóra hivatkozik, voltaképpen e tárterület tartalmát használjuk fel.

Ha változókat akarunk használni, tudnunk kell, miként nevezzük el őket. A névadáskor az alábbi szabályokat kell figyelembe venni:

• A név betűket, számjegyeket és aláhúzás karaktereket ( _ ) tartalmazhat.• A változónév első karaktere csak betű vagy aláhúzás karakter lehet, bár az utóbbi nem

ajánlott, mivel gyakran használják különleges utasításoknál, és az olvashatóságot is rontja.

• A kis- és nagybetűk különbözőnek számítanak, vagyis a Szamlalo és a szamlalo két különböző változót jelöl.

• A C# kulcsszavai nem használhatók változónévként, hiszen a C# nyelv részei

Néhány helyes és helytelen példa változónevekre:

Változónév HelyességeSzazalek helyesy2x5_w7h3 helyeseves_koltseg helyes_2010_ado helyes, de nem ajánlottszamlalo#ell helytelen – a # nem használható karakter

Készítette: Zsótér Csaba III. éves informatikus hallgató

19

Page 20: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

double helytelen, hiszen C# kulcsszó9byte helytelen, ugyanis az első karakter számjegy

A tiszta nagybetűs neveket általában állandóknak adjuk.

A változók használataMielőtt egy változót használatba vehetnénk, be kell vezetnünk. Ezzel eláruljuk a fordítónak a változó nevét, valamint az általa tárolt adatok típusát. Ha olyan változót próbálunk meg programunkban használni, amit előtte nem vezettünk be, fordítási hibát kapunk.

A változó bevezetésével a rendszer memóriát foglal le számára, a tárolt adatok típusának azonosítása pedig lehetővé teszi, hogy a rendszer a lehető legjobb teljesítményt érje el, és ne pocsékolja a memóriát.

A változókat a következő alakban vezetjük be:

típusnév változónév;

A típusnév az alkalmazott adattípus nevét adja meg, a változónév jelentése pedig az általunk adott nevet jelenti, amivel hivatkozunk a változóra. Például:

int my_number;

Több azonos típusú változót egy sorban is bevezethetünk, csak soroljuk fel őket a típusnév után vesszővel elválasztva. Ezzel tömörebbé tehetjük a kódot:

int count, number, start;

Értékadás a változóknakA változók bevezetése után végre következhet a lényeg: az értékek tárolása.

Értékadás alakja a következő:változónév = érték;

A változónév az általunk létrehozott változó neve, az érték pedig a benne tárolni kívánt érték. Ha például az 5-öt szeretnénk tárolni egy tarolt_szam nevű változóban a következőt kell beírnunk:

int tarolt_szam = 5;

Természetesen az értékadás történhetett volna két sorban is:

int tarolt_szam;tarolt_szam = 5;

A változók értékét természetesen meg is változtathatjuk – csak rendeljünk hozzá új értéket.

Készítette: Zsótér Csaba III. éves informatikus hallgató

20

Page 21: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

Változók kezdőérték nélkülHa egy változót úgy használunk, hogy előtte nem adtunk neki kezdőértéket, hibát kapunk. A legegyszerűbben akkor rendelhetünk értéket a változókhoz, amikor bevezetjük azokat. Ezt mindenképpen tegyük meg, még ha ez az érték időleges is.

Más nyelveken – mint a C vagy a C++ - a fordítás ilyen esetekben is végbemegy, és a kiírásnál, vagy a használatnál megjelenik a memóriában az adott helyen található szemét. A C# meggátolja, hogy ilyesmi bekövetkezzen.

A C# adattípusai

Korábban láttuk, hogy egy változó bevezetésekor egyúttal a típusát is meg kell adnunk. A C#-ban a következő típusú változókat vezethetjük be:

Sbyte 8 bit előjeles -128 127Byte 8 bit előjel

nélküli0 255

Short 16 bit előjeles

-32768 32767

Ushort 16 bit előjel nélküli

0 65535

Int 32 bit előjeles

-2147483648 2147483647

Uint 32 bit előjel nélküli

0 4294967295

Long 64 bit előjeles

-92233720368547758

08

9223372036854775807

Ulong 64 bit előjel nélküli

0 18446744073709551615

Láthatjuk, hogy a különböző típusoknak, különböző memória igény van, illetve a rajtuk végezhető matematikai műveletek végrehajtása is különböző nehézségű lehet. A megfelelő változótípust használva biztosíthatjuk, hogy programunk a lehető leghatékonyabban működjön.

A következőkben a számszerű adatokat négy kategóriára osztjuk:

• Egészek• Lebegőpontos számok• Tizedestörtek• Logikai értékek

Készítette: Zsótér Csaba III. éves informatikus hallgató

21

Page 22: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

Lebegőpontos értékekNem minden szám egész, így ha olyan számokat szeretnénk tárolni, melyekben tizedesjegyek szerepelnek, akkor itt is választhatjuk a szám nagyságától függő típust. Ezek a típusok a következők:

Float 7 1.5x10-45 3.4x1038

Double 15 5x10-324 1.7x10308

C#-ban létezik egy további adattípus, melyben tizedes számokat tárolhatunk – ez a decimal, melynek feladata a nagyobb pontosság biztosítása. Ha float vagy double értékben tároljuk a számértékeket, kerekítési hibákat kapunk, így például, ha 10,00-ból kivonunk -,90-et double változókat használva az eredmény 0,99999999999999645 lehet a várt 0,10 helyett. Ha a műveletet decimal értékekkel végezzük el, a kapott szám valóban 0,10 lesz.A decimal típus 16 bájton tárolja a számokat. Érdekessége, hogy nem rendelkezik előjel nélküli alakkal, tárolási korlátai pedig nagyjából 1,0 x 10-28 és 7,9 x 10-28, emellett pontossága 28 jegyig terjed.

A változó tárolására használt memóriamennyiség a típustól függ.

A számszerű adatok mellett vannak a karakterek, melynek típusa char.

char karakter = ’a’;

Logikai értékekLogikai vagy Boole-értékek. Sokszor tudnunk kell valamiről, hogy igaz-e vagy hamis – a logikai értékekkel ezt a tudást tárolhatjuk a 0 vagy az 1 érték formájában.

A C# logikai adattípusának neve bool, mely a memóriából egy bájtot hasít ki. Értéke true (igaz) vagy false (hamis) lehet, ezek egyúttal C# kulcsszavak is.

Adattípusok a .NET környezetbenA C# edig megismert adattípusait – bool, sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, bool és decimal – egyszerű típusoknak tekintettük. Tudjuk, hogy a C# programok a CLR környezetben futnak, ennek megfelelően minden eddig ismert adattípus megfelel egy, a CLR-ben, és így a .NET környezetben használt típusnak. Ezeket az adattípusokat éppen e közvetlen megfeleltetés miatt mondjuk egyszerűeknek. A következő táblázat mutatja be, hogy milyen C# adattípus milyen .NET környezetbeli adattípusnak felel meg.

Egymásnak megfeleltetett adattípusok a C#-ban és a .NET-ben.

C# adattípus .NET adattípussbyte System.SBytebyte System.Byteshort System.Int16

Készítette: Zsótér Csaba III. éves informatikus hallgató

22

Page 23: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

ushort System.UInt16int System.Int32uint System.UInt32long System.Int64ulong System.UInt64char System.Charfloat System.Singledouble System.Doublebool System.Booleandecimal System.Decimal

Ha egy egész típusú változót .NET megfelelősével szeretnénk bevezetni – jóllehet semmi okunk rá -, a következőképpen tehetjük meg:

System.Int32 szam = 5;

Megjegyzés: A CTS olyan szabályok csoportja, melyeknek a CLR adattípusai meg kell feleljenek. A C# egyszerű adattípusai és a .NET típusok kielégítik ezt a követelményt. Ha egy programnyelv követi a CTS szabályait adattípusai elkészítésében, a bennük tárolt adatok összeegyeztethetők lesznek más, a CTS szabályainak megfelelő nyelvekkel.

Literálok vagy változók?Sokszor előfordul, hogy a forráskódunkban konkrét számot vagy értéket szeretnénk megadni. Az ilyen értékek a literál értékek – vagyis pontosan azt tartalmazzák, amit a képernyőn láthatunk belőlük. A következő két kódrészlet is egy literál:

int x = 10;ez_igaz_string = „Minden GAMF-os okos!”;

Számos példában találhattunk már számértékű literálokkal. Egy ilyen érték alapértelmezés szerint int típusúnak számít, ha egész, és double típusúnak, ha lebegőpontos.

szam = 10; // 10 int típusú számliterál, függetlenül attól, // hogy a szam milyen típusú változó

szamd = 99.9; // A 99.9 szintén számliterál, most // azonban double

szam_vegyes = 100.; // Ez is double típusú a tizedespont // miatt

Egész literálokHa egész értéket használunk, ez nagyság szerint int, uint, long vagy ulong típusok valamelyikébe kerül, attól függően, hogy melyik adattípusba fér bele.

Készítette: Zsótér Csaba III. éves informatikus hallgató

23

Page 24: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

Ha magunk szeretnénk megadni a literál adattípusát, megtehetjük – csak biggyesszünk hozzá egy utótagot. Ha például a 10-et long típusként szeretnénk használni, a következőt írhatjuk: 10L. Az előjelnélküliséget egy u utótag jelzi, ami a típusra uraló utótaggal kombinálható.

Lebegőpontos literálokAmint a korábbiakban említettük, a tizedespontot tartalmazó literálok alapértelmezés szerint double típusúak – ha float típusúakká szeretnénk tenni valamelyiket, csak írjuk utána az f vagy az F betűt:

my_float = 4.4f;

Ha decimal típust szeretnénk használni, a megfelelő utótag az m, illetve az M. Ezt az alábbiak szerint használhatjuk:

my_decimal = 1.32m;

Logikai literálokA logikai (Boole) literálokról már ejtettünk szót a korábbiakban – csak két ilyen létezik a true és a false, melyek egyúttal kulcsszavak is a C#-ban.

Karakterlánc literálokA karakterek csoportjaiból szavakat, kifejezéseket, majd mondatokat állíthatunk össze. A karakterek csoportjait karakterláncoknak nevezzük, megadásuk idézőjelek között történhet.. Példának okáért a Console.WriteLine eljárás karakterláncokat fogad. A karakterlánc literál bármilyen, idézőjelek közé helyezett karaktercsoport lehet:

„Helló világ!”„123456789”

Állandók létrehozásaA literálok mellet sokszor szükség van olyan változókra is, melyek értékét „befagyasztjuk”. Így ha például bevezetünk egy PI nevű változót, és elhelyezzük benne a 3.14159 értéket, kívánhatjuk tőle, hogy ettől kezdve ne változzon, hiszen semmi nem indokolja a módosítását – sőt, ez kárt is okozna a programunk működésében.

Ha azt szeretnénk, hogy változónk értéke állandó (konstans) maradjon, vezessük be a const kulcsszóval.:

const float PI = 3.14159;

Készítette: Zsótér Csaba III. éves informatikus hallgató

24

Page 25: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

Hivatkozási típusokA C# az adatok tárolásának alapvetően kétféle módját ismeri: az érték és a hivatkozás szerinti tárolást. Az eddigiekben megismert alapvető adattípusok érték szerinti tárolásúak.

Ha egy változó érték szerint tárolja az adatokat, akkor valóban közvetlenül az adatokat tartalmazza. A hivatkozás szerinti tárolás ennél bonyolultabb – ilyenkor ugyanis nem közvetlenül az adatokat, hanem azok címét tároljuk a változóban, vagyis egy hivatkozást rájuk.

A C#-ban a következő adattípusok használnak hivatkozás szerinti tárolást:• Osztályok• Karakterláncok• Felületek• Tömbök• Képviselők

Műveletek a C#-ban

Alapvető adatok megjelenítéseKét szóban forgó eljárást kell megismernünk, melyet már eddig is használunk a példák során, melyekkel bármit a képernyőre írhattunk. Ez a két eljárás a következő:

• System.Console.WriteLine()• System.Console.Write()

Mindkettő adatokat ír a képernyőre, egy apró különbséggel. A WriteLine() az adatok kiírása után új sort kezd, míg a Write() nem. Használata:

System.Console.WriteLine(„Helló világ!!”);

Az idézőjelek közé helyezett szövegek kiírása mellett másfajta értékeket is megjeleníthetünk.

int nbr = 456;System.Console.WriteLine(„Íme egy szám: {0}”,nbr);

Ez eredmény a következő lesz a képernyőn:

Íme egy szám: 456

Láthatjuk, hogy a {0} helyét az idézőjelbe tett szöveget követő érték vette át. De nem csak egy érték helyettesíthető be – egymás után sorszámokkal behelyettesíthetjük a további megadott értékeket is:

System.Console.WriteLine(„Az 1.érték {0}, a 2.pedig {1}”,123, „Csaba”);

Készítette: Zsótér Csaba III. éves informatikus hallgató

25

Page 26: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

A képernyőn a következő jelenik meg:

Az 1.érték 123, a 2.pedig Csaba

A számozás mindig nullával kezdődik!

Változók értékének módosítása műveletekkelA műveletek a következő kategóriákba sorolhatók:

• Az alapvető értékadó műveletek• Matematikai műveletek• Viszonyító műveletek (összehasonlító műveletek)• Feltételes műveletek• Egyéb műveletek

Emellett meg kell ismerkednünk a műveleti jelek használatával is – eszerint háromféle művelet ismeretes a C# nyelvben:

• Egyváltozós művelet (unáris művelet)• Kétváltozós művelet (bináris művelet)• Háromváltozós művelet (ternáris művelet)

Egyváltozós műveletekAz egyváltozós műveletek a nevükből következően egyetlen változóra hatnak. Általános alakja:

[műveleti jel] [változó]

vagy

[változó][műveleti jel]

Példa:

-x;

Kétváltozós műveletekNem meglepő módon a kétváltozós műveletek működéséhez két változóra van szükség – ilyen például az összeadás művelete. Használatul általános alakja a következő:

[változó1][műveleti jel][változó2]

Példa:

Készítette: Zsótér Csaba III. éves informatikus hallgató

26

Page 27: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

3.3. Két változó összege – OsszApp.cs

using System;

namespace OsszApp{

class Class1{

static void Main(string[] args){

int valt1=15;int valt2=20;int eredmeny;

eredmeny = valt1+valt2;

Console.WriteLine("A két tag összege: {0}",eredmeny);}

}}

Háromváltozós műveletekAz ide tartozó műveletek a három kategória közül a legösszetettebbek. A C# egyetlen egy ilyet tartalmaz – a feltételes művelet. Alakja a következő:

Feltétel ? utasítás_ha_igaz : utasítás_ha_hamis;

Összetett aritmetikai értékadó műveletek

Műveleti jel Használati alakja Kifejtése+= x += 4 x = x + 4-= x -= 4 x = x – 4*= x *= 4 x = x * 4/= x /= 4 x = x / 4%= x %= 4 x = x % 4

Ezek az összetett műveletek lehetővé teszik, hogy egyszerre végezzünk el egy aritmetikai művelet és egy értékadást.

Egyváltozós aritmetikai műveletekAz eddig látott aritmetikai műveletek mind kétváltozósak voltak. Létezik azonban két olyan is, melynek csak egyetlen változóra van szükségük – a növelés (++) és a csökkentés (--)

Ezek eggyel növelik, illetve csökkentik a változók értékét.

++x; //x értékét eggyel megnöveltük

Készítette: Zsótér Csaba III. éves informatikus hallgató

27

Page 28: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

--y, //y értékét eggyel csökkentettük

Meg kell jegyezni, hogy a ++x nem ugyanaz, mint a x++. Ugyanis a ++x-nél előzetes , míg az x++-nál utólagos növelést végeztünk. Ez azt jelenti, hogy az első esetben megnöveljük az x értékét eggyel és úgy vehetjük használatba, míg a másodiknál előbb használatba veszzük, majd utána növeljük meg eggyel az értéket. Természetesen ez vonatkozik a csökkentő műveletre is. (--)

Összehasonlítás viszonyító műveletekProgramjainkban sokszor fontos szerep jut értékek összehasonlításának, amihez viszonyító műveleteket (összehasonlító műveleteket, „relációs operátorokat”) használhatunk. Ezek pedig a következők:

Viszonyító műveletek

Műveleti jel Jelentés> Nagyobb, mint< Kisebb, mint= = Egyenlő!= Nem egyenlő>= Nagyobb vagy egyenlő<= Kisebb vagy egyenlő

Amikor összehasonlítást végzünk relációs operátorokkal, kétféle eredményt kaphatunk: igazat (true) vagy hamisat (false). Például:

5 < 10 5 kisebb, mint a 10, tehát igaz5 > 10 5 nem nagyobb, mint a 10, tehát hamis

5 = = 10 5 nem egyenlő 10, tehát hamis5 != 10 5 nem egyenlő 10, tehát igaz

Az if utasításA viszonyító műveletek valódi értéke az, hogy segítségükkel döntéseket hozhatunk, melyek befolyásolhatják a program futásának menetét. Mindebben az if kulcsszó siet segítségünkre.

Az if kulcsszóval két értéket hasonlítunk össze; használati alakja a következő:

if (val1 [műveleti jel] val2)utasítás(ok);

Ha a val1 és a val2 közti összehasonlítás eredménye true, a program végrehajtja a megadott utasítás, ellenkező esetben átugorja azt. Készítette: Zsótér Csaba III. éves informatikus hallgató

28

Page 29: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

Megeshet, hogy egyszerre több feltétel teljesülését kell ellenőriznünk. Erre a feladatra szolgál az AND (&&) és az OR (||). Nézzünk erre egy egyszerű példát:

if(sex = = female && age >=21) {

//Az adott személy legalább 21 éves és nő(female).}

vagy

if(sex = = female || age >=21){

//Az adott személy nő, vagy legalább 21 éves}

A műveletek kiértékelési sorrendje

A műveleteket igen ritkán használjuk magukban, gyakoribb, hogy egyetlen utasításban több is szerepel. Ilyenkor azonban figyelni kell arra, hogy a műveleteknek megvan a maga prioritási sorrendje. Tehát megvan, hogy melyik művelet végződik el előbb, ez nevezik a műveletek kiértékelési sorrendjének (operátor-precedencia). Ezen sorrendet mutatja be a következő táblázat:

A műveletek kiértékelési sorrendje.

Szint Művelettípus Műveletek1 Elsődleges ( ) . [ ] x++ x—new typeof sizeof checked

unchecked2 Egyváltozós + - ! ~ ++x –x3 Multiplikatív * / % 4 Additív + - 5 Eltolási << >>6 Viszonyító < > <= >= is7 Egyenlőségi = = ! =8 Logikai AND &9 Logikai XOR ^10 Logikai OR |11 Feltételes AND &&12 Feltételes OR ||13 Feltételes művelet ? :14 Értékadás = *= /= %= += -= <<= >>= &= ^=

____________________________________________________________________________

Készítette: Zsótér Csaba III. éves informatikus hallgató

29

Page 30: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

A kiértékelési sorrendet megváltoztathatjuk a zárójelek segítségével ( ). Mivel a zárójelek magasabb szinten vannak, mint a műveletek, bennük található kifejezések kiértékelése előbb történik meg, mint a rajtuk kívül lévőké.

Adattípusok átalakításaHa áttérünk egy adattípusról egy másikra, át kell alakítani az adatainkat, sőt, erre az átalakításra akkor is szükség lehet, ha egy műveletben két különböző típusú adat is részt vesz. Az átalakításnak két típusa ismeretes: az automatikus (rejtett, beleértett, implicit) és a kényszerített (kifejezett, meghatározott, explicit) átalakítás.

Az automatikus átalakítások önműködően, hiba nélkül végbemennek. A kényszerített átalakítások esetében az átalakítást a programozó kényszeríti ki. Ennek alakja a következő:

CélVáltozó (adattípus) KiindulásiVáltozó;

Példa erre:

int intszam = 0;long longszam = 1234;intszam = (int)longszam;

A típuskényszerítés használatánál a programozó felelőssége annak ellenőrzése, hogy a változó valóban képes befogadni az átalakított értéket. Ha nem képes, az adatok csonkolást, vagy más módosítást szenvednek. Kényszerített átalakításra számos esetben lehet szükségünk:

Ahol kényszerített átalakításra van szükség…

Kiindulási típus Céltípussbyte byte, ushort, uint, ulong, charbyte sbyte, charshort sbyte, byte, ushort, uint, ulong, charushort sbyte, byte, short, charint sbyte, byte, short, ushort, uint, ulong,

charuint sbyte, byte, short, ushort, int, charlong sbyte, byte, short, ushort, int, uint,

ulong, charulong sbyte, byte, short, ushort, int, uint,

long, charchar sbyte, byte, shortfloat sbyte, byte, short, ushort, int, uint,

long, ulong, char, decimaldouble sbyte, byte, short, ushort, int, uint,

long, ulong, char, float, decimaldecimal sbyte, byte, short, ushort, int, uint,

long, ulong, char, float, double

Készítette: Zsótér Csaba III. éves informatikus hallgató

30

Page 31: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

TípusléptetésAz automatikus átalakítások szerepet kapnak a műveletek működése során is – használatukra a típusléptetésnél (operator promotion) kerül sor. Erre akkor van szükség, amikor két változón valamilyen matematikai alapműveletet végzünk, és típusuk nem azonos. Például, ha egy byte típusú változót egy int típusúhoz szeretnénk adni, a program előbb int típusúvá alakítja.

Minden int-nél kisebb számváltozó int típusúvá válik, az int-nél bővebb típusok pedig az alábbi sorrendet veszik át egymás szerepét:

intuintlongulongfloatdoubledecimal

A program működésének szabályozása

A program menetének egyszerű megváltoztatása számos előnnyel kecsegtet, ahogy pedig egyre több programozási tapasztalatot szerzünk, látni fogjuk, hogy milyen nagy szükség van kódrészletek ismétlésére, átugrására, vagy több kódrészlet közti választásra. Akármilyen módon szeretnénk is beleszólni a program menetébe, a C# lehetőséget ad ötletünk megvalósítására. E lehetőségek alapvetően két csoportba tartoznak:

• Kiválasztó utasítások• Bejáró utasítások

Kiválasztó utasításokA kiválasztó utasítások lehetővé teszik, hogy egy feltétel eredményétől függően válasszunk bizonyos kódrészletek végrehajtása között. Ide tartozik a korábban már megismert if, de a későbbiekben tárgyalt switch is ilyen utasítás.

Ismét az ifElevenítsük fel, hogy hogyan is működik az if utasítás:

if(gender == ’m’ || gender == ’f’){

System.Console.WriteLine(„The gender is valid”);}if(gender != ’m’ && gender != ’f’){

System.Console.WriteLine(„The gender value, {0} is not valid”,gender);

Készítette: Zsótér Csaba III. éves informatikus hallgató

31

Page 32: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

}

Ezen kód hatékonyságának növelése érdekében a C# használatba veszi az if-else utasítás szerkezet, melynek használata a következő:

if(feltétel){

//utasitás(ok);}else{

//ha az if feltétele hamis a program végrehajtja az itt álló kódot

}

Látható, hogy az else lehetőséget ad arra, hogy megadjuk egy kódrészletet, melyet a program akkor hajt végre, ha az if feltétele nem teljesül, vagyis hamis eredményt szolgáltat.

Az if utasítások beágyazásaA beágyazás egyszerűen azt jelenti, hogy egy utasítást egy másikon belül helyezünk el. A C#-ban szinte minden vezérlő utasítás beágyazható társaiba.

Ha if utasítást szeretnénk egy másikba ágyazni, egyszerűen helyezzük el a másik kódblokkjában. Például:

if(gender == ’m’){

//férfiről van szó}else{

if(gender == ’m’){

//nőről van szó}else{

//se nem nő, se nem férfi}

}

A beágyazás sokszor leegyszerűsíti a programozást, de lehetőségünk van arra is, hogy az if utasításokat felhalmozzuk.

Készítette: Zsótér Csaba III. éves informatikus hallgató

32

Page 33: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

Az if utasítások felhalmozásaAz if utasítások halmozásánál az else utasítást újabb if-fel egyesítjük. Nézzünk erre egy példát:

3.3 Az if-else utasítások felhalmozása

class Stacked{ static void Main() { char gender = 'x'; if( gender == 'm' ) { System.Console.WriteLine("The gender is male"); } else if ( gender == 'f' ) { System.Console.WriteLine("The gender is female"); } else { System.Console.WriteLine("The gender value, {0}, is not valid", gender); } System.Console.WriteLine("The if statement is now over!"); }}___________________________________________________________________________________________

A switch utasításA C# lehetőséget ad arra is, hogy egy változó értéke alapján döntéseket hozzunk – rendelkezésünkre bocsátja a switch utasítást. Használatának alakja a következő:

switch(érték){

case eredmény_1://az eredmény_1-hez tartozó kódbreak;

case eredmény_2://az eredmény_2-hez tartozó kódbreak;

...case eredmény_n:

//az eredmény_n-hez tartozó kódbreak;

default://az alapértelmezésben végrehajtott kódbreak;

}

Készítette: Zsótér Csaba III. éves informatikus hallgató

33

Page 34: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

Láthatjuk, hogy ebben az utasításban nincsenek feltételek – egy érték szerepel helyettük. Ez az érték lehet egy kifejezés eredménye, vagy akár egy változó is. Ezt hasonlítja össze a program a case utasításoknál megadott értékekkel, míg egyezést nem talál. Ha nincs egyezés, végrehajtja a default utasításban megadott kódot vagy ha ilyen nincs, továbblép a switch utáni első kódsorra.

Ha a program, egyezést talált, végrehajtja az ott megadott kódot, ha pedig egy újabb case utasításhoz ér, a switch utasításnak vége szakad. Így legfeljebb egy case tartalma hajtható végre, ezután a vezérlés a switch utáni első utasításhoz kerül. Nézzünk erre is egy példát:

3.4 Kockadobás a switch utasítással

class roll{ public static void Main() { int roll = 0; // A következő két sorban a dobas (roll) értékét egy 1 és 6 közti

//véletlen számmal azonosítjuk System.Random rnd = new System.Random(); roll = (int) rnd.Next(1,7); System.Console.WriteLine("Starting the switch... "); switch (roll) { case 1: System.Console.WriteLine("Roll is 1"); break; case 2: System.Console.WriteLine("Roll is 2"); break; case 3: System.Console.WriteLine("Roll is 3"); break; case 4: System.Console.WriteLine("Roll is 4"); break; case 5: System.Console.WriteLine("Roll is 5"); break; case 6: System.Console.WriteLine("Roll is 6"); break; default: System.Console.WriteLine("Roll is not 1 through 6"); break; } System.Console.WriteLine("The switch statement is now over!"); }}

Készítette: Zsótér Csaba III. éves informatikus hallgató

34

Page 35: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

Egy kód – több esetSokszor adódhat olyan helyzet, hogy több érték esetében ugyanazt a ködrészletet szeretnénk futtatni. Így ha az előző példánál maradva a páros oldalakat szeretnénk kiíratni, mikor azt dobunk, csoportosíthatjuk a case utasításokat. Alakja a következő:

switch (roll){

case 1:case 3:case 5:

System.Console.WriteLine(„A dobás páratlan!”);break;

case 2:case 4:case 6:

System.Console.WriteLine(„A dobás páros!”);Break;

default:System.Console.WriteLine(„A dobás nincs 1 és 6 között”);

}

Más nyelvekben – így a C++-ban is – több case utasítással futtathatjuk ugyanazt a kódot, ha elhagyjuk a break utasításokat. Ilyenkor a átugrik a következő case utasításra. Fontos tudnunk, hogy a C#-ban ez nem megengedett – a program nem ugorhat át egyik case utasításról a másikra. Ez azt jelenti, hogy ha csoportokba szeretnénk rendezni a case utasításokat, nem helyezhetünk el közöttük kódot. Ezt csak egy csoport utolsó case utasítása után tehetjük meg.

Lehetőségünk van arra is, hogy egy switch utasításon belül több case-hez tartozó kódrészletet is végrehajtsunk. Erre a goto utasítás ad lehetőséget, amellyel valamelyik case-hez vagy a default parancshoz ugorhatunk. Az alábbi kódrészlet a korábban megismert switch utasítást mutatja – ezúttal a goto használatával kiegészítve:

switch (roll){

case 1:goto case 5;break;

case 2:goto case 6;break;

case 3:goto case 5;break;

Készítette: Zsótér Csaba III. éves informatikus hallgató

35

Page 36: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

case 4:goto case 6;break;

case 5:System.Console.WriteLine(„A dobás páratlan!”);break;

case 6:System.Console.WriteLine(„A dobás nincs 1 és 6 között”);Break;

}

A switch utasítás vezérlő típusaiA switch utasítások vezérlésére csak meghatározott típusok használhatók – ezek a következők: sbyte,byte,short,ushort,int,uint,long,ulong, és char, valamint karakterláncok, és egy másik típus, melynek neve enum.

Ha a döntéshez használt kifejezés eredménye nem e típusokból való, kell hogy legyen egy egyértelmű belső átalakítás, amely átalakítja ezek valamelyikére. Ha nincs ilyen, vagy több van, a fordító hibaüzenetet ad.

Bejáró utasítások

A kiválasztó utasítások mellett a program menetér úgy is módosíthatjuk, ha egyes részleteket megismétlünk. E célra a C# számos bejáró utasítást bocsát rendelkezésünkre, amelyek egy adott kódrészletet többször képesek végrehajtani. Ezt nevezzük bejáróknak („iteráció”).

A C# bejáró utasításai a következők:• while• do• for• foreach

A while utasítás

A while segítségével egy kódrészletet mindaddig ismételhetünk, míg egy megadott feltétel igaz. Használati alakja a következő:

while (feltétel){

utasítás(ok);}

Készítette: Zsótér Csaba III. éves informatikus hallgató

36

Page 37: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

Kiugrás a while utasításból és a ciklus folytatásaLehetséges a while utasítás befejezése, mielőtt a feltétel hamissá válna, de arra is módunk van, hogy egy bejárást a hozzá tartozó utasítások mindegyikének végrehajtása előtt befejezzünk.

Ha ki szeretnénk lépni a while utasításból, a break parancsot használhatjuk, ami azonnal átadja a vezérlést a while utáni első utasításnak.

A while utasításban továbbugorhatunk a következő bejárásra is – erre szolgál a continue. Használata esetén a vezérlés visszakerül a while feltételhez. A következő példa a while, a break és a continue használatát mutatja be:

3.5 A while, a break és a continue használata

class even{ public static void Main() { int ctr = 0; while (true) { ctr++; if (ctr > 10 ) { break; } else if ( (ctr % 2) == 1 ) { continue; } else { System.Console.WriteLine("...{0}...", ctr); } } System.Console.WriteLine("Done!"); }}

A do utasítás

Ha a while utasítás feltétele az első ellenőrzéskor hamis, az utasításhoz tartozó kódblokk végrehajtására soha nem kerül sor. Gyakran szükség van azonban arra, hogy ezek az utasítások egyszer lefussanak – ilyenkor segít a do utasítás.

A do használatának alakja a következő:do{

Készítette: Zsótér Csaba III. éves informatikus hallgató

37

Page 38: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

utasítás(ok);

}while(feltétel);

A do először végrehajtja a belsejében foglalt utasításokat, és ezután vizsgálja a while feltételt. Ez a while pontosan úgy működik, mint az előzőekben tárgyaltaknál. Ha a feltétel igaz, akkor a program újra végrehajtja az utasításokat, ha pedig hamis, akkor a do…while utáni első utasítás kapja meg a vezérlést.

A for utasításJóllehet, a while és a do…while utasítások gyakorlatilag mindenféle kódismétlést lehetővé tesznek, nem ők az egyedüli szereplők ezen a színpadon. Ez pedig nem, mint a for utasítás. Használati alakja a következő:

for (beállítás; feltétel; növelés | csökkentés){

utasítás(ok);}

A beállítás („inicializálás”) kódja a for utasítás kezdetén lép működésbe. Csak ilyenkor működik, később nem tér vissza rá a program.

A beállítás után a program kiértékeli a feltételt. Ha az érték igaz, következhet az utasítások végrehajtása.

Az utasítás vagy kódblokk végrehajtása után következik a növelés vagy a csökkentés. A név félrevezető, mert itt valójában bármilyen C# utasítás állhat – általában azonban egy számláló növelése vagy csökkentése szerepel e helyen.

A for utasítás fejlécében található három utasítás – beállítás, feltétel, növelés – valójában többre képes, mint első látásra gondolnánk. Mindhárom területen tetszőleges kifejezések elhelyezhetők, sőt, egy helyen akár több is.

Ha több kifejezést szeretnénk használni egy részben, valahogyan el kell választanunk azokat egymástól – erre szolgál a vessző.

for(x=1,y=2;x+Y<100;x++,y++)//utasítások

vagy

for(x=0;++x<=10;System.Console.WriteLine(„{0}”,x) );

A foreach utasításA foreach utasítás hasonló bejárást valósít meg, mint a for, de különleges célokra használatos; képes végighaladni gyűjteményeken, például tömbökön. A foreach használata segíthet leegyszerűsíteni a tömbökkel végzett munkát, különösen akkor, ha egy teljes tömbön Készítette: Zsótér Csaba III. éves informatikus hallgató

38

Page 39: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

végig szeretnénk haladni. Ráadásul itt a tömbökre egyszerűen tartalmuk alapján hivatkozhatunk, nincs szükség szögletes zárójelekre és indexelésre. A foreach utasítás hátulütője, hogy használatával a tömböket csak olvashatjuk, elemeit módosítani nincs lehetőségünk.

Az utasítás forma a következő:foreach(adattípus változónév in tömbnév){

Utasítások;}

Nézzünk erre egy rövid programrészletet:

3.6 A foreach használata

using System; public class ForEach1{ public static void Main() { char[] name = new char[] {'B','r','a','d','l','e','y'}; Console.WriteLine("Display content of name array..."); foreach( char x in name ) { Console.Write("{0}", x); } Console.WriteLine("\n...Done."); }}

Az „átkos” gotoA goto utasítás használatát minden programnyelvben folyamatos vita kíséri. Mivel feltétel nélkül képes megváltoztatni a program menetét, igen nagy erőt képvisel – ehhez azonban nagy felelősség is tartozik. Sok fejlesztő egyáltalán nem használja ezt az utasítást, mert úgy vélik átláthatatlanná teszi a kódot.

A goto háromféle alakban használható a C#-ban. Kettőt – goto case és goto default – a switch utasításnál már megismerhettünk, míg a harmadik alakja a következő:

goto címke;

Ezzel az alakkal a vezérlést egy címkeutasításnak adjuk át.

Készítette: Zsótér Csaba III. éves informatikus hallgató

39

Page 40: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

Utasítások címkézéseA címkeutasítás egy olyan parancs, ami egyszerűen megjelöl egy helyet a programban. Használati alakja igen egyszerű:

címke_neve:Nézzünk erre egy példát:

3.7 A goto használata címkékkel

class score{ public static void Main() { int score = 0; int ctr = 0; System.Random rnd = new System.Random(); Start: ctr++; if (ctr > 10) goto EndThis; else score = (int) rnd.Next(60, 101); System.Console.WriteLine("{0} - You received a score of {1}", ctr, score); goto Start; EndThis: System.Console.WriteLine("Done with scores!"); }}

A C# programok lelke: az osztályok

Az osztályok meghatározásának kulcsszava a class, használatának alakja a legegyszerűbb esetben a következő:

class azonosító{

Osztálytörzs;}

Az osztály neve hasonló bármilyen bevezethető változó nevéhez, így – hasonlóan az ott tanultakhoz – érdemes beszédes nevet adnunk neki.

Készítette: Zsótér Csaba III. éves informatikus hallgató

40

Page 41: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

A .NET környezet rengeteg beépített osztállyal rendelkezik – ezek egyikét már a kezdetektől fogva használjuk: ez a Console. Ez az osztály számos adattagot és eljárást tartalmaz – utóbbiak közül többen ismerősek lehetnek, így a Write és a WriteLine is. Az osztály neve – a fenti sémában azonosító – ez esetben a Console, törzse pedig tartalmazza a Write és a WriteLine kódját.

Osztályok bevezetéseHa elkészítünk egy osztályt,a továbbiakban használhatjuk objektumok létrehozására. Az osztály valójában mindössze egy minta objektumok készítésére – önmagában nem képes sem adatok tárolására, sem eljárások végrehajtására. Ezeket a feladatokat az objektumok látják el.

Az objektumok bevezetését („deklarációját”) példányosításnak is hívják, magukat az objektumokat pedig az osztályok példányainak nevezik.

Az objektumok bevezetése a következő alakban történik:

osztály_neve objektumazonosító = new osztály_neve();

Példa: Van egy Pont nevű osztályunk, és abból akarunk készíteni egy objektumok, a következőt kell tennünk:

Pont kezdő_pont = new Pont();

Az osztály neve Pont, míg az objektum neve kezdő_pont Mivel a kezdő_pont egy objektum, az osztály meghatározásának megfelelően tartalmazhat adatokat és eljárásokat.

Ha a bevezetés sorát tüzetesebben megvizsgáljuk, feltűnhet néhány érdekes elem. Ezek közül a legfontosabb a new kulcsszó. Amint neve is sugallja, segítségével új objektumokat hozhatunk létre – példánk esetében egy új Pont objektumot. A new kulcsszó jelentése mindig új példány létrehozására utal. Ne feledjük, az osztály csak egyszerű meghatározás, nem tárol semmit. Az objektumnak ugyanakkor szüksége van helyre a tároláshoz – ezt a helyet foglalja le a new kulcsszó.

Az objektum bevezetésénél ez értékadás jobb oldalán az osztály neve mellett ki kell tennünk egy zárójelpárt. Ez teszi lehetővé, hogy az osztály szerkezete megjelenjen az objektumban.

Az osztály tagjaiMegnéztük tehát, hogy hogyan készítsünk objektumokat az osztály felépítése alapján – ideje, hogy megtanuljuk, mit is rejt az osztályok szerkezete. Az osztály törzsében alapvetően kétféle elemet találunk: adattagokat és függvénytagokat (tagfüggvényeket).

Készítette: Zsótér Csaba III. éves informatikus hallgató

41

Page 42: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

Az adattagok között változókat és állandókat láthatunk. Szerepelhet itt bármely a korábbiakban megismert egyszerű változótípus, de szerepelhet itt összetettebb adattípusok is. Az adattagok származhatnak akár osztályokból is.

Az osztálytörzs összetevőinek másik típusába tartoznak a tagfüggvények, melyek meghatározott feladatok elvégzésére alkalmas eljárások. E feladatok lehetnek egyszerűek, mint érték adása egy változónak, de összetettek is, mint például egy sornyi szöveg kiírása előre meg nem határozott számú érték alapján – ezt teszi a Write és a WriteLine. Ez utóbbi függvények a Console tagfüggvényei.

Az adattagok, vagy mezőkA változókat más néven mezőknek is nevezik. Amint a korábbiakban említettük, az osztályok adattagjai olyan változók, melyek tagjai az osztálynak. A Pont osztályban tároljuk majd várhatóan egy pont x és y koordinátáját. E koordináták típusa sokféle lehet, ha egésznek vesszük, akkor a Pont osztály meghatározása a következő:

class Pont{

int x;int y;

} Nos ezzel meg is vagyunk, csak egy valami hiányzik, és az nem más, mint a public elérési módnak a megadása. Egy változó ugyanis csak az őt tartalmazó kódblokkban elérhető, ha csak az ellenkezőjét nem jelezzük. Esetünkben a kódblokk a Pont osztály meghatározása. A public kulcsszó megadása nélkül az x és y változók nem volnának elérhetők az Pont osztályon kívülről.

Az adattagok eléréseMiután bevezettük az adattagokat, természetesen szeretnénk azokat elérni. Amint a korábbiakban láthattuk, a public kulcsszó lehetővé teszi, hogy kívülről elérhessük őket, de nem hivatkozhatunk egyszerűen a nevükre. Hivatkoznunk az objektum nevének és az adattag nevének együttes megadásával kell. Például a fenti példából kiindulva:

Pont kezdőpont = new Pont();

kezdőpont.x

és

kezdőpont.y

3.8 Osztálybevezetés adattagokkal

class Point{

Készítette: Zsótér Csaba III. éves informatikus hallgató

42

Page 43: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

public int x; public int y;} class pointApp{ public static void Main() { Point starting = new Point(); Point ending = new Point(); starting.x = 1; starting.y = 4; ending.x = 10; ending.y = 11; System.Console.WriteLine("Point 1: ({0},{1})", starting.x, starting.y); System.Console.WriteLine("Point 2: ({0},{1})", ending.x, ending.y); }}

Az osztályok adattagjai olyanok, mint a hagyományos változók. Használhatjuk őket műveletekben, vezérlő utasításokban, vagy bárhol, ahol egy hagyományos változót elérhetünk.

Meg kell említeni, hogy az osztályokat is egymásba ágyazhatjuk, hiszen az osztály is csak adattípus. Így egy osztály típusával – ami voltaképpen csak egy fejlettebb változótípus – bevezetett objektumok ugyanott használhatunk, ahol más változókat. Például:

class Pont{

public int x;public int y;

}

class Vonal{

public Pont kezdo = new Pont();public pont veg = new Pont();public double hossz;

}

Lehetőségünk van arra is, hogy a típusokat más osztályokba ágyazzuk be. Így, ha a Pont osztályt csak a Vonal osztályban használnánk, meghatározását is beletehetnénk ebbe az osztályba. Ekkor a Pont típusú objektum a Vonal osztály számára volnának elérhetők.

A beágyazás kódja a következőképpen fest:

class Vonal{

public class Pont{

Készítette: Zsótér Csaba III. éves informatikus hallgató

43

Page 44: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

public int x;public int y;

}

public Pont kezdo = new Pont();public Pont veg = new Pont();

}

Van itt azonban még egy kis aprócska változás: a Pont osztályt nyilvánossá kell tennünk a public kulcsszóval. Ha ezt nem tesszük meg, hibát kapunk. Persze, ha jobban belegondolunk, ez logikus is: hogyan lehetnének a Pontobjektum részei nyilvánosak, ha maga az osztály nem az.

Statikus változók használataElőfordulhat, hogy azt szeretnénk, ha egy osztállyal bevezetett objektumok bizonyos értékei megegyezzenek. Ha egy értéket meg szeretnénk osztani az adott osztály összes objektuma között, a static módosítót használhatjuk. Nézzünk egy teljes példát a használatára:3.9 A static módosító használata az adattagokban

class Point{ public int x; public int y;} class Line{ static public Point origin= new Point(); public Point ending = new Point();} class StatLine{ public static void Main() { Line line1 = new Line(); Line line2 = new Line(); // A szakaszok kezdőpontjának beállítása Line.origin.x = 1; Line.origin.y = 2; // A line1 végpontjának beállítása line1.ending.x = 3; line1.ending.y = 4; // A line2 végpontjának beállítása line2.ending.x = 7; line2.ending.y = 8; // Adatok kiírása System.Console.WriteLine("Line 1 start: ({0},{1})", Line.origin.x, Line.origin.y); System.Console.WriteLine("line 1 end: ({0},{1})",

Készítette: Zsótér Csaba III. éves informatikus hallgató

44

Page 45: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

line1.ending.x, line1.ending.y); System.Console.WriteLine("Line 2 start: ({0},{1})", line.origin.x, line.origin.y); System.Console.WriteLine("line 2 end: ({0},{1})\n", line2.ending.x, line2.ending.y); // a line2 kezdőpontjának módosítása Line.origin.x = 939; Line.origin.y = 747; // Az adatok kiírása System.Console.WriteLine("Line 1 start: ({0},{1})", Line.origin.x, Line.origin.y); System.Console.WriteLine("line 1 end: ({0},{1})", line1.ending.x, line1.ending.y); System.Console.WriteLine("Line 2 start: ({0},{1})", line.origin.x, line.origin.y); System.Console.WriteLine("line 2 end: ({0},{1})", line2.ending.x, line2.ending.y); }}

Ha egy statikus adattagot egy objektum nevével próbálunk elérni, hibát kapunk. Ilyenkor az osztály nevét kell használnunk az eléréshez.

Line.origin.x = 1;

Mivel az origin objektumot statikusként vezettük be, értéke közös minden Line típusú objektumban. Ezért a line1 és line2 sem birtokolhatja ezt az értéket, segítségükkel nem is lehet beállítani – az osztály nevét kell használnunk.

Az alkalmazás osztályA szemfülesebbek felfigyelhettek rá, hogy azt az osztályt, melyre minden alkalmazásunk épül, még nem tárgyaltuk. Az előző példában ez a

class StartLine

volt.

Sőt, hasonló sort eddigi összes programunkban találhatunk. Ebből is látszik, hogy a C# objektumközpontú programnyelv – minden eleme objektum, beleértve magukat a programokat is. Ahhoz, hogy objektumot készítsünk, szükségünk van egy osztályra. Futtatáskor a rendszer példányosítja ezt az osztályt – az alkalmazás osztályt -,és egy objektumot készít – ez lesz a programunk.

Tulajdonságok létrehozásaA korábbiakban említettük, hogy az objektumközpontú programok előnye, hogy szabályozzák az adatok belső megjelenését és elérését. Eddigi példáinkban azonban csak public változókat használtunk, melyeket bármely kódrészlet szabadon elérhetett.

Készítette: Zsótér Csaba III. éves informatikus hallgató

45

Page 46: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

Az objektumközpontú programokban általában ennél nagyobb mértékben szeretnénk beavatkozni az adatok elérésébe. Ha ugyanis mindenkinek megengedjük, hogy közvetlenül elérje az adattagokat, a későbbiekben a típusuk módosítása nehézségekbe ütközhet.

E gondok áthidalására alkalmazza a C# a tulajdonságokat, melyek az objektumközpontú programozásnak megfelelő adattagokat nyújtanak. A tulajdonságok a get és a set foglelt kifejezések teszik lehetővé értékük kiolvasását és módosítását. Nézzünk erre egy példát:

class Point{ int my_X; // a my_x privát (private) változó int my_Y; // a my_Y privát (private) változó public int x { get { return my_X; } set { my_X = value; } } public int y { get { return my_Y; } set { my_Y = value; } }}

A névterekrőlHa már megismerkedtünk az osztályok alapvető jellemzőivel, jó tudnunk azt is, hogy számos előre elkészített osztály („beépített osztály”) áll rendelkezésünkre feladatok széles körének elvégzésére. A .NET környezet alaposztályai mellett a programozókat más gyártók is elhalmozzák hasznosabbnál hasznosabb osztályokkal.

Már a korábbiakban is találkoztunk alaposztályokkal – ilyen volt például a Console. Azt is megtanultuk, hogy a Console rendelkezik legalább két tagfüggvénnyel – ezek a Write és a WriteLine. A következő sor például e jegyzet alkotójának nevét írja a képernyőre:

System.Console.WriteLine(„Zsótér Csaba”);

Készítette: Zsótér Csaba III. éves informatikus hallgató

46

Page 47: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

Tudjuk, hogy a „Zsótér Csaba” egy literál, a WriteLine pedig egy eljárás, ami a Console osztályba tartozik. Azt is tudjuk, hogy a Console egy osztály objektuma. Nos, mindez nagyszerű, de felmerül egy kérdés, hogy mi az a System?

Mivel rengeteg különböző osztály létezik, fontos, hogy megfelelő módon rendszerbe szervezzük őket. Az osztályokat névterek szerint csoportosíthatjuk, vagyis a névterek osztályok csoportjai. A Console osztály a System névtérhez tartozik.

A System.Console.WriteLine egy teljes név, mellyel pontosan utalunk a megvalósító kód helyére. Mindazonáltal, hogy ne kelljen ennyit írnunk, a C# lehetővé teszi, hogy rövidebben is hozzáférjünk az osztályokhoz és eljárásaikhoz – erre használhatjuk a using kulcsszót.

Ez a kulcsszó lehetővé teszi, hogy megjelöljük a használni kívánt névteret. Ezután a programunk tudni fogja, hol keresse a kívánt osztályokat és eljárásokat, ha névtér nélkül adjuk meg azokat. A using kulcsszó használatának alakja a következő:

using névtér_neve;

A System névtér esetében így néz ki a fenti példa:

Using System;

Amennyiben azt a sort beírtuk, a továbbiakban nem lesz szükségünk System feltüntetésére, he e névteren belüli osztályokra vagy eljárásokra hivatkozunk.

Beágyazott névterekTöbb névteret is csoportba foglalhatunk, és ezt a csoportot egy újabb névtérként tárolhatjuk. Ilyenkor a teljes név kiegészül a tartalmazó névtér nevével, illetve ez a név is megjelenik a using kulcsszónál. A System névtér is számos más névteret tartalmaz, mint a Drawing, a Data és a Windows.Forms. Ha e névterek osztályait használjuk, meg kell adnunk a teljes nevüket, vagy használatba kell vennünk a megfelelő névteret a using utasítással. Ha a System-en belüli Data névteret szeretnénk programunkban használatba venni a using utasítással, a következőt kell a kód elejére írnunk:

using System.Data;

Műveletek egységbe zárása: tagfüggvények

A C#-ban az eljárásokat függvényeknek vagy tagfüggvényeknek (metódusoknak) hívják. E két elnevezés azonban nem takar két jól elhatárolható fogalmat, így a gyakorlatban felcserélhetők. A legtöbb Java, C++ és C# programozó ezeket a függvényeket metódusként említi, míg mások tagfüggvényeként hivatkoznak rájuk. Akárhogy is nevezzék, tudnunk kell, hogy ugyanarról a dologról beszélnek.

Készítette: Zsótér Csaba III. éves informatikus hallgató

47

Page 48: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

Tagfüggvény alatt egy névvel ellátott kódrészletet értünk, melyet újrahasznosítható formában tárolunk. A tagfüggvények a program többi részétől függetlenül működnek, és – amennyiben körültekintően járunk el – a nevük által jelzett feladatot hajtják végre.

Fontos hangsúlyozni még egyszer, hogy a tagfüggvények valójában különálló névvel ellátott kódrészletek, melyeket a programból meghívhatunk. A tagfüggvény hívásakor a vezérlés belép a tagfüggvénybe, végrehajtja az ott található kódot, majd visszatér a hívó eljáráshoz.

A tagfüggvény felépítéseFontos hogy tisztában legyünk a tagfüggvények szerkesztésével. Ehhez a következő minta ad példát:

Tagfüggvény-fejléc{

Tagfüggvénytörzs}

A fajléc a tagfüggvények kulcsfontosságú része, amint számos alapvető jellemzőt meghatároz:

• a programok elérési lehetőségeit,• a visszatérési adattípust,• a tagfüggvénynek átadott értékeket,• a tagfüggvény nevét.

Például:

public double terulet()

A tagfüggvény nyilvános (public), vagyis az osztályon kívüli programokból is elérhető. Láthatjuk azt is, hogy a tagfüggvény visszatérési értékének típusa double, neve pedig terulet. Végül mivel a zárójel üres, nem adunk át értékeket a tagfüggvénynek. Jelen esetben csak az osztály adattagjait használjuk.

Értékek visszaadása tagfüggvényekbőlA tagfüggvények bevezetésekor megadhatunk egy visszatérési típust, melyet a fejlécben kell feltüntetnünk. A típusra nézve nincs megkötés, bármilyen érvényes adattípus használható e szerepben.

A tagfüggvény törzsében intézkednünk kell róla, hogy ezt az értéket visszaadjuk a hívó programnak – erre szolgál a return kulcsszó. Használata egyszerű: csak írjuk utána azt az értéket vagy változót, amelyet vissza szeretnénk adni. Ha a tagfüggvénynek nem kell semmilyen visszatérési értéket szolgáltatnia, akkor használhatjuk a void (üres) típust, mely jelzi, hogy nincs visszatérési érték.

Készítette: Zsótér Csaba III. éves informatikus hallgató

48

Page 49: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

A tagfüggvények törzsének felépítéseA tagfüggvény törzse tartalmazza a kódot, melyet a program a tagfüggvény hívását követően végrehajt. A szóban forgó kódrészletet a nyitó kapcsos zárójellel veszi kezdetét, és a záró párjával ér véget. Itt állhat bármilyen, az eddigiekben megismert kódrészlet, azzal a megkötéssel, hogy általában csak a megfelelő osztály adattagjaival, valamint az átadott adatokkal végezhet műveleteket.

Amennyiben a fejlécben megadtunk valamilyen visszatérési típust, a return kulcsszóval egy ilyen értéket kell visszaadnunk. A return utasítással visszaadott adat típusának meg kell egyeznie a fejlécben feltüntetett adattípussal.

A tagfüggvények meghívásaAhhoz hogy egy tagfüggvényt használatba vegyünk, meg kell hívnunk. Ezt ugyanúgy tehetjük meg, mint az adattagokkal: írjuk be az objektum nevét, majd ezt követően egy pontot és a tagfüggvény nevét. A két hívás között azonban különbség van, a tagfüggvények esetében ugyanis ki kell írnunk egy zárójelpárt, valamint közéjük az esetleges átadott paramétereket is.

Változókhoz hasonlóan, ha a tagfüggvény rendelkezik visszatérési típussal, a kapott érték a hívás helyére kerül.

Nézzünk most mindezekre egy példát:

3.10 Osztály tagfüggvényekkel class Circle{ public int x; public int y; public double radius; public double getArea() // A getArea tagfüggvény és a kódsor hozzá { double theArea; theArea = 3.14159 * radius * radius; return theArea; }

public double circumference() // A circumference tagfüggvény és //kódsora

{ double theCirc; theCirc = 2 * 3.14159 * radius; return theCirc; }} class CircleApp{ public static void Main() { Circle first = new Circle(); //first objektum, melynek

//típusa Circle

Készítette: Zsótér Csaba III. éves informatikus hallgató

49

Page 50: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

Circle second = new Circle(); //secound objektum, melynek

//típusa Circle

double area; double circ; first.x = 10; // Az x adattag elérése a first objektumban first.y = 14; // Az y adattag elérése a first objektumban first.radius = 3; // A radius adattag elérése a first objektumban second.x = 10; // Az x adattag elérése a secound objektumban second.y = 11; // Az x adattag elérése a secound objektumban second.radius = 4;// A radius adattag elérése a secound objektumban System.Console.WriteLine("Circle 1: Center = ({0},{1})", first.x, first.y); System.Console.WriteLine(" Radius = {0}", first.radius);

// A first objektum getArea() tagfüggvényének meghívás és visszaadott// értékének kiíratása a képernyűre

System.Console.WriteLine(" Area = {0}", first.getArea());

// A first objektum circimference() tagfüggvényének meghívása és// visszaadott értékének kiíratásaSystem.Console.WriteLine(" Circum = {0}", first.circumference());

// A secound objektum getArea() tagfüggvényének meghívás és // visszaadott értékének tárolása az area változóban

area = second.getArea();

// A secound objektum circumference() tagfüggvényének meghívás és // visszaadott értékének tárolása a circ változóban

circ = second.circumference();

System.Console.WriteLine("\nCircle 2: Center = ({0},{1})", second.x, second.y); System.Console.WriteLine(" Radius = {0}", second.radius); System.Console.WriteLine(" Area = {0}", area); System.Console.WriteLine(" Circum = {0}", circ); }}

Adattagok elérése tagfüggvényekbőlHa egy objektum tagfüggvényében vagyunk, nem kell külön kitennünk az objektum nevét adattagjai és tagfüggvényei előtt. Egy tagfüggvényen belül létrehozhatunk újabb változókat is – ezek azonban csak addig élnek, míg a tagfüggvény működik, így tehát a tagfüggvényhez kötődnek, és helyi változóknak hívjuk őket. Előző példában a getArea esetében egy double típusú theArea nevezetű változót készítettünk. Amikor a tagfüggvény véget ér, az itt tárolt érték – és maga a változó is – megsemmisül.

Készítette: Zsótér Csaba III. éves informatikus hallgató

50

Page 51: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

Értékadás tagfüggvények számáraAz értékek átadásához a fejlécben paramétereket kell feltüntetnünk. A fejléc formátuma ez esetben a következőképpen alakul:

Módosítok VisszatérésiTípus Név(Paraméterek)

A paramétereket tehát a zárójelen belül kell elhelyeznünk. Az egyes paramétereket az alábbi alakban adhatjuk meg:

[Jellemző] Típus Név

A Típus az átadott adattípusa, míg a Név az átadott változó neve. Emellett beállíthatunk egy Jellemző-t is melyről a későbbiekben lesz majd szó. Nézzünk egy egyszerű példát a paraméterátadás mechanizmusára:

3.11 Értékátadás using System; class Multiply{

static public long multi( long nbr1, long nbr2 ){

return (nbr1 * nbr2);}

} public class MultiApp{ public static void Main() { long x = 1234; long y = 5678; long a = 6789; long b = 9876; long result; result = Multiply.multi( x, y); Console.WriteLine("x * y : {0} * {1} = {2}", x, y, result); result = Multiply.multi(a, b); Console.WriteLine("a * b : {0} * {1} = {2}", a, b, result); result = Multiply.multi( 555L, 1000L );

Console.WriteLine("With Long values passed, the result was {0}", result);

}}

Fontos megjegyezni, hogy a tagfüggvényeknek átadott értékek száma meg kell egyezzen a meghatározásnál megadott paraméterek számával.

Készítette: Zsótér Csaba III. éves informatikus hallgató

51

Page 52: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

Statikus tagfüggvényekA korábbiakban láttuk, hogy a static módosító használata esetén a megfelelő adattagot a program nem az objektumhoz, hanem az osztályhoz tartozónak tekinti. A tagfüggvényeknél is minden hasonlóan működik, mint az adattagoknál. Ez azt jelenti, hogy az objektum helyett az osztály nevével hivatkozhatunk a tagfüggvényre.

Paraméterek elérési jellemzőiAz előző példában értékeket adtunk át a tagfüggvényeknek, melyeket lemásolt, majd működése befejeztével egyszerűen megszabadult tőlük. Ezt nevezzük érték szerinti átadásnak. Mindazonáltal ez csak egyike a tagfüggvény és a paraméterek közti kapcsolati formáknak. Három elérési mód is ismeretes:

• Érték szerinti• Hivatkozás szerinti• Kimeneti (out)

A pontos terminológia elvileg a következő: a tagfüggvény meghatározásánál paramétereket sorolunk fel, amikor azonban meghívjuk ezt a tagfüggvényt, az átadott értékek neve argumentum. A fenti jellemzőket a paraméterekkel adjuk meg; szerepük az argumentumok kezelésének szabályozása. A gyakorlatban mind az elvont paraméterre, mind a tényleges paraméterre (az „argumentumra”) általában a paraméter szót használják.

Érték szerinti paraméterátadásAmint a korábbiakban elhangzott, ilyenkor a tagfüggvényeknek átadott adatokból egy másolat készül, és a tagfüggvény ezt a másolatot használja. Az adatok eredeti példányai nem módosulnak.

Hivatkozás szerinti paraméterátadásElőfordul, hogy meg szeretnénk változtatni a tagfüggvényeknek átadott adatok eredetijét. Ilyenkor nem érték, hanem hivatkozást kell átadnunk, vagyis egy olyan változót, mellyel hozzáférhetünk az eredeti változóhoz. Ha a hivatkozást módosítjuk, az eredeti változó is megváltozik.

A hivatkozás tulajdonképpen a memória egy helyére mutat, melyen a program a hozzá tartozó adatokat tárolja. Például ha készítünk egy number nevezetű változót és tároljuk a memóriában. Készítünk egy hivatkozást, ami a number tárolási helyére mutat; ha a hivatkozást megváltoztatjuk, a number értéke is módosul.

Mivel egy hivatkozási változó különböző területekre mutathat, a hagyományos változóktól eltérően nem köthető a memória egy bizonyos helyéhez. A hivatkozás az eredeti változó tárolási helyére mutat, így a paraméterváltozó módosításai megjelennek az eredeti változóban is. Amikor egy olyan tagfüggvényt hívunk, melynek van hivatkozás szerinti paramétere, ez a paraméter mindig az éppen átadott változóra mutat.

Készítette: Zsótér Csaba III. éves informatikus hallgató

52

Page 53: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

A paraméterek meghatározásukkor jellemzőként alapértelmezésben az adattípus eredeti jellemzőjét kapják. Az egyszerű adattípusok esetében ez érték szerinti átadás jelent. Ha egy ilyen értéket mégis hivatkozás szerint kívánunk átadni, írjuk a paraméterek sorába az adattípus elé a ref kulcsszót.

Már megemlítettük, hogy az egyszerű adattípusok alapértelmezésben érték szerintiek, vagyis készítésükkor a program egy helyet jelöl ki számukra a memóriában. Más adattípusok, így az osztályok, alapértelmezésben hivatkozás szerintiek. Ez azt jelenti, hogy az osztály neve azt a címet tartalmazza, melyen az osztály adatai találhatók, nem pedig magukat az adatokat. Nézzünk egy példát erre:

3.12 Hivatkozás és érték szerinti paraméterátadás using System; class nbr{ public double squareByVal( double x ) { x = x * x; return x; } public double squareByRef( ref double x ) { x = x * x; return x; }}class RefVars{ public static void Main() { nbr doit = new nbr(); double nbr1 = 3; double retVal = 0;

Console.WriteLine("Before square -> nbr1 = {0}, retVal = {1}", nbr1, retVal); retVal = doit.squareByVal( nbr1 ); Console.WriteLine("After square -> nbr1 = {0}, retVal = {1}", nbr1, retVal);

Console.WriteLine("\n---------\n");

retVal = 0; // reset return value to zero

Console.WriteLine("Before square -> nbr1 = {0}, retVal = {1}", nbr1, retVal);

retVal = doit.squareByRef( ref nbr1 ); Console.WriteLine("After square -> nbr1 = {0}, retVal = {1}", nbr1, retVal); }

Készítette: Zsótér Csaba III. éves informatikus hallgató

53

Page 54: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

}

A kimeneti elérés használataA return csak egyetlen változó visszaadását teszi lehetővé, jóllehet sokszor többre volna szükségünk. A gondot a hivatkozási változók használatával orvosolhatjuk, de a C# erre a célra egy külön jellemzőt is hadrendbe állított.

Paramétereink között felsorolhatunk olyanokat is, melyekben kimeneti értékeket szeretnénk megadni – csak ki kell tennünk eléjük az out kulcsszót. Nevéből is láthatóan (out = ki) ez a kulcsszó jelzi, hogy a vele jelzett paramétereket kimenetre szánjuk. Ezért ha tagfüggvényünkben out paramétert használunk, mindenképpen gondoskodjunk olyan változóról, amely a kimeneti értéket tárolja. Nézzünk erre is egy példát:

3.13 Az out jellemző használata using System; class nbr{ public void math_routines( double x, out double half, out double squared, out double cubed ) { half = x / 2; squared = x * x; cubed = x * x * x; }} class Outter{ public static void Main() { nbr doit = new nbr(); double nbr = 600; double Half_nbr, Squared_nbr, Cubed_nbr;

doit.math_routines(nbr, out Half_nbr, out Squared_nbr, out Cubed_nbr);

Console.WriteLine("After method -> nbr = {0}", nbr); Console.WriteLine(" Half_nbr = {0}", Half_nbr); Console.WriteLine(" Squared_nbr = {0}", Squared_nbr); Console.WriteLine(" Cubed_nbr = {0}", Cubed_nbr); }}

Az out paraméterekben átadott változóknak nem kell kezdőértéket adnunk a tagfüggvény hívását megelőzően.

Készítette: Zsótér Csaba III. éves informatikus hallgató

54

Page 55: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

Az osztály-tagfüggvények típusai

A tagfüggvények használati alapjait már megtanultuk, de nem szabad figyelmen kívül hagyni néhány különleges tagfüggvény típust sem:

• Tulajdonságelérési tagfüggvények• Konstruktorok• Destruktorok

Tulajdonságelérési tagfüggvényekE tagfüggvényekkel – nevük get és set már dolgoztunk. Feladatuk, hogy lehetővé tegyék privát adatok használatát.

KonstruktorokAz objektumok létrehozásánál gyakran van szükségünk bizonyos kezdeti beállításokra. E feladat elvégzésére az objektumokban egy különleges tagfüggvény áll rendelkezésünkre – a konstruktor (létrehozó függvény). E tagfüggvénynek két típusa létezik:

• Példánykonstruktor – ami az egyes objektumpéldányok készítésénél használatos• Statikus konstruktor – amelyet a program még azelőtt meghív, hogy akár egyetlen

objektumot is létrehozott volna az osztályból.

PéldánykonstruktorA példánykonstruktor olyan tagfüggvény, amelyet a program egy adott osztály minden példányának elkészítésekor meghív. Bármilyen kódot elhelyezhetünk benne, amit más tagfüggvényekben, de a konstruktor általában az objektumok kezdeti beállítására használják, így többnyire változóknak adunk értéket bennük.

A konstruktor felépítése a következő:

Módosítók osztálynév(){

//a konstruktor törzse}

Láthatjuk tehát, hogy a konstruktor az osztály nevével határozhatjuk meg. A módosítók az eddig megismertek közül kerülhetnek ki; itt általában a public kulcsszót használjuk. Visszatérési típust nem tüntetünk fel.

Fontos tudnunk, hogy minden osztály rendelkezik alapértelmezett konstruktorral, melyet az objektum meghív, akkor is, ha magunk nem készítettünk ilyet a számára. A konstruktor megírásával többletlehetőséget kapunk a kezdeti beállítások meghatározására. Az objektum létrehozásakor a program automatikusan elindítja a konstruktort. Nézzünk erre példát:

3.14 Konstruktor használata

Készítette: Zsótér Csaba III. éves informatikus hallgató

55

Page 56: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

using System;

namespace Konstruktor{

class Constructor{

public int szam1;public int szam2;

public Constructor(){

szam1=0;szam2=0;

} }

class Class1{

static void Main(string[] args){

Constructor ujobj = new Constructor();

Console.WriteLine("Calling Constructor method...");Console.WriteLine("ujobj.szam1 = {0}",ujobj.szam1);Console.WriteLine("ujobj.szam2 = {0}",ujobj.szam2);

Constructor masikObj = new Constructor();

Console.WriteLine("Calling Constructor method...");Console.WriteLine("masikObj.szam1 = {0}",masikObj.szam1);Console.WriteLine("masikObj.szam2 = {0}",masikObj.szam2);

Console.WriteLine("Set new values...");masikObj.szam1 = 15;masikObj.szam2 = 20;

Console.WriteLine("masikObj.szam1 = {0}",masikObj.szam1);Console.WriteLine("masikObj.szam2 = {0}",masikObj.szam2);

}}

}

Statikus konstruktorokAz adattagokhoz és tagfüggvényekhez hasonlóan statikus konstruktorokat is készíthetünk. A static módosítóval ellátott konstruktort a program az osztály első objektumának létrehozása előtt hívja meg, és többé nem is használja. Egy példa erre:

3.15 Statikus konstruktor használata using System;

namespace StatikusKonstruktor{

class Konstruktor{

static public int szamlalo;

Készítette: Zsótér Csaba III. éves informatikus hallgató

56

Page 57: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

public int szam;

static Konstruktor(){

szamlalo = 100;}

public Konstruktor(){

szamlalo++;Console.WriteLine("In Constructor szamlalo = {0}

",szamlalo);szam = 0;

}}class Class1{

static void Main(string[] args){

Konstruktor obj1 = new Konstruktor();Konstruktor obj2 = new Konstruktor();Konstruktor obj3 = new Konstruktor();

}}

}

DestruktorokLehetőségünk van arra is, hogy műveleteket végezzünk az objektum megsemmisítésekor – erre szolgálnak a destruktorok (megsemmisítő függvények).

A destruktor valamikor azután indul el, hogy a program végleg befejezte az objektum használatát. Mi az hogy „valamikor”? Nos, az objektum megsemmisítése történhet rögtön az objektum használatának befejezésekor, de eltolódhat egészen a program végéig is. Sőt, még az is előfordulhat, hogy a program anélkül fejeződik be, hogy meghívta volna a destruktort. Ez azt jelenti, hogy nem igazán rendelkezünk befolyással a destruktorok használatának időpontjára, így alkalmazásuk korlátozott hasznossággal bír.

Egyes nyelvekben – ilyen a C++ is – magunk is meghívhatjuk a destruktort, így a programozó befolyásolhatja a végrehajtásának idejét. A C#-ban erre nincs lehetőségünk.

Ha technikai oldalról közelítjük meg a dolgokat, azt mondhatjuk, hogy a C# futásidejű környezete általában akkor hívja meg a destruktort, amikor az objektum nincs többé használatban. Ez a hívás többnyire akkor következik be, amikor a környezet szabad vagy felszabadítható memória után néz (ez a szemétgyűjtés). Ha a C# futásidejű környezetének nincs szüksége újabb memóriafoglalásra az objektum használatának befejezése és a program vége között, a destruktort egyáltalán nem hívja meg. Van rá lehetőség, hogy erőltessük a szemétgyűjtést, de jobban tesszük, ha inkább a destruktor használatát korlátozzuk.

Készítette: Zsótér Csaba III. éves informatikus hallgató

57

Page 58: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

A destruktor használataA C#-ban a destruktorok neve egy hullámos vonalból (tilde, ~), az osztály nevéből, valamint egy üres zárójelpárból áll:

~osztálynév(){

//A destruktor törzse}

Itt nem használhatunk semmiféle módosítót vagy egyéb kulcsszót. Nézzük az előző példát destruktorral kiegészítve:

3.16 Destruktor használata using System;

namespace Destruktor{

class Destruktor{

static public int szamlalo;public int szam;

static Destruktor(){

szamlalo = 100;}

public Destruktor(){

Console.WriteLine("In Constructor...");szamlalo++;Console.WriteLine("In Constructor szamlalo = {0} ",szamlalo);szam = 0;

}~Destruktor(){

Console.WriteLine("In Destructor...");}

}class Class1{

static void Main(string[] args){

Console.WriteLine("Star of Main method...");Destruktor obj1 = new Destruktor();Destruktor obj2 = new Destruktor();Destruktor obj3 = new Destruktor();Console.WriteLine("End of Main method...");

}}

}

Készítette: Zsótér Csaba III. éves informatikus hallgató

58

Page 59: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

Fontos megjegyezni, hogy a destruktor hívásában soha nem lehetünk biztosak. Az is előfordulhat, hogy nem futnak le.

Összetett adatszerkezetek: struktúrák, felsorolások és tömbök

StruktúrákA struktúrák (adatszerkezetek) újabb adattípust tesznek elérhetővé számunkra, melyben – hasonlóan az osztályokhoz – szerepelhetnek adatok és tagfüggvények is. Sőt, a hasonlóság még szembetűnőbb, ha elmondjuk, hogy itt is használhatunk konstrutorokat, állandókat, mezőket, tagfüggvényeket, tulajdonságokat, sorszámozókat (indexelőket), műveleteket és beágyazott típusokat.

Az osztályok és struktúrák közti különbségJóllehet számos hasonlóság áll fenn az osztályok és a struktúrák között, egy lényeges és több apróbb különbség van köztük. A lényeges különbség az adatok tárolásának módja, a struktúrák ugyanis – ellentétben az osztályokkal – érték szerinti tárolást biztosító típusok.

A struktúrák tagjaiA struktúrák tagjait ugyan úgy vezethetjük be, mint az osztályokéit:

struct Pont{

public int x;public int y;

}

3.17 A Pont struktúra használata struct Pont{ public int x; public int y;} class PontApp{ public static void Main() { Pont starting = new Pont(); Pont ending = new Pont(); starting.x = 1; starting.y = 4; ending.x = 10; ending.y = 11; System.Console.WriteLine("Pont 1: ({0},{1})",

Készítette: Zsótér Csaba III. éves informatikus hallgató

59

Page 60: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

starting.x, starting.y); System.Console.WriteLine("Pont 2: ({0},{1})", ending.x, ending.y); }}

Az osztályokhoz hasonlóan a struktúrák is tartalmazhatnak más adattípusokat, így más struktúrákat is.

struct Pont{ public int x; public int y;} struct Line{ public Pont starting; public Pont ending;}

A struktúrák tagfüggvényeiAz osztályokhoz hasonlóan a struktúrák is rendelkeznek tagfüggvényekkel és tulajdonságokkal, melyek pontosan úgy adhatunk meg, mint az osztályok esetében. Ez egyúttal azt is jelenti, hogy ugyanazokat a módosítókat és jellemzőket is használhatjuk. E tagfüggvények osztálybeli társaikhoz hasonlóan túlterhelhetők, és ugyanúgy adhatunk, illetve fogadhatunk értékeket tőlük.

A struktúrák konstruktoraiA hagyományos tagfüggvények mellett a struktúrák rendelkeznek konstruktorokkal is. Ha azonban konstruktor használata mellett döntünk, nem hagyhatjuk üresen a paraméterlistáját:

struct Point{ public int x; public int y; public Point(int x, int y) { this.x = x; this.y = y; }}

A struktúrák konstruktoraira vonatkozóan létezik még egy fontos megkötés: kezdőértéket kell adjanak a struktúra összes adattagjának. Ha a struktúra alapértelmezett (paraméter nélküli) konstruktorát használjuk, az automatikusan alapértelmezett értékeket ad az adattagoknak – ez általában 0-kat jelent. Ha azonban saját konstruktorunkat használjuk, ügyelnünk kell arra, hogy minden adattaggal magunk foglalkozzunk.

Készítette: Zsótér Csaba III. éves informatikus hallgató

60

Page 61: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

A new használatát mellőzhetjük, ha az alapértelmezett konstruktort használjuk, de ha példányosításkor sajátunkat szeretnénk alkalmazni, ezt nem kerülhetjük meg.

A struktúrák destruktoraiA cím kissé csalóka, ugyanis az osztályokkal ellentétben a struktúrák nem rendelkezhetnek destruktorral. Emiatt ne aggódjunk, hiszen a destruktorok – bizonytalan végrehajtásuk miatt – amúgy sem vesszük sok hasznukat. Ha egy struktúrában destruktort próbálunk meg alkalmazni, a fordító hibát jelez.

FelsorolásokA C#-ban lehetőségünk van felsoroló típusok használatára is. Segítségükkel olyan változókat hozhatunk létre, melyek értéke véges halmazból származik. Vegyük példaként a hét napjait. Ahelyett, hogy sorszámukkal – 1,2,3,4,5,6,7 – hivatkoznánk rájuk, sokkal könnyebben kezelhetővé válnak, ha nevükön szólíthatnánk őket.

A felsorolásokkal lehetővé válik ilyen értékek használata. Bevezetésük az enum kulcsszóval történhet, a következő alakban:

módosítók enum felsorolás_neve{

felsorolás_tag1,felsorolás_tag2,...Felsorolás_tagN

}

A módosító helyén a new kulcsszó, illetve az elérési módosítók állhatnak. A felsorolás_neve bármilyen érvényes azonosító lehet, míg a felsorolás_tag1 … felsorolás_tagN a felsorolt értékeket tartalmazzák.

Ha a felsorolást a gyakorlatban is használni akarjuk, akkor egy felsorolás_neve típusú objektumot kell készítenünk. Nézzünk erre egy példát:

3.18 Az enum kulcsszó használata using System; class Colors{ enum Color { red, white, blue } public static void Main() {

Készítette: Zsótér Csaba III. éves informatikus hallgató

61

Page 62: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

string buffer; Color myColor; Console.Write("Enter a value for a color: 0 = Red, 1 = White, 2 = Blue): ");

buffer = Console.ReadLine(); myColor = (Color) Convert.ToInt32(buffer); switch( myColor ) { case Color.red: System.Console.WriteLine("\nSwitched to Red..."); break; case Color.white: System.Console.WriteLine("\nSwitched to White..."); break; case Color.blue: System.Console.WriteLine("\nSwitched to Blue..."); break; default: System.Console.WriteLine("\nSwitched to default..."); break; }

System.Console.WriteLine("\nColor is {0} ({1})", myColor, (int) myColor); }}

A felsorolások alapértelmezett értékeinek módosításaA felsoroló változók alapértelmezett értéke 0 - és egyesével nő -, és ez még akkor is így van, ha a felsoroló típusnak nincs olyan tagja, amely 0-nek felelne meg. Mindazonáltal ezeket az alapértelmezett értékeket megváltoztathatjuk, így 0 helyett kezdhetünk például 1-gyel is.Ha szeretnénk, hogy a felsorolás 1-gyel kezdődjön, kétféleképpen járhatunk el. Először is, elhelyezhetünk egy kitöltő értéket a felsorolás első helyén. Ez egyszerű módszer, de ha jóval magasabb értéken szeretnénk kezdeni, már nem jöhet számításba.

A második módszer a felsorolás tagjainak közvetlen beállítása. Ezt megtehetjük literálokkal, más felsoroló értékekkel, vagy számításokkal. Erre példa a következő kódsor:

3.19 A felsorolás számértékeinek beállítása using System; public class Bday{ enum Month { January = 1, February = 2, March = 3, April = 4,

Készítette: Zsótér Csaba III. éves informatikus hallgató

62

Page 63: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

May = 5, June = 6, July = 7, August = 8, September = 9, October = 10, November = 11, December = 12 } struct birthday { public Month bmonth; public int bday; public int byear; } public static void Main() { birthday MyBirthday; MyBirthday.bmonth = Month.August; MyBirthday.bday = 11; MyBirthday.byear = 1981; // This is a lie... System.Console.WriteLine("My birthday is {0} {1}, {2}", MyBirthday.bmonth, MyBirthday.bday, MyBirthday.byear); }}

A felsorolás alapjául szolgáló típus módosításaAz eddigi példákban a felsorolások alapjául az int típus szolgált. Ez az alapértelmezés. De használhatjuk a byte, sbyte, int, uint, short, ushort, long és ulong típust is. Ha külön nem nyilatkozunk a típusválasztásról, a program az int típust használja. Az egyéb típusok beállítása akkor lehet ésszerű, ha tudjuk, hogy az int méretezésénél lényegesen nagyobb vagy kisebb értéket szeretnénk tárolni. Az alapértelmezett típust az alábbiak szerint változtathatjuk meg:módosítók enum felsorolás_neve : típusnév{

tag(ok)}

A típus megváltoztatásával azonban felelősség is jár: meg kell bizonyosodnunk arról, hogy tagjaink eddigi értékei megfelelnek az új típusnak.

TömbökA korábbiakban megtanultuk, hogy a különböző típusú adatokat együttesen osztályokban vagy struktúrákban tárolhatjuk. Sokszor adódik azonban, hogy tárolt adatok típusa azonos, és akkor nyújt segítséget a tömb típus.

Készítette: Zsótér Csaba III. éves informatikus hallgató

63

Page 64: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

A tömb tehát olyan változó, amely azonos típusú adatokat tartalmaz. Ezek az értékek egymás után állnak a számítógép memóriájában, így módosításuk és előkeresésük egyszerű.

Az, hogy egy változót egy másik után vezetünk be a kódszövegben, még nem jelenti azt, hogy értékeik a memóriában egymás mellé kerülnek. Épp ellenkezőleg: e változók a memória egészen különböző részeire eshetnek, még ha együtt adtuk is meg őket. A tömb azonban egyetlen változó, csak több elemmel. Ez az oka annak, hogy az elemek egymás után állnak a memóriában.

Ha tömböt szeretnénk bevezetni, az adattípus után egy szögletes zárójelpárt kell kitennünk:

Adattípus[] tömb_név;

Nézzük meg a tömb működését egy konkrét példán keresztül:

decimal[] tomb;

Ezzel létrehoztunk egy változót, amely képes decimal értékek tárolására, de memóriát nem foglaltunk le számukra. Ennek elvégzésére – hasonlóan a korábbiakban látottakhoz – a new kulcsszót kell használnunk. Ha tömbünket példányosítjuk, meg kell határoznunk, hány értéket fog tárolni. Ez úgy megtehetjük meg, ha a kezdeti értékadásnál szögletes zárójelben feltüntetjük a tömbelemek számát:

tomb = new decimal[12];

vagy

decimal[] tomb = new decimal[12];

Miután bevezettünk egy tömböt használni is szeretnénk. A tömb elemekből áll, melyeket sorszámuk (indexük) segítségével érhetünk el. A tömbök első elemének indexe 0, a másodiké 1, az utolsó elem sorszáma pedig eggyel kisebb, mint a tömb hossza.

A tömb adott elemének eléréséhez a tömb neve után fel kell tüntetnünk az elem sorszámát szögletes zárójelek között. Például:tomb[5] = 1235.25m;Nézzünk egy példát a tömbök használatára:

3.20 Tömbök használata using System; public class Balances{ public static void Main() { decimal[] balances = new decimal[12]; decimal ttl = 0m; System.Random rnd = new System.Random(); for (int indx = 0; indx < 12; indx++ )

Készítette: Zsótér Csaba III. éves informatikus hallgató

64

Page 65: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

{ balances[indx] = (decimal) (rnd.NextDouble() * 10000); } for( int indx = 0; indx < 12; indx++ ) { Console.WriteLine("Balance {0}: {1}", indx, balances[indx]); ttl += balances[indx]; } Console.WriteLine("================================"); Console.WriteLine("Total of Balances = {0}", ttl); Console.WriteLine("Average Balance = {0}", (ttl/12)); }}

A tömbelemek kezdeti beállításaA tömbök elemeinek kezdőértékeket a tömb bevezetésével és feltöltésével egyidőben, az értékek felsorolásával adhatunk. Az értékeket vesszővel elválasztva egy blokkban kell álljanak:

decimal [] tomb = new decimal[6] {1000.00m,2000.00m,3000.00m,4000.00m,5000.00m,6000.00m};

Érdemes megjegyezni, hogy ha így adunk kezdőértéket a tömb elemeinek, nem kell külön feltüntetnünk a tömb méretét szögletes zárójelek között. Az alábbi utasítás tehát egyenértékű az előzővel:

decimal [] tomb = new decimal[] {1000.00m,2000.00m,3000.00m,4000.00m,5000.00m,6000.00m};

A fordító automatikusan 6 elemű tömböt készít, hiszen ennyi értéket adtunk meg számára. Ha a deklarációban feltüntettük az elemek számát, nem kell mindegyiküknek kezdőértéket adnunk. Ha nem adjuk meg az elemek számát, csak felsoroljuk a tömbelemeket, a tömbben nem helyezhetünk el több értéket a későbbiekben.

Többdimenziós tömbökA többdimenziós tömb valójában tömbök tömbje – sőt, lehet tömbök tömbjének tömbje is! A szintek szaporodásával gondjaink is sűrűsödnek, és kódunk kezd átláthatatlanná válni, ezért három szintnél (három dimenziónál) többet nem érdemes használnunk. Az egyszerű tömbök tömbjét kétdimenziós tömbként is szokás emlegetni, ugyanis természetes módon ábrázolható két dimenzióban. A kétdimenziós tömbök bevezetésénél, az egyszerű tömböknél (egydimenziós) tanultakból indulunk ki:

byte[,] tomb = new byte[15,30];

A különbség annyi, hogy a deklaráció első felében megjelent egy vessző, a második felében pedig két, vesszővel elválasztott szám szerepel. Ha csak egy egyszerű többdimenziós tömböt szeretnénk készíteni, ami néhány karaktert tartalmaz, a következőt kell írnunk:

Készítette: Zsótér Csaba III. éves informatikus hallgató

65

Page 66: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

char[,] betuk = new char[2,3];

Ez egy kétdimenziós tömb, mely két, háromelemű karaktertömböt tartalmaz. Kezdőértékkel a bevezetés pillanatában is elláthatjuk:

char[,] betuk = new char[2,3] {{’a’,’b’,’c’},{’X’,’Y’,’Z’}};

vagy

betuk[0,0] = ’a’;betuk[0,1] = ’b’;betuk[0,2] = ’c’;betuk[1,0] = ’X’;betuk[1,1] = ’Y’;betuk[1,2] = ’Z’;

Különböző méretű tömböket tartalmazó tömbökAz előzőekben hallgatólagosan feltettünk valamit a kétdimenziós tömbökről – méghozzá azt, hogy a benne található altömbök mérete megegyező. Így a kétdimenziós tömb alakja egy téglalapot ad. De mi a helyzet akkor, ha a tömbök mérete eltér? Nézzük a következő példát:

char [][] myname = new char[2][];myname[0] = new char[] { ’Z’ , ’s’ , ’ó’ , ’t’ , ’é’ , ’r’ };myname[1] =new char[] {’C’ , ’s’ , ’a’ , ’b’ , ’a’ };

A myname egy tömbökből álló tömb, amely kettő, különböző méretű karaktertömböt tartalmaz. Mivel a tömbök különböző hosszúságúak, nem bánhatunk velük úgy, mint korábbi „szabályos” társaikkal.

Itt nem használhatunk egy közös szögletes zárójelpárba zárt, vesszővel elválasztott indexpárt – külön szögletes zárójelek közé kell helyeznünk őket. Kezdeti értékadás nélkül a következőképpen kell eljárnunk:

char myname = new char[2][];myname[0] = new char[6];myname[1] = new char[5];

A tömbök hosszának és határainak vizsgálataMeg kell említenünk, hogy a tömbök ismerik saját méretüket. A tömbméret a tömb Length nevű adattagjában találhatjuk meg. Ne csodálkozzunk – a tömbök is objektumok, mint minden más a C#-ban. A Length egyébként minden objektumban előfordul. A tomb nevű egydimenziós tömb hosszát például a tomb.Length alakban érhetjük el.

A Length használható többdimenziós tömbök esetében is, az altömbök méreteihez pedig a GetLength() tagfüggvénnyel juthatunk hozzá – ehhez csak az altömb számát kell megadnunk. Nézzünk erre egy példát:

Készítette: Zsótér Csaba III. éves informatikus hallgató

66

Page 67: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

3.21 „Fűrészfogas” kétdimenziós tömb

using System; public class Names{

public static void Main(){

char[][] name = new char[2][];

name[0] = new char[6] {'Z', 's', 'ó', 't', 'é', 'r'};name[1] = new char[5] {'C', 's', 'a', 'b', 'a'};

Console.WriteLine("Display the sizes of the arrays...\n");

Console.WriteLine("Length of name array {0}", name.Length);

for( int ctr = 0; ctr < name.Length; ctr++)

Console.WriteLine("Length of name[{0}] is {1}", ctr, name[ctr].Length);

//-------------------------------------------------------------

Console.WriteLine("\nDisplaying the content of the name array...");

for( int ctr = 0; ctr < name.Length; ctr++){

Console.Write("\n"); // new linefor( int ctr2 = 0; ctr2 < name[ctr].Length; ctr2++ ){

Console.Write("{0}", name[ctr][ctr2]);}

}Console.WriteLine("\n...Done displaying");

}}

Tömbök használata osztályokban és struktúrákbanA tömbök ugyanolyan típusok, mint az eddig megismertek – így hát bárhol használhatjuk őket, ahol egyéb társaikat, többek között osztályokban és struktúrákban is.

A tagfüggvényekhez való hozzáférés kifinomult módozatai

A tagfüggvények túlterheléseMinden objektumközpontú programozási nyelv egyik kulcsfontosságú szolgáltatása a többalakúság (polimorfizmus). Amint azt korábban már bemutattuk, a többalakúság az a képesség, amely lehetővé teszi, hogy akkor is eredményt érjünk el, ha több lehetőség áll rendelkezésünkre. A többalakúság egyik lehetséges megvalósítása a túlterhelés.

Készítette: Zsótér Csaba III. éves informatikus hallgató

67

Page 68: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

A C# nyelvben a túlterhelés nyújtotta lehetőségeket a leglátványosabban a tagfüggvényekkel kapcsolatban lehet megmutatni. Létrehozhatunk olyan osztályt, amely számos különböző típusú adattal képes dolgozni, de az eredmény mindig ugyanaz lesz.

A tagfüggvények túlterhelése azt jelenti, hogy több különböző tagfüggvényt hozunk létre azonos névvel. Ugyanakkor ezeknek a tagfüggvényeknek mégis egyedieknek kell lenniük valamilyen módon, másként a fordítóprogram nem tud különbséget tenni közöttük. Nézzünk egy példát a tagfüggvények túlterhelésére:

3.22 Tagfüggvények túlterhelése using System; public class Circle { public int x; public int y; public double radius; private const float PI = 3.14159F; public double Area() { return Area(radius); } public double Area( double rad ) { double theArea; theArea = PI * rad * rad;

Console.WriteLine(" The area for radius ({0}) is {1}", rad, theArea);

return theArea; } public double Area(int x1, int y1, double rad) { return Area(rad); } public double Area( int x1, int y1, int x2, int y2 ) { int x_diff; int y_diff; double rad; x_diff = x2 - x1; y_diff = y2 - y1; rad = (double) Math.Sqrt((x_diff * x_diff) + (y_diff * y_diff)); return Area(rad); } public Circle()

Készítette: Zsótér Csaba III. éves informatikus hallgató

68

Page 69: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

{ x = 0; y = 0; radius = 0.0; } } class CircleApp { public static void Main() { Circle myCircle = new Circle(); Console.WriteLine("Passing nothing..."); myCircle.Area(); Console.WriteLine("\nPassing a radius of 3..."); myCircle.Area( 3 );

Console.WriteLine("\nPassing a center of (2, 4) and a radius of 3...");

myCircle.Area( 2, 4, 3 );

Console.WriteLine("\nPassing center of (2, 3) and a point of (5, 6)...");

myCircle.Area( 2, 3, 4, 5 ); } }

Egy kicsit hosszú a kód, de ha alaposan áttanulmányozzuk, akkor világossá válik, hogy mit jelent a tagfüggvények túlterhelése.A kódsor rövid magyarázata: A program a kör területét számítja ki három különböző

módon – a sugár segítségével, a sugár és a középpont koordinátájának segítségével, valamint a középpont koordinátájának és a kör egy kerületi pontjának koordinátáinak segítségével. Erre három különböző paraméterlistájú, de azonos nevű tagfüggvény áll rendelkezésünkre. A program a paraméterlista alapján választja ki, hogy melyik tagfüggvényt használja a kör területének kiszámítására.

Konstruktor túlterheléseA közönséges tagfüggvények túlterhelése mellett lehetőségünk van a konstruktorok túlterhelésére is. Egy túlterhel konstruktor lehetőséget ad arra, hogy egy objektumnak bizonyos értékeket adjuk át, közvetlenül a létrehozás pillanatában. Nézzünk erre is egy példát:

3.23 Konstruktor túlterhelése using System; public class Circle { public int x; public int y; public int radius; private const float PI = 3.14159F;

Készítette: Zsótér Csaba III. éves informatikus hallgató

69

Page 70: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

public double area() { double theArea; theArea = PI * radius * radius; return theArea; } public double circumference() { double Circ; Circ = 2 * PI * radius; return Circ; } public Circle() { x = 0; y = 0; radius = 0; } public Circle( int r ) { x = 0; y = 0; radius = r; } public Circle ( int new_x, int new_y ) { x = new_x; y = new_y; radius = 0; } public Circle ( int new_x, int new_y, int r ) { x = new_x; y = new_y; radius = r; } public void print_circle_info() { Console.WriteLine("Circle: Center = ({0},{1})", x, y); Console.WriteLine(" Radius = {0}", radius); Console.WriteLine(" Area = {0}", area()); Console.WriteLine(" Circum = {0}", circumference()); } } class CircleApp { public static void Main() { Circle first = new Circle(); Circle second = new Circle(4); Circle third = new Circle(3,4); Circle fourth = new Circle(1, 2, 5);

Készítette: Zsótér Csaba III. éves informatikus hallgató

70

Page 71: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

Console.WriteLine("\nFirst Circle:"); first.print_circle_info(); Console.WriteLine("\nSecond Circle:"); second.print_circle_info(); Console.WriteLine("\nThird Circle:"); third.print_circle_info(); Console.WriteLine("\nFourth Circle:"); fourth.print_circle_info(); } }

Látható, hogy valamennyi konstruktort ugyanolyan névvel és azonos módon hoztuk létre. Az egyetlen eltérés itt a bemenő paraméterek számában van. Ezt nevezzük aláírásnak.

A tagfüggvények aláírásaA tagfüggvényeket azért lehet túlterhelni, mert valamennyinek egyedi aláírása („szignatúra”, signature) van. Amint azt az előbb láttuk, a túlterhelt tagfüggvényeket a rendszer a bemenő paraméterek száma alapján képes megkülönböztetni. Valójában e különbségtételnek más módszerei is léteznek. A tagfüggvények úgynevezett aláírását együttesen alkotják azok a tulajdonságok, amelyek a különbségtétel alapjául szolgálnak.

Egy tagfüggvény aláírása tehát alapvetően bemenő paramétereinek számából és típusából tevődik össze. Nézzük az előbbi kódsor konstruktorait:

Circle()Circle( int )Circle( int, int )Circle( int, int, int )

A 3.22 kódsorban bemutatott Area tagfüggvénynek szintén négy különböző aláírása volt, hiszen négy változatban létezett:

double Area()double Area( double )double Area( int, int, double )double Area( int, int, int, int )

A következő kód további négy olyan tagfüggvény-változatot mutat, amelynek szintén előfordulhatnak együtt ugyanazon osztályon belül:

MyFunc( int )MyFunc( float )MyFunc( ref int )MyFunc( val int )

Készítette: Zsótér Csaba III. éves informatikus hallgató

71

Page 72: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

Ugyanakkor vannak bizonyos dolgok, amelyek bár elvileg szolgálhatnának a különbségtétel alapjául, nem szerepelnek a tagfüggvények aláírásában. Ilyen például a visszatérési érték típusa. Nem számít különbözőnek két aláírás akkor sem, ha az egyik tagfüggvényben ugyanolyan típusú tömb szerepel bemenő paraméternek, mint amilyen skalár adattípus az adott helyen a másik példányban előfordul. A következő túlterhelési kísérletre tehát fordítási hibaüzenetet fogunk kapni:

int myMethod( int )int myMethod( int[] )

Az aláírások megkülönböztetésére a params kulcsszót sem használhatjuk. Ha tehát egy programban együtt fordul elő a következő két tagfüggvény, megint csak fordítási hibaüzenetet kapunk:

void Method( string, float )void Methos( string, params float[])

A C# ugyanakkor nem korlátozza a túlterhelt példányok számát. Amíg bármely két aláírás különbözik egymástól, tetszőlegesen sok változatát adhatjuk meg ugyanannak a tagfüggvénynek.

Változó számú paraméter használataMostanra tehát tisztában vagyunk azzal, miként hozhatunk létre és használhatunk tagfüggvényeket. Azt is megtanultuk, hogyan adjuk át kívülről adatokat a tagfüggvényeknek. Láttuk, hogy az adatátadásnak több különböző módja is létezik. Átadhatunk adatokat érték szerint vagy hivatkozás formájában, és olyan változókat is, amelyek alapján a tagfüggvény képes kiszámítani a visszatérési értéket. Azt is láttuk, hogyan adhat vissza értéket a tagfüggvény a return kulcsszó segítségével a külvilágnak. Mindehhez a tagfüggvények módszeres használata szükséges.

Mi van azonban akkor, ha egy tagfüggvénynek ismeretlen, és általában változó számú paramétert akarunk átadni bemenetként? Ilyenkor kér megoldás van: vagy többször hívjuk meg ugyanazt az eljárást, vagy írunk egy olyat, ami változó számú bemenő paramétert képes kezelni. Vegyük például a Console.WriteLine és a Console.Write tagfüggvényeket. Mindkettő egy-egy karakterláncot vár bemenetként, valamint előre meg nem határozható számú és típusú egyéb paramétert.

Ha ismeretlen számú paramétert akarunk egy tagfüggvénnyel fogadni, a params kulcsszót használhatjuk. Ennek a kulcsszónak a tagfüggvény paraméterlistájában utolsóként kell szerepelnie, és mindig egy tömbtípussal használjuk. Nézzünk erre egy példát:

3.24 A params kulcsszó használata using System; public class AddEm { public static long Add( params int[] args ) {

Készítette: Zsótér Csaba III. éves informatikus hallgató

72

Page 73: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

int ctr = 0; long Total = 0; for( ctr = 0; ctr < args.Length; ctr++) { Total += args[ctr]; } return Total; } } class MyApp { public static void Main() { long Total = 0;

Total = AddEm.Add( 1 ); Console.WriteLine("Total of (1) = {0}", Total); Total = AddEm.Add( 1, 2 ); Console.WriteLine("Total of (1, 2) = {0}", Total); Total = AddEm.Add( 1, 2, 3 ); Console.WriteLine("Total of (1, 2, 3) = {0}", Total); Total = AddEm.Add( 1, 2, 3, 4 ); Console.WriteLine("Total of (1, 2, 3, 4) = {0}", Total); } }

Ha a deklarációból kihagyjuk a params kulcsszót, akkor a 30., 33. és 36. sorokban látható kóddal nem sokra megyünk. Ahelyett, hogy egyszerűen átadnánk a tagfüggvényeknek a paramétereket, előbb el kellene azokat helyeznünk egy egészeket tartalmazó tömbben, majd a tömböt kellene átadnunk. A params kulcsszó legfőbb haszna éppen az, hogy ezt az egészet a fordítóprogramra bízza.

Látható, hogy az előbb nem hoztunk létre AddEm típusú objektumot. Mivel az Add tagfüggvény statikus (static) egyszerűen az osztály nevével (AddEm) hívtuk meg. Mindez tehát ezt jelenti, hogy a függvényt akkor is használhatjuk, ha egyetlen objektumot sem hozunk létre.

A params kulcsszó használata több különböző adattípussalAz előző példában nem tudtuk ugyan előre, hogy hány bemenő paraméter fog érkezni, azt azonban igen, hogy valamennyi egész érték lesz. Ugyanakkor mivel valamennyi adattípus alapja ugyanaz az osztály, nevezetesen az object, az object adattípus segítségével változó számú és egyben eltérő típusú bemenő paraméter kezelése is megoldható. Nézzünk erre is egy példát:

3.25 Különböző típusú adatok átadása using System;

Készítette: Zsótér Csaba III. éves informatikus hallgató

73

Page 74: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

public class Garbage { public static void Print( params object[] args ) { int ctr = 0; for( ctr = 0; ctr < args.Length; ctr++) { Console.WriteLine("Argument {0} is: {1}", ctr, args[ctr]); } } } class MyApp { public static void Main() { long ALong = 1234567890987654321L; decimal ADec = 1234.56M; byte Abyte = 42; string AString = "Cole McCrary";

Console.WriteLine("First call..."); Garbage.Print( 1 ); Console.WriteLine("\nSecond call..."); Garbage.Print( ); Console.WriteLine("\nThird call..."); Garbage.Print( ALong, ADec, Abyte, AString ); Console.WriteLine("\nFourth call..."); Garbage.Print( AString, "is cool", '!' ); } }

A params kulcsszó működésének még részletesebb vizsgálataÉrdemes az eddigieknél kicsit részletesebben is megvizsgálni, hogyan is működik pontosan a params kulcsszó, mi is történik a hatására. Ha egy tagfüggvénynek bemenő paramétereket adunk át, a fordítóprogram először is megvizsgálja, van-e olyan tagfüggvény, amelyre a „leírás” éppen ráillik. Ha talál ilyet, akkor egyszerűen meghívja. Ha nincs találat, a fordító megvizsgálja, hogy az adott névvel létezik-e olyan tagfüggvény, amelynek a megadásában szerepel a params kulcsszó. Ha talál megfelelőt, akkor azt hívja meg, de előbb az átadni kívánt értékeket elhelyezi egy tömbben, és ténylegesen azt adja át a tagfüggvénynek bemenetként.

Nézzük meg az AddEm.Add( 1, 2, 3, 4) hatására mi zajlik le:

Ennek hatására a fordítóprogram valójában a következő kódnak megfelelő műveletet végzi el:

int[] x = new int[4];

Készítette: Zsótér Csaba III. éves informatikus hallgató

74

Page 75: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

int[0] = 1;int[1] = 2;int[2] = 3;int[3] = 4;AddEm.Add(x);

A Main tagfüggvény használata és a parancssori paraméterek kezeléseAzt már tudjuk, hogy a Main tagfüggvény különleges helyet foglal el a programon belül, mivel először mindig ezt hívja meg az operációs rendszer. Ugyanakkor a Main-nek átadott paraméterek száma – akárcsak minden más tagfüggvénynél – szintén változó lehet. Az egyetlen eltérés az, hogy itt nem kell használnunk a params kulcsszót.

Ez azért lehetséges, mert a rendszer a parancssori paramétereket automatikusan elhelyezi egy karakterláncokat tartalmazó tömbben. Ez pedig éppen az a művelet, amit a params kulcsszó hatására a fordítóprogram elvégezne. Ha pedig a paraméterek eleve egy tömbben érkeznek, feleslegessé válik a params megadása.

Ha programunkat fel akarjuk készíteni parancssori paraméterek kezelésére, annak a szokásos módja általában a következő:

public static [int | void] Main(string[] args)

A void vagy int kulcsszó használata nem kötelező. A Main tagfüggvény amúgy általában vagy void típusú, vagy egy egész értéket ad vissza. A lényeg most a paraméterlista kezelése, amely nem más, mint egy karakterláncokat tartalmazó args nevű tömb. Ezt a nevet egyébként bármi másra lecserélhetjük, de a gyakorlatban a legtöbb C# programozó az args nevet használja. A parancssori paraméterek kezelésére nézzünk most egy példát:

3.26 Parancssori paraméterek kezelése using System; class CommandLine { public static void Main(string[] args) { int ctr=0; if (args.Length <= 0 ) { Console.WriteLine("No Command Line arguments were

provided."); return; } else { for( ctr = 0; ctr < args.Length; ctr++) { Console.WriteLine("Argument {0} is {1}", ctr+1,

args[ctr]); } } }

Készítette: Zsótér Csaba III. éves informatikus hallgató

75

Page 76: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

}

Az első kimenet azt mutatja, hogy mi történik, ha parancssori paraméterek nélkül hívjuk meg a programot.

C:\gyak\CommandLine>CommanLineNo Command Line arguments were provided

A másik kimenet azt mutatja, miként reagál a programunk különböző számú és típusú parancssori paraméterek megadására.

C:\gyak\CommandLine>CommanLine xxx 123 456 789.012Argumentum 1 is xxxArgumentum 2 is 123Argumentum 3 is 456Argumentum 4 is 789.012

HatókörökA változók nem tartanak örökké. Ezért tisztában kell lennünk, hogy egy változó meddig létezik, és mikor törli a futásidejű rendszer. A változó élettartamát és a hozzáférhetőségét együttesen hatókörnek (scope, érvényességi kör) nevezzük. A hatókörnek több szintje létezik, melyek közül a két leggyakrabban használt vagy említett a globális (általános, programszintű) és a helyi (lokális) érvényesség.

A globális változók a kód bármely pontjáról láthatók és hozzáférhetők. Ugyanakkor megadhatók olyan változók is, amelyek a kódnak csak egy kisebb-nagyobb területéről kezelhetők. Ezeket az adott területre nézve helyi változóknak nevezzük.

Munka, helyi hatókörrel rendelkező változókkalA hatókörök közül a legszűkebb az, amikor egy változó csak egy adott programblokkon belül kezelhető. Egy blokk tartalmazhat egy egyszerű ismétlődő programrészletet, de lehet ennél sokkal összetettebb és hosszabb is. Nézzünk erre példát:

3.27 Helyi változók hatókörön kívül using System; class Scope { public static void Main() { for( int x; x < 10; x++ ) { Console.WriteLine("x is {0}", x); } Console.WriteLine("Out of For Loop. x is {0}", x); } }

Készítette: Zsótér Csaba III. éves informatikus hallgató

76

Page 77: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

Ha ezt a kódsort megpróbáljuk futtatni, akkor egy hibát fogunk kapni, ugyanis a Console.WriteLine("Out of For Loop. x is {0}", x)-ban lévő x ezen a ponton nem létezik, nincs bevezetve. Az x változót a for cikluson belül hoztuk létre, ami azt jeleni, hogy amikor a for ciklus működése befejeződik, akkor az x változó is megszűnik létezni, megszűnik az érvényessége. Ha pedig egy változót saját hatókörén kívül próbálunk meg használni, az hibát eredményez.

Az osztályváltozók és a helyi változók megkülönböztetése Az osztályváltozók és a helyi változók megkülönböztetésének egyik módja az, hogy a megfelelő helyen mindig kiírjuk az osztály nevét. A korábbi leckék alapján ezt a módszert már ismerjük, de azért nem árt egy kis ismétlés.Attól függően, hogy az adott változót hogyan vezettük be, az osztály szerinti kifejezett hivatkozásnak is két módja van. Ha szabványos, nem statikus osztályváltozóról van szó, a this kulcsszót használhatjuk. Ha tehát egy x nevű változóhoz akarunk hozzáférni, akkor a this.x szerkezetet kell használnunk.

Ha a kérdéses adattag statikus (static), a this helyett ki kell írnunk az osztály nevét.

Osztály hatókörének megváltoztatása módosítókkalEmlékezzünk vissza, hogy a tagfüggvényekkel és adattagokkal kapcsolatban két, a hozzáférés lehetőségeit befolyásoló módosítót is használhatunk. Ezek a public és a private.

Ha egy tagfüggvénnyel vagy adattaggal kapcsolatban a public kulcsszót használjuk, az az osztályon kívülről is hozzáférhetővé válik. Ha ellenben a private kulcsszót használjuk, a kérdéses osztályelem csak az őt bevezető osztályon belülről lesz hozzáférhető. Alapértelmezés szerint egy osztály valamennyi tagfüggvénye és adateleme private.

Ha sem a private, sem a public kulcsszót nem adjuk meg egy változó deklarációjában, a fordító automatikusan privátnak fogja tekinteni. Egyes nyelvek lehetőséget adnak arra is, hogy változókat osztályon, illetve tagfüggvényeken kívül is megadjuk. E változók hatóköre ezekben a nyelvekben más, mint azoké, amelyek függvényeken vagy osztályokon belül szerepelnek. A C# ilyen szempontból különleges nyelv, mivel egyáltalán nem engedi változók osztályon kívüli bevezetését.

Objektumok nélküli osztályok létrehozása Lehetőségünk van arra is, hogy egy osztály létrehozása után kifejezetten megtiltsuk, hogy bárki létrehozzon ebbe az osztályba tartozó objektumot. Persze így első olvasásra a legtöbbünk számára nem egészen nyilvánvaló, mire is jó pontosan egy osztály, aminek egyetlen példánya sincs, és a tiltás miatt nem is lehet soha. Ugyanakkor, ha jól belegondolunk, mi magunk is használtunk már számos olyan osztályt, amelynek nem voltak példányai. Vegyük például a Console osztályt, amelynek minden különösebb előkészület vagy egy Console típusú objektum létrehozása nélkül szoktuk használni a WriteLine vagy bármelyik másik tagfüggvényét.

Készítette: Zsótér Csaba III. éves informatikus hallgató

77

Page 78: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

De hogyan is használhatunk egy osztályt annak elemeit képző objektum nélkül? Amint azt már megtanultuk, a static típusú tagfüggvények logikailag magához az osztályhoz tartoznak, és nem az osztály objektumaihoz. Ez egyben azt is jelenti, hogy ha egy osztály valamennyi adattagja és tagfüggvénye statikus, akkor semmi értelme az osztályba tartozó objektumokat létrehozni. Nézzünk erre egy egyszerű példát:

3.28 Statikus tagfüggvények using System; public class MyMath { public static long Add( params int[] args ) { int ctr = 0; long Answer = 0; for( ctr = 0; ctr < args.Length; ctr++) { Answer += args[ctr]; } return Answer; } public static long Subtract( int arg1, int arg2 ) { long Answer = 0; Answer = arg1 - arg2; return Answer; } } class MyMathApp { public static void Main() { long Result = 0; Result = MyMath.Add( 1, 2, 3 ); Console.WriteLine("Add result is {0}", Result); Result = MyMath.Subtract( 5, 2 ); Console.WriteLine("Subtract result is {0}", Result); } }

Privát konstruktor használataHa kifejezetten meg akarjuk akadályozni, hogy a felhasználó (programozó) létrehozhasson egy adott osztályba tartozó objektumokat, akkor nincs más teendők, mint a private kulcsszót szerepeltetni a konstruktornak a bevezetésében. Amint már oly sokszor említettünk, egy privát tagfüggvényt kizárólag az adott osztályon belülről lehet használni. Ha magát a konstruktort tesszük priváttá, az azt jelenti, hogy ezt a tagfüggvényt sem érhetjük el kívülről. Mivel pedig egy objektum létrehozásakor mindenképpen le kell futnia a konstruktornak, ez gyakorlatilag azt jelenti, hogy nem tudunk az adott osztályba tartozó objektumokat létrehozni. Nézzük az előző példát private konstruktorral: Készítette: Zsótér Csaba III. éves informatikus hallgató

78

Page 79: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

3.29 Privát konstruktor használata using System; public class MyMath { public static long Add( params int[] args ) { int ctr = 0; long Answer = 0; for( ctr = 0; ctr < args.Length; ctr++) { Answer += args[ctr]; } return Answer; } public static long Subtract( int arg1, int arg2 ) { long Answer = 0; Answer = arg1 - arg2; return Answer; } private MyMath() { // nothing to do here since this will never get called! } }

class MyMathApp2 { public static void Main() { long Result = 0; // MyMath var = new MyMath(); Result = MyMath.Add( 1, 2, 3 ); Console.WriteLine("Add result is {0}", Result);

Result = MyMath.Subtract( 5, 2 ); Console.WriteLine("Subtract result is {0}", Result); } }

A névterek használatának átismétléseA névterek elsődleges haszna az, hogy segítik az osztályok és egyéb típusok rendszerezését. Már eddig is számos olyan névteret használtunk, amelyeket maga a programozási környezet bocsátott rendelkezésünkre. Ilyen volt például a System névtér.

Egy névtér tartalmazhat más névtereket is, osztályokat, struktúrákat, felsorolásokat, felületeket és képviselőket (delegate). Készítette: Zsótér Csaba III. éves informatikus hallgató

79

Page 80: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

A névtér elnevezéseEgy névtér bármilyen nevet tartalmazhat, amely egyébként is használható a megfelelő típusokkal kapcsolatban. Ez gyakorlatilag azt jelenti, hogy a nevek szabványos karakterekből állhatnak, és az aláhúzás karaktert is tartalmazhatják. Maguknak a névtereknek a nevei ezen kívül pontot is tartalmazhatnak. Ezeken kívül egyéb megkötés a névterekkel kapcsolatban nincs, de mint máshol, itt is célszerű törekedni a kifejező nevek használatára.

A névtér bevezetéseA névterek megadásához a namespace kulcsszót kell használnunk, amelyet a létrehozni kívánt névtér neve követ. A névtérbe tartozó neveket kapcsos zárójelek között soroljuk fel. Például:

3.30 Névtér megadása using System; namespace Consts { public class PI { public static double value = 3.14159; private PI() {} // private constructor } public class three { public static int value = 3; private three() {} // private constructor } } namespace MyMath { public class Routine { public static long Add( params int[] args ) { int ctr = 0; long Answer = 0; for( ctr = 0; ctr < args.Length; ctr++) { Answer += args[ctr]; } return Answer; } public static long Subtract( int arg1, int arg2 ) { long Answer = 0; Answer = arg1 - arg2; return Answer; } }

Készítette: Zsótér Csaba III. éves informatikus hallgató

80

Page 81: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

} class MyMathApp { public static void Main() { long Result = 0; Result = MyMath.Routine.Add( 1, 2, 3 ); Console.WriteLine("Add result is {0}", Result); Result = MyMath.Routine.Subtract( 5, 2 ); Console.WriteLine("Subtract result is {0}", Result); Console.WriteLine("\nThe value of PI is {0}", Consts.PI.value ); Console.WriteLine("The value of three is {0}",

Consts.three.value ); } }

Minden forráskódot tartalmazó fájl egy-egy önálló névteret képvisel még akkor is, ha ezt kifejezetten nem adjuk meg. Ugyanakkor valamennyi fájl egy-egy globális névteret tartalmaz, ami azt jelenti, hogy a kérdéses fájlban nevesített névterekből a fájl minden egyéb eleme hozzáférhető.

A using kulcsszó és a névterekA using kulcsszó segítségével valamelyest könnyebben használhatunk névtereket. Ennek a kulcsszónak alapvetően két szerepe van. Először is a using segítségével álneveket rendelhetünk egy már létező névtérhez. Másodsorban a using segítségével megkönnyíthetjük egy adott névtér neveihez való hozzáférést, mivel segítségével előírhatjuk a megadott nevek automatikus kiegészítését teljesen meghatározott nevekké (fully qualified name).

A teljesen meghatározott névtereknek rövidítéseKorábban tulajdonképpen már láttuk, hogyan is használható a using kulcsszó arra, hogy segítségével rövideden és tömören előírjuk a teljesen meghatározott nevekké történő automatikus névkiegészítést. Ha a forráskód tartalmazza a using System; sort, akkor nem kell többé kiírnunk a System névtér nevét, ha az általa tartalmazott osztályokhoz vagy egyéb típusokhoz akarunk hozzáférni.

A megfelelő using kifejezésnek a kódszövegben azok előtt kell szerepelnie, amelyekkel kapcsolatos. Ez gyakorlatilag azt jelenti, hogy célszerű a névterekre való hivatkozásokat a legelső kódsorokban elhelyezni. Ha a using később szerepel, mint egy, az adott névtérre való hivatkozás, fordítási hibát kapunk.

Készítette: Zsótér Csaba III. éves informatikus hallgató

81

Page 82: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

Álnevek használata a using segítségévelA using kulcsszó segítségével álneveket (alias, másodnév) is rendelhetünk egy már létező névtérhez. Ez lehetővé teszi, hogy egy névtérnek vagy akár egy, a névtérben szereplő osztálynak menet közben másik nevet adjunk. Az álnév bármilyen karakterlánc lehet, ami közönséges névként is megfelelne. Az álnév megadásának formája a következő:

using álnév = névtérVagyosztálynév;

Vegyük például a következő kódsort:

Using doit = System.Console;

Ha egy kód tartalmazza ezt a sort, akkor mindazokon a helyeken, ahol a System.Console nevet kellene leírnunk, használhatjuk a doit nevet is. Nézzünk erre egy példát:

3.31 Álnév létrehozása a using segítségével using doit = System.Console;

class AliasApp { public static void Main() { doit.WriteLine("Hello World!"); } }

Problémák kezelése a programon belül: kivételek és hibák

Úgy kell programoznunk, hogy a kód kezelni tudja azt a dolgot is, ami más, mint a többi: a kivételt. Hasonlóan, ha mi magunk fedezzük fel a hibákat saját programunkban, akkor meg kell találnunk, és el kell távolítanunk a hibás részeket. Ezt nevezzük nyomkövetésnek (vagy hibakeresésnek).

A programok kezelésével kapcsolatos alapfogalmakAmikor programot írunk, számításba kell vennünk minden olyan hibát, amely a futás közben egyáltalán felmerülhet. Ha adatokat olvasunk be egy fájlból, bemenetet fogadunk egy felhasználótól, egy szolgáltatástól vagy akár saját programunk egy másik elemtől, mindig meg kell győződnünk arról, hogy amit kaptunk, vagy amivel dolgozunk, az éppen az, amire előzőleg számítottunk. Vegyünk például egy programot, ami egy lemezen található fájlt használ. Mi történik, ha ez a fájl valamiért nem is létezik? Ha nem készítjük fel programunkat az efféle váratlan – vagy nem is annyira váratlan – eseményekre, annak futási hiba lesz az eredménye.

Dönthetünk persze úgy is, hogy nem foglalkozunk az efféle gondokkal. Ekkor azonban jó esély van rá, hogy a felhasználók elpártolnak tőlünk, nem a mi programunkat fogják használni. Az efféle hibák hatása változó lehet. Amikor a programot megtervezzük, mindenképpen el kell gondolkodnunk azon, hogy melyek azok a hibák, amelyek a program

Készítette: Zsótér Csaba III. éves informatikus hallgató

82

Page 83: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

működése szempontjából kritikusnak számítanak, és melyek azok, amelyeket figyelmen kívül hagyhatunk.

A hibák kiküszöbölése logikus kódolássalMunkánk során azt fogjuk tapasztalni, hogy rengeteg hibát már magában a kódban kiküszöbölhetünk, ha azt logikusan építjük fel. Ha van olyan egyszerű programozási, vagy programtervezési logika, amivel egy hiba eleve meggátolható, mindenképpen használjuk fel. Bármikor megtehetjük például, hogy használat előtt ellenőrizzük egy érték hosszát, egy parancssori paraméter meglétét, vagy azt, hogy egy érték egy bizonyos tartományon belül van-e. Nézzük a következő dolgokat, és gondolkodjuk el azon, hogy egy programban hogyan ellenőrizhetjük őket:

• A felhasználó megkísérel megnyitni egy olyan fájlt, ami nem is létezik.• Túl sok elemet próbálunk elhelyezni egy tömbben.• A program egyik eleme egy karakterláncot próbál hozzárendelni egy egész

változóhoz.• Egy eljárás olyan hivatkozás típusú változót akar használni, aminek nincs beállított

kezdőértéke.

Írhatunk persze olyan kódot, amely ezen hibák keletkezését eleve meggátolja, de mi van, ha kifelejtünk valamit?

Mi okozhat kivételt?Ha magának a programnak a logikája nem gátolja meg a hiba felléptét, akkor kivétel keletkezik. A kivétel tehát nem más, mint egy kezeletlen programozási hiba. Nem tartoznak ebbe a kategóriába azok a logikai hibák, amelyek egy művelet eredménye, és nem a program szerkezete miatt keletkeznek. Ha a program futása közben kezeletlen hiba lép fel, a futásidejű végrehajtási rendszer megáll, és kivétel lép fel. A következő kódszöveg egy olyan kódot mutat be, amely futás közben kivételt vált ki:

3.32 Kivétel keletkezése using System; class Error { public static void Main() { int [] myArray = new int[5]; for ( int ctr = 0; ctr < 10; ctr++ ) { myArray[ctr] = ctr; } }}

Ha lefuttatjuk ezt a kis programot, ami nem csinál semmi hasznosat, de arra jó példa, hogy mikor keletkezik kivétel. Látható, hogy egy tömbön haladunk végig egy for ciklus segítségével, azonban a tömb csak öt elemből áll. Mi történik, amikor megpróbálunk a

Készítette: Zsótér Csaba III. éves informatikus hallgató

83

Page 84: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

hatodik elemre hivatkozni? Nos, ekkor keletkezik a kivétel. Ha futtatjuk, akkor a rendszer jelzi, hogy kivétel történt, és a következőhöz hasonló üzenet is megjelenik:

Unhandled Exception: System.IndexOutOfRangeException: Index was outside the bounds of the array.

Ezt a szöveget a programban használt egyik objektum állította elő, mégpedig maga a tömb. Összességében tehát a futásnak nem az lett az eredménye, amit a felhasználóval láttatni szerettünk volna. Ha valóban felhasználó barát programot akarunk írni, az efféle hibákat sokkal kifinomultabb módon kell kezelnünk. Ez a program ráadásul hirtelen megállt, amikor a hiba bekövetkezett, ez pedig nem mindig engedhető meg. A való életben gyakoriak az olyan helyzetek, amikor a program feletti uralmat akkor is meg kell tudnunk tartani, ha az történetesen valamilyen hibás műveletet hajtott végre.A futásidejű rendszer által megjelenített hibaüzenet:

KivételkezelésA kivételkezelés a futásidejű hibák kezelését jelenti. Lényege, hogy programunkban megadhatunk olyan kódrészleteket, amelyek képesek elfogni és barátságosabb módon kezelni a futás közben keletkező kivételes eseményeket. Az ilyen kivételkezelő eljárások lényege tehát éppen az, hogy általuk elkerülhessük az előbb bemutatott üzenetablakok hirtelen felbukkanását, a programok váratlan leállását, vagy az értelmezhetetlen hibaüzenetek kiírását. A kivételkezeléssel kapcsolatos két legfontosabb nyelvi elem a try és a catch.

Készítette: Zsótér Csaba III. éves informatikus hallgató

84

Page 85: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

A try és a catch használataA kivételkezelés kulcsát a try és a catch kulcsszavak jelentik. A try segítségével a kód egy blokkjához külön hibakezelést rendelhetünk, amely az ebben a kódrészletben keletkező valamennyi kivételt az általunk megadott kódrészhez irányítja.

A catch segítségével elfoghatjuk azt a kivételt, amit a try a megfelelő helyre irányított. A catch segítségével mi magunk határozhatjuk meg, hogy egy-egy hiba bekövetkezésekor milyek kód fusson le, ahelyett, hogy hagynánk egyszerűen leállni a programot. Nézzünk erre is egy egyszerű példát:

3.33 Try – Catch párok használata using System; class TryIt {

public static void Main() {

int [] myArray = new int[5];

try {

for ( int ctr = 0; ctr < 10; ctr++ ) {

myArray[ctr] = ctr;}

}

catch{

Console.WriteLine("Kivétel keletkezett!");}

Console.WriteLine("Az program befejeződött");

}}

A kivétellel kapcsolatos információ elfogásaAz előző példában a catch bármilyen hibát elfog, ami a try blokkban keletkezik. Ez a viselkedés persze nem mindig célravezető, így arra is lehetőségünk van, hogy a keletkezett kivétel típusát is meghatározzuk, és a hibákat azok fajtája szerint kezeljük. Ehhez a catch paranccsal kapcsolatban egy megfelelő paramétert kell használnunk. Ennek általános formája a következő:

catch( System.Exception e ) {}A catch parancs a kivételt paraméterként fogadja. Esetünkben ez a paraméter az e nevű változó. Látható, hogy az e típusa System.Exception. Ez egy teljesen meghatározott név, ami azt mutatja, hogy az Exception típus a System névtér része. Ha a System névteret a kód elején eleve beemeljük egy using parancs segítségével, akkor a catch blokk megadását le is rövidíthetjük:

Készítette: Zsótér Csaba III. éves informatikus hallgató

85

Page 86: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

catch(Exception e) {}

Az Exception típusú e változó a keletkezett kivétel leírásával kapcsolatos információkat hordoz. Használata lehet a következő:

catch( Exception e) { Console.WriteLine("The following exception was caught:\n{0}", e); }

Több catch használata egyetlen try paranccsalÍrhatunk olyan catch blokkot is, ami egy bizonyos hibafajta kezelésére szolgál. Nézzük a tömbös példát, és írjunk hozzá egy olyan catch blokkot, ami csak az IndexOutOfRangeException hibára reagál:

3.34 Egy megadott típusú kivétel kezelése using System; class CatchIndex { public static void Main() { int [] myArray = new int[5]; try { for ( int ctr = 0; ctr < 10; ctr++ ) { myArray[ctr] = ctr; } } catch (IndexOutOfRangeException e) { Console.WriteLine("You were very goofy trying to use a bad array

index!!", e); } catch (Exception e) { Console.WriteLine("Exception caught: {0}", e); } Console.WriteLine("\nDone with the catch statements. Done with

program."); }}

A kivételek kezelésének sorrendjeA catch parancs kiértékelésének sorrendje igen lényeges. A programok a hibakezelését mindig úgy kell felépítenünk, hogy az egyedi hibák a sorban elöl, az általánosabbak pedig

Készítette: Zsótér Csaba III. éves informatikus hallgató

86

Page 87: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

hátul következzenek. De a próba kedvéért cseréljük meg az előző példában szereplő két catch blokkot:

catch (Exception e) { Console.WriteLine("Exception caught: {0}", e); }catch (IndexOutOfRangeException e) { Console.WriteLine("You were very goofy trying to use a bad array

index!!", e); }

Ha újrafordítjuk a programot, hibaüzenetet kapunk. Mivel az általános catch(Exception e) parancs az összes kivételt elfogja, egyetlen más catch blokk sem fog soha lefutni.

A végső megoldás: a finally kulcsszóNéha szükség van arra, hogy egy bizonyos programblokkot mindenképpen végrehajthassunk, függetlenül attól, hogy a try parancs blokkjában megadott utasítások sikeresen végrehajtódtak-e. A C# nyelv finally kulcsszava éppen erre szolgál. A finally blokkban lévő kód mindig lefut.

Nézzünk egy kivételkezelést egy kicsit bonyolultabb példán keresztül:

3.35 Kód megjelenítése a konzolon using System;using System.IO; class ListFile{ public static void Main(string[] args) { try { int ctr=0; if (args.Length <= 0 ) { Console.WriteLine("Format: ListFile filename"); return; } else { FileStream fstr = new FileStream(args[0], FileMode.Open); try { StreamReader sReader = new StreamReader(fstr); string line; while ((line = sReader.ReadLine()) != null) { ctr++; Console.WriteLine("{0}: {1}", ctr, line); } } catch( Exception e )

Készítette: Zsótér Csaba III. éves informatikus hallgató

87

Page 88: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

{ Console.WriteLine("Exception during read/write: {0}\n", e); } finally { fstr.Close(); } } } catch (System.IO.FileNotFoundException) { Console.WriteLine ("ListFile could not find the file {0}", args[0]); } catch (Exception e) { Console.WriteLine("Exception: {0}\n\n", e); } }}

Format: ListFile filename

Ha futtatjuk a programot, akkor a megadott kódszöveget kapjuk a kimeneten. Ez egy tájékoztató szöveg, ami azt mondja, hogy a program neve után parancssorban meg kell adnunk egy fájl nevét is. Ha paraméterként magának a programnak a kódját tartalmazó fájlt adjuk meg, akkor a forráskódot íratjuk ki a képernyőre, melynek sorait a program beszámozza.

Természetesen fogadhatunk más fájlneveket is, és az eredménymindig hasonló lesz, ha az adott nevű fájl létezik. Ha viszont egy olyan nevet adunk meg, amely egyetlen létező fájlhoz sem tartozik, a következő üzenetet fogjuk kapni (xxx a fájl nevét jelenti, amit megadtunk a parancssorban):

ListFile could not find the file xxx

Látható, hogy hiba történ, a program nem valamilyen rejtélyes üzenetet küld a képernyőre, hanem érthetően megnevezi a hiba okát. Azt hogy nem talált ilyen (xxx) nevű fájlt.

A leggyakoribb kivételekA .NET keretrendszer számos gyakrabban előforduló kivétel meghatározását tartalmazza. Ezek közül néhánnyal már az előző példákban is találkoztunk. Most a leggyakoribb kivételeket soroljuk fel, melyek a System névtérben szereplő kivételosztályok közül a legfontosabbak:

System névtérben szereplő legfontosabb kivételtípusok

Kivétel neve LeírásMemberAccessException Hozzáférési hiba. Egy bizonyos tag, például egy

tagfüggvény nem hozzáférhető Készítette: Zsótér Csaba III. éves informatikus hallgató

88

Page 89: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

ArgumentException Paraméterhiba. Egy tagfüggvény valamelyik

paramétere hibás.ArgumentNullException Null paraméter. Egy tagfüggvény null értéket

kapott paraméterként, de az nem elfogadható.Kivétel neve LeírásArithmeticException Matematikai hiba. Valamilyen hibás matematikai

művelet végrehajtása miatt keletkezett kivétel. Ez a hibatípus általánosabb, mint az OverflowException vagy a DivideByZeroException.

ArrayTypeMismatchException Tömbtípussal kapcsolatos hiba. Ilyen hiba akkor keletkezik, ha egy tömbben nem a neki megfelelő adattípust akarjuk tárolni.

DivideByZeroException Nullával való osztás. Nevének megfelelően akkor keletkezik, ha egy műveletben nullával akarunk osztani.

FormatException Hibás formátum. Valamelyik paraméternek hibás a formátuma.

IndexOutOfRangeException Túl nagy vagy túl kicsi tömbindex. Ilyen típusú hiba akkor keletkezik, ha egy indexeléshez (sorszámozáshoz) használt érték kisebb, mint 0, vagy nagyobb, mint a legmagasabb tömbindex.

InvalidCastException Érvénytelen típus-átalakítás. Ilyen hiba akkor keletkezhet, ha nem sikerül végrehajtani egy kényszerített típusátalakítást.

MulticastNotSupportedException A többes művelet (multicast) nem támogatott. Ilyen hiba két nem null képviselő érvénytelen kombinálásakor keletkezik.

NotFiniteNumberException A megadott, vagy keletkezett érték nem véges szám. Ez gyakorlatilag egy hibás számalakot jelent.

NotSupportedException Nem támogatott tagfüggvény. Ez a kivételtípus akkor keletkezik, ha olyan tagfüggvényt hívunk meg, amelynek az adott osztályban nincs megvalósítása.

NullReferenceException Null értékre való hivatkozás. Ilyen hiba akkor keletkezik, ha hivatkozáson keresztül egy null objektum tartalmához akarunk hozzáférni.

OutOfMemoryException Elfogyott a szabad memória. Ilyen kivétel akkor keletkezik, ha egy új művelet elvégzéséhez nem áll rendelkezésre a kellő nagyságú szabad memória.

OverflowException Túlcsordulás következett be. Ilyen hiba akkor keletkezik, ha egy matematikai művelet eredménye túl nagy, vagy túl kicsi szám, és a checked kulcsszót használjuk.

StackOverflowException Veremtúlcsordulás lépett fel. Ilyen hiba akkor keletkezik, ha a veremben már túl sok művelet van.

TypeInitializationException Hibás típusbeállítás. Ilyenkor egy static típusú konstruktorral van valami gond.

Készítette: Zsótér Csaba III. éves informatikus hallgató

89

Page 90: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

Saját kivételosztályok megadásaA keretrendszerben eleve szereplő kivételtípusokon túl használhatunk saját kivételeket is. A C# nyelvben jobb, ha különböző hibakódok visszaadása helyett ilyen kivételeket váltunk ki; ennek megfelelően természetesen lényeges tervezési szempont, hogy programunkban minden egyes előfordulható kivételhez legyen megfelelő kivételkezelő blokk. Bár ez jelentősen növelheti a forráskód méretét, a végeredmény a felhasználó számára valószínűleg sokkal barátságosabban fog kinézni és működni.

Ha létrehozunk egy saját kivételtípust, akkor azt nyilván nemcsak elfogni szeretnénk, hanem a megfelelő ponton kiváltani is. A kivételek kiváltásához („kivételdobás”) a throw kulcsszót kell használnunk.

Ezzel a kulcsszóval persze nem csak a magunk által meghatározott kivételeket válthatjuk ki, hanem a keretrendszerben eleve meglévőket is. Eleve meglévő kivételtípusok alatt itt most olyan kivételt értünk, amelyek meghatározása az általunk korábban használatba vett bármilyen névtérben szerepel. Ennek megfelelően, ha a System névteret használjuk, a throw segítségével a felsorolt kivételek bármelyikét kiválthatjuk. Ehhez a következő alakú parancsot kell használnunk:

throw(exception );

Ha az adott kivétel, mint objektum még nem létezik, a new kulcsszót is használnunk kell a létrehozásához. A következő kódszövegben például azt láthatjuk, amint a program egy DivideByZeroException típusú kivételt vált ki.

3.36 Kivétel kiváltása using System; class Zero { public static void Main() { Console.WriteLine("Before Exception..."); throw( new DivideByZeroException() ); Console.WriteLine("After Exception..."); }}

A throw kulcsszó után a kerek zárójelek használata nem kötelező. A következő két kódsor egyenértékű:

throw( exception );throw exception;

Készítette: Zsótér Csaba III. éves informatikus hallgató

90

Page 91: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

Saját kivételek kiváltásaA bemutatottnál persze sokkal több értelme van annak, ha saját kivételtípust hozunk létre, és a program megfelelő pontján azt váltjuk ki. A saját kivételtípust a létrehozáskor először is be kell vezetnünk, valahogy így:

class KivételNeve : Exception {}

Ezen, rövid kódsor alapján megállapíthatjuk, hogy a kivétel nem egyéb, mint egy osztály. A kódsor többi része pedig azt jelenti, hogy az újonnan bevezetett osztály egy már létező osztályra, az Exception nevűre épül.

Nézzünk erre egy példát:

3.37 Saját kivételtípus létrehozása és kiváltása using System; class MyThreeException : Exception {} class MathApp { public static void Main() { int result; try { result = MyMath.AddEm( 1, 2 ); Console.WriteLine( "Result of AddEm(1, 2) is {0}", result); result = MyMath.AddEm( 3, 4 ); Console.WriteLine( "Result of AddEm(3, 4) is {0}", result); } catch (MyThreeException) { Console.WriteLine("Ack! We don't like adding threes."); } catch (Exception e) { Console.WriteLine("Exception caught: {0}", e); } Console.WriteLine("\nAt end of program"); }} class MyMath{ static public int AddEm(int x, int y) { if(x == 3 || y == 3) throw( new MyThreeException() );

return( x + y );

Készítette: Zsótér Csaba III. éves informatikus hallgató

91

Page 92: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

}}

Látható, hogy a MyMath osztály statikus AddEm tagfüggvényét használjuk arra, hogy összeadjunk két számot. Itt található egy if utasítás, ami megvizsgálja, hogy x vagy y egyenlő-e hárommal, ha igen, akkor kivételt vált ki, és kiírjuk a konzolra, hogy: Ack! We don't like adding threes.

Kivételek továbbdobásaAz eddigiek után talán már nem meglepő, hogy ha módunkban áll rendszerkivételt kiváltani, sőt saját kivételt is létrehozhatunk, akkor lehetőségünk van a kivételek „továbbdobására” is. A kérdés csak az, hogy mikor érdemes ilyen programszerkezetet kialakítani és pontosan mi értelme van ennek a megoldásnak?

Amint azt láttuk, a megfelelő programblokk megírása árán elfoghatunk és kezelhetünk bármilyen kivételt. Ha ezt egy olyan osztály belsejében tesszük, amit egy másik osztály hívott meg, akkor bizonyos esetekben célszerű lehet a hiba bekövetkezéséről a hívó felet is értesíteni. Az is előfordulhat persze, hogy mielőtt még ezt az értesítést kiküldenénk, bizonyos műveleteket mi magunk is el akarunk végezni.

Vegyünk egy olyan példát, ami egy korábban bemutatott programon alapul. Létrehozunk egy olyan osztályt, amelyik megnyit egy fájlt, megszámolja, hány karakter van benne, majd ezt a számot visszaadja a hívó félnek. Ha a számolást végző osztálynak nem sikerül megnyitnia a fájlt, kivétel keletkezik. Ezt a kivételt ott helyben el is foghatjuk,a számláló értékét nullára állítjuk, majd ezt az értéket „csendben” visszaadjuk a hívónak. Ebből azonban a hívó nem fogja soha megtudni, hogy valami gond volt a fájllal. Csak annyit fog látni, hogy a karakterek száma valamiért nulla, de hogy pontosan miért, az ebből nem derül ki.

Ebben a helyzetben sokkal jobb megoldás, ha a számláló értékét nullára állítjuk, a kivételt pedig továbbadjuk a hívónak. Így a hívó is pontosan tisztában lehet a történtekkel, és szükség esetén reagálhat.

Ahhoz, hogy egy kivételt továbbadhassunk, a catch parancsnál meg kell adnunk egy paramétert. Ez nem más, mint a kérdéses kivétel típusa. A következő példában azt mutatjuk meg, hogyan adhatunk tovább egy kivételt egy általános szerkezetű catch blokk segítségével:

catch (Exception e){

//Idejön a saját kivétellogikathrow(e); //az e az a paraméter, amit ez a catch kap

}

Ellenőrzött és ellenőrizetlen parancsok használataA C# nyelvnek van még két további kulcsszava is, amelyeknek hatása lehet a kivételkezelésre. Ezek a checked és az unchecked. Ha a kóddal kapcsolatban a checked kulcsszót használtuk, és egy változóban olyan értéket akarunk elhelyezni, ami túl

Készítette: Zsótér Csaba III. éves informatikus hallgató

92

Page 93: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

nagy vagy túl kicsi, kivétel keletkezik. Ha viszont az unchecked kulcsszót adtuk meg, a rendszer a kérdéses értéket automatikusan akkorára csonkítja, hogy beleférjen a megadott helyre. Nézzünk erre egy példát:

3.38 Checked kulcsszó használata using System; class CheckIt{ public static void Main() { int result; const int topval = 2147483647; for( long ctr = topval - 5L; ctr < (topval+10L); ctr++ ) { checked { result = (int) ctr; Console.WriteLine("{0} assigned from {1}", result, ctr); } } }}

Gépeljük be és nézzük meg mi lesz az eredmény. Ha a for ciklus elérte a 2147483647-et, és megpróbál ennél a számnál nagyobbat elhelyezni egy egészeket tartalmazó változóban, akkor kivétel keletkezik.

Most próbáljuk ki úgy ezt kódsort, hogy a checked kulcsszó helyett az unchecked-et használjuk. Ekkor pedig nem vált ki a program kivételt, hanem a változóban túlcsordulás következik be.

Használhatjuk ugyanakkor ezeket a kulcsszavakat műveletként (operátorként) is. Ebben az esetben az általános alakjuk a következő:

[un]checked (kifejezés)

Ilyenkor csak a kerek zárójelek között megadott kifejezésre fog vonatkozni az ellenőrzés előírása vagy tiltása.

Az előfeldolgozó utasításainak használataA C# nyelvű forráskódokban számos fordítói utasítás (direktíva) megtalálható. Ezek minden esetben azt határozzák meg, hogy a fordítóprogram hogyan kezelje, mikét értelmezze az általunk felírt forráskódot. A C és C++ nyelvekből ismerősek lehetnek ezek a lehetőségek. A C# nyelvben ugyanakkor kevesebb a direktíva; olyannyira kevés, hogy a következő táblázat összefoglalja az összes ilyen elemet.

A C# nyelv direktívái Direktíva Leírás Készítette: Zsótér Csaba III. éves informatikus hallgató

93

Page 94: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

#define Egy szimbólumot határoz meg.#else Egy else blokkot nyit meg.#elif Az if és az else parancs kombinációja.#endregion Egy régió végét jelzi.#endif Egy #if blokk végét jelzi.#if Egy értéket vizsgál.#error A megadott hibaüzenetet küldi ki fordítás közben.#line A forráskód adott sorának sorszámát jeleníti meg. Tartalmazhat

fájl nevet is, ami szintén megjelenik a kimeneten.#region Egy régió kezdetét jelzi. A régió a forráskód olyan része, amely

egy egyesített fejlesztőrendszerben külön nyitható, illetve becsukható.

#undef Meghatározatlanná tesz egy szimbólumot.#warning A megadott figyelmeztetést küldi ki fordítás közben.

Az előfeldolgozással kapcsolatos deklarációkA direktívákat igen könnyű felismerni egy forráskódban. Mindig a # jellel kezdődnek és a sor legelején állnak. Ugyanakkor a direktívák után soha nincs pontosvessző.

A két talán legfontosabb direktíva a #define és az #undef. Ezekkel beállíthatunk, illetve törölhetünk egy olyan szimbólumot, amit általában annak meghatározására használunk, hogy a lefordított kódba mely részletek kerüljenek bele, és melyek ne. Azzal, hogy lehetővé tesszük bizonyos kódrészletek kihagyását, illetve befoglalását, programunk kódját többféle módon használhatóvá tehetjük.

Fontos szerep juthat ezeknek a direktíváknak a nyomkövetés során is. Amikor egy nagyobb programot fejlesztünk, a munka bizonyos szakaszaiban általában azt szeretnénk, ha a képernyőn, vagy a kimeneten olyan információk is megjelennének, amelyeket utólag törölnénk, a megfelelő utasításokkal beállíthatunk, illetve törölhetünk bizonyos szimbólumokat.

A #define és az #undef használatának alapvető módja a következő:

#define xxxx

és

#undef xxxx

Nézzünk erre egy példát:

3.39 A #define direktíva használata #define DEBUG#define BOOKCHECK

Készítette: Zsótér Csaba III. éves informatikus hallgató

94

Page 95: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

#if DEBUG #warning Compiled listing in Debug Mode#endif#if BOOKCHECK #warning Compiled listing with Book Check on#endif#if DEBUG && PRODUCTION #error Compiled with both DEBUG and PRODUCTION!#endif using System;using System.IO; public class Reading{ public static void Main(String[] args) { if( args.Length < 1 ) { Console.WriteLine("Must include file name."); } else { #if DEBUG Console.WriteLine("==============DEBUG INFO==============="); for ( int x = 0; x < args.Length ; x++ ) { Console.WriteLine("Arg[{0}] = {1}", x, args[x]); } Console.WriteLine("========================================"); #endif string buffer; StreamReader myFile = File.OpenText(args[0]); while ( (buffer = myFile.ReadLine()) != null ) { #if BOOKCHECK if (buffer.Length > 72) { Console.WriteLine("*** Following line too wide to present in book ***"); } Console.Write( "{0:D3} - ", buffer.Length); #endif Console.WriteLine(buffer); }

myFile.Close(); } }

Készítette: Zsótér Csaba III. éves informatikus hallgató

95

Page 96: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

}

Értékek megadása a parancssorbanTöröljük a fenti kódsorban a #define DEBUG sort, és fordítsuk újra a programot. Láthatjuk, hogy a kiegészítő nyomkövetése információ eltűnt. Ahhoz hogy ezt a sort újbóli begépelése nélkül ismét meghatározhatóvá tegyük a DEBUG szimbólumot, a fordítóprogramnak a parancssorban is megadhatunk egy megfelelő szimbólumot a /define kapcsoló segítségével. A megfelelő parancs a követezőképpen néz ki:

csc /define:DEBUG Reading.cs

A /define teljes kiírása helyett használhatjuk a /d rövidítést is.

A #define és az #undef hatásaBár ezt eddig nem mutattuk meg, de az #undef segítségével menet közben törölhetjük is az addig meghatározott szimbólumokat. Az #undef direktívától kezdve egészen a programkód végéig a kérdéses szimbólum ismét meghatározatlanná válik.Fontos szabály, hogy a #define és az #undef direktíváknak feltétlenül meg kell előzniük azt a kódrészletet, amelyre vonatkoznak. Mindkettő előfordulhat megjegyzések és egyéb direktívák után, de egyik sem követhet deklarációt, vagy más lényeges kódelemet.

Sem a #define, sem az #undef direktíva nem jelenhet meg a forráskód belsejében.

Feltételes feldolgozás (#if, #elif, #endif)Amint azt már az előző példában is láttuk, az #if direktíva segítségével vizsgálhatjuk meg egy szimbólum beállított vagy be nem állított voltát, és feltételesen fordíthatunk bele a bináris kódba bizonyos részleteket. A C# nyelv a használható direktívák között a teljes #if-családot rendelkezésünkre bocsátja, vagyis az #if, #elif, #else és #endif direktívákat egyaránt használhatjuk. Ezekkel egyszerű (if), kétágú (if…else), illetve többágú (if…elif) döntési szerkezeteket valósíthatunk meg. Függetlenül a logikai szerkezet bonyolultságától, a kódrészt mindig egy #endif direktívának kell lezárnia. Az ilyen szerkezeteket a leggyakrabban arra használjuk, hogy segítségükkel különbséget tegyünk a fejlesztői és a végleges kód között:

#if DEBUG//nyomkövetési kód#elif PRODUCTION//végleges kód#else//fordítási hibás megjelenítése#endif

Készítette: Zsótér Csaba III. éves informatikus hallgató

96

Page 97: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

Előfeldolgozási kifejezések (!, ==,!=, &&, ||) Az #if direktíva és rokonai segítségével felépített logikai szerkezetek tartalmazhatnak néhány logikai műveleteket is. Ezek a !, = =, !=, &&, || logikai műveletek. Az előfeldolgozó direktívákban ezek jelentése gyakorlatilag megegyezik a szabványos if-es szerkezetekből ismertekkel.

Hibás és figyelmeztetések kezelésének előírása a kódban (#error, #warning)

Mivel a direktívák végrehajtása a fordítás részét képezi, természetesen módon merül fel az igény, hogy mi magunk is küldhessünk hibaüzeneteket, illetve figyelmeztetéseket, ha a munkának ebben a szakaszában valamilyen gond merül fel. Hibaüzenetek küldését a #error direktívával tehetjük meg. Ha a hiba nem súlyos, de azért figyelmeztetni szeretnénk a rendkívüli körülményekre magunkat, vagy munkatársainkat, a #warning direktíva segítségével tehetjük meg.

A sorszámok megváltoztatásaA C# nyelv egy újabb gyakran használt direktívája a #line. Ezzel a direktívával a kódban a sorok számát határozhatjuk meg. Ennek hatása akkor érzékelhető, amikor hibaüzeneteket küldünk ki fordítás közben. A #line direktíva használatát a következő kódsor mutatja be:

3.40 A #line direktíva használata using System;

public class Lines{ #line 100 public static void Main(String[] args) { #warning In Main... Console.WriteLine("In Main...."); myMethod1(); myMethod2(); #warning Done with main Console.WriteLine("Done with Main"); } #line 200 static void myMethod1() { Console.WriteLine("In Method 1"); #warning In Method 1... int x; // not used. Will give warning. } #line 300 static void myMethod2() { Console.WriteLine("in Method 2"); #warning In Method 2... int y; // not used. Will give warning. }}

Készítette: Zsótér Csaba III. éves informatikus hallgató

97

Page 98: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

A régiók rövid áttekintéseAz eddig bemutatottakon kívül van még a #region és #endregion direktívák. Ezeket arra használjuk, hogy segítségükkel logikai blokkokat, úgynevezett régiókat jelöljünk ki a forráskódon belül. Ezeket az olyan grafikus fejlesztői környezetek képesek kezelni, mint amilyen a Visual Studio .NET, lényegük pedig az, hogy az egyes régiókat külön-külön megnyithatjuk, illetve lekicsinyíthetjük a képernyőn. A #region direktíva egy ilyen blokk kezdetét, az #endregion pedig a végét hivatott jelezni.

Már meglévő kódrészletek újrahasznosítása öröklés segítségével

Az öröklés alapjaValamennyien örököltünk bizonyos vonásokat a szüleinktől. A hajunk vagy a szemünk színe és számos egyéb testi jellemzőnk hasonlíthat az övékhez. Ugyanakkor örökölt tulajdonságaink mellett bizonyos mértékig egyediek is vagyunk. Ugyanilyen vagy legalábbis nagyon hasonló viszony lehet egy program osztályai és objektumai között is. Ahogy a mi tulajdonságaink a szüleink bizonyos tulajdonságaiból vezethetők le, az osztályok tulajdonságai között is fennállhat kisebb-nagyobb mértékű rokonság. Lehetnek olyan tulajdonságaik, amelyek már a szüleikben is megvoltak pontosan ugyan abban a formában, de az is lehetséges, hogy néhány viselkedésformájukkal felülbírálják szüleik viselkedését.

Az objektumközpontú nyelvek örökléssel kapcsolatos alapelve lehetővé teszi, hogy egy már létező osztályból annak tulajdonságait alapul véve osztályt hozzunk létre. Az új osztály korlátozás nélkül felhasználhatja a szülőosztály viselkedésformáit, egyeseket közülük felülbírálhat, bizonyos képességeket kiegészíthet, némelyeket pedig maga is hozzátehet a meglévőkhöz.

Egyetlen szülőosztály alapján több származtatott osztályt is létrehozhatunk, ugyanakkor minden osztály csak egyetlen szülőosztálytól örökölhet. Ha például van egy control nevű alaposztályunk, attól számos származtatott osztály örökölhet, amelyek valószínűleg különböző típusú vezérlési feladatokat fognak ellátni.

Készítette: Zsótér Csaba III. éves informatikus hallgató

98

Page 99: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

Figyeljük meg, hogy ha az ábrát balról jobbra, vagyis az öröklés irányában olvassuk, akkor egyetlen szülőből több gyermek is származhat. Ha azonban jobbról balra haladunk, mindig egyes kapcsolatokat látunk, vagyis a gyermek-szülő viszony egyszeres. Egy származtatott osztálynak mindig csak egyetlen alaposztálya létezik.

Az egyszeres és a többszörös öröklésA C++ nyelvtől eltérően a C# nem támogatja a többszörös öröklést. Többszörös öröklésről akkor beszélünk, ha egy származtatott osztály több alaposztálytól is örököl tulajdonságokat, vagyis viselkedésformákat. Egy többszörös öröklést támogató rendszerben például létrehozhatunk egy név és cím osztályt, majd ezek kombinációjaként egy üzleti kapcsolat nevű harmadikat. Az utóbbi mind két alaposztály tulajdonságaival rendelkezik. Ez bár első látásra igen hasznos lehetőségnek tűnik, számos ponton túlbonyolíthatja a programok logikai szerkezetét. A felhasználói felületekkel kapcsolatban majd látni fogjuk, hogy a legtöbb dolgot valójában egyszeres örökléssel is meg tudunk oldani. Így anélkül használhatjuk az objektumközpontúság nyújtotta előnyöket, hogy a rendszer bonyolultságával kellene foglalkoznunk.

Az öröklés egyszerű formájaAz öröklés megértésének legegyszerűbb módja az, ha megnézünk vele kapcsolatban egy példát. A következő kódsor egy alaposztályt tartalmaz, melynek tulajdonságait majd a leszármazott osztályok öröklik:

3.41 Öröklés alaposztálya példája using System;using System.Text; class Person{ private string firstName; private string middleName; private string lastName ; private int age; public Person() { } public Person(string fn, string ln) { firstName = fn; lastName = ln; } public Person(string fn, string mn, string ln) { firstName = fn; middleName = mn; lastName = ln; }

public Person(string fn, string mn, string ln, int a) { firstName = fn; middleName = mn;

Készítette: Zsótér Csaba III. éves informatikus hallgató

99

Page 100: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

lastName = ln; age = a; } public void displayAge() { Console.WriteLine("Age {0}", age); } public void displayFullName() { StringBuilder FullName = new StringBuilder(); FullName.Append(firstName); FullName.Append(" "); if( middleName != "" ) { FullName.Append(middleName[0]); FullName.Append(". "); } FullName.Append(lastName);

Console.WriteLine(FullName); } } class NameApp { public static void Main() { Person me = new Person("Zsótér", "", "Csaba"); Person myWife = new Person("Melissa", "Anne", "Jones", 21); me.displayFullName(); me.displayAge();

myWife.displayFullName(); myWife.displayAge(); }}

Ha például a Person alaposztályból való öröklést akarunk előírni, akkor a következő formát használjuk az új osztály bevezetésekor:

Class SzármazottOsztályNeve : AlapOsztályNeve

Az öröklés jele tehát a kettőspont ( : ), amely az új származott osztály és az eredeti, vagy más néven alaposztály nevét választja el a deklarációban. Nézzük meg most az öröklést a Person alaposztályra:

3.42 Az öröklés legegyszerűbb formája using System;using System.Text;

class Person

Készítette: Zsótér Csaba III. éves informatikus hallgató

100

Page 101: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

{ protected string firstName; protected string middleName; protected string lastName; private int age;

public Person() { }

public Person(string fn, string ln) { firstName = fn; lastName = ln; }

public Person(string fn, string mn, string ln) { firstName = fn; middleName = mn; lastName = ln; }

public Person(string fn, string mn, string ln, int a) { firstName = fn; middleName = mn; lastName = ln; age = a; } public void displayAge() { Console.WriteLine("Age {0}", age); }

public void displayFullName() { StringBuilder FullName = new StringBuilder(); FullName.Append(firstName); FullName.Append(" "); if( middleName != "" ) { FullName.Append(middleName[0]); FullName.Append(". "); } FullName.Append(lastName); Console.WriteLine(FullName); } } class Employee : Person { private ushort hYear; public ushort hireYear {

Készítette: Zsótér Csaba III. éves informatikus hallgató

101

Page 102: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

get { return(hYear); } set { hYear = value; } } public Employee() : base() { } public Employee( string fn, string ln ) : base( fn, ln) { }

public Employee(string fn, string mn, string ln, int a) : base(fn, mn, ln, a) { }

public Employee(string fn, string ln, ushort hy) : base(fn, ln) { hireYear = hy; }

public new void displayFullName() { Console.WriteLine("Employee: {0} {1} {2}", firstName, middleName, lastName); } } class NameApp { public static void Main() { Person myWife = new Person("Melissa", "Anne", "Jones", 21); Employee me = new Employee("Zsótér","Csaba", 23); Employee you = new Employee("Kyle", "Rinni", 2000); myWife.displayFullName(); myWife.displayAge(); me.displayFullName(); Console.WriteLine("Year hired: {0}", me.hireYear); me.displayAge(); you.displayFullName(); Console.WriteLine("Year hired of him: {0}", you.hireYear); you.displayAge(); }}

Látható, hogy az Employee nevű osztály az, amelyik a Person osztályból származik. Az Employee osztály örökli a Person osztály minden tulajdonságát, és kiegészíti azt egy hYear nevű új változóval. Látható, hogy az Employee osztálynak a konstruktorai olyanok, melyek bemenő paramétereket várnak. A kettőspont utána a base kulcsszót látjuk. Ebben a helyzetben azt jelenti, hogy a származtatott osztályból megkívánjuk hívni az alaposztály

Készítette: Zsótér Csaba III. éves informatikus hallgató

102

Page 103: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

konstruktorát, mégpedig azzal a két paraméterrel, amit a származtatott osztály konstruktora kap paraméterként.

Az Employee osztály displayFullName tagfüggvényének deklarációjában a new kulcsszó is szerepel. Mivel az alaposztálynak is volt szintén egy ilyen nevű tagfüggvénye, és mivel a new kulcsszót is megadtuk, ez a tagfüggvény felülbírálja az alaposztály azonos nevű tagfüggvényét, és az a kód fut le, ami itt áll, és nem az alaposztály azonos nevű tagfüggvényének kódblokkja.

Alaptagfüggvények használata öröklött tagfüggvényekbenA base kulcsszót arra használhatjuk, hogy segítségével közvetlenül meghívjuk az alaposztály bizonyos tagfüggvényeit. Cseréljük ki az előző példában az Employee osztály displayFullName tagfüggvényét a következőre:

public new void displayFullName() { Console.Write("Employee: ");

base.displayFullName(); }

Ez a változat a származtatott osztály displayFullName tagfüggvényét érintette, amely immár nem maga végzi el a teljes munkát, hanem némi szöveg kiírása után egyszerűen meghívja az alaposztály megfelelő tagfüggvényét, vagyis az „eredeti” displayFullName tagfüggvényt. Ez a módszer lehetővé teszi, hogy az alaposztály képességeit anélkül egészítsük ki, hogy a teljes kódot újra kellene írnunk.

A többalakúság felfedezése és a származott osztályokMostanra tehát már tisztában vagyunk az öröklés használatának legegyszerűbb módjával. Láttuk, hogy az öröklés révén kiegészíthetünk egy alaposztályt új tagfüggvényekkel, illetve adattagokkal. Azt is láttuk, hogy szükség esetén az alaposztály valamennyi tagfüggvényét meghívhatjuk a base kulcsszó segítségével. Végezetül azt is láttuk, hogy a származtatott osztály felülbírálhatja az alaposztály tulajdonságait és tagfüggvényeit, ha ugyanolyan néven a new kulcsszó után maga is bevezeti a megfelelő elemeket.

Mindez tökéletesen működik, de vannak bizonyos okok, amelyek miatt néha nem árt, ha máshogy oldunk meg dolgokat. Az objektumközpontúság egyik alapja a többalakúság (polimorfizmus). Ha az öröklést megfelelő módon használjuk, az lehetővé teszi, hogy az általunk tervezett osztályok rendelkezzenek a szükséges mértékű többalakúsággal. Kicsit konkrétabban, az örökléssel a hasonlóan kezelhető osztályok egész rendszerét, hierarchiáját hozhatjuk létre.

Vegyük például az eddig használt Person és Employee osztályokat. Az teljesen biztos, hogy egy Employee típusú objektum tartalmát tekintve több mint a Person osztály egy eleme, de minden Employee egyben Person is.

3.42 Hozzárendelés a Person és Employee objektumokhoz using System;

class Person

Készítette: Zsótér Csaba III. éves informatikus hallgató

103

Page 104: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

{ protected string firstName; protected string lastName;

public Person() { }

public Person(string fn, string ln) { firstName = fn; lastName = ln; }

public void displayFullName() { Console.WriteLine("{0} {1}", firstName, lastName); } }

class Employee : Person { public ushort hireYear;

public Employee() : base() { }

public Employee( string fn, string ln ) : base( fn, ln) { } public Employee(string fn, string ln, ushort hy) : base(fn, ln) { hireYear = hy; }

public new void displayFullName() { Console.WriteLine("Employee: {0} {1}", firstName, lastName); } }

class NameApp { public static void Main() { Employee me = new Employee("Zsótér", "Csaba", 1983);

Person Brad = me;

me.displayFullName(); Console.WriteLine("Year hired: {0}", me.hireYear); Brad.displayFullName(); }}

Készítette: Zsótér Csaba III. éves informatikus hallgató

104

Page 105: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

Mármost ha a Brad nevű és Person típusú objektumhoz egy Employee típusú másik objektumot rendelünk, akkor vajon ki fogja megmondani, hogy melyik típus tagfüggvényeit vagy adattagjait kell a megfelelő pontokon érteni? Például meghívhatjuk-e a Brad.hireYear tagfüggvényt, miközben Brad állítólag Person, és ennek az osztálynak egyáltalán nincs ilyen nevű tagfüggvénye. Kilátástalan töprengés helyett a legegyszerűbb az lesz, ha kipróbáljuk, mi történik. Szúrjuk tehát be a következő sort:

Console.WriteLine("Year hired: ",Brad.hireYear);

Az ember azt gondolná, hogy egyszerűen megjelenik a képernyőn az 1963-as szám, de nem. Brad Person típusú objektum, a Person osztálynak pedig nincs hireYear nevű tagja. Az eredmény egy hibaüzenet lesz.

Bár az Employee típus a Person minden tulajdonságával rendelkezik, ennek az állításnak a fordítottja még akkor sem igaz, ha egy Person objektumnak egy Employee objektum tartalmát adjuk értékül. Megint kissé általánosabban megfogalmazva a tapasztalatokat, azt mondhatjuk , hogy egy származott osztály minden, ami az alaposztály volt, de az alaposztály soha nem lesz több azáltal, hogy más osztályokat származtatnak belőle.Na és akkor hol van a többalakúság? Egészen egyszerűen megfogalmazva, a többalakúság itt abban merül ki, hogy ugyanolyan névvel hívhatjuk meg különböző objektumok hasonló szerepet ellátó tagfüggvényeit. Példánkban a displayFullName tagfüggvényt mind a Person, mind az Employee objektumokkal kapcsolatban használhattuk. Bár az értékadásnál volt egy kis zavar a típusok között, a rendszer a megfelelő ponton mindig a megfelelő osztály adott nevű tagfüggvényét hívta meg. Soha nem kellett azzal bajlódnunk, hogy megmondjuk, mikor melyik tagfüggvényre gondolunk.

Munka virtuális tagfüggvényekkelAz objektumközpontú programozás gyakorlatában teljesen bevett szokás, hogy a származtatott típusú objektumokban az alaposztály tagfüggvényeire hivatkozunk. Vegyük például a következő problémát. Az előző példában a Brad nevű objektum típusa Person volt, de az értékadásnál egy Employee objektum tartalmát rendeltük hozzá. Melyik osztály tagfüggvényét hívta meg a rendszer, amikor a displayFullName nevet látta. Amint azt már tudjuk, az alaposztály tagfüggvénye futott le, annak ellenére, hogy az értékadásnál volt némi zavar a típusok körül.

Számos olyan esettel fogunk találkozni a gyakorlatban, amikor az ilyen helyzetekben azt szeretnénk, ha mégis az értékadásnál használt típus megfelelő tagfüggvénye futna le. Ezt a C# nyelvben az úgynevezett virtuális tagfüggvények segítségével oldhatjuk meg. A virtuális tagfüggvény lehetővé teszi, hogy meghíváskor ne az alaposztály, hanem az értékadásnál használt objektumnak megfelelő osztály megfelelő nevű tagfüggvénye fusson le.

Azt hogy egy tagfüggvény virtuális, az alaposztályban kell megadnunk a virtual kulcsszó használatával. Ha egy ilyen tagfüggvényt túlterhelünk, akkor futásidőben a rendszer a tényleges típust és nem a változó megadott típusát fogja használni a kérdéses adatokkal kapcsolatban. Ez azt jelenti, hogy egy alaposztály több származtatott osztályra mutathat, és a rendszer ezen származtatott osztályok megfelelő tagfüggvényeit fogja meghívni a megfelelő

Készítette: Zsótér Csaba III. éves informatikus hallgató

105

Page 106: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

helyeken. Ez pedig a displayFullName tagfüggvényekkel kapcsolatban azt jelenti, hogy azok mindig a megfelelő adatot fogják megjeleníteni.

Egy származtatott osztálynak feltétlenül jeleznie kell az override kulcsszóval, ha felülbírál egy virtuális tagfüggvényt. Nézzünk erre is egy példát:

3.43 Virtuális tagfüggvények használata using System; class Person{ protected string firstName; protected string lastName; public Person() { } public Person(string fn, string ln) { firstName = fn; lastName = ln; } public virtual void displayFullName() { Console.WriteLine("{0} {1}", firstName, lastName); } } class Employee : Person { public ushort hireYear; public Employee() : base() { } public Employee(string fn, string ln, ushort hy) : base(fn, ln) { hireYear = hy; } public override void displayFullName() { Console.WriteLine("Employee: {0} {1}", firstName, lastName); } }

class Contractor : Person { public string company; public Contractor() : base() { }

Készítette: Zsótér Csaba III. éves informatikus hallgató

106

Page 107: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

public Contractor(string fn, string ln, string c) : base(fn, ln) { company = c; } public override void displayFullName() { Console.WriteLine("Contractor: {0} {1}", firstName, lastName); } } class NameApp { public static void Main() { Person Brad = new Person("Bradley", "Jones"); Person me = new Employee("Bradley", "Jones", 1983); Person worker = new Contractor("Carolyn", "Curry", "UStorIt");

Brad.displayFullName(); me.displayFullName(); worker.displayFullName(); }}

Látható, hogy a Person osztály displayFullName tagfüggvényét most virtuálisként (virtual) vezettük be. Ez tehát azt jelzi a rendszernek, hogy ha menet közben egy Person típusú objektumnak egy, a Person osztályból származtatott másik osztályba tartozó objektum tartalmát adjuk értékül, akkor az adott objektummal kapcsolatban e származtatott osztály megfelelő nevű tagfüggvényét akarjuk használni.

A másik jelentős változást az Employee osztályon belül látjuk, ahol is a displayFullName tagfüggvénynél nem a new kulcsszót használtuk, hanem az override kulcsszót. Ez azt jelzi, hogy az Employee típus valamennyi adatával kapcsolatban a displayFullName tagfüggvénynek ezt a változatát akarjuk majd használni.

Munka elvont osztályokkalAz imént tulajdonképpen semmi nem kényszerített bennünket arra, hogy az Employee és a Contractor osztályokban újra bevezessük a displayFullName tagfüggvényt. Változtassuk meg tehát egy kicsit a programot. Szerepeljem minden hol a new kulcsszó, ahol eddig override kulcsszót használtunk.

public new void displayFullName()

Látni fogjuk, hogy a program futásának is más lesz az eredménye. Mi is történt voltaképpen? Bár az alaposztályban a kérdéses tagfüggvény virtuálisként vezettük be, ahhoz, hogy a többalakúság valóban megvalósuljon, vagyis hogy az értékadás automatikusan maga után vonja a megfelelő tagfüggvénynek az objektumhoz való hozzárendelést, a származtatott osztályban mindenképpen használni kell az override kulcsszót.

Készítette: Zsótér Csaba III. éves informatikus hallgató

107

Page 108: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

A tagfüggvények automatikus felülbírálását úgy kényszeríthetjük ki, hogy az alaposztály megfelelő tagfüggvényét elvontként adjuk meg. Ehhez a deklarációban az abstract kulcsszót kell használnunk. Az elvont tagfüggvény olyan tagfüggvény, amelynek egyáltalán nincs törzse, csak deklarációja. Az ilyen tagfüggvények kifejezetten azzal a céllal készülnek, hogy a származtatott osztályok felülbírálják – a jelen esetben megvalósítsák – őket.

Ha bármilyen tagfüggvényt elvontként vezetünk be, akkor az egész osztályt is ezzel a jelzővel kell ellátnunk. Az elvont osztályok használatát a következő példa szemlélteti, az Employee és a Contractor valamint a Person osztályok segítségével:

3.44. Elvont osztályok használata using System; abstract class Person{ protected string firstName; protected string lastName; public Person() { } public Person(string fn, string ln) { firstName = fn; lastName = ln; } public abstract void displayFullName(); } class Employee : Person { public ushort hireYear; public Employee() : base() { } public Employee(string fn, string ln, ushort hy) : base(fn, ln) { hireYear = hy; } public override void displayFullName() { Console.WriteLine("Employee: {0} {1}", firstName, lastName); } } class Contractor : Person { public string company; public Contractor() : base()

Készítette: Zsótér Csaba III. éves informatikus hallgató

108

Page 109: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

{ } public Contractor(string fn, string ln, string c) : base(fn, ln) { company = c; } public override void displayFullName() { Console.WriteLine("Contractor: {0} {1}", firstName, lastName); } } class NameApp { public static void Main() { Person me = new Employee("Bradley", "Jones", 1983); Person worker = new Contractor("Bryce", "Hatfield", "EdgeQuest"); me.displayFullName(); worker.displayFullName(); }}

Fontos megjegyezni, hogy elvont osztályt nem használhatunk objektumok készítésére. Ha tehát az alkalmazásosztályban megpróbálnánk egy Person típusú Brad nevű objektumot létrehozni, hibaüzenetet fogunk kapni. Tehát a következő kódsor hibát jelezne:

Person Brad = new Person("Bradley", "Jones");

Arról, hogy az elvont alaposztály abstract-ként bevezetett tagfüggvényei a megfelelő módon legyenek felülbírálva, maga a fordítóprogram gondoskodik.

Osztályok lezárásaAz elvont osztályokat eleve azzal a céllal hozzuk létre, hogy azokból más osztályok származtathatók legyenek. De mi van akkor, ha kifejezetten meg akarjuk gátolni, hogy egy általunk létrehozott osztályból mások is származtassanak? Mi van, ha le akarjuk zárni ezt az osztályt, megakadályozva az általa nyújtott szolgáltatások bárminemű kiegészítését?

A C# nyelvben erre a célra a sealed kulcsszó szolgál. Ha egy osztály deklarációjában a sealed jelző szerepel, az megakadályozza, hogy bármely más osztály örököljön tőle. Nézzünk erre egy igen egyszerű példát:

3.45 Egy lezárt osztály using System; sealed public class number{ private float pi;

Készítette: Zsótér Csaba III. éves informatikus hallgató

109

Page 110: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

public number() { pi = 3.14159F; } public float PI { get { return pi; } }}//public class numbers : number//{// public float myVal = 123.456F;//} class myApp { public static void Main() { number myNumbers = new number(); Console.WriteLine("PI = {0}", myNumbers.PI);// numbers moreNumbers = new numbers();// Console.WriteLine("PI = {0}", moreNumbers.PI);// Console.WriteLine("myVal = {0}", moreNumbers.myVal); }}

Ha beiktatjuk a kódban a megjegyzések közé tett sorokat is, akkor a fordítóprogram hibaüzenettel adja tudtunkra, hogy egy lezárt osztályból próbáltunk létrehozni egy másik osztályt, ami pedig nem megengedett, ha a sealed kulcsszóval vezetünk be egy osztályt.

Ha egy, a sealed jelzővel ellátott osztályban egy adattagot protected-ként próbálunk bevezetni, figyelmeztető üzenetet kapunk a fordítótól. Itt nyilván csak a private jelző használatának van értelme, hiszen ettől az osztálytól úgysem fog senki örökölni.

Az Object, avagy a legvégső alaposztályA C# nyelvben az osztályhierarchia, vagyis minden osztály legvégső alaposztálya az Object. Ez az osztály a .NET keretrendszer osztályhierarchiájának gyökere, vagyis ez az egyes számú alaposztály.

Azok alapján, amit eddig megtanultunk, könnyen beláthatjuk, hogy egy C# programban valamennyi osztály egyben Object is. Minden adattípus és osztály az Object alaposztályból származik, és minden, ami az Object alaposztályban szerepel, hozzáférhető bármely másik .NET osztályban is.

Az Object osztály tagfüggvényeinek áttekintéseEgy Object típusú objektumnak – és ezzel együtt minden más objektumnak – két olyan tagfüggvénye van, amelyek „közérdeklődésre ” tarthatnak számot. Ezek a GetType és a ToSrting. A GetType tagfüggvény egy objektum típusát adja vissza, a ToString pedig egy, a kérdéses objektumot leíró karakterláncot ad vissza. Nézzünk erre egy példát:

Készítette: Zsótér Csaba III. éves informatikus hallgató

110

Page 111: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

3.46 Minden objektum egyben Object is. using System; sealed class PI{ public static float nbr; static PI() { nbr = 3.14159F; } static public float val() { return(nbr); }} class myApp { public static void Main() { Console.WriteLine("PI = {0}", PI.val()); Object x = new PI(); Console.WriteLine("ToString: {0}", x.ToString()); Console.WriteLine("Type: {0}", x.GetType()); Console.WriteLine("ToString: {0}", 123.ToString()); Console.WriteLine("Type: {0}", 123.GetType()); }}

Emlékezzünk rá, hogy a C# nyelvben minden, még a literálok is objektumok, és minden objektum az Object osztályból származik. Ez pedig azt jelenti, hogy egy literális érték, például a 123 is objektum, mégpedig az Object osztályból levezetett típussal rendelkező objektum. Ebből pedig következik, hogy az Object osztálytól örökölt ToString tagfüggvény segítségével az 123 objektumot karakterlánccá alakíthatjuk. Nem túl meglepő módon az eredmény „123” karakterlánc lesz. Az is látható, hogy az 123 típusa System.Int32, ami egyben nem más, mint a .NET keretrendszerben a szabványos int típus szinonimája.

Becsomagolás és kicsomagolásMost hogy már viszonylag átfogó képünk van az alap- és származtatott osztályok kapcsolatairól, ideje megismerkednünk még két fogalommal, a becsomagolással (boxing) és a kicsomagolással (unboxing).

Korábban azt állítottuk, hogy a C# nyelvben minden objektum. Nos ez nem teljesen igaz. A helyes állítás inkább úgy hangzik, hogy mindent kezelhetünk objektumként. Már tudjuk hogy az érték szerinti adattípusok másként tárolódnak, mint a hivatkozási adattípusok, és hogy az objektumok valamennyien az utóbbi kategóriába tartoznak. Ugyanakkor az előző feladatban egy literálértéket kezdtünk el objektumként kezelni. Miért tehettük meg ezt?A C# valójában lehetőséget ad arra, hogy egy érték szerinti típust objektummá, vagyis hivatkozási típussá alakítsunk.

Készítette: Zsótér Csaba III. éves informatikus hallgató

111

Page 112: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

Becsomagolásnak (boxing) nevezzük azt a műveletet, amelyben egy érték szerinti típust objektummá, vagyis hivatkozási típussá alakítunk. A kicsomagolás (unboxing) az ellenkező irányú átalakítás. Amikor egy értéket kicsomagolunk, akkor olyan adattípusban kell elhelyeznünk, ami a tárolt adatnak megfelel.

A kicsomagoláshoz az szükséges, hogy egy objektumot kifejezetten érték szerinti típussá alakítsunk. Ezt kényszerített típusátalakítással (cast) tehetjük meg. A következő kódrészlet egy ilyen feladatot valósít meg:

3.47 Becsomagolás és kicsomagolás using System; class myApp { public static void Main() { float val = 3.14F; object boxed = val; float unboxed = (float) boxed; Console.WriteLine("val: {0}", val); Console.WriteLine("boxed: {0}", boxed); Console.WriteLine("unboxed: {0}", unboxed); Console.WriteLine("\nTypes..."); Console.WriteLine("val: {0}", val.GetType()); Console.WriteLine("boxed: {0}", boxed.GetType()); Console.WriteLine("unboxed: {0}", unboxed.GetType()); }}

Az is és as kulcsszavak használata – osztályok típusának átalakításaVan két olyan kulcsszó is, amelyeket az osztályokkal kapcsolatban használhatunk. Ezek az is és az as. A továbbiakban ezek használatával fogunk megismerkedni.

Az is kulcsszó használataNéha előfordul, hogy egy összehasonlítással szeretnénk meggyőződni róla, hogy egy bizonyos objektumnak azonos-e a típusa egy másikkal. Az ilyen műveletek elvégzését könnyíti meg a C# is kulcsszava. Az is kulcsszót arra használjuk, hogy segítségével eldöntsük, egy objektum típusa azonos-e az összehasonlításban megadott másik típussal. Használatának formája a következő:

(kifejezés is típus)

Itt a kifejezés egy hivatkozási típussá értékelődik ki, a típus pedig érvényes típus. A típus tehát általában egy osztály típusa lesz. Ha a kifejezés megfelel a típus-ban megadott típusnak, akkor a kifejezés értéke true (igaz), ellenkező esetben a visszatérési érték false (hamis). Nézzünk egy példát az is használatára:

Készítette: Zsótér Csaba III. éves informatikus hallgató

112

Page 113: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

3.48 Az is kulcsszó használata using System; class Person{ protected string Name; public Person() { } public Person(string n) { Name = n; } public virtual void displayFullName() { Console.WriteLine("Name: {0}", Name); } } class Employee : Person { public Employee() : base() { } public Employee(string n) : base(n) { } public override void displayFullName() { Console.WriteLine("Employee: {0}", Name); } }

class IsApp { public static void Main() { Person pers = new Person(); Object emp = new Employee(); string str = "String"; if( pers is Person ) Console.WriteLine("pers is a Person"); else Console.WriteLine("pers is NOT a Person"); if( pers is Object ) Console.WriteLine("pers is an Object"); else Console.WriteLine("pers is NOT an Object"); if( pers is Employee ) Console.WriteLine("pers is an Employee"); else Console.WriteLine("pers is NOT an Employee"); if( emp is Person ) Console.WriteLine("emp is a Person"); else Console.WriteLine("emp is NOT a Person"); if( str is Person ) Console.WriteLine("str is a Person");

Készítette: Zsótér Csaba III. éves informatikus hallgató

113

Page 114: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

else Console.WriteLine("str is NOT a Person"); }}

Ha lefordítjuk ezt a kódot, valószínűleg kapunk majd néhány figyelmeztető üzenetet. A fordítóprogram számára a megadott összehasonlítások egy része magától értetődő lesz, ezért jelzi, hogy ezek biztosan mindig igazzá fognak kiértékelődni. Az is kulcsszó kiváló eszköz arra, hogy segítségével a program futása közben ellenőrizzünk egy hivatkozási változó típusát.

Az as kulcsszó használataAz as hasonlóan működik, mint egy típusátalakítás. Feladata, hogy egy adott típusú objektumot egy másik típusú objektummá alakítson. Ennek az átalakításnak természetesen megvannak a maga korlátai. A céltípusnak valamilyen szinten meg kell felelnie a kiindulási típusnak. Emlékezzünk rá, hogy a típusátalakítás tulajdonképpen egyfajta kényszerítő eszköz, amellyel egy adott objektumra másik típust kényszerítünk rá. Az as kulcsszó használatának formája a következő:

kifejezés as AdatTípus

Itt a kifejezés egy hivatkozási típussá értékelődik ki, az AdatTípus pedig ilyen típus. Egy ehhez hasonló típusátalakítás a következőképpen nézne ki:

(AdatTípus) kifejezés

Bár az as kulcsszó használata valóban nagyon hasonlít a kényszerített típusátalakításhoz, a végeredmény a két esetben nem azonos. Ha a típusátalakítás során valamilyen hiba lép fel – például, mert egy karakterláncot akarunk számmá alakítani - kivétel keletkezik. Ha viszont az as használata közben lép fel ilyen hiba, akkor a kifejezés a null értéket kapja, és a hiba ellenére megadott típussá (AdatTípus) alakul. Eközben kivétel nem keletkezik. Mindez azt jelenti, hogy bizonyos helyzetekben az as kulcsszóval biztonságosabban hajthatunk végre típusátalakítást, mint egyébként.

Különböző objektumtípusok tömbjeiAmint láttuk, az as és az is kulcsszavakat használva „hatalmas dolgokat” vihetünk véghez. Akár olyan tömböket is létrehozhatunk, amelyeknek különböző típusúak az elemei. Ezt úgy érhetjük el, hogy a változók bevezetéséhez valamilyen alaptípust használunk. Azt ugyanakkor ne felejtsük el, hogy ez csak akkor lehetséges, ha a tárolt objektumok típusai valamennyien ugyanabba az öröklési hierarchiába tartoznak, vagyis van közös ősük. Nézzünk erre egy példát:

3.49 Object típusú objektumok tömbje using System; public class Person{ public string Name;

Készítette: Zsótér Csaba III. éves informatikus hallgató

114

Page 115: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

public Person() { } public Person(string nm) { Name = nm; } public virtual void displayFullName() { Console.WriteLine("Person: {0}", Name); }} class Employee : Person {// public ushort hireYear; public Employee() : base() { } public Employee(string nm) : base(nm) { } public override void displayFullName() { Console.WriteLine("Employee: {0}", Name); } } class Contractor : Person {// public string company; public Contractor() : base() { } public Contractor(string nm) : base(nm) { } public override void displayFullName() { Console.WriteLine("Contractor: {0}", Name); } } class NameApp { public static void Main() { Person [] myCompany = new Person[5]; int ctr = 0; string buffer;

Készítette: Zsótér Csaba III. éves informatikus hallgató

115

Page 116: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

do { do { Console.Write("\nEnter \'c\' for Contractor, \'e\' for Employee then press ENTER: "); buffer = Console.ReadLine(); } while (buffer == ""); if ( buffer[0] == 'c' || buffer[0] == 'C' ) { Console.Write("\nEnter the contractor\'s name: "); buffer = Console.ReadLine(); Contractor contr = new Contractor(buffer); myCompany[ctr] = contr as Person; } else if ( buffer[0] == 'e' || buffer[0] == 'E' ) { Console.Write("\nEnter the employee\'s name: "); buffer = Console.ReadLine(); Employee emp = new Employee(buffer); myCompany[ctr] = emp as Person; } else { Person pers = new Person("Not an Employee or Contractor"); myCompany[ctr] = pers; } ctr++; } while ( ctr < 5 ); Console.WriteLine( "\n\n\n==========================="); for( ctr = 0; ctr < 5; ctr++ ) { if( myCompany[ctr] is Employee ) { Console.WriteLine("Employee: {0}", myCompany[ctr].Name); } else if( myCompany[ctr] is Contractor ) { Console.WriteLine("Contractor: {0}", myCompany[ctr].Name); } else { Console.WriteLine("Person: {0}", myCompany[ctr].Name); } } Console.WriteLine( "==========================="); } }

Készítette: Zsótér Csaba III. éves informatikus hallgató

116

Page 117: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

Amint a példa is mutatja, az is és az as kulcsszavak segítségével különböző típusú objektumokat tárolhatunk ugyanabban a tömbben, feltéve, hogy azok közös alaptípussal rendelkeznek. Mivel a C# nyelvben valamennyi objektum közös őse az Object típus, ez a trükk tulajdonképpen minden esetben megoldható.

Információ formázása és bekérése

A konzolon keresztül megvalósított bemenet és kimenetA bemenet és kimenet kifejezésekkel már mindenki találkozott. Ebben a fejezetben arról lesz szó, miként tálalhatjuk egy program kimenetét szebb formában, illetve hogyan kérhetünk be a konzolon keresztül információt a felhasználótól.

Az információ formázásaAmikor valamilyen információt akarunk megjeleníteni, a leggyakrabban célszerű azt karakterlánccá alakítani. Amikor azt korábban láttuk, a Console osztály Write és WriteLine tagfüggvényei ilyen karakterláncokat tudnak megjeleníteni. A .NET keretrendszernek ezeken kívül számos egyéb tagfüggvénye és formátumleírója van, amelyeket szintén karakterláncokkal kapcsolatban használhatunk. A formátumleíró vagy formázó karakter olyan jel, amely az információ formázásának szükségességét írja elő.

A formázó karaktereket bármilyen karakterlánccal kapcsolatban használhatjuk. A következőkben e jelek közül tekintünk át néhányat. Íme az érintett típusok áttekintő listája:

• Szabványos számformátumok• Pénznemek• Exponenciális számok• Egyedi számformátumok• Dátumok és időpontok• Felsorolások

A formázó karaktereket többféle módon használhatjuk. A legegyszerűbb talán a Write és a WriteLine tagfüggvényekben való szerepeltetésük, ahol a kiegészítő formázás mikéntjét adhatjuk meg segítségükkel.

Jelek segítségével történő formázást a ToString tagfüggvény meghívásakor is használhatunk. Már tudjuk, hogy a ToString tagfüggvény az általános Object osztály része. Mivel minden objektum őse az Object osztály, a ToString tagfüggvény is bármely osztályban hozzáférhető. Az olyan osztályok esetében, mint például az Int32, a ToString tagfüggvénynek formázásra vonatkozó információt is megadhatunk. Ha például a var egy egész típusú változó, amely a 123-as számot tartalmazza, akkor a pénznemek formázására szolgáló C jel megadásával a var.ToString("C");utasítás kimenete a következő lesz:$123.00

Készítette: Zsótér Csaba III. éves informatikus hallgató

117

Page 118: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

A formázó karakterek használatának harmadik lehetősége a string adattípus kezelésével kapcsolatos. A string osztálynak van egy Format nevű statikus tagfüggvénye. Éppen azért, mert ez a tagfüggvény statikus, használható egyszerűen az osztály nevének megadásával is, a string.Format formában. A tagfüggvény használatának formája egyébként megegyezik azzal, amit a Console osztály képernyőt kezelő tagfüggvényei használnak:

string KarakterLánc = string.Format("formázó_karakterlánc",érték(ek));

Itt a KarakterLánc az az új karakterlánc, amely a formázott kimenetet tartalmazza. A formázó_karakterlánc nevének megfelelően a formázás mikéntjét leíró jelekből alkotott karakterlánc. Ezek a jelek természetesen megegyeznek a Write és WriteLine tagfüggvényekkel kapcsolatban használhatókkal. Az érték(ek) azokat az értékeket tartalmazza, amelyeknek formázott alakban kell majd megjelenniük a kimenő karakterláncban.

A C karakter a pénzösszegek formázásának leírására használt formázó karakter. Amint azt korábban említettük, ezt a jelet a ToString tagfüggvénynek kettős idézőjelek között bemenő paraméterként kell megadnunk. Ha a formázást eleve karakterláncon akarjuk végrehajtani, a formázó karaktereket helyőrzőként (placeholder) kell használnunk. Ennek legáltalánosabb formája a következő:

{hldr:X#}

Itt a hldr a helyőrző szám, amely a változó beillesztésének helyét mutatja, az X pedig az a formázást leíró jel, amely az illető változóra vonatkozik. Ha a kérdéses számot pénzösszegként akarjuk megjeleníteni, az X a C jel lenne. A # nem kötelező jel, és a megjelenítene kívánt számjegyek számát adja meg. A kettőskereszt jelentése a különböző formázásokkal kapcsolatban különböző lehet. Pénzösszeg formázásakor a tizedesjegyek számát adja.

Számok formázásaSzámértékek formázására számos különböző formázó karakter használható. Ezekről a következő táblázat ad összefoglalást:

A számértékek formázására használható formázó karakterek Formázó karakter Leírás Alapértelmezett formátum Példa kimenetreC vagy c Pénzösszeg $xx,xxx,xx $12,345,67

($xx,xxx,xx) ($12,345,67)D vagy d Decimális szám xxxxxxx 1234567

-xxxxxxx -1234567E vagy e Hatványalak x.xxxxxxE+xxx 1.234567E+123

x.xxxxxxe+xxx 1.234567e+123-x.xxxxxxE+xxx -1.23456E+123

Készítette: Zsótér Csaba III. éves informatikus hallgató

118

Page 119: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

-x.xxxxxxe+xxx -1.23456e+123x.xxxxxxE-xxx 1.23456E-123x.xxxxxxe-xxx 1.23456e-123-x.xxxxxxE-xxx -1.234567E+123-x.xxxxxxe-xxx -1.23456e+123

F vagy f Fixpontos xxxxxx.xx 123456.78ábrázolás -xxxxxx.xx -123456.78

N vagy n Számérték xx,xxx.xx 12,345.67X vagy x Hexadecimális 12d687 12D687

értékG vagy g Általános Változó (a legtömörebb formátumot használja)

formátumR vagy r Kerekített Megőrzi a pontosságot, ha a számokat alakítunk

karakterlánccá, majd vissza

Egyedi formátumok létrehozása képleírók segítségévelVannak esetek, amikor a rendszer beépített szolgáltatásai nem elégségesek ahhoz a formázási feladathoz, amit programunkkal meg szeretnénk oldani. Jellemző példa erre a vezetői engedélyek sorszámának, vagy a társadalombiztosítási számoknak a formázása, amelyekben kötőjelek fordulnak elő előre ismert helyeken. A következő táblázat bemutat néhány olyan, szintén formázással kapcsolatos jelet, amelyek segítségével a rendszer beépített formázó feleiből egyedi formázási utasításokat hozhatunk létre:

A képleírókban használható formázó karakterek Jelző Leírás0 Nulla helyőrző. A rendszer csak akkor tölti fel számjeggyel, ha szükséges# Üres helyőrző. A rendszer csak akkor tölti fel számjeggyel, ha szükséges. Egy pontot jelenít meg. Tizedespontként használható, Számjegycsoportok elválasztása vesszővel. Többszörözőként is használható% A megadott értéket százalékként jeleníti meg. (1.00 például 100% lesz)\ Vezérlő karakter jelzésére használatos. Vezérlő karakternek minősülnek az

úgynevezett kilépési karakterek (escape), mint amilyen az újsor (\n).’xyz’ Egyszeres idézőjelek (aposztrófok) között jeleníti meg a megadott szöveget."xyz" Kétszeres idézőjelek közé zárva jeleníti meg a megadott szöveget.3.50 Képleírók használata using System;

class Picts{ public static void Main() { int var1 = 1234; float var2 = 12.34F; // Nulla formázó Console.WriteLine("\nZero..."); Console.WriteLine("{0} -->{0:0000000}", var1); Console.WriteLine("{0} -->{0:0000000}", var2);

// Térköz formázó

Készítette: Zsótér Csaba III. éves informatikus hallgató

119

Page 120: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

Console.WriteLine("\nSpace..."); Console.WriteLine("{0} -->{0:0####}<--", var1); Console.WriteLine("{0} -->{0:0####}<--", var2);

// Csoportválasztó és többszöröző (,) Console.WriteLine("\nGroup Multiplier..."); Console.WriteLine("{0} -->{0:0,,}<--", 1000000); Console.WriteLine("Group Separator..."); Console.WriteLine("{0} -->{0:##,###,##0}<--", 2000000); Console.WriteLine("{0} -->{0:##,###,##0}<--", 3);

// Százalékformázó Console.WriteLine("\nPercentage..."); Console.WriteLine("{0} -->{0:0%}<--", var1); Console.WriteLine("{0} -->{0:0%}<--", var2);

// Literális formázás Console.WriteLine("\nLiteral Formatting..."); Console.WriteLine("{0} -->{0:'My Number: '0}<--", var1); Console.WriteLine("{0} -->{0:'My Number: '0}<--", var2); Console.WriteLine("\n{0} -->{0:Mine: 0}<--", var1); Console.WriteLine("{0} -->{0:Mine: 0}<--", var2); }}

A dátum és idő formázásaA dátumot és az időt szintén megjeleníthetjük formázott alakban. A formázó karakterek között számos olyat is találunk, amelyek segítségével a hét napjaitól kezdve a teljes dátumot és a pontos időt tartalmazó időbélyegig mindent megformázhatunk. Mielőtt azonban rátérnénk ezek bemutatására, meg kell tanulnunk, hogyan kérdezhetjük le programjainkból az aktuális dátumot és a pontos időt.

A dátum és idő lekérdezéseA C# nyelv és a .NET keretrendszer az idő és a dátum kezelésére és tárolására egy egész osztályt bocsát rendelkezésünkre. Ez a DateTime nevű osztály, amely a System névtér eleme. Ez az osztály az aktuális dátumot és a pontos időt egyaránt tárolja.

A DateTime osztálynak számos olyan tulajdonsága és tagfüggvénye van, amelyeket munkánk során minden bizonnyal hasznosnak fogunk találni. Ezenkívül az osztálynak van néhány statikus tagja is. Ezek közül a két leggyakrabban használt valószínűleg a Now és a Today. Nevének megfelelően a Now tag a meghívás pillanatában érvényes dátumot és időt tartalmazza, a Today pedig az aktuális dátumot. Mivel – mint említettük – ezek statikus tagok, az osztály nevével, és nem az osztály tagjaival kapcsolatban használhatók:

DateTime.NowDateTime.Today

A DateTime osztály egyéb tagjainak és tagfüggvényeinek leírását a fejlesztőrendszerhez kapott dokumentációban találhatjuk meg. Néhányat közülük ugyanakkor a rövid leírásukkal együtt itt is felsorolunk:

Készítette: Zsótér Csaba III. éves informatikus hallgató

120

Page 121: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

Date A DateTime objektum dátummal kapcsolatos részét adja vissza.Month A DateTime objektumban tárolt hónap értékét adja vissza.Day A DateTime objektumban tárolt nap értékét adja vissza (a

számformában megadott dátum nap értéke.)Year A DateTime objektumban tárolt év értékét adja vissza.DayOfWeek Azt adja vissza, hogy a DateTime objektumban tárolt időpont az

adott hét hányadik napjának felel meg.DayOfYear Azt adja vissza, hogy a DateTime objektumban tárolt időpont az

adott év hányadik napjának felel meg.TimeOfDay A DateTime objektumban tárolt időpont értékét adja vissza.Hour A DateTime objektumban tároltóra értékét adja vissza.Minute A DateTime objektumban tárolt percek értékét adja vissza.Second A DateTime objektumban tárolt másodpercek értékét adja vissza.Millisecond A DateTime objektumban tárolt ezredmásodpercek értékét adja

vissza.Ticks Azt az értéket adja vissza, ami megmutatja, hogy a DateTime

objektumban tárolt érték hányszorosa egy 100 nanomásodperc hosszúságú óraütésnek.

A dátum és idő formázásaSzámos olyan formázó karakter létezik, amelyek a dátum és idő formázására használhatók. Ezek segítségével a lehető legrövidebbtől kezdve a legrészletesebb formátumig mindenféle alakban megjeleníthetjük az időt. Ezzel kapcsolatos formázó karaktereket a következő táblázat sorolja fel:

A dátum és idő formázására használható karakterek Formázó Leírás Alapértelmezett forma Példa a keletkező

kimenetred Rövid dátum mm/dd/yyyy 5/6/2001D Hosszú dátum nap, hónap dd, yyyy Sunday, May 06, 2001f Teljes dátum és nap, hónap dd, Sunday, May 06,

rövid idő yyyy hh:mm AM/PM 2001 13:30 PMF Teljes dátum és nap, hónap dd, Sunday, May 06,

teljes idő yyyy hh:mm:ss AM/PM 2001 13:30:55 PMg Rövid dátum és mm/dd/yyyy HH:mm 6/5/2001 13:30 PM

rövid időG Rövid dátum és mm/dd/yyyy 6/5/2001 13:30:55 PM

hosszú idő HH:mm:ss AM/PMM vagy m Hónap és nap hónap nap May 06R vagy r RFC1123 ddd, dd hónap yyyy Sun, 06 May 2001

hh:mm:ss GMT 13:30:55 GMTs Rendezhető yyyy-mm-dd hh:mm:ss 2001-05-06 T13:30:55

(sortable)t Rövid idő hh:mm: AM/PM 13:30 PMT Hosszú idő hh:mm:ss AM/PM 13:30:55 PMu Rendezhető yyyy-mm-dd hh:mm:ss 2001-05-06 13:30:55Z

Készítette: Zsótér Csaba III. éves informatikus hallgató

121

Page 122: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

(univerzális)

U Rendezhető nap, hónap dd, yyyy Sunday, May 06, 2001(univerzális) hh:mm:ss AM/PM 13:30:55 PM

Y vagy y Én és hónap hónap, yyyy May, 2001

3.51 A dátum lehetséges formátumai using System;

class DtFormat{ public static void Main() { DateTime CurrTime = DateTime.Now;

Console.WriteLine("d: {0:d}", CurrTime ); Console.WriteLine("D: {0:D}", CurrTime ); Console.WriteLine("f: {0:f}", CurrTime ); Console.WriteLine("F: {0:F}", CurrTime ); Console.WriteLine("g: {0:g}", CurrTime ); Console.WriteLine("G: {0:G}", CurrTime ); Console.WriteLine("m: {0:m}", CurrTime ); Console.WriteLine("M: {0:M}", CurrTime ); Console.WriteLine("r: {0:r}", CurrTime ); Console.WriteLine("R: {0:R}", CurrTime ); Console.WriteLine("s: {0:s}", CurrTime ); Console.WriteLine("t: {0:t}", CurrTime ); Console.WriteLine("T: {0:T}", CurrTime ); Console.WriteLine("u: {0:u}", CurrTime ); Console.WriteLine("U: {0:U}", CurrTime ); Console.WriteLine("y: {0:y}", CurrTime ); Console.WriteLine("Y: {0:Y}", CurrTime ); }}

Felsorolások tagjainak megjelenítéseA felsorolásokról már tanultunk. Azt is láthattuk, hogy amikor a Write vagy a WriteLine tagfüggvényekkel kiíratjuk egy felsorolás elemeit, akkor csak a számérték helyett automatikusan a leíró forma jelenik meg. A karakterláncok formázására szolgáló vezérlő karakterekkel ezt a viselkedést is befolyásolhatjuk. Megjeleníthetjük a szöveges vagy a numerikus értéket, illetve kikényszeríthetjük a tizenhatos számrendszerben történő megjelenítést is. A következő táblázat ezeket a formázó karaktereket mutatja be:

A felsoroló típusokkal kapcsolatos formázó karakterek Formázó karakter LeírásD vagy d A felsoroló típus adott elemének számértékét jeleníti megG vagy g A felsoroló típus adott elemét karakterlánc formájában jeleníti meg.X vagy x A felsoroló típus adott elemének számértékét jeleníti meg

tizenhatos számrendszerben.

Nézzünk erre egy példát:

3.52 Felsoroló típusú értékek formázása Készítette: Zsótér Csaba III. éves informatikus hallgató

122

Page 123: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

using System;

class Enums{ enum Pet { Cat, Dog, Fish, Snake, Rat, Hamster, Bird }

public static void Main() { Pet myPet = Pet.Fish; Pet yourPet = Pet.Hamster;

Console.WriteLine("Using myPet: "); Console.WriteLine("d: {0:d}", myPet ); Console.WriteLine("D: {0:D}", myPet ); Console.WriteLine("g: {0:g}", myPet ); Console.WriteLine("G: {0:G}", myPet ); Console.WriteLine("x: {0:x}", myPet ); Console.WriteLine("X: {0:X}", myPet );

Console.WriteLine("\nUsing yourPet: "); Console.WriteLine("d: {0:d}", yourPet ); Console.WriteLine("D: {0:D}", yourPet ); Console.WriteLine("g: {0:g}", yourPet ); Console.WriteLine("G: {0:G}", yourPet ); Console.WriteLine("x: {0:x}", yourPet ); Console.WriteLine("X: {0:X}", yourPet ); }}

A karakterláncokkal végezhető kifinomultabb műveletekMost hogy már ismerjük a formázáshoz használható karakterláncok összehasonlításának módját, érdemes kicsit visszatérni néhány, a karakterláncokkal kapcsolatos problémához. Azt már tudjuk, hogy a karakterláncok különleges adattípust képviselnek, amelyek kifejezetten szöveges információ tárolására szolgál.

Amint azt már korábban is említettük, a string a C# nyelv egyik kulcsszava. Ez a kulcsszó tulajdonképpen nem más, mint a System névtérben található String osztály másik megnevezése. Ez egyben azt is jelenti, string rendelkezik a String osztály valamennyi tulajdonságával és tagfüggvényével.

A karakterláncokban tárolt értékek nem módosíthatók. Ha karakterláncokkal kapcsolatos tagfüggvényeket hívünk meg, vagy változásokat végzünk egy ilyen tárolt szövegen, valójában mindig egy új karakterlánc objektum jön létre. Ha egy karakterlánc egyetlen karakterét próbáljuk megváltozatni, hibaüzenetet kapunk.

Készítette: Zsótér Csaba III. éves informatikus hallgató

123

Page 124: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

Tudva, hogy a karakterláncok nem módosíthatók, most legtöbben valószínűleg úgy gondolják, hogy ez igen erősen korlátozza a típus használhatóságát. Ez részben érthető is, hiszen nyilván nehezen lehet elképzelni, miként működhetnek a karakterláncokat kezelő tagfüggvények és tulajdonságok, ha a tartalom érinthetetlen. A trükk az, hogy minden tagfüggvény, amely módosítaná a karakterlánc tartalmát, egy új karakterlánc objektumot hoz létre. Előfordulhat persze, hogy tényleg szükségünk van a karakterláncok tartalmának módosítására. Erre a célra a C# egy másik osztályt, a StringBuilder-t bocsátja rendelkezésünkre.

A karakterláncokat szokás nem változó (immutable) elemeknek is nevezni, ami ugyanazt jelenti, amiről eddig is szó volt: nem lehet a tartalmukat módosítani.

Karakterláncokkal kapcsolatos tagfüggvényekSzámos rendkívül hasznos tagfüggvény létezik, amelyek karakterláncok kezelésére, például összehasonlításukra szolgálnak. A következő táblázat csak a legfontosabb összetevőket tartalmazza, rövid leírásukkal együtt:

A karakterláncokkal kapcsolatban használható legfontosabb tagfüggvények Tagfüggvény LeírásA String / string osztállyal kapcsolatos statikus tagfüggvényekCompare Összehasonlítja két karakterlánc tartalmátCompareOrdinal Úgy hasonlítja össze két karakterlánc tartalmát, hogy közben nem

foglalkozik a nyelv vagy egyéb nemzetközi beállítások okozta eltérésekkel.

Concat Két vagy több karakterláncot egyetlen kimenő karakterlánccá fűz össze.

Copy Másolatot készít egy már meglévő karakterláncról.Equals Összehasonlítja két karakterlánc tartalmát, és megállapítja, hogy azok

azonosak-e. Ha igen, visszatérési értéke true ellenkező esetben false lesz.

Format A formázáshoz használt vezérlő karaktereket az általunk jelölt karakterláncokkal helyettesíti.

Join Két vagy több karakterláncot egyesít. Az eredetileg önálló szakaszok között egy, a felhasználó által meghatározott elválasztó karakterláncot helyez el.

A példányokkal kapcsolatban használható tagfüggvények és tulajdonságokChar A karakterlánc megadott helyen található karakterét adja vissza.Clone A tárolt karakterlánc másolatát adja vissza.CompareTo A kérdéses karakterlánc tartalmát egy másik karakterlánc tartalmával

hasonlítja össze. Negatív számot ad vissza, ha a kérdéses karakterlánc kisebb, mint az, amihez hasonlítjuk; nullát, ha a két karakterlánc egyenlő; és pozitív számot, ha az első karakterlánc a nagyobb.

CopyTo Átmásolja a karakterlánc egy részét vagy a teljes karakterláncot egy másik karakterláncba vagy karaktertömbbe.

Készítette: Zsótér Csaba III. éves informatikus hallgató

124

Page 125: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

EndsWith Megállapítja, hogy a kérdéses karakterlánc vége azonos-e egy másik

karakterlánccal. Ha igen, visszatérési értéke true, ellenkező esetben false lesz.

Equals Összehasonlít két karakterláncot, és megállapítja, hogy azonos értéket tartalmaznak-e. Ha igen, visszatérési értéke true, ellenkező esetben false lesz.

IndexOf Egy karakter vagy karakterlánc első előfordulásának helyét adja vissza az adott karakterláncban. -1-gyel tér vissza, ha a keresett szakasz egyáltalán nem szerepel.

Insert Beilleszt egy karakterláncot a megadott karakterláncba. Az eredményt egy új karakterláncban adja vissza.

LastIndexOf Egy karakter vagy karakterlánc utolsó előfordulásának helyét adja vissza. Visszatérési értéke -1, ha a keresett rész egyáltalán nem fordul elő.

Length Az adott karakterlánc hosszát adja vissza. (Karakterek számát)PadLeft A karakterlánc tartalmát jobbra igazítja, a fennmaradó üres helyet

pedig egy megadott karakterrel, vagy szóközzel tölti.PadRigth A karakterlánc tartalmát balra igazítja, a fennmaradó üres helyet

pedig egy megadott karakterrel, vagy szóközzel tölti.Tagfüggvény LeírásRemove Megadott számú karaktert töröl a karakterlánc egy megadott helyétől

kezdődően.Split A Join tagfüggvény ellentéte. A kérdéses karakterláncot kisebb

karakterláncokra tördeli a megadott mezőelválasztók mentén.sWith Megállapítja, hogy a kérdéses karakterlánc egy adott karakterrel vagy

karakterlánccal kezdődik-e. Ha igen, visszatérési értéke true, ellenkező esetben false lesz. Ha a keresett karakter null, a visszatérési érték akkor is true lesz.

Substring Az eredeti karakterlánc egy részhalmazát adja vissza a megadott helytől kezdődően.

ToCharArray A kérdéses karakterlánc tartalmát a kimenetként megadott char típusú tömbbe másolja.

ToLower Az adott karakterlánc tartalmát csupa kisbetűvel megjelenítve adja vissza.

ToUpper Az adott karakterlánc tartalmát csupa nagybetűvel megjelenítve adja vissza.

Trim Eltávolítja a megadott karakterláncot a kérdéses karakterlánc elejéről és végéről.

TrimEnd Eltávolít egy megadott karakterláncot a kérdéses karakterlánc végéről.

TrimStart Eltávolít egy megadott karakterláncot a kérdéses karakterlánc elejéről.

Készítette: Zsótér Csaba III. éves informatikus hallgató

125

Page 126: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

@ - a különleges karakterlánc formázóAz eddig bemutatott példaprogramokban számos különleges karaktert láthattunk már. Ahhoz például, hogy egy karakterláncban egy idézőjelet jelenítsünk meg, egy kilépési karaktert kell használnunk. A következő parancs például a "Hello World!" szöveget jeleníti meg a képernyőn, így, kettős idézőjelek közé zárva:

System.Console.WriteLine("\"Hello World!\"");

Ha egy fordított perjelet akarunk kiíratni, szintén ilyen vezérlő karaktereket kell használnunk:

System.Console.WriteLine("My code: C:\\Jegyzet\\C# jegyzet");

A C# nyelvben azonban lehetőségünk van arra is, hogy a vezérlő karaktereket a @ karakter segítségével sokkal rövidebb formában adjuk meg. Ha egy karakterláncot az a karakter előz meg, akkor a rendszer annak tartalmát literálisan, azaz betű szerint értelmezi. Az ilyen karakterláncokat a szaknyelvben eredeti karakterlánc-literáloknak (verbatim string literal) nevezik. Az előbbi kódsorban bemutatott elérési út a @ segítségével a következőképpen néz ki:

System.Console.WriteLine(@"My code: C:\Jegyzet\C# jegyzet");

Ha rendszeresen használjuk a @ karaktert, észre fogjuk venni, hogy az egyetlen trükkös dolog, ami megmarad, a kettős idézőjelek megjelenítése:

System.Console.WriteLine(@"""Hello World!""");

Karakterláncok építéseA System.Text névtér tartalmaz egy StringBuilder nevű osztályt, amely lehetővé teszi olyan karakterláncok felépítését, amelyek tartalma módosítható. A StringBuilder osztály segítségével létrehozott objektumok működésüket tekintve rendkívül hasonlítnak a közönséges karakterláncokhoz. Az eltérés tulajdonképpen csak annyi, hogy eme osztály tagfüggvényei közvetlenül az objektumban tárolt értéket tudják módosítani.

A StringBuilder osztály tagfüggvényei és tulajdonságai Tagfüggvény LeírásAppend Hozzáfűzi a megadott objektumot az aktuális StringBuilder

objektumhoz.AppendFormat Vezérlő karakterekkel megadott formátum alapján objektumokat

illeszt be egy karakterláncba.Capacity Beállítja vagy lekérdezi a kérdéses objektumban tárolható karakterek

számát. Ezt az értéket menet közben is növelhetjük, egészen a MaxCapacity értékig.

Chars Beállítja vagy lekérdezi az adott pozíción található karakter értékét.EnsureCapacity Gondoskodik róla, hogy az adott StringBuilder objektum

kapacitása legalább a megadott érték legyen. Ha átadunk egy értéket az EnsureCapacity tagfüggvénynek, akkor erre az értékre fogja állítani a Capacity értékét. Ha a MaxCapacity értéke kisebb annál, mint amit megadtunk, kivétel keletkezik.

Készítette: Zsótér Csaba III. éves informatikus hallgató

126

Page 127: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

Equals Megvizsgálja, hogy a kérdéses StringBuilder objektum tartalma

azonos-e a megadott értékkel.Insert Beilleszt egy objektumot a StringBuilder objektum adott

pontjára.Length Beállítja vagy lekérdezi a StringBuilder objektumban

pillanatnyilag tárolt karakterlánc hosszát. Ez az érték nem lehet nagyobb, mint az objektum Capacity tulajdonságában tárolt. Ha a pillanatnyi érték nagyobb, mint a beállítani kívánt hossz, a rendszer csonkolja a tárolt értéket.

MaxCapacíty A StringBuilder objektum legnagyobb kapacitását kérdezi le.Remove A megadott helytől kezdődően megadott számú karaktert eltávolít a

kérdéses StringBuilder objektumból.Replace Az adott karakter valamennyi előfordulását lecseréli egy másik

karakterre.ToString A StringBuilder objektumot közönséges String objektummá

alakítja

A StringBuilder osztályt pontosan ugyanúgy használhatjuk, mint az összes többit.

Nézzünk erre egy példát:

3.53 A StrinfBuilder osztály használata using System;using System.Text;

class buildName{ public static void Main() { StringBuilder name = new StringBuilder(); string buffer; int marker = 0;

Console.Write("\nEnter your first name: "); buffer = Console.ReadLine();

if ( buffer != null ) { name.Append(buffer); marker = name.Length; }

Console.Write("\nEnter your last name: "); buffer = Console.ReadLine(); if ( buffer != null ) { name.Append(" "); name.Append(buffer); }

Console.Write("\nEnter your middle name: "); buffer = Console.ReadLine();

Készítette: Zsótér Csaba III. éves informatikus hallgató

127

Page 128: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

if ( buffer != null ) { name.Insert(marker+1, buffer); name.Insert(marker+buffer.Length+1, " "); } Console.WriteLine("\n\nFull name: {0}", name);

Console.WriteLine("\n\nInfo about StringBuilder string:"); Console.WriteLine("value: {0}", name); Console.WriteLine("Capacity: {0}", name.Capacity); Console.WriteLine("Maximum Capacity: {0}", name.MaxCapacity); Console.WriteLine("Length: {0}", name.Length); }}

Információ bekérése a konzolrólAz eddigi példáinkban is gyakran használtuk a Console osztály Read és ReadLine nevű tagfüggvényeit. A most következő részben a konzolról érkező bemenet kezelésével fogunk bővebben foglalkozni, valamint azzal, miként alakíthatjuk az így megszerzett információt használhatóbb formájúvá.

A Read tagfüggvény használataA System névtérben található Console osztálynak kér olyan tagfüggvénye van, amelyekkel a felhasználótól érkező bemenetet fogadhatjuk. Ezek a Read és a ReadLine.

A Read tagfüggvény egyszerre csak egy karaktert olvas be a bemenő adatfolyamból. A beolvasott karaktert egész számként (int) adja vissza. Ha elérte a bemeneti adatfolyam végét, a visszatérési értéke -1 lesz.

A konzolról érkező karakterfolyamot a Ctrl+Z billentyűkombinációval szakíthatjuk meg.

3.54 A Read tagfüggvény használata using System;using System.Text;

class ReadIt{ public static void Main() { StringBuilder Input = new StringBuilder();

int ival; char ch = ' '; Console.WriteLine("Enter text. When done, press CTRL+Z:"); while ( true ) { ival = Console.Read();

Készítette: Zsótér Csaba III. éves informatikus hallgató

128

Page 129: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

if ( ival == - 1 ) break; ch = ( char ) ival;

Input.Append(ch); } Console.WriteLine("\n\n==========>\n"); Console.Write( Input ); Console.Write("\n\n"); }}

A ReadLine tagfüggvény használataA ReadLine tagfüggvényt is számos alkalommal használtuk már a bemutatott példaprogramokban. Mindazonáltal érdemes megemlíteni, hogy a ReadLine egy sornyi szöveget olvas be a bemenetről. A sor végét kocsivissza vagy újsor karakter jelzi, vagy mindkettő. A ReadLine ezután a záró kocsivissza és újsor kivételével valamennyi beolvasott karaktert visszaadja. Ha olvasás közben elérte a bemeneti adatfolyam végét, visszatérési értéke null lesz. Nézzünk erre is egy példát:

3.55 Karakterek olvasása a ReadLine segítségével using System;using System.Text;

class ReadLine{ public static void Main() { StringBuilder Input = new StringBuilder(); string buff;

Console.WriteLine("Enter text. When done, press Ctrl+Z:");

while ( (buff = Console.ReadLine()) != null ) { Input.Append(buff); Input.Append("\n"); } Console.WriteLine("\n\n==========>\n"); Console.Write( Input ); Console.Write("\n\n"); }}

A Convert osztály használataA valódi programokban nem csak az a cél, hogy a Read vagy a ReadLine tagfüggvények egyikével megszerezzük a bemenetről érkező információt, hanem az is, hogy azt a megfelelő formára alakítva adjuk tovább a program többi részeinek. Ez az utókezelés állhat egyszerűen annyiból, hogy a beolvasott szöveget egy már meglévővel valamilyen módon egyesítjük, de

Készítette: Zsótér Csaba III. éves informatikus hallgató

129

Page 130: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

az sem ritka, hogy teljesen más adattípussá kell alakítanunk, mielőtt a program feldolgozhatná.

A System névtérben található Convert osztály kifejezetten az ilyen adat-átalakításokra szolgál.

A Convert lezárt osztály, amely számos statikus tagfüggvényt tartalmaz. Ezek mindegyike valamilyen adattípusok közti átalakítást valósít meg. Mivel minden tagfüggvénye statikus, a következő minta szerint használhatjuk őket:

Convert.tagfüggvény(eredeti_érték);

Nagyon fontos észben tartani, hogy ez az osztály az alaposztályok könyvtárának része, vagyis más programozási nyelvekből is használhatjuk. C# adattípusok helyett a Convert osztály .NET adattípusokat állít elő.

Típusok közti átalakítást végző tagfüggvények Tagfüggvény Mivé alakítToBoolean Logikai értékkéToByte 8 bites előjel nélküli egésszéToChar Unicode karakterréToDateTime DateTime típussáToDecimal Tízes számrendszerű értékkéToDouble Kétszeres pontosságú számmáTípusok közti átalakítást végző tagfüggvények (folytatás) Tagfüggvény Mivé alakítToInt16 16 bites előjeles egésszéToInt32 32 bites előjeles egésszéToInt64 64 bites előjeles egésszéToSByte 8 bites előjeles egésszéToSingle Egyszeres pontosságú lebegőpontos számmáToString KarakterlánccáToUInt16 16 bites előjel nélküli egésszéToUInt32 32 bites előjel nélküli egésszéToUInt64 64 bites előjel nélküli egésszé

3.56 A Convert osztály egyik tagfüggvényének használata using System;using System.Text;

class Converts{ public static void Main() { string buff; int age;

Console.Write("Enter your age: ");

buff = Console.ReadLine();

Készítette: Zsótér Csaba III. éves informatikus hallgató

130

Page 131: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

try { age = Convert.ToInt32(buff);

if( age < 21 ) Console.WriteLine("You are under 21."); else Console.Write("You are 21 or older."); } catch( ArgumentException ) { Console.WriteLine("No value was entered... (equal to null)"); } catch( OverflowException ) { Console.WriteLine("You entered a number that is too big or too

small."); } catch( FormatException ) { Console.WriteLine("You didn't enter a valid number."); } catch( Exception e ) { Console.WriteLine("Something went wrong with the conversion."); throw(e); } }}

Bevezetés az objektumközpontú programozásba: felületek

Felületek: előzetesVegyük egy kicsit alaposabban szemügyre a következő kódot. Mi az, ami feltűnő benne:

public abstract class cShape{

public abstract long Area();public abstract long Circumference();public abstract int sides();

}

Látható, hogy ez egy elvont osztály, és valamennyi tagfüggvénye is elvont. Ez eddig nem különleges, hiszen az elvont osztályokról már volt szó. Csak az ismétlés kedvéért itt is leírjuk, hogy elvont általában az az osztály, amelynek van legalább egy elvont tagfüggvénye. Az elvont tagfüggvények olyan függvények, amelyeket öröklésnél a származtatott osztálynak kötelező jelleggel felül kell bírálnia.

Az osztályok mellett a hivatkozási típusok egy másik nagy csoportját a felületek (interfész, interface) alkotják, amelyek működésüket tekintve nagyon hasonlóak a fenti cShape osztályhoz. Egy felület azt írja le, hogy mi fog szerepelni egy osztályban, vagyis hogy minek a deklarációja fog benne megjelenni. Ugyanakkor maga a felület semmiféle szolgáltatást nem

Készítette: Zsótér Csaba III. éves informatikus hallgató

131

Page 132: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

határoz meg. A felület tehát erősen hasonlít egy elvont osztályhoz, és ezzel együtt a fent bemutatott osztályhoz.

Ami azt illeti, a hasonlóság olyan alapvető, hogy a cShape osztályt pillanatok alatt felületté alakíthatjuk. Ehhez nem kell mást tennünk, mint elhagyni az abstract módosítókat, a class kulcsszót pedig lecserélni interface-re:

public interface ISharpe{

long Area();long Circumference();int sides();

}

Az osztályok és a felületek kapcsolataA felület tulajdonképpen egy tisztán elvont osztály. Külső megjelenése ugyanakkor számos ponton eltér. Először és mindenekelőtt a felület soha semmit nem valósít meg ténylegesen, vagyis futtatható kód egyáltalán nincs benne. A megvalósítás annak az osztálynak a feladata, amely magába foglalja az adott felületet. A felület tehát megadja mindannak a meghatározását, ami az adott osztályban történni fog, illetve meg lesz valósítva, de maga soha semmit nem hoz létre konkrétan. A felület csak amolyan leírása egy majdnem létrejövő osztálynak.

A felület abban is gyökeresen különbözik egy osztálytól, hogy a felület valamennyi tagja nyilvános. Ha a hatókörökkel kapcsolatban bármilyen más beállítással próbálkozunk, fordítási hibaüzenetet kapunk.A felületek csak tagfüggvényeket, tulajdonságokat, eseményeket és indexelőket tartalmazhatnak. Nem tartalmazhatnak viszont adattagokat, konstruktorokat, destruktorokat, és statikus tagokat.

Az elvont osztályok tulajdonságai, illetve a velük kapcsolatos megkötések meglehetősen hasonlítanak mindehhez. Ugyanakkor az elvont osztályok másra is alkalmasak, mint a felületek.

A felületek használataBár első látásra egy felület nem tűnhet olyan hatékonynak és jól használhatónak, mint egy hasonló osztály, nagy előnye, hogy a felületet olyan helyeken is használhatjuk, ahol osztályt nem. Egy osztály mindössze egyetlen másik osztálytól örökölhet, viszont több felületet is megvalósíthat. Az is lényeges szempont, hogy a közönséges struktúrák nem örökölhetnek sem más struktúráktól, sem osztályoktól. Felületeket ugyanakkor ezek is megvalósíthatnak.

A C#, szemben a C++-szal és más objektumközpontú nyelvekkel, nem támogatja a többszörös öröklődést, vagyis azt, hogy egy osztály több más osztálytól örököljön. Ezt a lehetőséget a nyelv tervezői természetesen szándékosan hagyták ki, mégpedig az általa okozott problémák, és a keletkező logikai szerkezetek nemkívánatos összetettsége miatt. A C# a többszörös öröklés előnyeit ugyanakkor úgy valósítja meg, hogy lehetővé teszi több felület egyidejű megvalósítását.

Készítette: Zsótér Csaba III. éves informatikus hallgató

132

Page 133: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

Miért használjunk felületeket?Van néhány előnye annak, ha elvont osztályok helyett felületeket használunk a programjainkban.

Először is, a felületek segítségével a közönséges struktúrák között is megvalósítható egyfajta öröklés. Ezenkívül, mivel egyetlen osztály több felületet is megvalósíthat egyszerre, többszörös öröklési rendszer kialakítására is lehetőségünk van. Ugyanakkor ezt egy elvont osztállyal nem tudnánk megtenni.

A felületek használatának legfontosabb előnye azonban az, hogy általuk olyan szolgáltatásokkal egészíthetünk ki egy osztályt vagy osztályok egy csoportját, amelyeket azok más módon nem tudnának megvalósítani. Ha több osztályon belül hozzuk létre ugyanazt a viselkedési formát, akkor tulajdonképpen egy feltételezéssel élünk: feltesszük, hogy a kérdéses viselkedésforma minden osztályban ugyanolyan lesz. Ha ugyanezt felületek használatával oldjuk meg, semmiféle előfeltevésre nincs szükségünk. A felületeken keresztül átvitt műveletek garantáltan egyformák lesznek.

A felületek alkalmazásának másik nagy előnye, hogy általuk valamennyi osztályt rákényszeríthetjük arra, hogy megvalósítsanak minden, a felületben leírt műveletet. Ha egy osztály egy alaposztálytól örököl, nem köteles valamennyi abban leírt kódot ténylegesen megvalósítania. Ez pedig egészen érdekes hibákat okozhat, ha az osztály valamelyik felhasználója azzal a feltételezéssel él, hogy a származtatott osztály szolgáltatásköre teljes.

Felületek meghatározásaÖsszefoglalva az eddig elhangzottakat, a felület tulajdonképpen annak leírása, hogy minek kell az őt megvalósító osztályban szerepelnie. A felületek meghatározásának alapvető szerkezete a következő:

interface INév{

tagok;}

A tagfüggvényeket a hatókörükre vonatkozó bármilyen módosító nélkül adhatjuk meg. Amint korábban említettük, a felületeken belül úgyis valamennyi tagfüggvény nyilvános. Ezenkívül nem adhatunk meg semmiféle részletet a tagfüggvények törzsével kapcsolatban sem. A legtöbb esetben tehát csak a tagfüggvények visszatérési típusa és a neve szerepel a meghatározásban, amelyeket egy zárójelpáros és egy pontosvessző követ:

interface IFormatForPrint{

void FormatForPrint(PrintClass PrinterType);int NotifyPrintComplete();

}

Felület meghatározása tagfüggvényekkelAz elméletből ennyi elég is lesz egyelőre. Lássunk valamilyen konkrét kódot:

Készítette: Zsótér Csaba III. éves informatikus hallgató

133

Page 134: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

Az IShape felület meghatározása a következőképpen fest:

public interface IShape{

double Area();double Circumference();int Sides();

}

Azzal, hogy ezt a felületet beemeljük egy osztályba, automatikusan egyetértünk bizonyos „játékszabályokkal”. Először is gondoskodni fogunk róla, hogy a kérdéses osztály ténylegesen megvalósítsa a Circle és Square tagfüggvényeket, mégpedig abban a formában, ami a felület meghatározásában szerepel. Jelen esetben ez azt jelenti, hogy valamennyi osztálynak tartalmazni kell majd az Area, Sides és Circumference tagfüggvényeket. A leglényegesebb ebben az egészben, hogy mindkét osztály garantáltan az IShape felületben megadott jellegzetességekkel fog rendelkezni. Ennek jótékony hatását magában a programban láthatjuk:

3.57 Az IShape felület használata using System; public interface IShape{ double Area(); double Circumference(); int Sides();} public class Circle : IShape{ public int x; public int y; public double radius; private const float PI = 3.14159F; public double Area() { double theArea; theArea = PI * radius * radius; return theArea; } public double Circumference() { return ((double) (2 * PI * radius)); } public int Sides() { return 1; } public Circle() { x = 0;

Készítette: Zsótér Csaba III. éves informatikus hallgató

134

Page 135: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

y = 0; radius = 0.0; }}

public class Square : IShape{ public int side; public double Area() { return ((double) (side * side)); } public double Circumference() { return ((double) (4 * side)); } public int Sides() { return 4; } public Square() { side = 0; }} public class Shape{ public static void Main() { Circle myCircle = new Circle(); myCircle.radius = 5; Square mySquare = new Square(); mySquare.side = 4; Console.WriteLine("Displaying Circle information:"); displayInfo(myCircle); Console.WriteLine("\nDisplaying Square information:"); displayInfo(mySquare); } static void displayInfo( IShape myShape ) { Console.WriteLine("Area: {0}", myShape.Area()); Console.WriteLine("Sides: {0}", myShape.Sides()); Console.WriteLine("Circumference: {0}", myShape.Circumference()); }}

Ezen a ponton felmerül egy kérdés. Van-e a displayInfo tagfüggvénynek kétféle túlterhelt változata, amelyek közül az egyik Circle, a másik Square típusú objektumot tud

Készítette: Zsótér Csaba III. éves informatikus hallgató

135

Page 136: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

fogadni? Nincs. A displayInfo tagfüggvény egy IShape típusú objektumot vár bemenetként. A szó gyakorlati értelmében ilyen objektumtípus persze nem létezik, viszont vannak olyan objektumtípusok, amelyek rendelkeznek az IShape felületben körülírt jellemzőkkel. Ez a tagfüggvény tehát többalakú, hiszen bármely objektummal képes működni, amely megvalósítja az IShape felületet. Ezt az teszi lehetővé, hogy az IShape-ben megadott tagfüggvényekre támaszkodik, ezek pedig valamennyi, az IShape előírásainak megfelelő osztály köteles pontosan a leírt formában megvalósítani.

Tulajdonságok megadása a felületeken belülEgy felület meghatározása tartalmazhatja bizonyos tulajdonságok megadását is. Akárcsak a felületek más tagjainál, itt sem szerepelhet konkrét, a megvalósítással kapcsolatos kód. Egy tulajdonság felületen belüli magadásának formája a következő:

módosító(k) adattípus név{

get;set;

}

Nézzünk erre egy példát:

3.58 Tulajdonságok megadása egy felületen belül using System; public interface IShape{ int Sides { get; set; } double Area();} public class Square : IShape{ private int InSides; public int SideLength; public double Area() { return ((double) (SideLength * SideLength)); } public int Sides { get { return InSides; } set { InSides = value; } } public Square() { Sides = 4;

Készítette: Zsótér Csaba III. éves informatikus hallgató

136

Page 137: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

}} public class Props{ public static void Main() { Square mySquare = new Square(); mySquare.SideLength = 5;

Console.WriteLine("\nDisplaying Square information:"); Console.WriteLine("Area: {0}", mySquare.Area()); Console.WriteLine("Sides: {0}", mySquare.Sides); }}

Ugyanakkor érdemes megjegyezni, hogy ilyen helyeken nem feltétlenül muszáj mind a kettőt megadni. Az is teljesen elfogadható volna, ha a tulajdonság csak get vagy csak set tagfüggvényekkel rendelkezne. Ugyanakkor ha a felület meghatározása mindkét tagfüggvényt tartalmazza, akkor valamennyi, a felületet megvalósító osztály köteles is megvalósítani mindkettőt.

Több felület használataA közönséges örökléssel szemben a felületek használatának egyik nagy előnye, hogy egy osztályon belül egyszerre több felületet is megvalósíthatunk. Ez lehetővé teszi egyfajta többszörös öröklési rendszer kialakítását anélkül, hogy az ezzel járó logikai összetettség programunk hibátlanságát veszélyeztetné.

Több felület egyidejű használata esetén azokat a deklarációban egymástól vesszővel elválasztva kell felsorolnunk:

class Square : IShape,IShapeDisplay{

...

}

Ezek után persze mind a két felületben szereplő valamennyi tagfüggvényt és egyéb logikai szerkezetet meg kell valósítanunk az osztályban.

3.59 Több felület megvalósítása egyetlen osztályban. using System; public interface IShape{ double Area(); int Sides { get; }} public interface IShapeDisplay{ void Display();

Készítette: Zsótér Csaba III. éves informatikus hallgató

137

Page 138: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

} public class Square : IShape, IShapeDisplay{ private int InSides; public int SideLength; public int Sides { get { return InSides; } } public double Area() { return ((double) (SideLength * SideLength)); } public double Circumference() { return ((double) (Sides * SideLength)); }

public Square() { InSides = 4; } public void Display() { Console.WriteLine("\nDisplaying Square information:"); Console.WriteLine("Side length: {0}", this.SideLength); Console.WriteLine("Sides: {0}", this.Sides); Console.WriteLine("Area: {0}", this.Area()); }} public class Multi{ public static void Main() { Square mySquare = new Square(); mySquare.SideLength = 7; mySquare.Display(); }}

Kifejezett felülettagok használataNos, eddig minden simán ment a felületekkel kapcsolatban. Most azonban vizsgáljuk meg, mi történik, ha egy felület nevet akar használni, ami már foglalt, mert szerepel a kód egy másik, korábbi részében.

Ha egy osztály két vagy több olyan felületet valósít meg, amelyekben ugyanaz a tagfüggvény szerepel, akkor ezt a tagfüggvényt csak egyszer kell megvalósítania. Ennek a megvalósításnak eleget kell tennie mindkét felület előírásainak.

Készítette: Zsótér Csaba III. éves informatikus hallgató

138

Page 139: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

Vannak persze esetek, amikor a kérdéses tagfüggvényt függetlenül akarjuk megvalósítani mindkét felülethez. Ebben az esetben kifejezett (explicit) felületmegvalósítást kell alkalmaznunk. Ez azt jelenti, hogy a kérdéses, többször előforduló tag megvalósításánál meg kell adni a felület nevét is. A tagfüggvény hívásakor ezen kívül a megfelelő típus-átalakításról is gondoskodni kell, amint a következő példa is mutatja:

3.60 Explicit felületmegvalósítás using System; public interface IShape{ double Area(); int Sides { get; } void Display();} public interface IShapeDisplay{ void Display();} public class Square : IShape, IShapeDisplay{ private int InSides; public int SideLength; public int Sides { get { return InSides; } } public double Area() { return ((double) (SideLength * SideLength)); } public double Circumference() { return ((double) (Sides * SideLength)); } public Square() { InSides = 4; }

void IShape.Display() { Console.WriteLine("\nDisplaying Square Shape\'s information:"); Console.WriteLine("Side length: {0}", this.SideLength); Console.WriteLine("Sides: {0}", this.Sides); Console.WriteLine("Area: {0}", this.Area()); } void IShapeDisplay.Display() { Console.WriteLine("\nThis method could draw the shape..."); }

Készítette: Zsótér Csaba III. éves informatikus hallgató

139

Page 140: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

} public class Explicit{ public static void Main() { Square mySquare = new Square(); mySquare.SideLength = 7; IShape ish = (IShape) mySquare; IShapeDisplay ishd = (IShapeDisplay) mySquare; ish.Display(); ishd.Display(); }}

Új felületek levezetése már létező felületekbőlAkár az osztályoknál, a felületekkel kapcsolatban is megvan az a lehetőségünk, hogy egy már létezőből egy újat vezessünk le. A felületek egymás közti öröklését ráadásul teljesen hasonlóan lehet megvalósítani, mint az osztályokét. A kódrészlet azt mutatja be, miként lehet öröklés révén kiegészíteni az imént létrehozott IShape felületet.

public interface IShape{

long Area();long Circumference();int Sides{get;set;}

}interface I3DShape : Shape{

int Depth{get;set;}}

Az I3DShape felület az öröklés játékszabályainak megfelelően tartalmazza az IShape valamennyi elemét, de lehetnek benne új elemek is. Jelen esetben a Depth nevű tulajdonság jelenti ezt a többletet. Az I3DShape felületet ezek után természetesen pontosan ugyanúgy használhatjuk, mint bármelyik más felületet. Összesen négy megvalósítandó tagja lesz, mégpedig az Area, a Circumference, a Sides és a Depth.

Felületek tagjainak elrejtéseLehetőségünk van arra is, hogy megvalósítsuk egy felület egyik elemét, de ezzel egyidőben elrejtsük az ahhoz való hozzáférést az alaposztály elől. Ilyesmit általában akkor teszünk, ha meg kell felelnünk a felület teljes megvalósításával kapcsolatos elvárásoknak, viszont nem akarjuk az általunk fejlesztett osztályt túlságosan összekuszálni mindenféle tagfüggvényekkel és egyéb elemekkel.

Ahhoz, hogy elrejtsünk egy felületet, kifejezett módon kell azt megvalósítanunk. Nézzünk erre egy példát:

Készítette: Zsótér Csaba III. éves informatikus hallgató

140

Page 141: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

3.61 Egy felület egyik tagjának elrejtése egy osztály elől using System; public interface IShape{ // members left out to simplify example... int ShapeShifter( int val ); int Sides { get; set; }} public class Shape : IShape{ private int InSides; public int Sides { get { return InSides; } set { InSides = value; } } int IShape.ShapeShifter( int val ) { Console.WriteLine("Shifting Shape...."); val += 1; return val; } public Shape() { Sides = 5; }}

public class Hide{ public static void Main() { Shape myShape = new Shape(); Console.WriteLine("My shape has been created."); Console.WriteLine("Using get accessor. Sides = {0}", myShape.Sides);// myShape.Sides = myShape.ShapeShifter(myShape.Sides); // error IShape tmp = (IShape) myShape; myShape.Sides = tmp.ShapeShifter( myShape.Sides);

Console.WriteLine("ShapeShifter called. Sides = {0}", myShape.Sides); }}

Készítette: Zsótér Csaba III. éves informatikus hallgató

141

Page 142: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

A program válaszoló képességének kialakítása képviselők,

események és indexelők segítségével

Hogyan használjunk egy indexelőt?Az indexelő lehetővé teszi, hogy segítségével megcímezzünk (indexeljünk) egy objektumot, és rajta keresztül hozzáférjünk az illető objektumban tárolt adatokhoz. Lényegét tekintve az indexelő lehetővé teszi, hogy egy objektumot tömbként kezeljünk.

Más szempontból az indexelők erősen hasonlítanak a tulajdonságokhoz, ugyanis itt is a get és a set tagfüggvényeket használjuk az elemek beállítására és lekérdezésére, illetve az indexelők megadására. Ugyanakkor a tulajdonságoktól eltérően itt nem egy adattaghoz férünk hozzá az indexen keresztül, hanem magából az objektumból olvasunk ki egy értéket. Amikor egy tulajdonságot adunk meg, mindenekelőtt nevet kell adnunk neki. Az indexelő megadásakor viszont a this kulcsszót használjuk, amely magára az objektumpéldányra mutat, s így tulajdonképpen az objektum nevét használjuk. Az indexelők megadásának formája a következő:

public adatTípus this[int index]{get{//amit akarunk...return aÉrték}set{//amit akarunk...//Általában egy értéket kell beállítani az osztályon belül//az index és a hozzárendelt érték alapján}}Ha létrehoztunk egy indexelőt, az lehetővé teszi, hogy szögletes zárójelek használatával ( [] ) állítsuk be, illetve kérdezzük le az objektum valamelyik értékét. Amint az a fenti példából is látható, az adatTípus helyén meg kell adnunk, hogy az indexelő milyen típusú adatot állít be, illetve ad vissza. A get szakaszban ezt az adattípust adjuk vissza, a set utasítás leírásánál pedig ezzel az adattípussal végzünk valamilyen műveletet.

Akárcsak a tulajdonságoknál és a tagfüggvényeknél, itt is használhatjuk a value kulcsszót. Ez az az érték, amit a set eljárásnak átadunk. Ennyi bevezető után az lesz a legjobb, ha egy egyszerű példán megnézzük egy indexelő működését:

3.62 Az indexelők használata using System;

public class SpellingList{ protected string[] words = new string[size]; static public int size = 10;

public SpellingList()

Készítette: Zsótér Csaba III. éves informatikus hallgató

142

Page 143: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

{ for (int x = 0; x < size; x++ ) words[x] = String.Format("Word{0}", x); }

public string this[int index] { get { string tmp;

if( index >= 0 && index <= size-1 ) tmp = words[index]; else tmp = "";

return ( tmp ); } set { if( index >= 0 && index <= size-1 ) words[index] = value; } } }

public class Indexer{ public static void Main() { SpellingList myList = new SpellingList();

myList[3] = "====="; myList[4] = "Csaba"; myList[5] = "was"; myList[6] = "Here!"; myList[7] = "=====";

for ( int x = 0; x < SpellingList.size; x++ ) Console.WriteLine(myList[x]); }}

A képviselők felfedezéseEbben a szakaszban a nyelvi elemek egy egészen kifinomult csoportjáról, a képviselőkről lesz szó. A képviselő (delegate, megbízott) olyan hivatkozási típus, amely egy tagfüggvényhívás aláírását adja meg. A képviselők ezek után képes az ennek a hívási formátumnak megfelelő tagfüggvényhívásokat fogadni és végrehajtani.

Már volt szó a felületekről, hogy a felület olyan hivatkozási típus, amely leírja egy osztály felépítését, de maga ebből semmit nem valósít meg. A képviselők ennek megfelelően gyakran hasonlítják a felületekhez. A képviselő ugyanis megadja egy tagfüggvény felépítését, de nem valósítja meg azt. Ehelyett a képviselő fogadni tudja a tagfüggvényhívásokat, és az aláírás alapján végrehajtja azokat.

Készítette: Zsótér Csaba III. éves informatikus hallgató

143

Page 144: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

Mint megannyi más esetben egy példa bemutatásával válik világossá a kép. Jelen esetben egy olyan programot fogunk írni, amely két számot képes nagyság szerint növekvő vagy csökkenő sorba rendezni. A rendezés irányát most magában a kódban határozzuk meg, de annak sem lenne semmi akadálya, hogy a felhasználótól kérjük be ezt az információt. A rendezés irányától függően más-más tagfüggvény fut le. Ennek ellenére a kód a kód csak egyetlen hívást fog tartalmazni, mégpedig egy képviselőn keresztül. A megfelelő tagfüggvényt a képviselő fogja megkapni.

A képviselők létrehozásának formája a következő:

public delegate visszatérésiTípus KépviselőNeve(paraméterek)

Itt a public kulcsszót bármilyen más, a helyzetnek megfelelő elérésmódosítóval helyettesíthetjük, a delegate kulcsszó pedig azt jelzi a fordítónak, hogy képviselőről van szó. A deklaráció fennmaradó része annak a tagfüggvénynek a hívási aláírását adja meg, amellyel a képviselő működni fog. A képviselő neve arra a helyre kerül, ahol közönséges esetben a tagfüggvény neve állna.

Példánkban egy Sort nevű képviselőt hozunk létre, amely több különböző rendezési tagfüggvényt képes helyettesíteni. E tagfüggvényeknek nem lesz visszatérési értékük, tehát a képviselő visszatérési értéke is void. A képviselők által kezelt tagfüggvények valamennyien két egész, hivatkozás típusú bemenő paraméterrel működnek. Ez lehetővé teszi, hogy a rendező algoritmus szükség esetén módosítsák saját bemenő paramétereiket. A képviselő teljes meghatározásának alakja a következőképpen néz ki esetünkben:

public delegate void Sort(ref int a, ref int b);

Figyeljük meg, hogy a meghatározást pontosvessző zárja. Bár ez a sor megtévesztésig hasonlít egy tagfüggvény bevezetésére, valójában nem az. Egy igazi tagfüggvénynek törzse is van, amit azonban ide leírtunk, az csupán a forma meghatározása megvalósítás nélkül.A képviselő több különböző tagfüggvény sablonja. Esetünkben a Sort bármely olyan tagfüggvénynek megfeleltethető, amely nem ad vissza értéket, viszont két hivatkozás típusú egész paramétert vár bemenetként. A következő kódrészlet egy ezzel a képviselővel használható tagfüggvényre mutat példát:

public static void Ascending(ref int first, ref int second){

if(first>second){

int tmp = first;first = second;second = tmp;

}}

Látható, hogy ez az Ascending nevű tagfüggvény nem ad vissza értéket, vagyis visszatérési típusa void. Ezenkívül pontosan két egészre vonatkozó hivatkozás típusú bemenő paramétert vár a hívótól. Ez pontosan megegyezik a Sort képviselőben meghatározott aláírással, tehát

Készítette: Zsótér Csaba III. éves informatikus hallgató

144

Page 145: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

az Ascending használható a Sort-on keresztül. Mivel a bemenő paraméterek hivatkozások, a módosítás a külső adatokat is érinti.

Létrehozhatunk egy másik, ugyanilyen alakú tagfüggvényt Descending néven:

public static void Descending(ref int first, ref int second){

if(first<second){

int tmp = first;first = second;second = tmp;

}}

Ez a tagfüggvény minden az előző másolata. Egyetlen eltérés, hogy most a nagyobb értéket tartjuk meg elöl. Természetesen tetszőleges sok további tagfüggvényt is megadhatunk a Sort képviselőhöz. Az egyetlen követelmény velük kapcsolatban az, hogy aláírásuknak egyeznie kell az ott megadottal. Ezen kívül arra is lehetőség van, hogy a különböző programok más és más rendezési logikát alkalmazzanak, de azt valamennyien a Sort képviselőhöz rendeljék.

Most tehát ott tartunk, hogy meghatároztunk agy képviselőt, és megírtunk hozzá két vagy több olyan tagfüggvényt, amelyeket képviselhet. A következő lépés természetesen az, hogy kapcsolatot teremtsünk a képviselő és a tagfüggvények között, vagyis összerendeljük őket. Ezt képviselő objektumok létrehozásával tehetjük meg. A képviselő objektum pont olyan, mint bármely más objektumtípus. Az egyetlen lényeges kitétel, hogy itt a kezdeti a képviselőhöz hozzárendelni kívánt tagfüggvény nevével kell elvégezni.

Ahhoz hogy egy, az Ascending tagfüggvénnyel kapcsolatban használható képviselő objektumot hozzunk létre, a következőt kell tennünk:

Sort up = new Sort(Ascending);

Ez egy up nevű képviselő objektumot hoz létre, amit aztán a szokásos módon használhatunk. Az up objektum a korábban megvalósított Ascending tagfüggvénnyel van összerendelve. A következő sor egy másik, a Descending tagfüggvénnyel összerendelt képviselő objektumot ad meg, amit most down-nak hívnak:

Sort down = new Sort(Descending);

Létrehoztuk tehát egy képviselő leírását, írtunk hozzá neki megfelelő tagfüggvényeket, majd ezeket képviselő objektumok segítségével összerendeltük. A következő – magától értetődő – kérdés az, hogy miként lehet mindezt működésbe hozni, felhasználni arra, amire létrehoztuk? Létre kell hoznunk egy tagfüggvényt, amely a képviselő objektumot, mint bemenő paramétert fogadja. Ez az általános kód fogja aztán végrehajtani a képviselő segítségével a megfelelő tagfüggvényt:

Készítette: Zsótér Csaba III. éves informatikus hallgató

145

Page 146: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

public void DoSort(Sort ar){

ar(ref val1, ref val2);}

Amint látható, a DoSort tagfüggvény az ar nevű bemenő paraméterben egy képviselő objektumot vár bemenetként, majd végrehajtja azt, amit ebben talál. Figyeljük meg, hogy az ar-nak ugyanolyan aláírása van, mint a képviselőnek. A DoSort tagfüggvény lényegében bármelyik Sort típusú képviselő objektumban megnevezett tagfüggvényt képes végrehajtani. Ha tehát az up objektumot adjuk át neki paraméterként, akkor az ar(ref val1, ref val2); az Ascending tagfüggvény hívásával lesz egyenértékű. Ha viszont down a bemenő paraméter tartalma, akkor az ar(ref val1, ref val2); a Descending hívásának felel meg.

Nézzük meg most a teljes programkódot:

3.63 Egy egyszerű képviselő használata using System;

public class SortClass{

static public int val1;static public int val2;

public delegate void Sort(ref int a, ref int b);

public static void DoSort(Sort ar){

ar(ref val1, ref val2);}

}

public class Delegate{

public static void Ascending(ref int first, ref int second){

if(first>second){

int tmp = first;first = second;second = tmp;

}}

public static void Descending(ref int first, ref int second){

if(first<second){

int tmp = first;first = second;second = tmp;

}}

Készítette: Zsótér Csaba III. éves informatikus hallgató

146

Page 147: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

public static void Main(){

SortClass.Sort up = new SortClass.Sort(Ascending);SortClass.Sort down = new SortClass.Sort(Descending);

SortClass.val1 = 350;SortClass.val2 = 30;

Console.WriteLine("Before Sort: val1={0} , val2={1}",SortClass.val1,SortClass.val2);

SortClass.DoSort(up);Console.WriteLine("After Sort: val1={0} ,

val2={1}\n\n",SortClass.val1,SortClass.val2);

Console.WriteLine("Before Sort: val1={0} , val2={1}",SortClass.val1,SortClass.val2);SortClass.DoSort(down);

Console.WriteLine("After Sort: val1={0} , val2={1}",SortClass.val1,SortClass.val2);}

}

Ebben a példában „bedrótozott” értékeket és előre ismert hívásokat használtunk. A képviselők alkalmazásának igazi haszna viszont ténylegesen majd csak akkor mutatkozik meg, ha ennél sokkal dinamikusabb programokat kezdünk fejleszteni.

Események használataMunkánk során azt fogjuk tapasztalni, hogy a képviselőket leginkább az események kezelésével kapcsolatban alkalmazzuk. Az esemény egy olyan értesítés, amit egy osztály bocsát ki, jelezve, hogy valami történt. Az értesítés alapján azután mi, vagy pontosabban fogalmazva az általunk írt más osztályok megtehetik a szükséges lépéseket.Az események feldolgozásának jellemző példája a Windows operációs rendszerek működése. Egy olyan operációs rendszerben, mint a Microsoft Windows, gyakran jelenik meg a képernyőn egy ablak, vagy párbeszédablak, amelyben a felhasználó különböző műveleteket végezhet. Egy gombra kattinthat, kijelölhet egy menüpontot, begépelhet valamilyen szöveget, és így tovább. Valahányszor a felhasználó elvégzi valamelyiket e tevékenységek közül, egy esemény keletkezik. Ha például egy gombra kattintott, akkor egy ButtonClick esemény kerül a rendszerhez feldolgozás végett. A feldolgozást egy ehhez az eseményhez tartozó eseménykezelő végzi, amely ismeri és végre is hajtja a gombnyomás hatására végrehajtandó műveletek sorozatát.

Események létrehozásaEgy esemény létrehozása és használata többlépcsős folyamat. Először létre kell hozunk egy, az eseményhez tartozó képviselőt, majd egy osztályt, amely a paraméterek átadásában lesz segítségünkre, azután meg kell adnunk az esemény hatására lefutó kódot, meg is kell írnunk ezt az úgynevezett eseménykezelőt, és végül el kell érnünk, hogy ez esemény valahogy be is következhessen.

Készítette: Zsótér Csaba III. éves informatikus hallgató

147

Page 148: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

Az eseményképviselők működéseAz eseményekkel kapcsolatos munka első lépése tehát az, hogy az illető eseményhez létrehozunk egy képviselőt. Az eseményhez létrehozott képviselők formátuma kötött:

delegate void EseményKezelőNév(object forrás, xxxEseményParam e)

Egy esemény képviselője mindig két paramétert vesz át. Az első object forrás, vagyis az az objektum, amelyben az esemény bekövetkezett. A második, az xxxEseményParam e egy olyan osztály, amely az eseménykezelő által használható adatokat tartalmazza. Ez az osztály mindig az EventArgs osztályból származik, amely a System névtér része.

A következő programsor egy eseményhez tartozó képviselőt hoz létre. Ez az esemény hozzárendelt karaktereket ellenőriz. Ha megtalál egy adott karaktert, akkor egy ennek megfelelő esemény keletkezik. Az ehhez az eseményhez tartozó képviselőt a következőképpen lehet megadni:

delegate void CharEventHandler(object source, CharEventArgs e);

Ez a sor egy CharEventHandler nevű képviselőt hoz létre. A bevezetésből látszik, hogy CharEventHandler néven létre kell majd hoznunk egy, az EventHandler osztályból származó másik osztályt.

Származtatás az EventArgs osztálybólAz EventArgs osztályt arra használjuk, hogy segítségével az eseménykezelőknek paramétereket adjuk át. Ettől az osztálytól tetszőleges másik osztály örökölhet, ami lehetővé teszi, hogy valamennyi eseményhez létrehozzunk egy olyan osztályt, amely az alapokon kívül tartalmazza az adott esemény kezeléséhez szükséges összes egyéb információt.

A származtatott osztály formája a következő kell legyen:

public class xxxEseményParam : EventArgs{

//adattagok

public xxxEseményParam(típus név){

//értékek beállítása}

}

Az így származtatott osztályt kiegészíthetjük az adott esemény kezeléséhez szükséges egyéb elemekkel, a konstruktorban pedig gondoskodhatunk ezen új elemek beállításáról is. Az eseménykezelőnek ezt az osztályt fogjuk átadni. Még egyszer fontos hangsúlyozni, hogy ennek az átadott objektumnak, illetve osztálynak valamennyi, az esemény kezeléséhez szükséges információt tartalmazni kell.

Készítette: Zsótér Csaba III. éves informatikus hallgató

148

Page 149: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

Láthatjuk, hogy egy CharEventHandler nevű képviselőt kell létrehoznunk, amelynek egy CharEventArgs típusú objektumot kell átadni. Ezt az osztályt a következőképpen származtathatjuk az EventArgs alaposztályból:

class CharEventArgs : EventArgs{

public char CurrChar;public CharEventArgs(char InChar){

this.CurrChar = InChar;}

}

Függetlenül attól, hogy pontosan milyen eseményt akarunk kezelni, az ehhez szükséges osztályt mindig az EventArgs osztály alapján kell létrehozni a fent bemutatott módon. Jelen esetben az új osztály mindössze egyetlen új elemet tartalmaz az alaposztályhoz képest, nevezetesen egy egyetlen karaktert jelölő CurrChar nevű adattagot. Ezt az értéket fogja megkapni a kérdéses esemény kezelését végző kód. Osztályunk tartalmaz egy konstruktort is, amely az osztályba tartozó objektumok létrehozásakor képes fogadni a CurrChar-ban elhelyezendő karaktert.

Az Event osztály kódjának használataSzintén egy osztályt kell ahhoz létrehoznunk, hogy egy adott eseményt kiválthassunk, vagyis lehetővé tegyük, hogy az a program megfelelő pontján bekövetkezhessen. Tulajdonképpen ez az osztály fogja tartalmazni az esemény deklarációját. Ennek formája a következő:

public event xxxExeményKezelő Eseménynév;

Itt az xxxExeményKezelő annak a képviselőnek a neve, amit az esemény kezeléséhez létrehoztunk, az Eseménynév pedig a bevezetni kívánt esemény neve. Összefoglalva tehát ez a kódsor az event kulcsszó segítségével létrehoz egy Eseménynév nevű eseménypéldányt, amely egy xxxExeményKezelő típus képviselője. Az Eseménynév nevet akkor fogjuk használni, amikor tagfüggvényeket rendelünk a képviselőkhöz, valamint amikor ezeket a tagfüggvényeket végre akarjuk hajtani.

Íme egy példa egy eseményosztály létrehozására:

class CharChecker{

char InChar;public event CharEventHandler TestChar;public char In_Char{

get { return InChar;}set {

if (TestChar!=null{

CharEventArgs args = new CharEventArgs(value);TestChar(this,args);

Készítette: Zsótér Csaba III. éves informatikus hallgató

149

Page 150: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

InChar = args.CurrChar;

}}

}}

Ez az osztály tartalmazza azt a kódot, amely a megfelelő feltételek teljesülése esetén kiváltja a kérdéses eseményt.

Eseménykezelők létrehozásaMost tehát ott tartunk, hogy létrehoztunk egy képviselőt, megírtuk z a programszerkezetet, amely továbbítja a szükséges információt az eseménykezelőhöz, és létrehoztuk azt a kódot is, amely kiváltja magát az eseményt. Most azt a kódot kell megírnunk, ami akkor fut le, ha egy esemény bekövetkezett, vagyis létre kell hoznunk a körítéshez magát az eseménykezelőt. Az eseménykezelő olyan kódrészlet, ami értesítést kap, ha egy adott esemény bekövetkezett. Ahhoz hogy illeszkedjen a korábban kialakított rendszerbe, az eseménykezelő tagfüggvénynek ugyanolyan formájúnak kell lennie, mint a képviselőnek. Ez a forma a következő:

void kezelőnév(object forrás, xxxEseményParam paramNév){//eseménykezelő kód}

A kezelőnév annak a tagfüggvénynek a neve, ami az esemény hatására lefut. A tagfüggvény törzsében természetesen bármi lehet, ami az eseményre megfelelő válasz. Példánkban legyen ez az esemény az, hogy ha a bemenő paraméter A volt, akkor azt a program cserélje le X-re. Nézzük a kódot:

static void Drop_A(object sender, CharEventArgs e){

if(e.CurrChar=='a' || e.CurrChar=='A'){

Console.WriteLine("Don't like 'a'!");e.CurrChar = 'x';

}}

Az események és eseménykezelők egymáshoz rendeléseEzen a ponton csaknem valamennyi programelemet ismerjük, ami az események kezeléséhez szükséges. Ideje tehát hogy elvégezzük az esemény és a hozzá tartozó eseménykezelő összerendelését. Ez a főprogramban történik meg.

Ahhoz, hogy egy eseményt és egy kezelőt egymáshoz rendelhessünk, először létre kell hoznunk egy eseményobjektumot. A Character példánál maradva egy CharChecker objektumnak kell létrejönnie a következő módon:

CharChecker tester = new CharChecker();

Készítette: Zsótér Csaba III. éves informatikus hallgató

150

Page 151: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

Ha pedig megvan az objektum, akkor már hozzáférhető az esemény is. Valahányszor lefut a CharChecker objektum set blokkja, végrehajtódik az abban megalkotott logikai szerkezet is, amelynek része az esemény létrehozása és az eseményobjektum végrehajtása.

Ebben a pillanatban azonban az esemény és az eseménykezelő még független egymástól. Az eseménykezelő és az eseményobjektum összerendeléséhez a += műveletet kell használnunk, mégpedig a következő módon:

ObjektumEseményNévvel.EseményObj += new EseményképviselőNév(EseményNév);

A mi példánkban a következőképpen néz ki:

tester.TestChar += new CharEventHandler(Drop_A);

Nézzük egyben az egész programot:

3.64 Események és eseménykezelők használata using System;

delegate void CharEventHandler(object source, CharEventArgs e);

public class CharEventArgs : EventArgs{ public char CurrChar; public CharEventArgs(char InChar) { this.CurrChar = InChar; }}

class CharChecker{ char InChar; public event CharEventHandler TestChar; public char In_Char { get { return InChar; } set { if (TestChar != null ) { CharEventArgs args = new CharEventArgs(value); TestChar(this, args); InChar = args.CurrChar; } } }}

class Events{ public static void Main() { CharChecker tester = new CharChecker();

Készítette: Zsótér Csaba III. éves informatikus hallgató

151

Page 152: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

tester.TestChar += new CharEventHandler(Drop_A); tester.In_Char = 'B'; Console.WriteLine("{0}", tester.In_Char); tester.In_Char = 'r'; Console.WriteLine("{0}", tester.In_Char); tester.In_Char = 'a'; Console.WriteLine("{0}", tester.In_Char); tester.In_Char = 'd'; Console.WriteLine("{0}", tester.In_Char); } static void Drop_A(object source, CharEventArgs e) { if(e.CurrChar == 'a' || e.CurrChar == 'A' ) { Console.WriteLine("Don't like 'a'!"); e.CurrChar = 'X'; } }}

Több eseménykezelő egyetlen eseményhezLehetőség van arra is, hogy egyetlen eseményhez több eseménykezelőt rendeljünk, ha úgynevezett üzenetszórást (multicasting) alkalmazunk. A további eseménykezelőknek mindenben ugyanazt a formát kell követniük, mint az elsőnek, vagyis ugyanolyan objektumtípust kell fogadniuk, egyik paraméterüknek az EventArgs osztályból származott osztályba kell tartoznia, és esetünkben visszatérési típusuk void kell legyen. Az újabb eseménykezelőknek az adott eseményhez való hozzárendelését továbbra is a += művelettel oldjuk meg, mégpedig pontosan úgy, mint az előbb.Nézzünk erre is egy példát:

3.65 Több eseménykezelő egyetlen eseményhez using System;

delegate void CharEventHandler(object source, CharEventArgs e);

public class CharEventArgs : EventArgs{ public char CurrChar; public CharEventArgs(char CurrChar) { this.CurrChar = CurrChar; }}

class CharChecker{ char curr_char; public event CharEventHandler TestChar;

Készítette: Zsótér Csaba III. éves informatikus hallgató

152

Page 153: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

public char Curr_Char { get { return curr_char; } set { if (TestChar != null ) { CharEventArgs args = new CharEventArgs(value); TestChar(this, args); curr_char = args.CurrChar; } } }}

class MyApp{ public static void Main() { CharChecker tester = new CharChecker(); tester.TestChar += new CharEventHandler(Drop_A); tester.TestChar += new CharEventHandler(Change_D); tester.Curr_Char = 'B'; Console.WriteLine("{0}", tester.Curr_Char); tester.Curr_Char = 'r'; Console.WriteLine("{0}", tester.Curr_Char); tester.Curr_Char = 'a'; Console.WriteLine("{0}", tester.Curr_Char); tester.Curr_Char = 'd'; Console.WriteLine("{0}", tester.Curr_Char); } static void Drop_A(object source, CharEventArgs e) { if(e.CurrChar == 'a' || e.CurrChar == 'A' ) { Console.WriteLine("Don't like 'a'!"); e.CurrChar = 'X'; } } // új eseménykezelő.... static void Change_D(object source, CharEventArgs e) { if(e.CurrChar == 'd' || e.CurrChar == 'D' ) { Console.WriteLine("D's are good!"); e.CurrChar = 'Z'; } }}

Készítette: Zsótér Csaba III. éves informatikus hallgató

153

Page 154: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

Eseménykezelők eltávolításaAz eseménykezelőket hozzárendelhetjük eseményekhez, de ezt a hozzárendelést menet közben meg is szüntethetjük. Az eseménykezelők eltávolításához a -= műveletet kell használnunk a hozzárendelést szolgáló += helyett.

3.66 Esemény eltávolítás using System;

delegate void CharEventHandler(object source, CharEventArgs e);

public class CharEventArgs : EventArgs{ public char CurrChar; public CharEventArgs(char CurrChar) { this.CurrChar = CurrChar; }}

class CharChecker{ char curr_char; public event CharEventHandler TestChar; public char Curr_Char { get { return curr_char; } set { if (TestChar != null ) { CharEventArgs args = new CharEventArgs(value); TestChar(this, args); curr_char = args.CurrChar; } } }}

class MyApp{ public static void Main() { CharChecker tester = new CharChecker();

tester.TestChar += new CharEventHandler(Drop_A); tester.TestChar += new CharEventHandler(Change_D);

tester.Curr_Char = 'B'; Console.WriteLine("{0}", tester.Curr_Char);

tester.Curr_Char = 'r'; Console.WriteLine("{0}", tester.Curr_Char);

tester.Curr_Char = 'a'; Console.WriteLine("{0}", tester.Curr_Char);

tester.Curr_Char = 'd';

Készítette: Zsótér Csaba III. éves informatikus hallgató

154

Page 155: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

Console.WriteLine("{0}", tester.Curr_Char);

Console.WriteLine("\nRemoving event handler...."); tester.TestChar -= new CharEventHandler(Change_D);

tester.Curr_Char = 'D'; Console.WriteLine("{0}", tester.Curr_Char);

tester.Curr_Char = 'a'; Console.WriteLine("{0}", tester.Curr_Char);

tester.Curr_Char = 'd'; Console.WriteLine("{0}", tester.Curr_Char); }

static void Drop_A(object source, CharEventArgs e) { if(e.CurrChar == 'a' || e.CurrChar == 'A' ) { Console.WriteLine("Don't like 'a'!"); e.CurrChar = 'X'; } }

static void Change_D(object source, CharEventArgs e) { if(e.CurrChar == 'd' || e.CurrChar == 'D' ) { Console.WriteLine("D's are good!"); e.CurrChar = 'Z'; } }}

Ha egy eseményhez több eseménykezelőt rendelünk, lefutásuk sorrendjét nem szabályozhatjuk. Ezen kívül természetesen az eseménykezelőkben is keletkezhetnek kivételek, illetve általánosabban ezek a kódrészletek is ugyanúgy viselkednek, mint a program többi része. Ha kivétel keletkezik, arra nincs garancia, hogy az adott eseményhez rendelt többi eseménykezelő lefut.

Parancsaink végrehajtatása műveletekkel: túlterhelés

Ismétlés: függvények túlterheléseAz előzőekben már megtanultuk a tagfüggvények kifinomultabb használatánál, hogy egy tagfüggvényt akár többszörösen is túlterhelhetünk. A lényeg csupán az, hogy minden túlterhelt példánynak más legyen az aláírása. Az aláírást a tagfüggvény visszatérési típusa és bemenő paraméterei határozzák meg. A következő túlterhelt tagfüggvényeknek például egytől egyig különböző az aláírása:

int mymethod( int x, int y );int mymethod( int x );int mymethod( long x );

Készítette: Zsótér Csaba III. éves informatikus hallgató

155

Page 156: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

int mymethod( char x, long y, long z );int mymethod( char x, long y, int z );

Már arról is esett szó, hogy nem csak a közönséges tagfüggvényeket, hanem az osztályok konstruktorait is túl lehet terhelni. Most még egy korlátot ledöntünk: megtanuljuk, hogy nemcsak a konstruktorok, de az osztályok műveletei (operátorai) is túlterhelhetők.

Műveletek túlterheléseA tagfüggvények és az osztályok tagjaihoz hozzáférést engedő nyelvi elemek túlterhelésén kívül a legtöbb objektumorientált nyelv azt is lehetővé teszi, hogy műveleteket terheljünk túl. Ez alól a C# sem kivétel, hiszen az a nyelv is lehetővé teszi számos matematikai művelet – például az összeadás és kivonás –, illetve számos logikai és összehasonlító művelet túlterhelését.

A String osztály kiváló példája az olyan osztályoknak, amelyek eleve rendelkeznek ilyen túlterhelt művelettel, és annak nagy hasznát is látjuk. Normális esetben az összeadásnak két osztállyal kapcsolatban nem kellene működnie, a C# nyelvben azonban minden további nélkül összeadhatunk két karakterláncot a szokásos + művelettel. Az eredmény pontosan az lesz, amit egy ilyen művelettől elvárunk: a két karakterlánc összefűzésével keletkező szöveget. Nézzünk rögtön egy példát:

"animal" + " " + "crackers"

E művelet eredménye a következő:

"animal crackers"

Mindez pedig úgy lehetséges, hogy a String osztály és a string adattípus túlterheli az összeadás műveletét.

Látni fogjuk, hogy a műveletek túlterhelésével egyes programjaink nemcsak áttekinthetőbbek lesznek, de jobban is fognak működni.

Az alapvető kéttényezős matematikai műveletek túlterheléseA kéttényezős műveletek nevüknek megfelelően két dologgal végeznek valamilyen műveletet. Ilyen művelet az összeadás (+), a kivonás (-), a szorzás (*), az osztás (/) és a maradékos osztás (%). Ezeket bármelyik osztályban túlterhelhetjük. A túlterhelhető kéttényezős műveletek teljes listája a következő:

+ összeadás- kivonás* szorzás/ osztás% maradékos osztás& logikai ÉS| logikai VAGY^ logikai tagadás (NOT)<< balra tolás

Készítette: Zsótér Csaba III. éves informatikus hallgató

156

Page 157: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

>> jobbra tolás

A műveletek túlterhelésének formája hasonló a tagfüggvényeknél megismert formához. Az általános módszer a következő:

public static visszatérési_típus operator op (típus x, típus y){

...return visszatérési_típus;

}

Egy túlterhelt műveletnek minden esetben nyilvánosnak kell lennie, hiszen másként nem lehetne hozzáférni. A statikusságra szintén szükség van, hiszen ez biztosítja, hogy a művelethez az osztály és ne az objektumpéldányok szintjén tudjuk hozzáférni.

Az operator szó természetesen azt jelzi a fordítónak, hogy itt nem egy közönséges tagfüggvényről, hanem művelet (operátor) túlterheléséről van szó. Azt a kulcsszót maga a túlterhelni kívánt művelet (op) követi, végezetül a művelet paraméterei következnek.

Ebben a példában egy kéttényezős műveletet terhelünk túl, tehát két paraméterünk van. A paraméterek közül az egyik típusa mindenképpen az az osztály kell legyen, amelyben a túlterhelést végezzük. A másik paraméter típusa bármi lehet. Fontos gyakorlati szempont, hogy ha egy osztályban túlterhelünk egy műveletet, akkor célszerű megvalósítani azt minden olyan adattípushoz, amivel kapcsolatban az adott osztályon belül egyáltalán szóba jöhet.

Az AChar osztály az összeadás műveletét terheli túl, úgy, hogy egy egész számot képes hozzáadni egy tárolt karakterhez. Ez a következőképpen fest:

public static AChar operator+ (AChar x, int y);

Nézzünk erre egy példát:

3.67 Kéttényezős műveletek túlterhelése using System;

public class AChar{ private char private_ch;

public AChar() { this.ch = ' '; } public AChar(char val) { this.ch = val; }

public char ch { get{ return this.private_ch; } set{ this.private_ch = value; } }

static public AChar operator+ ( AChar orig, int val )

Készítette: Zsótér Csaba III. éves informatikus hallgató

157

Page 158: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

{ AChar result = new AChar(); result.ch = (char)(orig.ch + val); return result; } static public AChar operator- ( AChar orig, int val ) { AChar result = new AChar(); result.ch = (char)(orig.ch - val); return result; }}

public class myAppClass{ public static void Main(String[] args) { AChar aaa = new AChar('a'); AChar bbb = new AChar('b');

Console.WriteLine("Original value: {0}, {1}", aaa.ch, bbb.ch);

aaa = aaa + 25; bbb = bbb - 1;

Console.WriteLine("Final values: {0}, {1}", aaa.ch, bbb.ch); }}

Az alapvető egytényezős matematikai műveletek túlterheléseAz egytényezős műveletek nevüknek megfelelően csak egy paraméterrel dolgoznak. Ezek közül a túlterhelhetők a következők:

+ - ++ -- ! ~ true false

Az egytényezős műveleteket a kéttényezősekhez hasonlóan kell túlterhelni. Az egyetlen eltérés, hogy bemenetként itt csak egy paramétert kell megadni, amelynek típusa értelemszerűen megegyezik azzal az osztállyal, amelyben a túlterhelés történik. Nézzünk erre egy példát:

3.68 Az egytényezős + és – túlterhelése using System;using System.Text;

public class AChar{ private char private_ch;

public AChar() { this.ch = ' '; } public AChar(char val) { this.ch = val; }

public char ch { get{ return this.private_ch; }

Készítette: Zsótér Csaba III. éves informatikus hallgató

158

Page 159: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

set{ this.private_ch = value; } }

static public AChar operator+ ( AChar orig ) { AChar result = new AChar(); if( orig.ch >= 'a' && orig.ch <='z' ) result.ch = (char) (orig.ch - 32 ); else result.ch = orig.ch;

return result; } static public AChar operator- ( AChar orig ) { AChar result = new AChar(); if( orig.ch >= 'A' && orig.ch <='Z' ) result.ch = (char) (orig.ch + 32 ); else result.ch = orig.ch;

return result; } } public class myAppClass{ public static void Main(String[] args) { AChar aaa = new AChar('g'); AChar bbb = new AChar('g'); AChar ccc = new AChar('G'); AChar ddd = new AChar('G'); Console.WriteLine("ORIGINAL:"); Console.WriteLine("aaa value: {0}", aaa.ch); Console.WriteLine("bbb value: {0}", bbb.ch); Console.WriteLine("ccc value: {0}", ccc.ch); Console.WriteLine("ddd value: {0}", ddd.ch); aaa = +aaa; bbb = -bbb; ccc = +ccc; ddd = -ddd; Console.WriteLine("\n\nFINAL:"); Console.WriteLine("aaa value: {0}", aaa.ch); Console.WriteLine("bbb value: {0}", bbb.ch); Console.WriteLine("ccc value: {0}", ccc.ch); Console.WriteLine("ddd value: {0}", ddd.ch); }}

Az összehasonlító és logikai műveletek túlterheléseA különböző összehasonlító műveletek („relációs operátorok”, viszonyító műveletek) is túlterhelhetők. Itt a következő műveletek jöhetnek szóba:

Készítette: Zsótér Csaba III. éves informatikus hallgató

159

Page 160: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

<<=>>=

Szintén ide tartoznak a logikai műveletek:

==!=

Ezekben az esetekben a deklaráció kissé különbözik az előbbiekben bemutatottól. Ahelyett, hogy a túlterhelő osztály típusának megfelelő objektumot adnának vissza, ezeknek a műveleteknek a visszatérési értéke mindig logikai (Boolean). Ha jól meggondoljuk, ez logikus is, hiszen e műveletek feladata, hogy két tényezőt valamilyen szempontból összehasonlítsák, vagy „megmondják az igazat”.

Nézzünk egy példát:

3.69 Az összehasonlító műveletek túlterhelése using System;using System.Text; public class Salary{ private int AMT; public Salary() { this.amount = 0; } public Salary(int val) { this.amount = val; } public int amount { get{ return this.AMT; } set{ this.AMT = value; } } static public bool operator < ( Salary first, Salary second ) { bool retval; if ( first.amount < second.amount ) retval = true; else retval = false; return retval; } static public bool operator <= ( Salary first, Salary second ) { bool retval; if ( first.amount <= second.amount ) retval = true; else

Készítette: Zsótér Csaba III. éves informatikus hallgató

160

Page 161: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

retval = false; return retval; } static public bool operator > ( Salary first, Salary second ) { bool retval;

if ( first.amount > second.amount ) retval = true; else retval = false; return retval; } static public bool operator >= ( Salary first, Salary second ) { bool retval; if ( first.amount >= second.amount ) retval = true; else retval = false;

return retval; } public override string ToString() { return( this.amount.ToString() ); }} public class myAppClass{ public static void Main(String[] args) { Salary mySalary = new Salary(24000); Salary yourSalary = new Salary(24000); Salary PresSalary = new Salary(200000);

Console.WriteLine("Original values: "); Console.WriteLine(" my salary: {0}", mySalary); Console.WriteLine(" your salary: {0}", yourSalary); Console.WriteLine(" a Pres' salary: {0}", PresSalary); Console.WriteLine("\n---------------------------\n"); if ( mySalary < yourSalary ) Console.WriteLine("My salary less than your salary"); else if ( mySalary > yourSalary ) Console.WriteLine("My salary is greater than your salary"); else Console.WriteLine("Our Salaries are the same"); if ( mySalary >= PresSalary ) Console.WriteLine("\nI make as much or more than a president."); else

Készítette: Zsótér Csaba III. éves informatikus hallgató

161

Page 162: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

Console.WriteLine("\nI don't make as much as a president."); } }

A logikai műveletek túlterheléseAz „egyenlő” és nem „egyenlő” logikai műveletek túlterhelése nagyobb erőfeszítést igényel. Először is nem tehetjük meg, hogy közülük csak az egyiket terheljük túl. Ha saját változatra van szükségünk az egyikből, meg kell írnunk a másikat is. Ezen kívül, ha túl akarjuk terhelni ezt a két műveletet, akkor szintén túl kell terhelnünk az Equals() és a GetHashCode() tagfüggvényeket is. Akárcsak a ToString, ezek is a közös ős, az Object osztályrészeiként öröklődnek. Ezeket a tagfüggvényeket azért kell egyidejűleg túlterhelni, mert a logikai műveletek a színfalak mögött használják őket.

Ha két azonos osztályba tartozó objektum összehasonlítását akarjuk megoldani, akkor az adott osztály Equals tagfüggvényét kell felülbírálni. Ennek formája a következő:

public override bool Equals (object val){//annak meghatározása, hogy az adott osztályok egyenértékűek-e//visszatérés (true vagy false)}

Ezt a tagfüggvényt arra használjuk, hogy segítségével két azonos típusú objektumot összehasonlítunk. Belül persze megint bármilyen logikát kialakíthatunk. Ez lehet egyetlen érték vizsgálata is.

Ha túl akarjuk terhelni a == és != műveleteket, a GetHashCode tagfüggvényt is felül kell bírálnunk. A GetHashCode olyan egész értéket ad vissza, amely egy adott osztály egy elemét egyértelműen azonosítja. E tagfüggvény kódját általában nem kell megváltoztatnunk. Egyszerűen csak annyi a dolgunk, hogy felülbíráljuk, és a következő kóddal visszaadjuk belőle a kérdéses objektum azonosító kódját:public override int GetHashCode(){

return this.ToString().GetHashCode();}

Miután felülbíráltuk az Equals és a GetHashCode tagfüggvényeket, meg kell valósítanunk a két művelet túlterhelt változatát. Ezt már pontosan ugyanúgy tehetjük meg, mint a többi összehasonlító műveletnél. Az egyetlen eltérés, hogy most az Equals tagfüggvényt kell használnunk a szokásos művelet helyett.

Az Equals tagfüggvény valójában a GetHashCode tagfüggvény visszatérési értékét használja annak eldöntésére, hogy két objektum azonos-e.

3.70 Az „egyenlő” és „nem egyenlő” műveletek túlterhelése using System;using System.Text;

Készítette: Zsótér Csaba III. éves informatikus hallgató

162

Page 163: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

public class Salary{ private int AMT;

public Salary() { this.amount = 0; } public Salary(int val) { this.amount = val; }

public int amount { get{ return this.AMT; } set{ this.AMT = value; } }

public override bool Equals(object val) { bool retval;

if( ((Salary)val).amount == this.amount ) retval = true; else retval = false; return retval; }

public override int GetHashCode() { return this.ToString().GetHashCode(); } static public bool operator == ( Salary first, Salary second ) { bool retval; retval = first.Equals(second); return retval; } static public bool operator != ( Salary first, Salary second ) { bool retval;

retval = !(first.Equals(second)); return retval; } public override string ToString() { return( this.amount.ToString() ); } } public class myAppClass{ public static void Main(String[] args) {

Készítette: Zsótér Csaba III. éves informatikus hallgató

163

Page 164: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

string tmpstring; Salary mySalary = new Salary(24000); Salary yourSalary = new Salary(24000); Salary PresSalary = new Salary(200000); Console.WriteLine("Original values: {0}, {1}, {2}", mySalary, yourSalary, PresSalary); if (mySalary == yourSalary) tmpstring = "equals"; else tmpstring = "does not equal"; Console.WriteLine("\nMy salary {0} your salary", tmpstring); if (mySalary == PresSalary) tmpstring = "equals"; else tmpstring = "does not equal"; Console.WriteLine("\nMy salary {0} a president\'s salary", tmpstring); }}

Összefoglalás: A C# túlterhelhető műveletei+ - ++ -- ! true false + - * / % & | ^ << >>< <= > >= == !=

Összefoglalás: A C# nem túlterhelhető műveletei= . ?: && || new is Sizeof typeof checked unchecked

Nem terhelhetjük túl ezeken kívül a zárójeleket, illetve az összetett műveleteket (+=, -=, stb.). Az összetett műveletek mindig a megfelelő kéttényezős túlterhelt műveleteket használjál.

Az egyedüli műveleti jel, ami még kimaradt, a szögletes zárójel ( [] ). Amint azt már korábban már említettünk, ezt az indexelők terhelik túl.

A .NET alaposztályok eljárásai

Osztályok a .NET környezetbenA .NET környezet számos osztályt, felsorolást, struktúrát, felületet és más adattípust határoz meg – többek ezret, melyek mindegyikét használhatjuk a C# programjainkban.

A közös nyelvleírás (CLS)A környezet osztályait a szerzők a közös nyelvleírás (CLS) szem előtt tartásával készítették el – erről a leírásról már volt szó, a C# futásidejű környezet (CLR) tárgyalásánál.

Készítette: Zsótér Csaba III. éves informatikus hallgató

164

Page 165: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

A CLS valójában egy szabálygyűjtemény, melyet minden, a .NET környezetben futó nyelvnek követnie kell. E szabályok közé tartozik a közös típusrendszer (CTS) használata. Ha egy program e szabályokhoz tartja magát, a közös futásidejű környezet képes futtatni, függetlenül attól, hogy milyen nyelven írták.

A CLS szabályainak követése azzal az előnnyel jár, hogy az egyik nyelven megírt programot így egy másikból is meghívhatjuk. Mivel a környezet eljárásai megfelelnek a CLS szabályainak, nemcsak a C#-ban használhatjuk azokat, hanem más, a CLS-nek megfelelő nyelvekben, így a Visual Basic.NET-ben, valamint a JScript.NET-ben is.

A típusok szervezése névterekkelA környezet kódrészletei névterek szerint csoportosulnak – így végül névterek százaiban osztályok ezrei sorakoznak. Egyes névterek másokon belül állnak. Így, a már megismert DateTime és Random típusok a System névteren belül találhatók, de ugyanitt ráakadhatunk más névterekre is, mint a be- és kimenetért felelős típusok jelentős részét magába foglaló IO, valamint az XML adatok kezelését szolgáló XML. A névterekről bővebben az elektronikus dokumentációból tájékozódhatunk.

Az ECMA szabványok használataNem minden, a névterekben található típus egyeztethető össze az összes nyelvvel, ráadásul más cégek fejlesztőeszközei sem feltétlenül illeszkednek ebbe a rendszerbe. A C# fejlesztésekor a Microsoft rengeteg osztályt bejegyzett a szabványügyi hivatalnál. Ez lehetőséget adott más fejlesztőknek olyan segédeszközök és C# fordítók készítésére, melyek ugyanezeket a névtereket és típusokat használják. Ezért felelnek meg a Microsoft fejlesztőeszközeivel készített programok más cégek eszközeinek.

A C# nyelv és a BCL szabványosítása lehetővé tette, hogy mások is fejlesztőeszközöket – például fordítókat és futásidejű környezeteket – készítsenek a C#-hoz. Számos C# fordító létezik már különböző rendszerekre, ilyen a Mac OS, a FreeBSD és a Linux. Emellett számos kezdeményezés indult, hogy a teljes System névteret, és néhány társát átírják más felületekre, többek között Linuxra. Ennek során a legtöbb esetben az ECMA szabványt használják, valamint azokat az osztályokat, melyeket a Microsoft irányadásként hozott létre újak készítéséhez.

A szabványosított osztályokat a System névtérben találhatjuk, míg más névterekben ilyen szabványosítással nem találkozhatunk. Ha egy osztály nem része a szabványnak, könnyen előfordulhat, hogy nem támogatja minden operációs rendszer és futásidejű környezet, melyet egyébként a C#-pal együttműködésre szántak. A Microsoft SDK több névteret – Microsoft VisualBasic, Microsoft.CSharp, Microsoft JScript és Microsotf.Win32 – is tartalmaz, melyek nem kerültek bele az ECMA szabványba, így ezeket nem feltétlenül támogatja minden fejlesztőkörnyezet. Az ECMA-ról és a C# szabványról bővebb tájékoztatást kaphatunk az Msdn.Microsoft.com/net/ecma címen

Az ECMA mellett a .NET jelentős része az ISO szabványnak is megfelel.

Készítette: Zsótér Csaba III. éves informatikus hallgató

165

Page 166: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

Tájékozódás a környezet osztályairólAz alaposztály-könyvtárban (BCL) osztályok és egyéb típusok ezreivel találkozhatunk. Mielőtt valamire saját programot írnánk, érdemes utánanézni a dokumentációban, nem-e létezik már a feladatot megoldó, előre megírt osztály.

Könyvtár- és rendszeradatok kezeléseA programunkat futtató számítógépekről adatok tömege áll rendelkezésünkre – hogy miként használjuk fel őket, az ránk van bízva. Mindezt az Environment osztály segítségével tehetjük meg. Nézzünk erre egy példát:

3.71 Az Environment osztály használata using System; class EnvApp{ public static void Main() { Console.WriteLine("================================="); Console.WriteLine(" Command: {0}", Environment.CommandLine); Console.WriteLine("Curr Dir: {0}", Environment.CurrentDirectory); Console.WriteLine(" Sys Dir: {0}", Environment.SystemDirectory); Console.WriteLine(" Version: {0}", Environment.Version); Console.WriteLine(" OS Vers: {0}", Environment.OSVersion); Console.WriteLine(" Machine: {0}", Environment.MachineName); Console.WriteLine(" Memory: {0}", Environment.WorkingSet); Console.WriteLine("================================="); string [] args = Environment.GetCommandLineArgs(); for ( int x = 0; x < args.Length; x++ ) { Console.WriteLine("Arg {0}: {1}", x, args[x]); } Console.WriteLine("================================="); string [] drives = Environment.GetLogicalDrives(); for ( int x = 0; x < drives.Length; x++ ) { Console.WriteLine("Drive {0}: {1}", x, drives[x]); } Console.WriteLine("================================="); Console.WriteLine("Path: {0}", Environment.GetEnvironmentVariable("Path")); Console.WriteLine("================================="); }}

Matematikai eljárások használataAz egyszerű matematikai műveletek – mint az összeadás, a kivonás, vagy a maradékképzés – egy ideig kielégítik igényeinket. Eljön azonban a pillanat, amikor már többre vágyunk. Szerencsére a C# alaposztályai számos matematikai eljárást bocsátanak rendelkezésünkre, melyek a System.Math névtérben találhatók.

Készítette: Zsótér Csaba III. éves informatikus hallgató

166

Page 167: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

A Math lezárt osztály, ami, ha emlékszünk rá, azt jelenti, hogy az öröklésben nem vehet részt. Ráadásképpen, minden itt használatos osztály ás adattag statikus, így nem készíthetünk Math típusú objektumot sem; az adattagokat és tagfüggvényeket tehát az osztály nevével kell megadnunk.

Matematikai eljárások a Math osztályban Tagfüggvény neve Visszatérési értékeAbs A szám abszolút értéke.Ceiling A legkisebb egész, ami nem kisebb a megadott számnál.Exp E a megadott kitevőre emelve – fordítottja a Log függvényFloor A legnagyobb egész, ami nem nagyobb a megadott számnál.IEEERemainder Két megadott szám osztásának maradéka. (Ez az osztási művelet

megfelel az ANSI / IEEE 754-1985 szabványa 5.1. bekezdésének: IEEE Standard for Binary Floating-Point Arithmetic; Institute of Electrical and Electonics Engineering, Inc; 1985.)

Log A megadott szám természetes logaritmusa.Log10 A megadott szám tízes alapú logaritmusa.Max Két érték nagyobbika.Min Két érték kisebbike.Pow Megadott szám megadott kitevőjű hatványaRound A megadott szám kerekített értéke. A kerekítés pontosságát

meghatározhatjuk; .5-nél lefelé kerekítünk.Sign A megadott szám előjelét jelző érték. Negatív számok esetén -1,

nullánál 0, pozitív számok esetén 1.Sqrt A megadott érték négyzetgyöke.Acos Az a szög, amelynek koszinusza megegyezik a megadott értékkel.Asin Az a szög, amelynek szinusza megegyezik a megadott értékkel.Atan Az a szög, amelynek tangense megegyezik a megadott értékkel.Atan2 Az a szög, amelynek tangense megegyezik a két megadott érték

hányadosával.Cos A megadott szög koszinusza.Cosh A megadott szög koszinusz hiperbolikusza.Sin A megadott szög szinusza.Sinh A megadott szög szinusz hiperbolikusza.Tan A megadott szög tangense.Tanh A megadott szög tangens hiperbolikusza.

A Math osztályban találhatunk még két állandót is, ezek a PI és az E.

FájlkezelésA fájlok használatának lehetősége sokat lendíthet programunk használhatóságán – vonatkozhat ez saját fájljaink írására és olvasására, vagy más fájlok kezelésére. A következőkben lerakjuk a fájlkezelés alapjait, megismerkedünk a folyamok fogalmával.

Készítette: Zsótér Csaba III. éves informatikus hallgató

167

Page 168: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

A fájlok kezeléséért a System.IO névtérben található File alaposztály felel. Itt számos statikus tagfüggvényt találhatunk a fájlkezeléshez (minden itt fellelhető tagfüggvény statikus.).

Fájlkezelési tagfüggvények Tagfüggvény LeírásAppendText Szöveget fűz a fájlhoz.Copy Új fájlt készít egy meglévő alapján.Create Új fájlt készít a megadott helyen.CreateText Új fájlt készít, amely alkalmas szöveg tárolásáraDelete Törli az adott helyen található fájlt. Ennek léteznie kell, ellenkező

esetben kivétel keletkezik.Exists Meghatározza, hogy adott helyen létezik-e a megadott file.GetAttributes Tájékoztat a fájl tulajdonságairól.GetCreationTime Megadja a fájl létrehozásának dátumát és idejét.GetLastAccessTime Megadja a fájl legutóbbi elérésének dátumát és idejét.GetLastWriteTime Megadja a legutóbbi fájlba írás dátumát és idejét.Move Lehetővé teszi, hogy fájlt áthelyezzünk, illetve megváltoztassuk a

nevét.Open Megnyitja az adott helyen található fájlt. Ezzel írhatunk bele, illetve

olvashatunk belőle.OpenRead Megnyit egy fájlt olvasásra.OpenWrite Megnyit egy fájlt írásra.OpenText Megnyit egy fájlt, melynek tartalma szövegként értelmezhető.SetAttributes Beállítja a megadott fájl tulajdonságait.SetCreationTime Beállítja a fájl létrehozásának dátumát és idejét.SetLastAccessTime Beállatja az utolsó elérés dátumát és idejét.SetLastWriteTime Beállítja az utolsó módosítása dátumát és idejét.

3.72 Fájl másolása using System;using System.IO;

class FileCopy{ public static void Main() { string[] CLA = Environment.GetCommandLineArgs();

if ( CLA.Length < 3 ) { Console.WriteLine("Format: {0} orig-file new-file", CLA[0]); } else { string origfile = CLA[1]; string newfile = CLA[2];

Console.Write("Copy...."); try

Készítette: Zsótér Csaba III. éves informatikus hallgató

168

Page 169: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

{ File.Copy(origfile, newfile); }

catch (System.IO.FileNotFoundException) { Console.WriteLine("\n{0} does not exist!", origfile); return; } catch (System.IO.IOException) { Console.WriteLine("\n{0} already exists!", newfile); return; } catch (Exception e) { Console.WriteLine("\nAn exception was thrown trying to copy file."); Console.WriteLine(e); return; } Console.WriteLine("...Done"); } }}

A fájlok adataiA fájlok kezelésében a File mellett a FileInfo osztály is segítségünkre lehet. Használatát a következő kódsor mutatja be:

3.73 A FileInfo osztály alkalmazása using System;using System.IO;

class FileSize{ public static void Main() { string[] CLA = Environment.GetCommandLineArgs(); FileInfo fiExe = new FileInfo(CLA[0]);

if ( CLA.Length < 2 ) { Console.WriteLine("Format: {0} filename", fiExe.Name); } else { try { FileInfo fiFile = new FileInfo(CLA[1]);

if(fiFile.Exists)

Készítette: Zsótér Csaba III. éves informatikus hallgató

169

Page 170: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

{ Console.WriteLine("==================================="); Console.WriteLine("{0} - {1}", fiFile.Name, fiFile.Length ); Console.WriteLine("==================================="); Console.WriteLine("Last Access: {0}", fiFile.LastAccessTime); Console.WriteLine("Last Write: {0}", fiFile.LastWriteTime); Console.WriteLine("Creation: {0}", fiFile.CreationTime); Console.WriteLine("==================================="); } else { Console.WriteLine("{0} doesn't exist!", fiFile.Name); } } catch (System.IO.FileNotFoundException) { Console.WriteLine("\n{0} does not exist!", CLA[1]); return; } catch (Exception e) { Console.WriteLine("\nAn exception was thrown trying to copy file."); Console.WriteLine(e); return; } } }}

Egyszerű adatfájlok kezeléseJól jöhet, ha ismerjük a fájlok másolásának módját, és az sem haszontalan, ha hozzá tudunk jutni a jellemzőikhez, de igazi hasznukat akkor vesszük, ha megtanuljuk, miként írjuk és olvassuk tartalmukat. Mindehhez azonban egy új fogalommal is meg kell ismerkednünk, a C#-ban ugyanis e műveletekhez általában folyamokat használunk.

Ismerkedés a folyamokkalA fájl kifejezést többnyire a lemezen vagy a memóriában tárolt adatokkal kapcsolatban használjuk. Ha pedig ezekkel műveleteket végzünk, folyamokat alkalmazunk. A folyam adatáramlást jelent, amely nem kell, hogy szöveges legyen, és nem szükséges fájlhoz tartoznia sem.

A folyamokat használhatjuk a memóriából, hálózatról, a Világhálóról, egy karakterláncból, vagy máshonnan származó adatok küldésére és fogadására. Emellett gyakran alkalmazzuk őket adatfájlok ki- és bemeneti műveleteiben.

A fájlolvasás meneteHa írunk egy fájlba, vagy olvasunk belőle, egy meghatározott műveleti sorrendet kell követnünk. Először meg kell nyitni a fájlt. Ha új fájlt hozunk létre, ez általában együtt jár a megnyitással. Ha ezzel elkészültünk, egy folyamra van szükségünk ahhoz, hogy az adatokat a fájlban elhelyezzük, vagy kiolvassuk onnan. A folyam létrehozásakor meg kell adnunk az

Készítette: Zsótér Csaba III. éves informatikus hallgató

170

Page 171: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

adatok áramlásának irányát, és hozzá kell rendelnünk a fájlhoz. Ekkor kezdődhet csak meg az olvasás, illetve az írás. Előbbi esetben megeshet, hogy a fájl végére is ügyelnünk kell. Ha elkészültünk az írással vagy az olvasással, be kell zárnunk a fájlt.

Fájlok létrehozása és megnyitásaA folyamoknak különböző típusai léteznek – hogy melyiküket használjuk, az a fájlban található adatok típusától függ. Most a szöveges adatok írására és olvasására összpontosítunk, de hamarosan sorra kerülnek a bináris adatok is.

Egy lemezen található fájlt szöveges olvasásra, illetve írásra könnyen megnyithatunk akár a File, akár a FileInfo osztály segítségével. Mindkettő számos tagfüggvényt bocsát rendelkezésünkre.

Fájlfüggvények szöveges olvasásra és írásra. Tagfüggvény LeírásAppendText Megnyit egy fájlt, melyhez így szöveget fűzhetünk (ebben egy

StreamWriter lesz segítségünkre.)Create Új fájlt hoz létre.CreateText Új fájlt készít, és megnyitja szöveges használatra (valójában egy

StreamWriter folyamot hoz létre).Open Megnyit egy fájlt írásra vagy olvasásra (valójában egy FileStream

objektumot nyit meg).OpenRead Megnyit egy fájlt olvasásra.OpenText Megnyit egy már létező fájlt olvasásra (Ehhez egy StreamReader

folyamot készít).OpenWrite Megnyit egy fájlt írásra és olvasásra.

A File és a FileInfo osztály minden hasonlóság mellett különbözik egymástól. A File kizárólag statikus tagfüggvényeket tartalmaz, ráadásul automatikusan ellenőrzi a fájlhoz tartozó jogosultságokat. A FileInfo osztályt ugyanakkor példányok készítésére használjuk. Ha tehát egy fájlt egyszer nyitunk meg, nyugodtan használhatjuk a File osztályt, ha azonban erre többször is szükségünk lehet a programban, érdemes a FileInfo osztályt igénybe venni.

Szöveg fájlba írásaA fájlkezelés megérésének egyik leghatékonyabb módja, ha az elmélet után rögtön egy példaprogramot nézünk meg:

3.74 Szöveg fájlba írása using System;using System.IO;

public class Writing{ public static void Main(String[] args) { if( args.Length < 1 ) { Console.WriteLine("Must include file name.");

Készítette: Zsótér Csaba III. éves informatikus hallgató

171

Page 172: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

} else { StreamWriter myFile = File.CreateText(args[0]); myFile.WriteLine("Mary Had a Little Lamb,"); myFile.WriteLine("Whose Fleece Was White as Snow."); for ( int ctr = 0; ctr < 10; ctr++ ) myFile.WriteLine ("{0}", ctr); myFile.WriteLine("Everywhere that Mary Went,"); myFile.WriteLine("That Lamb was sure to go."); myFile.Close(); } }}

Szöveg olvasása fájlbólA szövegfájlok olvasása hasonlóan zajlik írásokhoz. Ez előbb készített szöveget fogjuk kiolvasni a megadott fájlból:

3.75 Szövegfájl olvasása using System;using System.IO;

public class Reading{ public static void Main(String[] args) { if( args.Length < 1 ) { Console.WriteLine("Must include file name."); } else { string buffer;

StreamReader myFile = File.OpenText(args[0]);

while ( (buffer = myFile.ReadLine()) != null ) { Console.WriteLine(buffer); }

myFile.Close(); } }}

Bináris adatok fájlba írásaHa szöveg fájlokat használunk, a számokat szöveggé kell alakítanunk és vissza, pedig sokszor jól jönne, ha az értékeket közvetlenül tudnánk tárolni és kiolvasni. Így például, ha egy

Készítette: Zsótér Csaba III. éves informatikus hallgató

172

Page 173: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

csokornyi egész számot közvetlenül a fájlba tudnánk írni, egészként is olvashatnánk ki – de ha szövegként tároljuk, a később kiolvasott értékeket egyenként át kellene alakítanunk karakterláncról egész számmá. Szerencsére ennyi körülményeskedésre nincs szükség, hiszen alkalmazhatunk egy bináris folyamot (BinaryStream), melyen keresztül bináris adatokat vihetünk át.

Bináris adatok jellemzője, hogy megtartják az adattípus eredeti tárolási formáját, nem alakítják szöveggé.

3.76 Adatok bináris fájlba írása using System;using System.IO;

class MyStream{ public static void Main(String[] args) { if( args.Length < 1 ) { Console.WriteLine("Must include file name."); } else { FileStream myFile = new FileStream(args[0], FileMode.CreateNew); BinaryWriter bwFile = new BinaryWriter(myFile);

for (int i = 0; i < 100 ; i++) { bwFile.Write(i ); }

bwFile.Close(); myFile.Close(); } }}

A FileMode értékei Érték LeírásaAppend Megnyit egy létező fájlt, vagy újat készít.Create Új fájlt készít. Ha a fájl már létezik, a program törli, és új fájlt hoz létre

helyette.CreateNew Új fájlt hoz létre. Ha a fájl már létezik, kivételt vált ki.Open Megnyit egy létező fájlt.OpenOrCreate Megnyit egy fájlt, vagy – ha nem létezik – létrehozza. Truncate Megnyit egy létező fájlt, és törli a tartalmát.

Bináris adatok olvasása fájlokbólA bináris adatok írása után természetesen merül fel az igény olvasásukra.:

3.77 Bináris adatok olvasása using System;using System.IO;

Készítette: Zsótér Csaba III. éves informatikus hallgató

173

Page 174: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

class BinReader{ public static void Main(String[] args) { if( args.Length < 1 ) { Console.WriteLine("Must include file name."); } else { FileStream myFile = new FileStream(args[0], FileMode.Open); BinaryReader brFile = new BinaryReader(myFile);

// Read data Console.WriteLine("Reading file...."); while( brFile.PeekChar() != -1 ) { Console.Write("<{0}> ", brFile.ReadInt32()); }

Console.WriteLine("....Done Reading.");

brFile.Close(); myFile.Close(); } }}

Windowsalkalmazások készítése

Windows ablakok készítéseNapjainkban a legtöbb operációs rendszer eseményvezérelt programokat és ablakokat használ a felhasználókkal való kapcsolattartásra. Ha már fejlesztetünk Microsoft Windows rendszeren, nyilván találkoztunk a Win32 könyvtáraiban az ablakok készítését segítő eljárásokkal. A .NET környezet alap-osztály könyvtárában is szintén létezik egy hasonló, az ablakokkal kapcsolatos osztálycsalád. Ez utóbbinak nagy előnye, hogy bármely, a környezetbe tartozó programnyelvből elérhető. Emellett készítésénél odafigyeltek arra, hogy használata, és így az ablak alapú alkalmazások készítése könnyebb legyen. Mindemellett, ahogy a .NET környezet és futásidejű rendszer más felületekre is átírják, programjaink egy csapásra hordozhatóvá válnak.

Készítette: Zsótér Csaba III. éves informatikus hallgató

174

Page 175: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

Ha programunkban ablakot szeretnénk használni, egy osztályt kell készítenünk, amely a Form osztályból öröklődik. Ez utóbbi a System.Windows.Forms névtérben található meg. Használatát egy igen egyszerű példán mutatjuk meg:

4.1 Egyszerű ablak using System.Windows.Forms; public class FirstFrm : Form{ public static void Main( string[] args ) { FirstFrm frmHello = new FirstFrm(); Application.Run(frmHello); }}

Láthatjuk hogy ez igen rövidke program, különösen ahhoz képest, mennyi mindent tesz. Ahhoz persze, hogy lássuk, előbb le kell fordítanunk – ennek mikéntjével foglalkozunk a következőkben.

Fordítási beállításokAz előző programocska fordításánál a korábbiaktól eltérően kell eljárnunk. Előfordulhat, hogy a parancssorban hivatkoznunk kell egyes, a programban használt alaposztályokra.

Az ablakosztályok a System.Windows.Forms.dll nevű úgynevezett szerelvényben (assembly) találhatók, programunk fordításakor tehát erre kell hivatkoznunk. A kódszöveg elejére biggyesztett using utasítással valójában nem építünk be egyetlen fájlt sem a programunkba, ez csak hivatkozást ad a fájlban tárolt névtér egy pontjára. Amint azt a korábbiakban láthattuk, mindez lehetővé teszi, hogy az osztályoknak ne kelljen a teljes nevét kiírnunk.

A legtöbb ablakkal kapcsolatos vezérlő és lehetőség ebben a szerelvényben található. Fordításkor, hogy biztosak legyünk a használatban, egy hivatkozást használhatunk a parancssorban. Ha a Microsoft parancssori fordítóját használjuk, az egyszerű fordítási utasításhoz a /reference: fájlnév kapcsolót kell hozzáfűznünk, ahol a fájlnév a szerelvény nevét jelenti. Ha tehát az ablakok szerelvényét használva szeretnénk lefordítani az előző programot, a következőt kell a parancssorba írnunk:

csc /reference:System.Windows.Forms.dll Form1.cs

A /reference: helyett használható a rövidebb /r:alak is. Ha végrehajtjuk a parancsot, a fordítás így is rendben lefut .A Microsoft .NET környezet 1.1-es és későbbi változatainak C# fordítói automatikusan alkalmaznak egyes hivatkozásokat, köztük a System.Windows.Forms.dll-t is.

A kapott eredmény a fordítás után:

Készítette: Zsótér Csaba III. éves informatikus hallgató

175

Page 176: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

Nos pontosan ezt szerettük volna! Vagy mégsem? Ha a programot közvetlenül egy operációs rendszeren – mondjuk Microsoft Windowson – futtatjuk, az eredmény kissé eltérő lesz. Ilyenkor ugyanis a képernyőn marad a parancssor ablak is, ami pedig csak púp a hátunkon.

Ahhoz hogy eltüntessük a nem kívánt ablakot, el kell árulnunk a fordítónak, hogy programunkat Windows rendszerbeli használatra szánjuk. Erre a /target: (rövidítve /t:) kapcsoló használható, méghozzá a winexe beállítással. Tehát a következőt kell a parancssorba írnunk, ha a kívánt eredményt szeretnénk:

csc /r:System.Windows.Forms.dll /t:winexe From1.cs

Ha most elindítjuk a programot, a képernyőn nem jelenik meg a parancssor ablaka.

Az Application.Run tagfüggvény működéseA Windows alkalmazások eseményvezérelt programok, melyek általában egy vezérlőket tartalmazó ablakot jelenítenek meg. Ezt követően a program egy ciklusba kerül, ami

Készítette: Zsótér Csaba III. éves informatikus hallgató

176

Page 177: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

mindaddig fut, amíg a felhasználó valamit nem tesz az ablakon, illetve az ablakos környezetben. A tettek üzenetek keletkezésével járnak, ezek pedig eseményeket váltanak ki. Ha létezik az üzenethez eseménykezelő, a rendszer végrehajtja, egyébként a ciklus fut tovább.

Úgy látszik, mintha a ciklus soha nem érne véget. Nos, magától nem is, de egy esemény befejezheti a program futását. Az alapablak, melynek osztályától (Form) öröklünk, tartalmazza a bezárás vezérlőjét, valamint a parancsmenüben egy Bezárás (Close) pontot – ezek a vezérlők olyan eseményt válthatnak ki, melyek bezárják az ablakot, és befejezik a programot.

Mindezek fényében bizonyára rájöttünk már, mire is jó az Application osztály, vagy még inkább ennek Run tagfüggvénye. Feladata a ciklus fenntartása és a program működésének biztosítása, míg a futás végét jelző esemény be nem következik. Erre akkor kerül sor, ha a felhasználó megnyomja a Close gombot vagy a parancsmenü Close pontját választja.

Az Application osztály Run tagfüggvénye kezel minden, a programfutás közben létrejövő üzenetet. Ezek származhatnak az operációs rendszertől, alkalmazásoktól, vagy más, a rendszerben futó programoktól. Ha egy üzenet megfelel valamelyik eseménykezelőnek, a rendszer végrehajtja azt. Ha nincs alkalmas eseménykezelő, a rendszer figyelmen kívül hagyja az üzenetet.

Ablakok testreszabásaAz előzőekben egy egyszerű ablakot mutattunk be. A Form osztályhoz azonban számos tulajdonság, tagfüggvény és esemény tartozik – túl sok is ahhoz, hogy mindegyikükről szót ejtsünk. Nem kell azonban aggódnunk, a dokumentációban minden kérdésünkre megtaláljuk a választ.

Készítette: Zsótér Csaba III. éves informatikus hallgató

177

Page 178: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

Az előző példában egy egyszerű, üres ablakot készítettünk. A következőkben is ezzel az üres ablakkal dolgozunk tovább, de minden példával egyre jobban beleszólunk a működésébe.

A kódban eleve elérhető néhány elem, így a vezérlőmenü, valamint a Minimize, Maximize és a Close gombok a címsoron. Ezek ki- vagy bekapcsolását tulajdonságaik segítségével szabályozhatjuk:

ControlBox Meghatározza, hogy megjelenítsük-e a vezérlőmenüt.HelpButton Jelzi, hogy a súgógomb megjelenik-e az ablak címsorában. E

megjelenésre csak akkor van mód, ha a MaximizeBox és a MinimizeBox értéke egyaránt false.

MaximizeBox Jelzi, hogy megjelenik-e a Maximize gomb.MinimizeBox Jelzi, hogy megjelenik-e a Minimize gomb.Text Az ablak címsorának szövegét tartalmazza.

A fenti értékek hatással lehetnek egymásra is. Így a HelpButton értéke például csak akkor lehet true, ha a MaximizeBox és a MinimizeBox értéke egyaránt false.

4.2 Az ablak címsorának tulajdonságai using System.Windows.Forms; public class FormApp : Form { public static void Main( string[] args ) { FormApp frmHello = new FormApp(); frmHello.MinimizeBox = true; frmHello.MaximizeBox = false; frmHello.HelpButton = true; frmHello.ControlBox = true; frmHello.Text = @"My Form's Caption"; Application.Run(frmHello); }}

Készítette: Zsótér Csaba III. éves informatikus hallgató

178

Page 179: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

Észre vehetjük, hogy a Help gomb megjelenítéséről is gondoskodtunk, azzal, hogy értékét true-re állítottuk. A képernyőn ez azért nem érezteti hatását, mert e gomb csak akkor jelenhet meg, hogy ha a MaximizeBox és a MinimizeBox értéke egyaránt false. Ha átírjuk a kódot a következőre:

frmHello.MinimizeBox = false; frmHello.MaximizeBox = false; frmHello.HelpButton = true; frmHello.ControlBox = true;

Akkor, ha újra fordítjuk a Help gomb megjelenik:

Készítette: Zsótér Csaba III. éves informatikus hallgató

179

Page 180: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

Létezik egy további kombináció, amiről érdemes szólnunk. Ha a ControlBox tulajdonságot false-ra állítjuk, mind a Close, mind a vezérlőmenü eltűnik. Sőt, ha a ControlBox, a MinimizeBox és a MaximizeBox értéke egyaránt false, és a címfeliratnak nincs szövege, a címsor is eltűnik. Ekkor az ablakot az Alt+F4 billentyűkkel zárhatjuk be:

Ez lesz a program futásának eredménye.

Ablakok átméretezéseAz ablakok következő lényeges tulajdonságai a mérete és az alakja. Ezek módosítására számos tulajdonságot és tagfüggvényt használhatunk:

A Form osztály méretezési eszközei. AutoScale Beállításával az ablak automatikusan átméretezi magát, a rajta

használt betűtípusnak és vezérlőknek megfelelően.AutoScaleBaseSize Az automatikus méretezés alapmérete.AutoScroll Beállítása esetén az ablak automatikus görgetési képességgel

rendelkezik.AutoScrollMargin Az automatikus görgetéshez használt margó mérete.AutoScrollMinSize Az automatikus görgetéskor látható terület legkisebb mérete.AutoScrollPosition Az automatikus görgetéskor látható terület helyzete.ClientSize Az ablak belső területének („ügyfélterületének”) mérete.DefaultSize Védett tulajdonság, amely az ablak alapértelmezett méretét adja

meg.DesktopBounds Az ablak helye és mérete.DesktopLocation Az ablak helye.Height Az ablak magassága.MaximizeSize Az ablak legnagyobb lehetséges mérete.MinimizeSize Az ablak legkisebb lehetséges mérete.Size Az ablak mérete. A Size objektumban a set és a get

utasításokkal egy x és y értéket érhetünk el. Készítette: Zsótér Csaba III. éves informatikus hallgató

180

Page 181: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

SizeGripStyle A méretező fogantyú típusa. A SizeGripStyle felsoroló

típus egy értéke, ami Auto (automatikusan megjelenik, ha szükség van rá), Hide (rejtett), vagy Show (mindig látható) lehet.

StartPosition Az ablak kiindulási helyzete. A FormStartPosition felsoroló típus egyik értéke, ami CenterParent (középre igazítva a szülőablakon), CenterScreen (középre igazítva a képernyőn), Manual (a helyet és a méretet a kiindulási helyzettel adhatjuk meg), WindowsDefaultBounds (alapértelmezett helyzet), vagy WindowsDefaultLocation (alapértelmezett helyzet, megadott méretekkel) lehet.

Width Az ablak szélessége.

4.3 Ablak átméretezése using System.Windows.Forms;using System.Drawing; public class FormSize : Form{ public static void Main( string[] args ) { FormSize myForm = new FormSize(); myForm.Text = "Form Sizing"; myForm.Width = 400; myForm.Height = 100; Point FormLoc = new Point(200,350); myForm.StartPosition = FormStartPosition.Manual; myForm.DesktopLocation = FormLoc; Application.Run(myForm); }}

A program futásának eredménye a következő lesz:

Az ablak méretének módosítása egyszerű, a Width és Height tulajdonságok beállításával. Az ablak helyének beállítása már több odafigyelést igényel. Látható, hogy egy Point objektumot készítünk, ami az ablak kívánt helyzetének koordinátáit tartalmazza. Ezt az objektumot használjuk ki a DesktopLocation tulajdonságnál. Ahhoz, hogy a Point objektumra ilyen egyszerűen hivatkozzunk, használatba kell vennünk a System.Drawing névteret. Készítette: Zsótér Csaba III. éves informatikus hallgató

181

Page 182: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

Az ablak színeinek és hátterének megváltoztatásaAz ablak háttérszínét a BackColor tulajdonsággal állíthatjuk be. A színeket a System.Drawing névtér Color struktúrájából vehetjük:

A szín beállítása egyszerű: csak rendeljük hozzá a tulajdonsághoz egy értéket:

myForm.BackColor = Color.HotPink;

Az ablak színének megadásához hasonló fontossággal bír a háttérkép beillesztése. Ezt a BackGroundImage tulajdonság beállításával tehetjük meg:

4.4 Háttérkép használata using System.Windows.Forms;using System.Drawing; public class PicForm : Form{ public static void Main( string[] args ) { PicForm myForm = new PicForm(); myForm.BackColor = Color.HotPink; myForm.Text = "PicForm - Backgrounds"; if (args.Length >= 1) { myForm.BackgroundImage = Image.FromFile(args[0]); Size tmpSize = new Size(); tmpSize.Width = myForm.BackgroundImage.Width; tmpSize.Height = myForm.BackgroundImage.Height; myForm.ClientSize = tmpSize;

myForm.Text = "PicForm - " + args[0]; } Application.Run(myForm); }}

Készítette: Zsótér Csaba III. éves informatikus hallgató

182

Page 183: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

Ez a program egy képet jelenít meg az ablak hátterében, melyet a parancssorban adhatunk meg. Ha ezt nem tesszük meg, a háttér egyszínű rózsaszín lesz (HotPink). A parancssor a következő:

PicForm Bömbi5.jpg

Az ablak szegélyének módosításaAz ablakszegély megadása nem csak az ablak megjelenésére van hatással; ez határozza meg a méretezhetőséget is. A szegély módosításához a Form osztály BorderStyle tulajdonságát használhatjuk, amely a FormBorderStyle felsorolás valamelyik értékét veheti fel:

A FormBorderStyle felsorolás értékei Értek LeírásaFixed3D Az ablak rögzített (nem méretezhető), szegélye pedig térhatású.FixedDialog Az ablak rögzített (nem méretezhető), szegélye pedig vastag.FixedSingle Az ablak rögzített (nem méretezhető), szegélye pedig egyetlen

vonalból áll.FixedToolWindow Az ablak rögzített (nem méretezhető), szegélye pedig

eszköztárszegély.None Az ablak nem rendelkezik szegéllyel.Sizeable Az ablak átméretezhető.SizeableToolWindow Az ablak méretezhető eszköztárszegéllyel rendelkezi.

4.5 Ablak szegélyének módosítása. using System.Windows.Forms;using System.Drawing; public class BorderForm : Form{ public static void Main( string[] args ) { BorderForm myForm = new BorderForm();

Készítette: Zsótér Csaba III. éves informatikus hallgató

183

Page 184: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

myForm.BackColor = Color.SteelBlue; myForm.Text = "Borders"; myForm.FormBorderStyle = FormBorderStyle.Fixed3D; Application.Run(myForm); }}

Amint az ábra is mutatja, ablakunk szegélye rögzített. Ha futás közben megpróbálnánk átméretezni, nem járnánk sikerrel.

Vezérlők elhelyezése az ablakonMindezidáig csak az ablakok külsejével foglalkoztunk, tartalom, vagyis vezérlők nélkül azonban egy ablak mit sem ér. A vezérlő lehet gomb, listamező, szövegmező, kép, vagy akár egyszerű szöveg. Beillesztése a legegyszerűbben olyan grafikus fejlesztőeszközök esetében lehetséges, mint a Microsoft Visual C#.NET vagy a SharpDevelop. Itt ugyanis egyszerűen megragadhatjuk a vezérlőt és a kívánt helyre húzhatjuk az ablakon. Ezzel együtt a fejlesztőkörnyezet elhelyezi programunkban az ablak működéséhez szükséges alapvető kódot is.

Persze nincs feltétlenül szükség grafikus fejlesztőeszközre, és ha ilyet is használunk, akkor is érdemes tudnunk, hogy az egyszerű műveletek mögött pontosan mi is zajlik. Néhány szabványos vezérlőt a következő táblázatban soroltunk fel:

A BCL néhány szabványos vezérlője. Button CheckBox CheckedListBox ComboBoxContainerControl DataGrid DateTimePicker DomainUpDownForm GroupBox HScrollBar ImageListLabel LinkLabel ListBox ListViewMonthCalendar NumericUpDown Panel PictureBoxPrintReviewControl ProgressBar ProperyGrid RadioButton

Készítette: Zsótér Csaba III. éves informatikus hallgató

184

Page 185: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

RichTextBox ScrollableControl Splitter StatusBarStatusBarPanel TabControl TabPage TabStripTextBox Timer ToolBar ToolBarButtonToolTip TrackBar TreeView VScrollBarUserControl

A táblázatban látható vezérlők meghatározása a System.Windows.Forms névtérben található.

Címkék és szövegmegjelenítésA Label vezérlő segítségével szövegrészleteket jeleníthetünk meg a képernyőn. Ahhoz, hogy a vezérlőt az ablakra helyezzük, előbb létre kell hoznunk, majd pedig testreszabni a tulajdonságai és tagfüggvényei segítségével. Ha elvégeztük a kívánt beállításokat, elhelyezhetjük a vezérlőt az ablakon.

A címke olyan vezérlő, amely adatokat közöl a felhasználóval, de nem teszi lehetővé, hogy az módosítsa a tartalmát (a programozó persze megteheti). A címke létrehozása ugyanúgy történik, mint más objektumoké:

Label myLabel = new Label();

Most már van egy üres címkénk, amit elhelyezhetünk az ablakon. Ehhez az ablak Controls tulajdonságának Add tagfüggvényét kell igénybe vennünk:

myForm.Control.Add(myLabel);

Ha más vezérlőre van szükség, ennek nevét egyszerűen helyettesítsük be a myLabel helyére. Nézzünk egy példát erre:

4.6 Címke használata. using System;using System.Windows.Forms;using System.Drawing;

public class ControlApp : Form{ public static void Main( string[] args ) { ControlApp myForm = new ControlApp();

myForm.Text = Environment.CommandLine; myForm.StartPosition = FormStartPosition.CenterScreen;

Label myDateLabel = new Label(); Label myLabel = new Label();

myLabel.Text = "This program was executed at:"; myLabel.AutoSize = true; myLabel.Left = 50; myLabel.Top = 20;

DateTime currDate = DateTime.Now;;

Készítette: Zsótér Csaba III. éves informatikus hallgató

185

Page 186: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

myDateLabel.Text = currDate.ToString();

myDateLabel.AutoSize = true; myDateLabel.Left = 50 + myLabel.PreferredWidth + 10; myDateLabel.Top = 20;

myForm.Width = myLabel.PreferredWidth + myDateLabel.PreferredWidth + 110;

myForm.Height = myLabel.PreferredHeight+ 100;

myForm.Controls.Add(myDateLabel); myForm.Controls.Add(myLabel); Application.Run(myForm); }}

A program futásának eredménye:

Gombok használataA Windows alkalmazások legismertebb vezérlői közé tartoznak a gombok, melyek készítésére a Button osztály szolgál. A gombok annyiban térnek el a címkéktől, hogy általában szeretnénk, hogy valami bekövetkezzen, amikor a felhasználó rájuk kattint.

Mielőtt a gombok működésének részleteibe bocsátkoznánk, érdemes pár percig elidőznünk készítésüknél és kirajzolásuknál. A címkékhez hasonlóan az első lépés itt is a példányosítás.

Button myButton = new Button();

Ha létrehoztuk a gombot, tulajdonságai segítségével testreszabhatjuk megjelenését. A Label vezérlőkhöz hasonlóan itt is túlságosan sok tulajdonság, adattag és tagfüggvény áll rendelkezésünkre, hogy mindegyiket bemutassuk, azonban néhányat a következő táblázatban sorolunk fel, rövid leírásukkal:

A gombok néhány tulajdonsága Tulajdonság LeírásaBackColor A gomb háttérszínének beállítása vagy kiolvasása.BackgroundImage A gomb háttérképének beállítása vagy kiolvasása.Bottom A gomb alja és a gombot tartalmazó tároló teteje közti távolság.Enabled A gomb engedélyezését jelző érték beállítása vagy kiolvasása.Height A gomb magasságának beállítása vagy kiolvasása.Image A gomb előterében elhelyezett kép beállítása van kiolvasása.

Készítette: Zsótér Csaba III. éves informatikus hallgató

186

Page 187: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

Left A gomb bal oldala helyzetének beállítása vagy kiolvasása.Right A gomb jobb oldalának beálltása vagy kiolvasása.Text A gomb feliratának beállítása vagy kiolvasása.TextAlign A gomb felirata igazításának beállítása vagy kiolvasása.Top A gomb teteje helyzetének beállítása vagy kiolvasása.Visible A gomb láthatóságát jelző érték beállítása vagy kiolvasása.Width A gomb szélességének beállítása vagy kiolvasása .

Ha tüzetesebben szemügyre vesszük a táblázatban található értékeket, észre vehetjük, hogy néhányukat már használtuk a címkéknél. Nos, e hasonlóságnak oka van – minden vezérlő egy általánosabb, Control nevezetű osztályból származik. Ez teszi lehetővé, hogy minden vezérlő ugyanazokat a tagfüggvényeket és neveket használja ugyanazon feladatok elvégzésére. Így például a Top mindig a vezérlő tetejét adja meg, legyen az gomb, szövegmező, vagy más egyéb.

GombeseményekNe feledjük azonban, hogy a gombok használatának igazi értelme, hogy segítségükkel műveleteket indíthatunk el – ehhez pedig eseményeket használunk.

Miután egy gombot létrehoztunk, eseményeket rendelhetünk hozzá. Először el kell készítenünk az esemény kezelésére szolgáló tagfüggvényt, melyet a program az esemény bekövetkezésekor hív meg. Amint a korábbiakban megtanultuk, ez a tagfüggvény két paramétert kell fogadjon: az eseményt kiváltó objektum nevét, valamint egy System.EventArgs változót. A tagfüggvénynek ezen kívül védettnek kell lennie, és void típusúnak. A meghatározás a következőképpen fest:

protected void tagfüggvényNév (object sender, System.EventArgs paraméterek)

Ha ablakokkal dolgozunk, a tagfüggvények nevét többnyire az eseményt kiváltó vezérlő és az esemény nevéből állítjuk össze. Például, ha az ABC gombra kattintunk, az eseménykezelő neve lehet ABC_Click.

Az esemény kiváltásához össze kell kötnünk az eseményt a megfelelő képviselővel. Az ablakok eseményeit a System.EventHandler képviselő objektum felügyeli, így ha ezt rendeljük eseménykezelőinkhez, hívásuk a megfelelő időben következik be. A hozzárendelés a következőképpen történhet:

VezérlőNév.Esemény += new System.EventHandler(this.tagfüggvényNév);

Nézzük meg az előző programot úgy, hogy egy gombot helyezünk el, amely megnyomásakor frissíti az időt:

4.7 Gombok és események használata using System;using System.Windows.Forms;using System.Drawing;

Készítette: Zsótér Csaba III. éves informatikus hallgató

187

Page 188: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

public class ButtonApp : Form{ private Label myDateLabel; private Button btnUpdate; public ButtonApp() { InitializeComponent(); } private void InitializeComponent() { this.Text = Environment.CommandLine; this.StartPosition = FormStartPosition.CenterScreen; this.FormBorderStyle = FormBorderStyle.Fixed3D; myDateLabel = new Label(); // Create label DateTime currDate = new DateTime(); currDate = DateTime.Now; myDateLabel.Text = currDate.ToString(); myDateLabel.AutoSize = true; myDateLabel.Location = new Point( 50, 20); myDateLabel.BackColor = this.BackColor; this.Controls.Add(myDateLabel); // Add label to form this.Width = (myDateLabel.PreferredWidth + 100); btnUpdate = new Button(); // Create a button btnUpdate.Text = "Update"; btnUpdate.BackColor = Color.LightGray; btnUpdate.Location = new Point(((this.Width/2) - (btnUpdate.Width /

2)),(this.Height - 75)); this.Controls.Add(btnUpdate); // Add button to form btnUpdate.Click += new System.EventHandler(this.btnUpdate_Click); btnUpdate.MouseEnter += new

System.EventHandler(this.btnUpdate_MouseEnter);

btnUpdate.MouseLeave += new System.EventHandler(this.btnUpdate_MouseLeave);

myDateLabel.MouseEnter += new

System.EventHandler(this.myDataLabel_MouseEnter);

myDateLabel.MouseLeave += new System.EventHandler(this.myDataLabel_MouseLeave);

} protected void btnUpdate_Click( object sender, System.EventArgs e) { DateTime currDate =DateTime.Now ; this.myDateLabel.Text = currDate.ToString(); }

Készítette: Zsótér Csaba III. éves informatikus hallgató

188

Page 189: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

protected void btnUpdate_MouseEnter( object sender, System.EventArgs e) { this.BackColor = Color.HotPink; } protected void btnUpdate_MouseLeave( object sender, System.EventArgs e) { this.BackColor = Color.Blue; } protected void myDataLabel_MouseEnter(object sender, System.EventArgs e) { this.BackColor = Color.Yellow; } protected void myDataLabel_MouseLeave(object sender, System.EventArgs e) { this.BackColor = Color.Green; } public static void Main( string[] args ) { Application.Run( new ButtonApp() ); }}

A kimenet pedig a következő lett.

OK gomb készítéseSzámos ablakon találkozunk az OK gombbal, melyre a felhasználó akkor kattint, ha befejezte a műveleteit az ablakon. A kattintás eredményeképpen az ablak többnyire bezáródik. Ha magunk hozzuk létre az ablakot, és az Application osztály Run tagfüggvényével

Készítette: Zsótér Csaba III. éves informatikus hallgató

189

Page 190: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

működtetjük, készíthetünk egy eseménykezelőt, ami kilép a Run tagfüggvényből, ha a felhasználó az OK gombra kattintott:

private void btnOK_Click(object sender, System.EventArgs e){

Application.Exit();}

Ha nem szeretnénk kilépni a teljes alkalmazásból, illetve működési ciklusból, használhatjuk e célra az ablak Close tagfüggvényét is.

Az OK gomb működését azonban egy, az eddigiektől gyökeresen eltérő módszerrel is megvalósíthatjuk. Először is, feledkezzünk el az Application osztály Run tagfüggvényéről; használjuk inkább a Form osztály ShowDialog tagfüggvényét. Ez ugyanis egy párbeszédablakot jelenít meg, és mindaddig így hagyja, míg a felhasználó el nem végzett rajta minden szükséges műveletet. A párbeszédablak egyébiránt egy egyszerű ablak, létrehozása minden más részletében megegyezik a korábban megismert ablakoknál látottakkal.

Általában, ha a felhasználó leüti egy ablakon az ENTER billentyűt, ez működésbe hozza az OK gombot. Az ENTER-t könnyen összeköthetjük egy gombbal az ablak AcceptButton tulajdonságának használatával. Amelyik gombot ehhez hozzárendeljük, az lép működésbe az ENTER lenyomására.

Szövegmezők használataA közismert vezérlők közé tartoznak a szövegmezők is, melyek a felhasználó szöveges bemenetét képesek fogadni. Így tehát a szövegmezőkkel és eseményeikkel a programunkban is használható adatokhoz juthatunk a felhasználótól:

4.8 Szövegmező vezérlő használata using System;using System.Windows.Forms;using System.Drawing; public class GetName : Form{ private Button btnOK; private Label lblFirst; private Label lblMiddle; private Label lblLast; private Label lblFullName; private Label lblInstructions; private TextBox txtFirst; // szövegmező private TextBox txtMiddle; private TextBox txtLast; public GetName() { InitializeComponent(); }

Készítette: Zsótér Csaba III. éves informatikus hallgató

190

Page 191: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

private void InitializeComponent() { this.FormBorderStyle = FormBorderStyle.Fixed3D; this.Text = "Get User Name"; this.StartPosition = FormStartPosition.CenterScreen; lblInstructions = new Label(); lblFirst = new Label(); lblMiddle = new Label(); lblLast = new Label(); lblFullName = new Label(); txtFirst = new TextBox(); txtMiddle = new TextBox(); txtLast = new TextBox(); btnOK = new Button(); lblFirst.AutoSize = true; lblFirst.Text = "First Name:"; lblFirst.Location = new Point( 20, 20); lblMiddle.AutoSize = true; lblMiddle.Text = "Middle Name:"; lblMiddle.Location = new Point( 20, 50); lblLast.AutoSize = true; lblLast.Text = "Last Name:"; lblLast.Location = new Point( 20, 80); lblFullName.AutoSize = true; lblFullName.Location = new Point( 20, 110 ); txtFirst.Width = 100; txtFirst.Location = new Point(140, 20); txtMiddle.Width = 100; txtMiddle.Location = new Point(140, 50); txtLast.Width = 100; txtLast.Location = new Point(140, 80); lblInstructions.Width = 250; lblInstructions.Height = 60; lblInstructions.Text = "Enter your first, middle, and last name." + "\nYou will see your name appear as you

type." + "\nFor fun, edit your name after entering

it."; lblInstructions.TextAlign = ContentAlignment.MiddleCenter; lblInstructions.Location = new Point(((this.Width/2) - (lblInstructions.Width / 2 )), 140); this.Controls.Add(lblFirst); // Add label to form this.Controls.Add(lblMiddle); this.Controls.Add(lblLast); this.Controls.Add(lblFullName); this.Controls.Add(txtFirst);

Készítette: Zsótér Csaba III. éves informatikus hallgató

191

Page 192: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

this.Controls.Add(txtMiddle); this.Controls.Add(txtLast); this.Controls.Add(lblInstructions); btnOK.Text = "Done"; btnOK.BackColor = Color.LightGray; btnOK.Location = new Point(((this.Width/2) - (btnOK.Width / 2)), (this.Height - 75)); this.Controls.Add(btnOK); // Add button to form btnOK.Click += new System.EventHandler(this.btnOK_Click);

txtFirst.TextChanged += new System.EventHandler(this.txtChanged_Event);

txtMiddle.TextChanged += new System.EventHandler(this.txtChanged_Event);

txtLast.TextChanged += new System.EventHandler(this.txtChanged_Event);

} protected void btnOK_Click( object sender, System.EventArgs e) { Application.Exit(); } protected void txtChanged_Event( object sender, System.EventArgs e) { lblFullName.Text = txtFirst.Text + " " + txtMiddle.Text + " " + txtLast.Text; } public static void Main( string[] args ) { Application.Run( new GetName() ); }}

A kimeneten láthatjuk, hogy programjaink kezdenek egyre hasznosabbak lenni:

Készítette: Zsótér Csaba III. éves informatikus hallgató

192

Page 193: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

Windows alkalmazások készítése

Választógombok használataA választógombok készítésénél is ugyanúgy járunk el, mint a már említett vezérlőknél, de ezért foglaljuk össze ennek a menetnek a lépéseit:

1. Példányosítjuk a vezérlő objektumot.2. Beállítjuk tulajdonságai értékeit.3. Elhelyezzük a vezérlőt az ablakon.

A választógombok különlegessége, hogy csoportban fordulnak elő, és egy csoporton belül egyszerre csak egyiküket jelölhetjük ki. Így olyan esetben vehetjük hasznukat, ha a felhasználónak kevés lehetőség közül kell választania. Olyankor is jól jöhetnek, ha a választást meg kell jelenítenünk, például a nem, vagy a családi állapot kiválasztása esetén. A választógombok készítésére a RadioButton osztály használatos.

4.9 Választógombok használata és csoportba rendezése using System.Drawing;using System.Windows.Forms; public class BadRadio : Form{ private RadioButton rdMale; private RadioButton rdFemale; private RadioButton rdYouth; private RadioButton rdAdult; private Button btnOK; private Label lblText1; private Label lblText2; public BadRadio() { InitializeComponent(); } private void InitializeComponent() { this.rdMale = new System.Windows.Forms.RadioButton(); this.rdFemale = new System.Windows.Forms.RadioButton(); this.lblText1 = new System.Windows.Forms.Label(); this.rdYouth = new System.Windows.Forms.RadioButton(); this.rdAdult = new System.Windows.Forms.RadioButton(); this.lblText2 = new System.Windows.Forms.Label(); this.btnOK = new System.Windows.Forms.Button(); // Form1 this.ClientSize = new System.Drawing.Size(350, 225); this.Text = "Radio Buttons 1"; // rdMale this.rdMale.Location = new System.Drawing.Point(50, 65); this.rdMale.Size = new Size(90, 15);

Készítette: Zsótér Csaba III. éves informatikus hallgató

193

Page 194: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

this.rdMale.TabIndex = 0; this.rdMale.Text = "Male"; // rdFemale this.rdFemale.Location = new System.Drawing.Point(50, 90); this.rdFemale.Size = new System.Drawing.Size(90, 15); this.rdFemale.TabIndex = 1; this.rdFemale.Text = "Female"; // lblText1 this.lblText1.Location = new System.Drawing.Point(50, 40); this.lblText1.Size = new System.Drawing.Size(90, 15); this.lblText1.TabIndex = 2; this.lblText1.Text = "Sex"; // rdYouth this.rdYouth.Location = new System.Drawing.Point(220, 65); this.rdYouth.Size = new System.Drawing.Size(90, 15); this.rdYouth.TabIndex = 3; this.rdYouth.Text = "Over 21"; // rdAdult this.rdAdult.Location = new System.Drawing.Point(220, 90); this.rdAdult.Size = new System.Drawing.Size(90, 15); this.rdAdult.TabIndex = 4; this.rdAdult.Text = "Under 21"; // lblText2 this.lblText2.Location = new System.Drawing.Point(220, 40); this.lblText2.Size = new System.Drawing.Size(90, 15); this.lblText2.TabIndex = 5; this.lblText2.Text = "Age Group"; // btnOK this.btnOK.Location = new System.Drawing.Point(130, 160); this.btnOK.Size = new System.Drawing.Size(70, 30); this.btnOK.TabIndex = 6; this.btnOK.Text = "OK"; this.btnOK.Click += new System.EventHandler(this.btnOK_Click); this.Controls.Add(rdMale); this.Controls.Add(rdFemale); this.Controls.Add(lblText1); this.Controls.Add(rdYouth); this.Controls.Add(rdAdult); this.Controls.Add(lblText2); this.Controls.Add(btnOK); } private void btnOK_Click(object sender, System.EventArgs e) { Application.Exit(); } static void Main() { Application.Run(new BadRadio()); }

Készítette: Zsótér Csaba III. éves informatikus hallgató

194

Page 195: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

}

A kód egy ablakot készít négy választógombbal és egy gombbal. Találhatunk rajta továbbá két szövegmezőt is, melyek a felhasználó tulajdonságára szolgálnak. Ha futtatjuk, akkor láthatjuk, hogy a választógombok a vártnak megfelelően működnek. Vagy mégsem? Ha bejelölünk egy választógombot, az össze többi kiválasztása megszűnik. Az igazi az volna, ha a két kategóriát el tudnánk valahogy választani egymástól.

A legtöbb vezérlő rendelkezik TabIndex tulajdonsággal, amely meghatározza a vezérlők kiválasztásának sorrendjét, ha a felhasználó a Tab billentyűt nyomkodja. Az első vezérlő sorszáma 0, a másodiké 1 és így tovább.

A választógombok kezeléséhez nem kell külön kódot írnunk, hiszen a kiválasztás és kiválasztás megszüntetésének kódja már eleve megvan bennük. Amit azonban a korábbiakban említettünk, ez a kód nem egészen úgy működik, ahogy azt elképzeltük. Valami változtatásra van szükség, hogy a választógombok két csoportja egymástól függetlenül működhessen.

Tárolók használataFeladatunk megoldásában a tárolókat hívjuk segítségül, melyek lehetővé teszik, hogy vezérlőket csoportokba foglaljuk. Egy tárolót már eddig is használtunk – a főablakot. Emellett azonban saját tárolókat is létrehozhatunk, melyeket az ablak tárolójában vagy más tárolókban elhelyezhetünk.

Van azonban egy másik lehetőség is a vezérlők elválasztására: a csoportmező (GroupBox) használata. Ez ugyanúgy működik, mint egy tároló, néhány további lehetőséggel, így például címkefeliratot is feltüntethetünk rajta:

4.10 Választógombok csoportba helyezésé using System.Drawing;using System.Windows.Forms; public class GoodRadio : Form{ private GroupBox gboxAge; private GroupBox gboxSex; private RadioButton rdMale;

Készítette: Zsótér Csaba III. éves informatikus hallgató

195

Page 196: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

private RadioButton rdFemale; private RadioButton rdYouth; private RadioButton rdAdult; private Button btnOK; public GoodRadio() { InitializeComponent(); } private void InitializeComponent() { this.gboxAge = new GroupBox(); this.gboxSex = new GroupBox(); this.rdMale = new RadioButton(); this.rdFemale = new RadioButton(); this.rdYouth = new RadioButton(); this.rdAdult = new RadioButton(); this.btnOK = new Button(); // Form1 this.ClientSize = new Size(350, 200); this.Text = "Grouping Radio Buttons"; // gboxSex this.gboxSex.Location = new Point(15, 30); this.gboxSex.Size = new Size(125, 100); this.gboxSex.TabStop = false; this.gboxSex.Text = "Sex"; // rdMale this.rdMale.Location = new Point(35, 35); this.rdMale.Size = new Size(70, 15); this.rdMale.TabIndex = 0; this.rdMale.Text = "Male"; // rdFemale this.rdFemale.Location = new Point(35, 60); this.rdFemale.Size = new Size(70, 15); this.rdFemale.TabIndex = 1; this.rdFemale.Text = "Female"; // gboxAge this.gboxAge.Location = new Point(200, 30); this.gboxAge.Size = new Size(125, 100); this.gboxAge.TabStop = false; this.gboxAge.Text = "Age Group"; // rdYouth this.rdYouth.Location = new Point(35, 35); this.rdYouth.Size = new Size(70, 15); this.rdYouth.TabIndex = 3; this.rdYouth.Text = "Over 21"; // rdAdult this.rdAdult.Location = new Point(35, 60); this.rdAdult.Size = new Size(70, 15); this.rdAdult.TabIndex = 4; this.rdAdult.Text = "Under 21";

Készítette: Zsótér Csaba III. éves informatikus hallgató

196

Page 197: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

// btnOK this.btnOK.Location = new Point(130, 160); this.btnOK.Size = new Size(70, 30); this.btnOK.TabIndex = 6; this.btnOK.Text = "OK"; this.btnOK.Click += new System.EventHandler(this.btnOK_Click); this.Controls.Add(gboxSex); this.Controls.Add(gboxAge); this.gboxSex.Controls.Add(rdMale); this.gboxSex.Controls.Add(rdFemale); this.gboxAge.Controls.Add(rdYouth); this.gboxAge.Controls.Add(rdAdult); this.Controls.Add(btnOK); } private void btnOK_Click(object sender, System.EventArgs e) { Application.Exit(); } static void Main() { Application.Run(new GoodRadio()); } }

E kód a csoportmezők használatára összpontosít. Ha helyettük tárolókat használtunk volna, a kód hasonlóan festene, annyi különbséggel, hogy ez esetben nem használhatnánk a tárolók Text és más tulajdonságait. Ahhoz, hogy kialakítsuk ugyanezt a képet, más vezérlőkre is szükségünk lenne (például címkékre).

A kód igazán fontos része az, ahol a két csoportot (ezek a gboxSex és a gboxAge) az ablakra helyezzük. A választógombok nem az ablakra, hanem a csoportmezőkbe kerülnek.

Készítette: Zsótér Csaba III. éves informatikus hallgató

197

Page 198: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

Mivel pedig ezek a csoportmezők az ablak (this) részei, a bennük található elemek is megjelennek az ablakon. this.Controls.Add(gboxSex); this.Controls.Add(gboxAge); this.gboxSex.Controls.Add(rdMale); this.gboxSex.Controls.Add(rdFemale); this.gboxAge.Controls.Add(rdYouth); this.gboxAge.Controls.Add(rdAdult);

A választógombokat a csoportmezőkbe helyezéssel külön tárolókba tettük. Ennek eredményeképpen, ha futtatjuk a kódot, láthatjuk, hogy a Sex és Age kategóriák nincsenek hatással egymásra. Lehetőségünk van arra is, hogy tudatos kapcsolatot teremtsünk a két kategória között; ehhez a vezérlőknek egymás tulajdonságait kell módosítaniuk.

Listamezők használataAz ablakon gyakran használatos vezérlő a listamező (ListBox), amely lehetővé teszi, hogy egy kisméretű mezőben sok elemet soroljunk fel. Beállíthatjuk úgy, hogy a felhasználó görgethesse a tartalmát és akkor több elemet is kiválaszthasson. Mivel a listamezők tulajdonságainak beállítása kissé összetettebb feladat, mint a korábbiakban bemutatott vezérlők esetében, érdemes elidőznünk itt egy kicsit.

A listamezők készítése pontosan ugyanúgy történhet, mint más, eddig megismert vezérlőké. Először ablakunk részeként bevezetjük a listamezőt:

private ListBox myListBox;

Ezután következhet a példányosítás:

myListBox = new ListBox();

Természetesen e két utasítást egy sorba is írhattuk volna:

private ListBox myListBox = new ListBox();

Ha elkészültünk, akkor ideje, hogy elhelyezzük az ablakon, éppen úgy, mint más társait:

This.Controls.Add(myListBox);

A listát azonban fel kell töltenünk elemekkel. (Egyébként mire is használnánk?) Itt már nem kapaszkodhatunk az korábban tanultakba.

Az elemek beillesztése több lépésből álló folyamat. Mindenekelőtt tudatnunk kell a listamezővel, hogy tartalmát frissíteni szeretnénk. Ehhez meg kell hívnunk a BeginUpdate tagfüggvényt – a myListBox esetében ez a következőképpen néz ki:

myListBox.BeginUpdate();

Készítette: Zsótér Csaba III. éves informatikus hallgató

198

Page 199: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

Ha ezzel elkészültünk, szabad az út az elemek beillesztéséhez. Ne gondoljunk itt valami bonyolult dologra, mindössze a listamező Item tagjának Add tagfüggvényét kell használnunk:

myListBox.Items.Add("Első elem");myListBox.Items.Add("Második elem");myListBox.Items.Add("Harmadik elem");

Ha befejeztük az elemek beillesztését, közölnünk kell a listamezővel, hogy elkészültünk. Erre szolgál az EndUpdate tagfüggvény:

myListBox.EndUpdate();

Nézzük meg ezt az egészet egy teljes kódsorban:

4.11 Listamezők használata using System.Windows.Forms;using System.Drawing; public class GetName : Form{ private Button btnOK; private Label lblFullName; private TextBox txtFullName; private ListBox lboxSex; private Label lblSex; private ListBox lboxAge; public GetName() { InitializeComponent(); } private void InitializeComponent() { this.FormBorderStyle = FormBorderStyle.Fixed3D; this.Text = "Get User Info"; this.StartPosition = FormStartPosition.CenterScreen; lblFullName = new Label(); txtFullName = new TextBox(); btnOK = new Button(); lblSex = new Label(); lboxSex = new ListBox(); lboxAge = new ListBox(); lblFullName.Location = new Point(20, 40); lblFullName.AutoSize = true; lblFullName.Text = "Name:"; txtFullName.Width = 170; txtFullName.Location = new Point(80, 40); btnOK.Text = "Done";

Készítette: Zsótér Csaba III. éves informatikus hallgató

199

Page 200: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

btnOK.Location = new Point(((this.Width/2) - (btnOK.Width / 2)), (this.Height - 75)); lblSex.Location = new Point(20, 70); lblSex.AutoSize = true; lblSex.Text = "Sex:"; lboxSex.Location = new Point(80, 70); lboxSex.Size = new Size(100, 20); lboxSex.SelectionMode = SelectionMode.One; lboxSex.BeginUpdate(); lboxSex.Items.Add(" "); lboxSex.Items.Add(" Boy "); lboxSex.Items.Add(" Girl "); lboxSex.Items.Add(" Man "); lboxSex.Items.Add(" Lady "); lboxSex.EndUpdate();

lboxAge.Location = new Point(80, 100); lboxAge.Size = new Size(100, 60); lboxAge.SelectionMode = SelectionMode.One; lboxAge.BeginUpdate(); lboxAge.Items.Add(" "); lboxAge.Items.Add(" Under 21 "); lboxAge.Items.Add(" 21 "); lboxAge.Items.Add(" Over 21 "); lboxAge.EndUpdate(); lboxAge.SelectedIndex = 0; this.Controls.Add(btnOK); this.Controls.Add(lblFullName); this.Controls.Add(txtFullName); this.Controls.Add(lboxSex); this.Controls.Add(lblSex); this.Controls.Add(lboxAge); btnOK.Click += new System.EventHandler(this.btnOK_Click); } protected void btnOK_Click( object sender, System.EventArgs e) { Application.Exit(); } public static void Main( string[] args ) { Application.Run( new GetName() ); }}

Készítette: Zsótér Csaba III. éves informatikus hallgató

200

Page 201: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

A ListBox vezérlő kiválasztási módjai Mód LeírásaSelectionMode.One Egyszerre csak egy elem választható.SelectionMode.MultiExtended Egyszerre több elem választható, és a választáshoz

használhatjuk a Shift, Ctrl és nyílbillentyűket is.SelectionMode.MultiSimple Egyszerre több elem választható.SelectionMode.None Egyetlen elem sem választható.

A listamezők minden más tulajdonságukban úgy viselkednek, mint a vezérlők általában. Események útján értesülhetünk arról, ha a felhasználó más elemet választott, vagy ha elhagyta a vezérlőt. Írhatunk olyan kódot is, amely biztosítja, hogy a felhasználó válasszon a lehetőségek közül, de számos más lehetőségünk is van.

Menük az ablakokonAz ablakok „élővé” tételére nem csak a vezérlők adnak lehetőséget, használhatunk menüket is. A legtöbb ablakos alkalmazásokban igénybe veszik ezt a lehetőséget, legalább egy Fájl vagy Súgó menü erejéig, melyek kiválasztásuk esetében általában adnak néhány menüpontot. Ablakainkon magunk is használhatunk menüket.

Nézzük megint azt a példát, amikor egy gomb segítségével frissítettük az ablakon megjelenő dátumot. Írjuk át ezt a kódot olyanra, hogy menü segítségével frissítsük a dátumot:

4.12 Egyszerű menü készítése using System;using System.Windows.Forms;using System.Drawing; public class Menu : Form{ private Label myDateLabel; private MainMenu myMainMenu;

Készítette: Zsótér Csaba III. éves informatikus hallgató

201

Page 202: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

public Menu() { InitializeComponent(); } private void InitializeComponent() { this.Text = "STY Menus"; this.StartPosition = FormStartPosition.CenterScreen; this.FormBorderStyle = FormBorderStyle.Fixed3D; myDateLabel = new Label(); // Create label DateTime currDate = new DateTime(); currDate = DateTime.Now; myDateLabel.Text = currDate.ToString(); myDateLabel.AutoSize = true; myDateLabel.Location = new Point( 50, 70); myDateLabel.BackColor = this.BackColor; this.Controls.Add(myDateLabel); // Add label to form this.Width = (myDateLabel.PreferredWidth + 100); myMainMenu = new MainMenu(); MenuItem menuitemFile = myMainMenu.MenuItems.Add("File");

menuitemFile.MenuItems.Add(new MenuItem("Update Date", new EventHandler(this.MenuUpdate_Selection)));

menuitemFile.MenuItems.Add(new MenuItem("Exit", new EventHandler(this.FileExit_Selection)));

this.Menu = myMainMenu; }

protected void MenuUpdate_Selection( object sender, System.EventArgs e ) { DateTime currDate = new DateTime(); currDate = DateTime.Now; this.myDateLabel.Text = currDate.ToString(); } protected void FileExit_Selection( object sender, System.EventArgs e ) { this.Close(); } public static void Main( string[] args ) { Application.Run( new Menu() ); }}

Készítette: Zsótér Csaba III. éves informatikus hallgató

202

Page 203: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

Az ablak elsődleges menüjét főmenünek hívjuk, amely jelen esetben a File pontot tartalmazza, de mások is lehetnek rajta.

A kódban egy myMainMenu nevű MainMenu objektumot hozunk létre. Ha tüzetesebben megnézzük a kódot, látjuk, hogy mennyi minden történik benne. Egy új, menuitemFile nevű tagot vezettünk be, majd hozzárendeljük a myMainMenu objektumhoz adott új elemet. Az új elem hozzáadásának kódját érdemes külön is leírnunk:

myMainMenu.MenuItems.Add("File");

Vagyis főmenünek egy File nevű elemet adunk.

Általánosabban fogalmazva az itt látottakat elmondhatjuk, hogy a MenuItems.Add tagfüggvényt, meghívhatjuk akár egy menü, akár egy menüelem esetében. Ha főmenüben tesszük, egy menühöz jutunk, míg ha egy menüpontban hívjuk meg ezt a tagfüggvényt, egy almenü elemét illesztjük be.

Ha a MenuItems.Add tagfüggvénynek egy paramétert adunk át, akkor úgy veszi, hogy a menüpont szövegét szeretnénk megadni. Látható, hogy találunk olyan tagfüggvényhívást, melynek két paramétert adunk át. Az első a menüpont neve, a második pedig egy új eseménykezelő. Ezt hívja meg a rendszer, ha a felhasználó a menüpontot választja.

Készíthetünk olyan menüt is, amelyik gyorsbillentyű hatására is működik. Ehhez nem kell mást csinálni, mint a menü azon betűje elé írni egy & jelet, amit ki szeretnénk választani, és a kódot kiegészíteni a ShortCut adattípussal, amely lehetővé teszi, hogy általában a ShortCut.Ctrl * jelölést használjuk, ahol a * helyén tetszőleges betű állhat.

Készítette: Zsótér Csaba III. éves informatikus hallgató

203

Page 204: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

Nézzük meg, hogy is néz ez ki:

MenuItem menuitemFile = myMainMenu.MenuItems.Add("&File"); menuitemFile.MenuItems.Add(new MenuItem("Update &Date",

new EventHandler(this.MenuUpdate_Selection), Shortcut.CtrlD));

menuitemFile.MenuItems.Add(new MenuItem("E&xit",new EventHandler(this.FileExit_Selection), Shortcut.CtrlX));

MenuItem menuitemHelp = myMainMenu.MenuItems.Add("&Help");

menuitemHelp.MenuItems.Add(new MenuItem("&About", new EventHandler(this.FileAbout_Selection)));

Az előző kódsorba helyettesítsük be azt a fenti kódsort, akkor a következő kimenetet kapjuk:

Bejelölhető menüelemekA menük ismert és széles körben elterjedt lehetősége, hogy elemeiket bejelöléssel, ki- vagy bekapcsolhatjuk. A következő kódszövegben bemutatjuk, miként építhetjük be ezt a lehetőséget menüinkbe, továbbá egy új módszert is adunk a menük bevezetésére és meghatározására:

4.13 Menüpontok bejelölése using System;using System.Windows.Forms;using System.Drawing;

public class CheckedMenu : Form {

Készítette: Zsótér Csaba III. éves informatikus hallgató

204

Page 205: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

private Label myDateLabel; private MainMenu myMainMenu; private MenuItem menuitemFile; private MenuItem menuitemUD; private MenuItem menuitemActive; private MenuItem menuitemExit; private MenuItem menuitemHelp; private MenuItem menuitemAbout; public CheckedMenu() { InitializeComponent(); } private void InitializeComponent() { this.Text = "STY Menus"; this.StartPosition = FormStartPosition.CenterScreen; this.FormBorderStyle = FormBorderStyle.Sizable; myDateLabel = new Label(); // Create label DateTime currDate = new DateTime(); currDate = DateTime.Now; myDateLabel.Text = currDate.ToString(); myDateLabel.AutoSize = true; myDateLabel.Location = new Point( 50, 70); myDateLabel.BackColor = this.BackColor; this.Controls.Add(myDateLabel);

// Set width of form based on Label's width this.Width = (myDateLabel.PreferredWidth + 100); CreateMyMenu(); } protected void MenuUpdate_Selection( object sender, System.EventArgs e) { if( menuitemActive.Checked == true) { DateTime currDate = new DateTime(); currDate = DateTime.Now; this.myDateLabel.Text = currDate.ToString(); } else { this.myDateLabel.Text = "** " + this.myDateLabel.Text + " **"; } } protected void FileExit_Selection( object sender, System.EventArgs e) { Application.Exit(); } protected void FileAbout_Selection( object sender, System.EventArgs e)

Készítette: Zsótér Csaba III. éves informatikus hallgató

205

Page 206: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

{ // display an about form } protected void ActiveMenu_Selection( object sender, System.EventArgs e) { MenuItem tmp; tmp = (MenuItem) sender; if ( tmp.Checked == true ) tmp.Checked = false; else tmp.Checked = true; } public void CreateMyMenu() { myMainMenu = new MainMenu(); // FILE MENU menuitemFile = myMainMenu.MenuItems.Add("&File"); menuitemUD = new MenuItem(); menuitemUD.Text = "Update &Date"; menuitemUD.Shortcut = Shortcut.CtrlD; menuitemUD.Click += new EventHandler(this.MenuUpdate_Selection); menuitemFile.MenuItems.Add( menuitemUD ); menuitemExit = new MenuItem(); menuitemExit.Text = "E&xit"; menuitemExit.Shortcut = Shortcut.CtrlX; menuitemExit.ShowShortcut = false; menuitemExit.Click += new EventHandler(this.FileExit_Selection); menuitemFile.MenuItems.Add( menuitemExit ); // HELP MENU menuitemHelp = myMainMenu.MenuItems.Add("&Help"); menuitemActive = new MenuItem(); menuitemActive.Text = "Active"; menuitemActive.Click += new EventHandler(this.ActiveMenu_Selection); menuitemActive.Checked = true; menuitemHelp.MenuItems.Add( menuitemActive );

menuitemAbout = new MenuItem(); menuitemAbout.Text = "&About"; menuitemAbout.Shortcut = Shortcut.CtrlA; menuitemAbout.ShowShortcut = false; menuitemAbout.Click += new EventHandler(this.FileAbout_Selection); menuitemHelp.MenuItems.Add( menuitemAbout ); this.Menu = myMainMenu; } public static void Main( string[] args ) { Application.Run( new CheckedMenu() ); } }

Készítette: Zsótér Csaba III. éves informatikus hallgató

206

Page 207: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

A korábbiaktól eltérően az osztálymeghatározás elején nem csak a MainMenu objektumot és a többi legfelső szintű menüpontot vezetjük be, hanem az összes, az ablakon előforduló menüelemet.

A korábbiakkal megegyező módon a menü létrehozásának kódját most is külön tagfüggvénybe helyeztük. A kód mindemellett tartalmaz egy másik újdonságot is. A Help menü Active pontját ugyanis ki- és bekapcsolhatjuk. Ha bekapcsoljuk, a dátumot és az időt frissíthetjük a másik menü Update Date pontjával, míg ha kikapcsoljuk, a dátum és az idő csillagok közt jelenik meg az ablakon. Minderre a menüelem Checked tulajdonsága kínál lehetőséget, valamint némi kód a menüelem eseménykezelőjében. Ebben az eseménykezelőben nincs semmi újdonság, mégis érdemes foglalkoznunk vele. Hasonlóan más eseménykezelőkhöz, az első paraméter itt is az eseményt kiváltó objektum, melynek típusa – menüelemről lévén szó – MenuItem. Készítünk egy ideiglenes MenuItem típusú változót, és a paraméter értékét ebbe helyezzük. Ezentúl a tmp nevű változó segítségével érhetjük el a MenuItem osztály tulajdonságait és tagfüggvényeit.

Tudjuk azt is, hogy eseményünket az Active menüpont kiválasztása váltotta ki. Megvizsgáljuk, hogy a menüpont Checked tulajdonságának értéke igaz-e. Ha igen, akkor hamisra állítjuk, ha pedig nem, akkor igazra. Tagfüggvényünk tehát ki-be kapcsolgatja az Active menüpontot.

A MenuUpdate eseménykezelőt szintén módosítottuk némileg. Új alakjában kiírja az aktuális dátumot és az időt, ha az Active menüpontot a felhasználó bejelölte, míg ellenkező esetben csillagok között írja ki a korábbi értéket. Az eseménykezelő lényege nem abban rejlik, hogy mit ír ki – igazán fontos az, hogy képesek vagyunk kezelni a menüpontok jelölését.

Helyi menü készítéseAz eddig látott hagyományos menük mellett helyi előugró menüket is létrehozhatunk. Ezek a képernyő bármely pontján megjelenhetnek, a jobb egérgomb lenyomására:

Készítette: Zsótér Csaba III. éves informatikus hallgató

207

Page 208: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

4.14 Helyi menü alkalmazása using System;using System.Windows.Forms;using System.Drawing;

public class PopUp : Form{ private ContextMenu myPopUp; public PopUp() { InitializeComponent(); }

private void InitializeComponent() { this.Text = "STY Pop-up Menu"; this.StartPosition = FormStartPosition.CenterScreen; CreatePopUp(); } protected void PopUp_Selection( object sender, System.EventArgs e) { this.Text = ((MenuItem) sender).Text; } private void CreatePopUp() { myPopUp = new ContextMenu(); myPopUp.MenuItems.Add("First Item", new EventHandler(this.PopUp_Selection)); myPopUp.MenuItems.Add("Second Item", new EventHandler(this.PopUp_Selection)); myPopUp.MenuItems.Add("-"); myPopUp.MenuItems.Add("Third Item", new EventHandler(this.PopUp_Selection)); this.ContextMenu = myPopUp; }

public static void Main( string[] args ) { Application.Run( new PopUp() ); }}

Készítette: Zsótér Csaba III. éves informatikus hallgató

208

Page 209: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

A kódban láthatjuk, hogy ha helyi menüt szeretnénk készíteni, a MainMenu helyett a ContextMenu osztályt kell használnunk. Itt készítettünk egy myPopUp változót.

Az elemek beillesztése ugyanúgy történik, mint a MainMenu osztály esetében; itt is a MenuItem.Add tagfüggvényt kell használnunk. Programunkban négy elemet helyezünk a helyi menübe. Az első, a második és a negyedik semmiféle meglepetéssel nem szolgál, de a harmadik elem különleges. Első látásra úgy látszik, mintha egy egyszerű elválasztójelet illesztenénk be menüpont gyanánt – a valóságban azonban egy, a menüt teljes szélességében átérő vonal keletkezik. Ha minden elemet beillesztettünk a myPopUp objektumba, a menü az aktuális ablak helyi menüjévé válik.

Azt is észre vehetjük, hogy a három menüelem ugyanazt a PopUp_Selection eseménykezelőt használja.

A MessageBox osztályA Windows programozása során gyakran lehet szükségünk üzenetablakok használatára, ezért nem meglepő, hogy létezik számukra egy osztály a BCL-ben is. Ez lehetővé teszi, hogy üzeneteket egy előugró ablakban jelenítsük meg – ezt tesszük a következő kódban:

4.15 A MessageBox osztály használata using System;using System.Windows.Forms;using System.Drawing; public class MsgBox : Form{ private ContextMenu myPopUp; public MsgBox() { MessageBox.Show( "You have started the application.", "Status"); InitializeComponent(); CreatePopUp(); MessageBox.Show( "Form has been initialized.", "Status"); }

Készítette: Zsótér Csaba III. éves informatikus hallgató

209

Page 210: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

private void InitializeComponent() { this.Text = "STY C# Pop-up Menu"; this.StartPosition = FormStartPosition.CenterScreen; } protected void PopUp_Selection( object sender, System.EventArgs e) { MessageBox.Show( ((MenuItem) sender).Text, this.Text + " Msg Box"); } private void CreatePopUp() { myPopUp = new ContextMenu(); myPopUp.MenuItems.Add("First Item", new EventHandler(this.PopUp_Selection)); myPopUp.MenuItems.Add("Second Item", new EventHandler(this.PopUp_Selection)); myPopUp.MenuItems.Add("-"); myPopUp.MenuItems.Add("Third Item", new EventHandler(this.PopUp_Selection)); this.ContextMenu = myPopUp; } public static void Main( string[] args ) { Application.Run( new MsgBox() ); MessageBox.Show( "You are done with the application", "Status"); }}

Készítette: Zsótér Csaba III. éves informatikus hallgató

210

Page 211: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

A kód egyetlen MessageBox objektumot használ, melyet különböző tartalommal a program más – más pontján jelenít meg.

A MessageBox legegyszerűbb alakjában egy olyan párbeszédablakot ad, melyen egy megadott szöveg, valamit egy OK gomb szerepel, de emellett megadhatjuk a címsorát is. A MessageBox osztály Show tagfüggvénye két paramétert fogad. Az első a megjelenítendő üzenet, míg a második a címsor tartalma.

A Microsoft Windows beépített párbeszédablakainak használataA MessageBox osztály mellett számos, összetettebb, előre elkészített párbeszédablak áll rendelkezésünkre. Közülük az alábbiak különösen hasznunkra lehetnek:

• Color Selection (Színkiválasztás) párbeszédablak (ColorDialog).• Print Preview (Nyomtatási kép) párbeszédablak (PrintPreviewDialog).• Fonts (Betűtípusok) párbeszédablak (FontDialog).• File Open (Fájl megnyitás) párbeszédablak (OpenFileDialog).• File Save (Fájl mentése) párbeszédablak (SaveFileDialog).

Ezek a párbeszédablakok a BCL részét képezik.

4.16 A beépített párbeszédablakok használata using System;using System.Windows.Forms;using System.Drawing;

public class Canned : Form{ private MainMenu myMainMenu;

public Canned() { InitializeComponent(); }

private void InitializeComponent() { this.Text = "Canned Dialogs"; this.StartPosition = FormStartPosition.CenterScreen; this.FormBorderStyle = FormBorderStyle.Sizable; this.Width = 400;

myMainMenu = new MainMenu(); MenuItem menuitemFile = myMainMenu.MenuItems.Add("&File"); menuitemFile.MenuItems.Add(new MenuItem("Colors Dialog", new EventHandler(this.Menu_Selection))); menuitemFile.MenuItems.Add(new MenuItem("Fonts Dialog", new EventHandler(this.Menu_Selection))); menuitemFile.MenuItems.Add(new MenuItem("Print Preview Dialog", new EventHandler(this.Menu_Selection))); menuitemFile.MenuItems.Add("-"); menuitemFile.MenuItems.Add(new MenuItem("Exit", new EventHandler(this.Menu_Selection)));

Készítette: Zsótér Csaba III. éves informatikus hallgató

211

Page 212: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

this.Menu = myMainMenu; } protected void Menu_Selection( object sender, System.EventArgs e ) { switch (((MenuItem) sender).Text ) { case "Exit": Application.Exit(); break;

case "Colors Dialog": ColorDialog myColorDialog = new ColorDialog(); myColorDialog.ShowDialog(); break; case "Fonts Dialog": FontDialog myFontDialog = new FontDialog(); myFontDialog.ShowDialog(); break; case "Print Preview Dialog": PrintPreviewDialog myPrintDialog = new PrintPreviewDialog(); myPrintDialog.ShowDialog(); break; default: MessageBox.Show("DEFAULT", "PopUp"); break; } } public static void Main( string[] args ) { Application.Run( new Canned() ); }}

Láthatjuk, milyen egyszerűen beépíthetjük programjainkba, a látványos eredményt pedig a következő ábrákon láthatjuk:

Készítette: Zsótér Csaba III. éves informatikus hallgató

212

Page 213: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

Saját párbeszédablak készítéseLehetőségünk van arra is, hogy saját párbeszédablakot hozzunk létre – ezt pontosan ugyanúgy tehetjük meg, mint az ablakok esetében: meg kell határoznunk egy ablak objektumot, elhelyezni rajta a kívánt vezérlőket, majd megjeleníteni.

Az ablakok megjelenítésének két módja ismert. Az egyik használata esetén a felhasználó nem mehet tovább anélkül, hogy reagált volna az ablak tartalmára. Ezzel a viselkedéssel találkozhatunk akkor, ha az ablakot a ShowDialog tagfüggvénnyel jelenítettük meg. A másik megjelenítési mód esetén függetlenül az ablakon történtektől újabb ablakot és vezérlőket vehetünk használatba – ez a Show tagfüggvény működésének eredménye:

Készítette: Zsótér Csaba III. éves informatikus hallgató

213

Page 214: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

4.17 A Show és a ShowDialog tagfüggvény különbségei using System;using System.Windows.Forms;using System.Drawing;

public class MyForm : Form{ private MainMenu myMainMenu;

public MyForm() { InitializeComponent(); }

private void InitializeComponent() { this.Text = "Canned Dialogs"; this.StartPosition = FormStartPosition.CenterScreen; this.FormBorderStyle = FormBorderStyle.Sizable; this.Width = 400;

myMainMenu = new MainMenu(); MenuItem menuitemFile = myMainMenu.MenuItems.Add("&File"); menuitemFile.MenuItems.Add(new MenuItem("My Form", new EventHandler(this.Menu_Selection))); menuitemFile.MenuItems.Add(new MenuItem("My Other Form", new EventHandler(this.Menu_Selection))); menuitemFile.MenuItems.Add("-"); menuitemFile.MenuItems.Add(new MenuItem("Exit", new EventHandler(this.Menu_Selection))); this.Menu = myMainMenu; } protected void Menu_Selection( object sender, System.EventArgs e ) { switch (((MenuItem) sender).Text ) { case "Exit": Application.Exit(); break; case "My Form": subForm aForm = new subForm(); aForm.Text = "A Show form"; aForm.Show(); break; case "My Other Form": subForm bForm = new subForm(); bForm.Text = "A ShowDialog form"; bForm.ShowDialog(); break; default: MessageBox.Show("DEFAULT", "PopUp"); break; } }

Készítette: Zsótér Csaba III. éves informatikus hallgató

214

Page 215: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

public static void Main( string[] args ) { Application.Run( new MyForm() ); }} public class subForm : Form{ private MainMenu mySubMainMenu; public subForm() { InitializeComponent(); } private void InitializeComponent() { this.Text = "My sub-form"; this.StartPosition = FormStartPosition.CenterScreen; this.FormBorderStyle = FormBorderStyle.FixedDialog; this.Width = 300; this.Height = 250; mySubMainMenu = new MainMenu(); MenuItem menuitemFile = mySubMainMenu.MenuItems.Add("&File"); menuitemFile.MenuItems.Add(new MenuItem("Close", new EventHandler(this.CloseMenu_Selection))); this.Menu = mySubMainMenu; } protected void CloseMenu_Selection( object sender, System.EventArgs e ) { this.Close(); }}

Készítette: Zsótér Csaba III. éves informatikus hallgató

215

Page 216: Amit a C# tudni érdemes

C#Amit a C# tudni érdemes!

Felhasznált irodalom

1. Programming C#, 2nd EditionSzerző: Jesse Liberty, Kiadó: O’Reilly, Kiadás éve: 2002.

2. C# Essentials, 2nd EditionSzerző: Ben Albahari, Peter Drayton, Brad Merrill Kiadó: O’Reilly,Kiadás éve: 2001.

3. C#, Your visual blueprint for building .NET applications by Eric Butow, Tommy RaynSzerző: Eric Butow, Tommy Rayn Kiadó:Hungry Minds Inc.Kiadás éve: 2002.

4. C#.NET Web Developer’s GuideSzerző:Adrian Turtschi

DotThatCom.comJason WerryGreg HackJoseph AlbahariSaurabh Nandu Technical Editor

Wei Meng Lee Series Editor

Kiadó: Syngress Publishing, Inc. Kiadás éve: 2002.

5. Designing Microsoft ASP.NET ApplicationsSzerző: Douglas J. Reilly Kiadó: Microsoft Press Kiadás éve: 2002.

6. Programming Microsoft Windows with C#Szerző: Charles Petzold Kiadó: Microsoft Press Kiadás éve: 2002.

7. Teach Yourself The C# Language in 21 DaysSzerző:Bradley L. Jones Kiadó: Pearson Education Inc.Kiadás éve: 2004.

Készítette: Zsótér Csaba III. éves informatikus hallgató

216