Content extract
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 . 18 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 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 . 30 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 . 32 A switch utasítás . 33 Egy kód – több eset . 34 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 . 36 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. 39 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 . 47 Értékek visszaadása tagfüggvényekből . 48 A tagfüggvények törzsének felépítése . 48 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 . 50 Statikus tagfüggvények . 51
Paraméterek elérési jellemzői . 51 Érték szerinti paraméterátadás . 52 Hivatkozás szerinti paraméterátadás. 52 A kimeneti elérés használata . 53 Az osztály-tagfüggvények típusai. 54 Tulajdonságelérési tagfüggvények . 54 Konstruktorok . 55 Példánykonstruktor . 55 Statikus konstruktorok . 56 Destruktorok . 57 Készítette: Zsótér Csaba III. éves informatikus hallgató 2 C# Amit a C# tudni érdemes! A destruktor használata . 57 Összetett adatszerkezetek: struktúrák, felsorolások és tömbök . 58 Struktúrák . 58 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 . 60 Felsorolások. 60 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 . 74 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 . 76 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 . 79 A névtér bevezetése . 79 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 . 81 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 . 82 Mi okozhat kivételt? . 83 Kivételkezelés. 84 A try és a catch használata . 84 A kivétellel kapcsolatos információ elfogása . 85 Több catch használata egyetlen try paranccsal . 85 A kivételek kezelésének sorrendje. 86 A végső megoldás: a finally kulcsszó . 86 A leggyakoribb kivételek. 88 Saját kivételosztályok megadása . 89 Saját kivételek kiváltása . 90 Kivételek továbbdobása. 91 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 . 95 A #define és az #undef hatása . 96 Készítette: Zsótér
Csaba III. éves informatikus hallgató 3 C# Amit a C# tudni érdemes! Feltételes feldolgozás (#if, #elif, #endif) . 96 Előfeldolgozási kifejezések (!, ==,!=, &&, ||) . 96 Hibás és figyelmeztetések kezelésének előírása a kódban (#error, #warning) . 96 A sorszámok megváltoztatása . 97 A régiók rövid áttekintése. 97 Már meglévő kódrészletek újrahasznosítása öröklés segítségével . 97 Az öröklés alapja . 97 Az egyszeres és a többszörös öröklés . 98 Az öröklés egyszerű formája . 99 Alaptagfüggvények használata öröklött tagfüggvényekben . 102 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 . 113 Különböző objektumtípusok tömbjei . 114 Információ formázása és bekérése. 116 A konzolon keresztül megvalósított bemenet és kimenet . 116 Az információ formázása. 116 Számok formázása . 118 Egyedi formátumok létrehozása képleírók segítségével . 118 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 . 123 @ - a különleges karakterlánc formázó . 125 Karakterláncok építése . 126 Információ bekérése a konzolról . 127 A Read tagfüggvény használata. 128 A ReadLine tagfüggvény használata . 128 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 . 131 A felületek használata. 132 Miért használjunk felületeket?. 132 Felületek meghatározása. 133 Felület meghatározása tagfüggvényekkel . 133 Tulajdonságok megadása a felületeken belül . 135 Több felület használata . 136 Kifejezett felülettagok használata . 138 Új felületek levezetése már létező felületekből . 139 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 . 141 Hogyan használjunk egy indexelőt? . 141 A képviselők felfedezése . 143 Események használata . 146 Események létrehozása . 147 Készítette: Zsótér Csaba III. éves informatikus hallgató 4 C# Amit a C# tudni érdemes! Az eseményképviselők működése . 147 Származtatás az EventArgs osztályból . 147 Az Event osztály kódjának használata . 148 Eseménykezelők létrehozása . 149 Az események és eseménykezelők egymáshoz rendelése . 150 Több
eseménykezelő egyetlen eseményhez . 151 Eseménykezelők eltávolítása . 153 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. 155 Az alapvető kéttényezős matematikai műveletek túlterhelése . 156 Az alapvető egytényezős matematikai műveletek túlterhelése . 157 Az összehasonlító és logikai műveletek túlterhelése . 159 A logikai műveletek túlterhelése . 161 Összefoglalás: A C# túlterhelhető műveletei. 163 Összefoglalás: A C# nem túlterhelhető műveletei . 163 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 . 164 Az ECMA szabványok használata. 164 Tájékozódás a környezet osztályairól . 165 Könyvtár- és rendszeradatok kezelése . 165 Matematikai eljárások használata . 166 Fájlkezelés . 167 A fájlok adatai . 168 Egyszerű adatfájlok kezelése . 169
Ismerkedés a folyamokkal . 170 A fájlolvasás menete. 170 Fájlok létrehozása és megnyitása. 170 Szöveg fájlba írása . 171 Szöveg olvasása fájlból . 171 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. 174 Az Application.Run tagfüggvény működése 176 Ablakok testreszabása . 177 Ablakok átméretezése . 179 Az ablak színeinek és hátterének megváltoztatása . 181 Az ablak szegélyének módosítása . 182 Vezérlők elhelyezése az ablakon . 183 Címkék és szövegmegjelenítés . 184 Gombok használata . 185 Gombesemények . 186 OK gomb készítése . 188 Szövegmezők használata . 189 Windows alkalmazások készítése . 192 Választógombok használata . 192 Tárolók használata . 194 Listamezők használata . 197 Menük az ablakokon. 200 Bejelölhető menüelemek . 203 Helyi menü készítése . 206 Készítette:
Zsótér Csaba III. éves informatikus hallgató 5 C# Amit a C# tudni érdemes! A MessageBox osztály . 208 A Microsoft Windows beépített párbeszédablakainak használata . 210 Saját párbeszédablak készítése . 212 Felhasznált irodalom. 215 Készítette: Zsótér Csaba III. éves informatikus hallgató 6 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 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.icsharpcodenet 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.premiacom 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.sourceforgenet 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 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 Radiuscs 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 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 Hellocs class Hello { public static void Main() { System.ConsoleWriteLine("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 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 ASPNET 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ás Az 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 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ág A 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és Az ö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ág Az 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 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ályok Az 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áris A 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 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 JScriptNET által
használtak A C# programok A C# alkalmazások felépítése 3.1 kódszöveg Appcs // 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.14159 The 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 C# Amit a C# tudni érdemes! A megjegyzésekről A 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és Ilyeneket 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és A 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és A 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 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>,<seeals o>,<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 – Xmlappcs // 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.ConsoleWriteLine("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 32 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> Összefoglaló leírás az osztályról.</summary> Készítette: Zsótér Csaba III. éves informatikus hallgató 16 C# Amit a C# tudni érdemes!
<remarks> Ez egy hosszabb megjegyzés melyben részletesebben leírhatjuk az osztályt. </remarks> </member> <member name="M:Xmlapp.Main(SystemString[])"> <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ökkel A 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 31 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: int radius = 4 ; Nos ez nem valami olvasható, de működik. Készítette: Zsótér Csaba III. éves informatikus hallgató 17 ; C# Amit a C# tudni érdemes! 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övei A kulcsszavak különleges kifejezések egyedi jelentéssel, melyek a programnyelvek alapját adják. A C# kulcsszavait a 31 táblázatban soroljuk fel: 3.1 táblázat A C#
kulcsszavai abstract byte class delegate event fixed if internal new override readonly short struct try unsafe while as case const do explicit float implicit is null params ref sizeof switch typeof ushort base catch continue double extern for in lock object private return stackalloc this uint using bool char decimal else false foreach int long operator protected sbyte static throw ulong virtual break checked default enum finally goto interface namespace out public sealed string true unchecked void 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álok A 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ók A 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 31 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 A C# alkalmazások szerkezete Készítette: Zsótér Csaba III. éves informatikus hallgató 18 C# Amit a C# tudni érdemes! 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#-ban A kifejezés olyan kódrészlet, amelyek kulcsszavakból állnak. A következők például egyszerű
kifejezések: PI = 3.14159 PI * 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ás Van 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 Szazalek y2x5 w7h3 eves koltseg 2010 ado szamlalo#ell double 9byte Helyessége helyes helyes helyes helyes, de nem ajánlott helytelen – a # nem használható karakter helytelen, hiszen C# kulcsszó helytelen, ugyanis az első karakter számjegy Készítette: Zsótér Csaba III. éves informatikus hallgató 19 C# Amit a C# tudni érdemes! A tiszta nagybetűs neveket általában állandóknak adjuk. A változók használata Mielő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óknak A 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 C# Amit a C# tudni érdemes! Változók kezdőérték nélkül Ha 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 Byte Short Ushort Int Uint Long Ulong 8 bit előjeles 8 bit előjel nélküli 16 bit előjeles 16 bit előjel nélküli 32 bit előjeles 32 bit előjel nélküli 64 bit előjeles 64 bit előjel nélküli -128 127 0 255 -32768 32767 0 65535 -2147483648 2147483647 0 4294967295 - 92233720368547758 92233720368547758 07 08 0 18446744073709551 615 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 C# Amit a C# tudni érdemes! Lebegőpontos értékek Nem 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 Double 15 5x10-324 3.4x1038 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 -,90et 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ékek Logikai 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örnyezetben A 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 sbyte byte short Készítette: Zsótér Csaba III. éves informatikus hallgató .NET adattípus System.SByte System.Byte System.Int16 22 C# Amit a C# tudni érdemes! ushort int uint long ulong char float double bool decimal System.UInt16 System.Int32 System.UInt32 System.Int64 System.UInt64 System.Char System.Single System.Double System.Boolean 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álok Ha 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 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álok Amint 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álok A 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álok A 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ása A 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 C# Amit a C# tudni érdemes! Hivatkozási típusok A 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ése Ké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.ConsoleWriteLine() System.ConsoleWrite() 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.ConsoleWriteLine(„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.ConsoleWriteLine(„Í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.ConsoleWriteLine(„Az 1érték {0}, a 2pedig {1}”,123, „Csaba”); Készítette: Zsótér Csaba III. éves informatikus hallgató 25 C# Amit a C# tudni érdemes! A képernyőn a következő jelenik meg: Az 1.érték 123, a 2pedig Csaba A számozás mindig nullával kezdődik! Változók értékének módosítása műveletekkel A 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űveletek Az 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űveletek Nem 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 C# Amit a C# tudni érdemes! 3.3 Két változó összege – OsszAppcs 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űveletek Az 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 x += 4 x -= 4 x *= 4 x /= 4 x %= 4 Kifejtése x=x +4 x=x–4 x=x*4 x=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űveletek Az 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 --y, //y értékét eggyel csökkentettük Készítette: Zsótér Csaba III. éves informatikus hallgató 27 C# Amit a C# tudni érdemes! 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űveletek Programjainkban 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 > 10 5 kisebb, mint a 10, tehát igaz 5 nem nagyobb, mint a 10, tehát hamis 5 = = 10 5 != 10 5 nem egyenlő 10, tehát hamis 5 nem egyenlő 10, tehát igaz Az if utasítás A 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 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 1 Művelettípus Elsődleges 2 3 4 5 6 7 8 9 10 11 12 13 14 Egyváltozós Multiplikatív Additív Eltolási Viszonyító Egyenlőségi Logikai AND Logikai XOR Logikai OR Feltételes AND Feltételes OR Feltételes művelet Értékadás Műveletek ( ) . [ ] x++ xnew typeof sizeof checked unchecked + - ! ~ ++x –x * / % + << >> < > <= >= is == != & ^ | && || ?: = *= /= %= += -= <<= >>= &= ^= 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é. Készítette: Zsótér Csaba III. éves informatikus hallgató 29 C# Amit a C# tudni érdemes! Adattípusok átalakítása Ha á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 sbyte byte short ushort int uint long ulong char float double decimal Céltípus byte, ushort, uint, ulong, char sbyte, char sbyte, byte, ushort, uint, ulong, char sbyte, byte, short, char sbyte, byte, short, ushort, uint, ulong, char sbyte, byte, short, ushort, int, char sbyte, byte, short, ushort, int, uint, ulong, char sbyte, byte, short, ushort, int, uint, long, char sbyte, byte, short sbyte, byte, short, ushort, int, uint, long, ulong, char, decimal sbyte, byte, short, ushort, int, uint, long, ulong, char, float, decimal sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double
Típusléptetés Az 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 Készítette: Zsótér Csaba III. éves informatikus hallgató 30 C# Amit a C# tudni érdemes! 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: int uint long ulong float double decimal 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ások A 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 if Elevenítsük fel, hogy hogyan is működik az if utasítás: if(gender == ’m’ || gender == ’f’) { System.ConsoleWriteLine(„The gender is valid”); } if(gender != ’m’ && gender != ’f’) { System.ConsoleWriteLine(„The gender value, {0} valid”,gender); } is not 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ő:
Készítette: Zsótér Csaba III. éves informatikus hallgató 31 C# Amit a C# tudni érdemes! 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ása A 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. Az if
utasítások felhalmozása Az 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 Készítette: Zsótér Csaba III. éves informatikus hallgató 32 C# Amit a C# tudni érdemes! class Stacked { static void Main() { char gender = x; if( gender == m ) { System.ConsoleWriteLine("The gender is male"); } else if ( gender == f ) { System.ConsoleWriteLine("The gender is female"); } else { System.ConsoleWriteLine("The gender value, {0}, is not valid", gender); } System.ConsoleWriteLine("The if statement is now over!"); } } A switch utasítás A 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ód break; case eredmény 2: //az eredmény 2-hez tartozó kód break; . case eredmény n: //az eredmény n-hez tartozó kód break; default: //az alapértelmezésben végrehajtott kód break; } 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. Készítette: Zsótér Csaba III. éves informatikus hallgató 33 C# Amit a C# tudni érdemes! 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 SystemRandom(); roll = (int) rnd.Next(1,7); System.ConsoleWriteLine("Starting the switch "); switch (roll) { case 1: System.ConsoleWriteLine("Roll is 1"); break; case 2: System.ConsoleWriteLine("Roll is 2"); break; case 3: System.ConsoleWriteLine("Roll is 3"); break; case 4: System.ConsoleWriteLine("Roll is 4"); break; case 5: System.ConsoleWriteLine("Roll is 5"); break; case 6: System.ConsoleWriteLine("Roll is 6"); break; default: System.ConsoleWriteLine("Roll is not 1 through 6"); break; } System.ConsoleWriteLine("The switch statement is now over!"); } } Egy kód – több eset Sokszor 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) { Készítette: Zsótér Csaba III. éves informatikus hallgató 34 C# Amit a C# tudni érdemes! case 1: case 3: case 5: System.ConsoleWriteLine(„A dobás páratlan!”); break; case 2: case 4: case 6: System.ConsoleWriteLine(„A dobás páros!”); Break; default: System.ConsoleWriteLine(„A között”); dobás nincs 1 és 6 } 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; case 4: goto case 6; break; case 5: System.ConsoleWriteLine(„A dobás páratlan!”); break; Készítette: Zsótér Csaba III. éves informatikus hallgató 35 C# Amit a C# tudni érdemes! case 6: System.ConsoleWriteLine(„A dobás nincs 1 és 6 között”); Break; } A switch utasítás vezérlő típusai A 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); } Kiugrás a while utasításból és a
ciklus folytatása Lehetsé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. Készítette: Zsótér Csaba III. éves informatikus hallgató 36 C# Amit a C# tudni érdemes! 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.ConsoleWriteLine("{0}", ctr); } }
System.ConsoleWriteLine("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 { 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 Készítette: Zsótér Csaba III. éves informatikus hallgató 37 C# Amit a C# tudni érdemes! igaz, akkor a program újra végrehajtja az utasításokat, ha pedig hamis, akkor a dowhile utáni első utasítás kapja meg a vezérlést. A for utasítás Jóllehet, a while és a dowhile 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.ConsoleWriteLine(„{0}”,x) ); A foreach utasítás A 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 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. Készítette: Zsótér Csaba III. éves informatikus hallgató 38 C# Amit a C# tudni érdemes! 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(" Done"); } } Az „átkos” goto A 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. Utasítások címkézése A 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: Készítette: Zsótér Csaba III. éves informatikus hallgató 39 C# Amit a C# tudni érdemes! 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 SystemRandom(); Start: ctr++; if (ctr > 10) goto EndThis; else score = (int) rnd.Next(60, 101); System.ConsoleWriteLine("{0} - You received a score of {1}", ctr, score); goto Start; EndThis: System.ConsoleWriteLine("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. 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. Készítette: Zsótér Csaba III. éves informatikus hallgató 40 C# Amit a C# tudni érdemes! Osztályok bevezetése Ha 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 tagjai Megné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). 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. Készítette: Zsótér Csaba III. éves informatikus hallgató 41 C# Amit a C# tudni érdemes! Az adattagok, vagy mezők A 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ése Miutá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 { 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; Készítette: Zsótér Csaba III. éves informatikus hallgató 42 C# Amit a C# tudni érdemes! ending.y = 11; System.ConsoleWriteLine("Point 1: ({0},{1})", starting.x, startingy);
System.ConsoleWriteLine("Point 2: ({0},{1})", ending.x, endingy); } } 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 { 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. Készítette: Zsótér Csaba III. éves informatikus hallgató 43 C# Amit a C# tudni érdemes! Statikus változók használata Elő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.originx = 1; Line.originy = 2; // A line1 végpontjának beállítása line1.endingx = 3; line1.endingy = 4; // A line2 végpontjának beállítása line2.endingx = 7; line2.endingy = 8; // Adatok kiírása System.ConsoleWriteLine("Line 1 start: ({0},{1})", Line.originx, Lineoriginy); System.ConsoleWriteLine("line 1 end: ({0},{1})", line1.endingx, line1endingy); System.ConsoleWriteLine("Line 2 start: ({0},{1})", line.originx, lineoriginy); System.ConsoleWriteLine("line 2 end: ({0},{1}) ", line2.endingx, line2endingy); // a line2 kezdőpontjának módosítása Line.originx = 939; Line.originy = 747; // Az adatok kiírása System.ConsoleWriteLine("Line 1 start: ({0},{1})", Line.originx, Lineoriginy); System.ConsoleWriteLine("line 1 end:
({0},{1})", Készítette: Zsótér Csaba III. éves informatikus hallgató 44 C# Amit a C# tudni érdemes! line1.endingx, line1endingy); System.ConsoleWriteLine("Line 2 start: ({0},{1})", line.originx, lineoriginy); System.ConsoleWriteLine("line 2 end: ({0},{1})", line2.endingx, line2endingy); } } 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.originx = 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ály A 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ása A 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. 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; int my Y; // a my x privát (private) változó // a my Y privát (private) változó Készítette: Zsótér Csaba III. éves informatikus hallgató 45 C# Amit a C# tudni érdemes! public int x { get { return } set { my X = } } public int y { get { return } set { my Y = } } my X; value; my Y; value; } A névterekről Ha 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.ConsoleWriteLine(„Zsótér Csaba”); 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.ConsoleWriteLine 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. Készítette: Zsótér Csaba III. éves informatikus hallgató 46 C# Amit a C# tudni érdemes! 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évterek Tö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. 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ése Fontos hogy tisztában legyünk a tagfüggvények szerkesztésével. Ehhez a következő minta ad példát: Készítette: Zsótér Csaba III. éves informatikus hallgató 47 C# Amit a C# tudni érdemes! 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ől A 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. A tagfüggvények törzsének felépítése A 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. Készítette: Zsótér Csaba III. éves informatikus hallgató 48 C# Amit a C# tudni érdemes! A tagfüggvények meghívása Ahhoz 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 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.ConsoleWriteLine("Circle 1: Center = ({0},{1})", Készítette: Zsótér Csaba III. éves informatikus hallgató 49 C# Amit a C# tudni érdemes! first.x, firsty); System.ConsoleWriteLine(" 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.ConsoleWriteLine(" Area = {0}", first.getArea()); // A first objektum circimference() tagfüggvényének meghívása és // visszaadott értékének kiíratása
System.ConsoleWriteLine(" 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.ConsoleWriteLine(" Circle 2: Center = ({0},{1})", second.x, secondy); System.ConsoleWriteLine(" Radius = {0}", second.radius); System.ConsoleWriteLine(" Area = {0}", area); System.ConsoleWriteLine(" Circum = {0}", circ); } } Adattagok elérése tagfüggvényekből Ha 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. Értékadás tagfüggvények számára Az é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 Készítette: Zsótér Csaba III. éves informatikus hallgató 50 C# Amit a C# tudni érdemes! 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. Statikus tagfüggvények A 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ői Az 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 Készítette: Zsótér Csaba III. éves informatikus hallgató 51 C# Amit a C# tudni érdemes! 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ás Amint 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ás Elő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. 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 Készítette: Zsótér Csaba III. éves informatikus hallgató 52 C# Amit a C# tudni érdemes! { 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(" --------- "); 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); } } A kimeneti elérés használata A 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 Készítette: Zsótér Csaba III. éves informatikus hallgató 53 C# Amit a C# tudni érdemes!
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 Console.WriteLine(" Half nbr Console.WriteLine(" Squared nbr Console.WriteLine(" Cubed nbr = = = = {0}", {0}", {0}", {0}", nbr); Half nbr); Squared nbr); 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. 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ények E tagfüggvényekkel – nevük get és set már dolgoztunk. Feladatuk, hogy lehetővé tegyék privát adatok használatát. Készítette: Zsótér Csaba III. éves informatikus hallgató 54 C# Amit a C# tudni érdemes! Konstruktorok Az 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ánykonstruktor A 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 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) Készítette: Zsótér Csaba III. éves informatikus hallgató 55 C# Amit a C# tudni érdemes! { Constructor ujobj = new Constructor(); Console.WriteLine("Calling Constructor method"); Console.WriteLine("ujobjszam1 = {0}",ujobjszam1); Console.WriteLine("ujobjszam2 = {0}",ujobjszam2); Constructor masikObj = new Constructor(); Console.WriteLine("Calling Constructor method"); Console.WriteLine("masikObjszam1 = {0}",masikObjszam1); Console.WriteLine("masikObjszam2 = {0}",masikObjszam2); Console.WriteLine("Set new values"); masikObj.szam1 = 15; masikObj.szam2 = 20; Console.WriteLine("masikObjszam1 = {0}",masikObjszam1); Console.WriteLine("masikObjszam2 = {0}",masikObjszam2); } } } Statikus konstruktorok Az 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; 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) Készítette: Zsótér Csaba III. éves informatikus hallgató 56 C# Amit a C# tudni érdemes! { Konstruktor obj1 = new Konstruktor(); Konstruktor obj2 = new Konstruktor(); Konstruktor obj3 = new Konstruktor(); } } } Destruktorok Lehető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. A destruktor használata A 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 { Készítette: Zsótér Csaba III. éves informatikus hallgató 57 C# Amit a C# tudni érdemes! 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"); } } } 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ák A 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. Készítette: Zsótér Csaba III. éves informatikus hallgató 58 C# Amit a C# tudni érdemes! Az osztályok és struktúrák közti különbség Jó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 tagjai A 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 starting.y ending.x = ending.y = = 1; = 4; 10; 11; System.ConsoleWriteLine("Pont 1: ({0},{1})", starting.x, startingy);
System.ConsoleWriteLine("Pont 2: ({0},{1})", ending.x, endingy); } } 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; Készítette: Zsótér Csaba III. éves informatikus hallgató 59 C# Amit a C# tudni érdemes! public Pont ending; } A struktúrák tagfüggvényei Az 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 konstruktorai A 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. 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 destruktorai A 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ások A 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 Készítette: Zsótér Csaba III. éves informatikus hallgató 60 C# Amit a C# tudni érdemes! 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() { 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.ConsoleWriteLine(" Switched to Red"); break; case Color.white: System.ConsoleWriteLine(" Switched to White"); break; case Color.blue: Készítette: Zsótér Csaba III. éves informatikus hallgató 61 C# Amit a C# tudni érdemes! System.ConsoleWriteLine(" Switched to Blue"); break; default: System.ConsoleWriteLine(" Switched to default"); break; } System.ConsoleWriteLine(" Color is {0}
({1})", myColor, (int) myColor); } } A felsorolások alapértelmezett értékeinek módosítása A 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, 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; Készítette: Zsótér Csaba III. éves informatikus hallgató 62 C# Amit a C# tudni érdemes! MyBirthday.bmonth = MonthAugust; MyBirthday.bday = 11; MyBirthday.byear = 1981; // This is a lie. System.ConsoleWriteLine("My birthday is {0} {1}, {2}", MyBirthday.bmonth, MyBirthdaybday, MyBirthdaybyear); } } A felsorolás alapjául szolgáló típus módosítása Az 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ök A 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. 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; Készítette: Zsótér Csaba III. éves informatikus hallgató 63 C# Amit a C# tudni érdemes! 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 SystemRandom(); for (int indx = 0; indx < 12; indx++ ) { 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)); } } Készítette: Zsótér Csaba III. éves informatikus hallgató 64 C# Amit a C# tudni érdemes! A tömbelemek kezdeti beállítása A 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,200000m,300000m,400000m,500000m,600000m}; É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,200000m,300000m,400000m,500000m,600000m}; 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ök A 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: 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] betuk[0,1] betuk[0,2] betuk[1,0] betuk[1,1] = = = = = ’a’; ’b’; ’c’; ’X’; ’Y’; Készítette: Zsótér Csaba III. éves informatikus hallgató 65 C# Amit a C# tudni érdemes! betuk[1,2] = ’Z’; Különböző méretű tömböket tartalmazó tömbök Az 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álata Meg 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: 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 "); Console.WriteLine("Length of name array {0}", nameLength); Készítette: Zsótér Csaba III. éves informatikus hallgató 66 C# Amit a C# tudni érdemes! for( int ctr = 0; ctr < name.Length; ctr++) Console.WriteLine("Length of name[{0}] is {1}", ctr, name[ctr].Length); //------------------------------------------------------------Console.WriteLine(" Displaying the content
of the name array."); for( int ctr = 0; ctr < name.Length; ctr++) { Console.Write(" "); // new line for( int ctr2 = 0; ctr2 < name[ctr].Length; ctr2++ ) { Console.Write("{0}", name[ctr][ctr2]); } } Console.WriteLine(" Done displaying"); } } Tömbök használata osztályokban és struktúrákban A 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ése Minden 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 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; Készítette: Zsótér Csaba III. éves informatikus hallgató 67 C# Amit a C# tudni érdemes! 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() { 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(" Passing a radius of 3"); myCircle.Area( 3 ); Console.WriteLine(" Passing a center of (2, 4) and a radius of 3."); Készítette: Zsótér Csaba III. éves informatikus hallgató 68 C# Amit a C# tudni érdemes! myCircle.Area( 2, 4, 3 ); Console.WriteLine(" Passing 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ése A 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; 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; } Készítette: Zsótér Csaba III. éves informatikus hallgató 69 C# Amit a C# tudni érdemes! 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 Console.WriteLine(" Radius Console.WriteLine(" Area Console.WriteLine(" Circum } = = = = ({0},{1})", x, y); {0}", radius); {0}", area()); {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); Console.WriteLine(" First Circle:"); first.print circle info(); Console.WriteLine(" Second Circle:"); second.print circle info(); Console.WriteLine(" Third Circle:"); third.print circle info(); Console.WriteLine(" Fourth 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 Készítette: Zsótér Csaba III. éves informatikus hallgató 70 C# Amit a C# tudni érdemes! A tagfüggvények aláírása A 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 double double double Area() Area( double ) Area( int, int, 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( MyFunc( MyFunc( MyFunc( int ) float ) ref int ) val int ) 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[]) Készítette: Zsótér Csaba III. éves informatikus hallgató 71 C# Amit a C# tudni érdemes! 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álata Mostanra 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 ConsoleWriteLine és a
ConsoleWrite 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 ) { 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 ); Készítette: Zsótér Csaba III. éves informatikus hallgató 72 C# Amit a C# tudni érdemes! 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ípussal Az 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; 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"; Készítette: Zsótér Csaba III. éves informatikus hallgató 73 C# Amit a C# tudni érdemes! Console.WriteLine("First call"); Garbage.Print( 1 ); Console.WriteLine(" Second call");
Garbage.Print( ); Console.WriteLine(" Third call"); Garbage.Print( ALong, ADec, Abyte, AString ); Console.WriteLine(" Fourth 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]; 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ése Azt 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. Készítette:
Zsótér Csaba III. éves informatikus hallgató 74 C# Amit a C# tudni érdemes! 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]); } } } } Az első kimenet azt mutatja, hogy mi történik, ha parancssori paraméterek nélkül hívjuk meg a programot. C:gyakCommandLine>CommanLine No 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:gyakCommandLine>CommanLine xxx 123 456 789.012 Argumentum 1 is xxx Argumentum 2 is 123 Argumentum 3 is 456 Argumentum 4 is 789.012 Készítette: Zsótér Csaba III. éves informatikus hallgató 75 C# Amit a C# tudni érdemes! Hatókörök A 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ókkal A 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); } } 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. Készítette: Zsótér Csaba III. éves informatikus hallgató 76 C# Amit a C# tudni érdemes! Osztály hatókörének megváltoztatása módosítókkal Emlé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. 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; Készítette: Zsótér Csaba III. éves informatikus hallgató 77 C# Amit a C# tudni érdemes! } 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álata Ha 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: 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; Készítette: Zsótér Csaba III. éves informatikus hallgató 78 C# Amit a C# tudni érdemes! 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ése A 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). A névtér elnevezése Egy 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ése A 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 Készítette: Zsótér Csaba III. éves informatikus hallgató 79 C# Amit a C# tudni érdemes! { 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; } } } class MyMathApp { public static void Main() { long Result = 0; Result = MyMath.RoutineAdd( 1, 2, 3 ); Console.WriteLine("Add result is {0}", Result); Result = MyMath.RoutineSubtract( 5, 2 ); Console.WriteLine("Subtract result is {0}", Result); Console.WriteLine(" The value of PI is {0}",
ConstsPIvalue ); Console.WriteLine("The value of three is {0}", Consts.threevalue ); } } Készítette: Zsótér Csaba III. éves informatikus hallgató 80 C# Amit a C# tudni érdemes! 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évterek A 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ése Korá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. Álnevek használata a using segítségével A 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 { Készítette: Zsótér Csaba III. éves informatikus hallgató 81 C# Amit a C# tudni érdemes! 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 alapfogalmak Amikor 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 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ással Munká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. Készítette: Zsótér Csaba III. éves informatikus hallgató 82 C# Amit a C# tudni érdemes! Í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 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: Készítette: Zsótér Csaba III. éves informatikus hallgató 83 C# Amit a C# tudni érdemes! Kivételkezelés A 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. A try és a catch használata A 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];
Készítette: Zsótér Csaba III. éves informatikus hallgató 84 C# Amit a C# tudni érdemes! 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ása Az 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 SystemException 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: 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: {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; Készítette: Zsótér Csaba III. éves informatikus hallgató 85 C# Amit a C# tudni érdemes! 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(" Done with the catch statements Done with program."); } } A kivételek kezelésének sorrendje A 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 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 Készítette: Zsótér Csaba III. éves informatikus hallgató 86 C# Amit a C# tudni érdemes! 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 ) { Console.WriteLine("Exception during read/write: {0} ", e); } finally { fstr.Close(); } } } catch (System.IOFileNotFoundException) { Console.WriteLine ("ListFile could not find the file {0}", args[0]); } catch (Exception e) { Console.WriteLine("Exception: {0} ", e); } } } Készítette: Zsótér Csaba III. éves informatikus hallgató 87 C# Amit a C# tudni érdemes! 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ételek A .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 MemberAccessException ArgumentException ArgumentNullException Kivétel neve ArithmeticException ArrayTypeMismatchException DivideByZeroException Leírás Hozzáférési hiba. Egy
bizonyos tag, például egy tagfüggvény nem hozzáférhető Paraméterhiba. Egy tagfüggvény valamelyik paramétere hibás. Null paraméter. Egy tagfüggvény null értéket kapott paraméterként, de az nem elfogadható. Leírás 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. Tömbtípussal kapcsolatos hiba. Ilyen hiba akkor keletkezik, ha egy tömbben nem a neki megfelelő adattípust akarjuk tárolni. Nullával való osztás. Nevének megfelelően akkor keletkezik, ha egy műveletben nullával akarunk osztani. Készítette: Zsótér Csaba III. éves informatikus hallgató 88 C# Amit a C# tudni érdemes! FormatException IndexOutOfRangeException InvalidCastException MulticastNotSupportedException NotFiniteNumberException NotSupportedException NullReferenceException OutOfMemoryException OverflowException StackOverflowException
TypeInitializationException Hibás formátum. Valamelyik paraméternek hibás a formátuma. 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. É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. A többes művelet (multicast) nem támogatott. Ilyen hiba két nem null képviselő érvénytelen kombinálásakor keletkezik. A megadott, vagy keletkezett érték nem véges szám. Ez gyakorlatilag egy hibás számalakot jelent. 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. Null értékre való hivatkozás. Ilyen hiba akkor keletkezik, ha hivatkozáson keresztül egy null objektum tartalmához akarunk hozzáférni. 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. 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. Veremtúlcsordulás lépett fel. Ilyen hiba akkor keletkezik, ha a veremben már túl sok művelet van. Hibás típusbeállítás. Ilyenkor egy static típusú konstruktorral van valami gond. Saját kivételosztályok megadása A 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. Készítette: Zsótér Csaba III. éves informatikus hallgató 89 C# Amit a C# tudni érdemes! 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; Saját kivételek kiváltása A 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 {} Készítette: Zsótér Csaba III. éves informatikus hallgató 90 C# Amit a C# tudni érdemes! 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 dont like adding threes."); catch (Exception e) { Console.WriteLine("Exception caught: {0}", e); } Console.WriteLine(" At end of program"); } } class MyMath { static public int AddEm(int x, int y) { if(x == 3 || y == 3) throw( new MyThreeException() ); return( x + y ); } } 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 dont like adding threes. Kivételek továbbdobása Az 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 Készítette: Zsótér Csaba III. éves informatikus hallgató 91 C# Amit a C# tudni érdemes! 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étellogika throw(e); //az e az a paraméter, amit ez a catch kap } Ellenőrzött és ellenőrizetlen parancsok használata A 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 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); Készítette: Zsótér Csaba III. éves informatikus hallgató 92 C# Amit a C# tudni érdemes! } } } } 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álata A 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 #define #else #elif #endregion #endif #if #error #line #region #undef #warning Leírás Egy szimbólumot határoz meg. Egy else blokkot nyit meg. Az if és az else parancs kombinációja. Egy régió végét jelzi. Egy #if blokk végét jelzi. Egy értéket vizsgál. A megadott hibaüzenetet küldi ki fordítás közben. A forráskód adott sorának sorszámát
jeleníti meg. Tartalmazhat fájl nevet is, ami szintén megjelenik a kimeneten. 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ó. Meghatározatlanná tesz egy szimbólumot. A megadott figyelmeztetést küldi ki fordítás közben. Készítette: Zsótér Csaba III. éves informatikus hallgató 93 C# Amit a C# tudni érdemes! Az előfeldolgozással kapcsolatos deklarációk A 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 #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"); } Készítette: Zsótér Csaba III. éves informatikus hallgató 94 C# Amit a C# tudni érdemes! 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} - ", bufferLength); #endif Console.WriteLine(buffer); } myFile.Close(); } } } Értékek megadása a parancssorban Tö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. Készítette: Zsótér Csaba III. éves informatikus hallgató 95 C# Amit a C# tudni érdemes! A #define és az #undef hatása Bá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ú (ifelse), illetve többágú (ifelif) 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 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. Készítette: Zsótér Csaba III. éves informatikus hallgató 96 C# Amit a C# tudni érdemes! A sorszámok megváltoztatása A 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 } } A régiók rövid áttekintése Az 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 alapja Valamennyien ö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 Készítette: Zsótér Csaba III. éves informatikus hallgató 97 C# Amit a C# tudni érdemes! 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. 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és A 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. Készítette: Zsótér Csaba III. éves informatikus hallgató 98 C# Amit a C# tudni érdemes! Az öröklés egyszerű formája Az ö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 private string private string private int firstName; middleName; lastName ; 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(" "); } Készítette: Zsótér Csaba III. éves informatikus hallgató 99 C# Amit a C# tudni érdemes! 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 { 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;
Készítette: Zsótér Csaba III. éves informatikus hallgató 100 C# Amit a C# tudni érdemes! 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 { 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; Készítette:
Zsótér Csaba III. éves informatikus hallgató 101 C# Amit a C# tudni érdemes! } 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}", mehireYear); me.displayAge(); you.displayFullName(); Console.WriteLine("Year hired of him: {0}", youhireYear); 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 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ényekben A 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(); Készítette: Zsótér Csaba III. éves informatikus hallgató 102 C# Amit a C# tudni érdemes! } 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ályok Mostanra 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 { 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 { Készítette: Zsótér Csaba III. éves informatikus hallgató 103 C# Amit a C# tudni érdemes! 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}", mehireYear); Brad.displayFullName(); } } 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: ",BradhireYear); 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 Készítette: Zsótér Csaba III. éves informatikus hallgató 104 C# Amit a C# tudni érdemes! 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ényekkel Az 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ő 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) { Készítette: Zsótér Csaba III. éves informatikus hallgató 105 C# Amit a C# tudni érdemes! 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() { } 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(); Készítette: Zsótér Csaba III. éves informatikus hallgató 106 C# Amit a C# tudni érdemes! 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ályokkal Az 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. 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; Készítette: Zsótér Csaba III. éves informatikus hallgató 107 C# Amit a C# tudni érdemes! 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() { } 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"); Készítette: Zsótér Csaba III. éves informatikus hallgató 108 C# Amit a C# tudni érdemes! 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ása Az 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; 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(); Készítette: Zsótér Csaba III. éves informatikus hallgató 109 C# Amit a C# tudni érdemes! Console.WriteLine("PI = {0}", myNumbersPI); numbers moreNumbers = new numbers(); Console.WriteLine("PI = {0}", moreNumbersPI); Console.WriteLine("myVal = {0}", moreNumbersmyVal); // // // } } 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ály A 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ése Egy 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: 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() { Készítette: Zsótér Csaba III. éves informatikus hallgató 110 C# Amit a C# tudni érdemes! Console.WriteLine("PI = {0}", PIval()); Object x = new PI(); Console.WriteLine("ToString: {0}", xToString()); Console.WriteLine("Type: {0}", xGetType()); Console.WriteLine("ToString: {0}", 123ToString()); Console.WriteLine("Type: {0}", 123GetType()); } } 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 SystemInt32, ami egyben nem más, mint a .NET keretrendszerben a szabványos int típus szinonimája Becsomagolás és kicsomagolás Most 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. 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; Készítette: Zsótér Csaba III. éves informatikus hallgató 111 C# Amit a C# tudni érdemes! Console.WriteLine("val: {0}", val); Console.WriteLine("boxed: {0}", boxed); Console.WriteLine("unboxed: {0}", unboxed); Console.WriteLine(" Types"); Console.WriteLine("val: {0}", valGetType()); Console.WriteLine("boxed: {0}", boxedGetType()); Console.WriteLine("unboxed: {0}", unboxedGetType()); } } Az is és as kulcsszavak használata – osztályok típusának átalakítása Van 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álata Né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: 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() Készítette: Zsótér Csaba III. éves informatikus hallgató 112 C# Amit a C# tudni érdemes! { 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"); 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álata Az 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ő: Készítette: Zsótér Csaba III. éves informatikus
hallgató 113 C# Amit a C# tudni érdemes! 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ömbjei Amint 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; 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() { } Készítette: Zsótér Csaba III. éves informatikus hallgató 114 C# Amit a C# tudni érdemes! 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; do { do { Console.Write(" Enter c for Contractor, e for Employee then press ENTER: "); buffer = Console.ReadLine(); } while (buffer == ""); if ( buffer[0] == c || buffer[0] == C ) { Console.Write(" Enter the contractors name: "); buffer = Console.ReadLine(); Contractor contr = new Contractor(buffer); myCompany[ctr] = contr as Person; } else if ( buffer[0] == e || buffer[0] == E ) { Console.Write(" Enter the employees name: "); buffer = Console.ReadLine(); Employee emp = new Employee(buffer); myCompany[ctr] = emp as Person; Készítette: Zsótér Csaba III. éves informatikus hallgató 115
C# Amit a C# tudni érdemes! } else { Person pers = new Person("Not an Employee or Contractor"); myCompany[ctr] = pers; } ctr++; } while ( ctr < 5 ); Console.WriteLine( " ==========================="); 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( "==========================="); } } 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 kimenet A 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ása Amikor 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ő. Készítette: Zsótér Csaba III. éves informatikus hallgató 116 C# Amit a C# tudni érdemes! 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 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 Készítette: Zsótér Csaba III. éves informatikus hallgató 117 C# Amit a C# tudni érdemes! 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ása Szá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 kimenetre C 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 -1234567 E vagy e Hatványalak x.xxxxxxE+xxx 1.234567E+123 x.xxxxxxe+xxx 1.234567e+123 -x.xxxxxxE+xxx -1.23456E+123 -x.xxxxxxe+xxx -1.23456e+123 x.xxxxxxE-xxx 1.23456E-123 x.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.67 X vagy x Hexadecimális 12d687 12D687 érték G vagy g Általános Változó (a legtömörebb formátumot használja) formátum R 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ével Vannak 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észítette: Zsótér Csaba III. éves informatikus hallgató 118 C# Amit a C# tudni érdemes! 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ás 0 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. (100 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 ( ). ’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(" Zero"); Console.WriteLine("{0} -->{0:0000000}", var1);
Console.WriteLine("{0} -->{0:0000000}", var2); // Térköz formázó Console.WriteLine(" Space"); Console.WriteLine("{0} -->{0:0####}<--", var1); Console.WriteLine("{0} -->{0:0####}<--", var2); // Csoportválasztó és többszöröző (,) Console.WriteLine(" Group 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(" Percentage"); Console.WriteLine("{0} -->{0:0%}<--", var1); Console.WriteLine("{0} -->{0:0%}<--", var2); // Literális formázás Console.WriteLine(" Literal Formatting"); Console.WriteLine("{0} -->{0:My Number: 0}<--", var1); Console.WriteLine("{0} -->{0:My Number: 0}<--",
var2); Console.WriteLine(" {0} -->{0:Mine: 0}<--", var1); Console.WriteLine("{0} -->{0:Mine: 0}<--", var2); } } Készítette: Zsótér Csaba III. éves informatikus hallgató 119 C# Amit a C# tudni érdemes! A dátum és idő formázása A 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ése A 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.Now DateTime.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: Date Month Day Year DayOfWeek DayOfYear TimeOfDay Hour Minute Second Millisecond Ticks A DateTime objektum dátummal kapcsolatos részét adja vissza. A DateTime objektumban tárolt
hónap értékét adja vissza. A DateTime objektumban tárolt nap értékét adja vissza (a számformában megadott dátum nap értéke.) A DateTime objektumban tárolt év értékét adja vissza. Azt adja vissza, hogy a DateTime objektumban tárolt időpont az adott hét hányadik napjának felel meg. Azt adja vissza, hogy a DateTime objektumban tárolt időpont az adott év hányadik napjának felel meg. A DateTime objektumban tárolt időpont értékét adja vissza. A DateTime objektumban tároltóra értékét adja vissza. A DateTime objektumban tárolt percek értékét adja vissza. A DateTime objektumban tárolt másodpercek értékét adja vissza. A DateTime objektumban tárolt ezredmásodpercek értékét adja vissza. 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. Készítette: Zsótér Csaba III. éves informatikus hallgató 120 C# Amit a C# tudni érdemes! A
dátum és idő formázása Szá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 d D f F g G M vagy m R vagy r s t T u U Y vagy y Rövid dátum Hosszú dátum Teljes dátum és rövid idő Teljes dátum és teljes idő Rövid dátum és rövid idő Rövid dátum és hosszú idő Hónap és nap RFC1123 Rendezhető (sortable) Rövid idő Hosszú idő Rendezhető (univerzális) Rendezhető (univerzális) Én és hónap mm/dd/yyyy nap, hónap dd, yyyy nap, hónap dd, yyyy hh:mm AM/PM nap, hónap dd, yyyy hh:mm:ss AM/PM mm/dd/yyyy HH:mm mm/dd/yyyy HH:mm:ss AM/PM hónap nap ddd, dd hónap yyyy hh:mm:ss GMT yyyy-mm-dd hh:mm:ss May 06
Sun, 06 May 2001 13:30:55 GMT 2001-05-06 T13:30:55 hh:mm: AM/PM hh:mm:ss AM/PM yyyy-mm-dd hh:mm:ss 13:30 PM 13:30:55 PM 2001-05-06 13:30:55Z nap, hónap dd, yyyy hh:mm:ss AM/PM hónap, yyyy Sunday, May 06, 2001 13:30:55 PM 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: Console.WriteLine("D: Console.WriteLine("f: Console.WriteLine("F: Console.WriteLine("g: Console.WriteLine("G: Console.WriteLine("m: Console.WriteLine("M: Console.WriteLine("r: Példa a keletkező kimenetre 5/6/2001 Sunday, May 06, 2001 Sunday, May 06, 2001 13:30 PM Sunday, May 06, 2001 13:30:55 PM 6/5/2001 13:30 PM {0:d}", {0:D}", {0:f}", {0:F}", {0:g}", {0:G}", {0:m}", {0:M}", {0:r}", Készítette: Zsótér Csaba III. éves informatikus hallgató CurrTime CurrTime CurrTime CurrTime CurrTime CurrTime CurrTime
CurrTime CurrTime 121 ); ); ); ); ); ); ); ); ); 6/5/2001 13:30:55 PM C# Amit a C# tudni érdemes! Console.WriteLine("R: Console.WriteLine("s: Console.WriteLine("t: Console.WriteLine("T: Console.WriteLine("u: Console.WriteLine("U: Console.WriteLine("y: Console.WriteLine("Y: {0:R}", {0:s}", {0:t}", {0:T}", {0:u}", {0:U}", {0:y}", {0:Y}", CurrTime CurrTime CurrTime CurrTime CurrTime CurrTime CurrTime CurrTime ); ); ); ); ); ); ); ); } } Felsorolások tagjainak megjelenítése A 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ás D vagy d A felsoroló típus adott elemének számértékét jeleníti meg G 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 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 ); Készítette: Zsótér Csaba III. éves informatikus hallgató 122 C# Amit a C# tudni
érdemes! Console.WriteLine("G: {0:G}", myPet ); Console.WriteLine("x: {0:x}", myPet ); Console.WriteLine("X: {0:X}", myPet ); Console.WriteLine(" Using 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űveletek Most 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. 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ények Szá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: Készítette: Zsótér Csaba III. éves informatikus hallgató 123 C# Amit a C# tudni érdemes! A karakterláncokkal kapcsolatban használható legfontosabb tagfüggvények Tagfüggvény Leírás A String / string osztállyal kapcsolatos statikus tagfüggvények Compare Összehasonlítja két
karakterlánc tartalmát CompareOrdinal Ú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ágok Char 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. 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. Készítette: Zsótér Csaba III. éves informatikus hallgató 124 C# Amit a C# tudni érdemes! Tagfüggvény Remove Split sWith Substring ToCharArray ToLower ToUpper Trim TrimEnd TrimStart Leírás Megadott számú karaktert töröl a karakterlánc egy megadott helyétől kezdődően. 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. 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. Az eredeti karakterlánc egy részhalmazát adja vissza a megadott helytől kezdődően. A kérdéses karakterlánc tartalmát a kimenetként megadott char típusú tömbbe másolja. Az adott karakterlánc tartalmát csupa kisbetűvel megjelenítve adja vissza. Az adott karakterlánc tartalmát csupa nagybetűvel megjelenítve adja vissza. Eltávolítja a megadott karakterláncot a kérdéses karakterlánc elejéről és végéről. Eltávolít egy megadott karakterláncot a kérdéses karakterlánc végéről. Eltávolít egy megadott karakterláncot a kérdéses karakterlánc elejéről. @ - 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.ConsoleWriteLine(""Hello World!""); Ha egy fordított perjelet akarunk kiíratni, szintén ilyen vezérlő karaktereket kell használnunk: System.ConsoleWriteLine("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.ConsoleWriteLine(@"My code: C:JegyzetC# jegyzet"); Készítette: Zsótér Csaba III. éves informatikus hallgató 125 C# Amit a C# tudni érdemes! 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.ConsoleWriteLine(@"""Hello World!"""); Karakterláncok építése A 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ás
Append 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. 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: Készítette: Zsótér Csaba III. éves informatikus hallgató 126 C# Amit a C# tudni érdemes! 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(" Enter your first name: "); buffer = Console.ReadLine(); if ( buffer != null ) { name.Append(buffer); marker = name.Length; } Console.Write(" Enter your last name: "); buffer = Console.ReadLine(); if ( buffer != null ) { name.Append(" "); name.Append(buffer); } Console.Write(" Enter your middle name: "); buffer = Console.ReadLine(); if ( buffer != null ) { name.Insert(marker+1, buffer); name.Insert(marker+bufferLength+1, " "); } Console.WriteLine(" Full name: {0}", name); Console.WriteLine(" Info about StringBuilder string:"); Console.WriteLine("value: {0}", name); Console.WriteLine("Capacity: {0}", nameCapacity); Console.WriteLine("Maximum Capacity: {0}", nameMaxCapacity); Console.WriteLine("Length: {0}", nameLength); } } Információ bekérése a konzolról Az 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á. Készítette: Zsótér Csaba III. éves informatikus hallgató 127 C# Amit a C# tudni érdemes! A Read tagfüggvény használata A 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(); if ( ival == - 1 ) break; ch = ( char ) ival; Input.Append(ch); } Console.WriteLine(" ==========> "); Console.Write( Input ); Console.Write(" "); } } A ReadLine tagfüggvény használata A 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; Készítette:
Zsótér Csaba III. éves informatikus hallgató 128 C# Amit a C# tudni érdemes! 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(" "); } Console.WriteLine(" ==========> "); Console.Write( Input ); Console.Write(" "); } } A Convert osztály használata A 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 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ít ToBoolean 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á Készítette: Zsótér Csaba III. éves informatikus hallgató 129 C# Amit a C# tudni érdemes! Típusok
közti átalakítást végző tagfüggvények (folytatás) Tagfüggvény Mivé alakít ToInt16 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(); 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 didnt enter a valid number"); } catch( Exception e ) { Console.WriteLine("Something went wrong with the conversion"); throw(e); } } } Készítette: Zsótér Csaba III. éves informatikus hallgató 130 C# Amit a C# tudni érdemes! Bevezetés az objektumközpontú programozásba: felületek Felületek: előzetes Vegyü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 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 kapcsolata A 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. Készítette: Zsótér Csaba III. éves informatikus hallgató 131 C# Amit a C# tudni érdemes! 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álata Bá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. 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. Készítette: Zsótér Csaba III. éves informatikus
hallgató 132 C# Amit a C# tudni érdemes! 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ényekkel Az elméletből ennyi elég is lesz egyelőre. Lássunk valamilyen
konkrét kódot: 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(); Készítette: Zsótér Csaba III. éves informatikus hallgató 133 C# Amit a C#
tudni érdemes! 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; 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; } Készítette: Zsótér Csaba III. éves informatikus hallgató 134 C# Amit a C# tudni érdemes! } 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(" Displaying Square information:"); displayInfo(mySquare); } static void displayInfo( IShape myShape ) { Console.WriteLine("Area: {0}", myShapeArea()); Console.WriteLine("Sides: {0}", myShapeSides()); Console.WriteLine("Circumference: {0}", myShapeCircumference()); } } 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 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ül Egy 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; Készítette: Zsótér Csaba III. éves informatikus hallgató 135 C# Amit a C# tudni érdemes! 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; } }
public class Props { public static void Main() { Square mySquare = new Square(); mySquare.SideLength = 5; Console.WriteLine(" Displaying Square information:"); Console.WriteLine("Area: {0}", mySquareArea()); Console.WriteLine("Sides: {0}", mySquareSides); } } 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álata A 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 Készítette: Zsótér Csaba III. éves informatikus hallgató 136 C# Amit a C# tudni érdemes!
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(); } 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() {
Készítette: Zsótér Csaba III. éves informatikus hallgató 137 C# Amit a C# tudni érdemes! Console.WriteLine(" Displaying Square information:"); Console.WriteLine("Side length: {0}", thisSideLength); Console.WriteLine("Sides: {0}", thisSides); Console.WriteLine("Area: {0}", thisArea()); } } public class Multi { public static void Main() { Square mySquare = new Square(); mySquare.SideLength = 7; mySquare.Display(); } } Kifejezett felülettagok használata Nos, 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. 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 Készítette: Zsótér Csaba III. éves informatikus hallgató 138 C# Amit a C# tudni érdemes! { 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(" Displaying Square Shapes information:"); Console.WriteLine("Side length: {0}", thisSideLength); Console.WriteLine("Sides: {0}", thisSides); Console.WriteLine("Area: {0}", thisArea()); } void IShapeDisplay.Display() { Console.WriteLine(" This method could draw the shape"); } } 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ől Aká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. Készítette: Zsótér Csaba III. éves informatikus hallgató 139 C# Amit a C# tudni érdemes! 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ése Lehető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: 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() Készítette: Zsótér Csaba III. éves informatikus hallgató 140 C# Amit a C# tudni érdemes! { 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}", myShapeSides); myShape.Sides = myShapeShapeShifter(myShapeSides); // error IShape tmp = (IShape) myShape; myShape.Sides = tmpShapeShifter( myShapeSides); // Console.WriteLine("ShapeShifter called Sides = {0}", myShapeSides); } } 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 } } Készítette: Zsótér Csaba III. éves informatikus hallgató 141 C# Amit a C# tudni érdemes! 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() { 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] myList[5] myList[6] myList[7] = = = = = "====="; "Csaba"; "was"; "Here!"; "====="; Készítette: Zsótér Csaba III. éves informatikus hallgató 142 C# Amit a C# tudni érdemes! for ( int x = 0; x < SpellingList.size; x++ ) Console.WriteLine(myList[x]); } } A képviselők felfedezése Ebben 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. 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. Készítette: Zsótér Csaba III. éves informatikus hallgató 143 C# Amit a C# tudni érdemes! 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 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. Készítette: Zsótér Csaba III. éves informatikus hallgató 144 C# Amit a C# tudni érdemes! 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: 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); } Készítette: Zsótér Csaba III. éves informatikus hallgató 145 C# Amit a C# tudni érdemes! } 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; } } public static void Main() { SortClass.Sort up = new SortClassSort(Ascending); SortClass.Sort down = new SortClassSort(Descending); SortClass.val1 = 350; SortClass.val2 = 30; Console.WriteLine("Before Sort: val1={0} , val2={1}",SortClass.val1,SortClassval2); SortClass.DoSort(up); Console.WriteLine("After Sort: val1={0} , val2={1} ",SortClass.val1,SortClassval2); Console.WriteLine("Before Sort: val1={0} , val2={1}",SortClass.val1,SortClassval2); SortClass.DoSort(down); Console.WriteLine("After Sort: val1={0} , val2={1}",SortClass.val1,SortClassval2); } } 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álata Munká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. Készítette: Zsótér Csaba III. éves informatikus hallgató 146 C# Amit a C# tudni érdemes! 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ása Egy 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. Az eseményképviselők működése Az 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ól Az 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. Készítette: Zsótér Csaba III. éves informatikus hallgató 147 C# Amit a C# tudni érdemes! 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. 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álata Szinté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ű Készítette: Zsótér Csaba III. éves informatikus hallgató 148 C# Amit a C# tudni érdemes! 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); 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ása Most 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 || eCurrChar==A) Készítette: Zsótér Csaba III. éves informatikus hallgató 149 C# Amit a C# tudni érdemes! { Console.WriteLine("Dont like a!"); e.CurrChar = x; } } Az események és eseménykezelők egymáshoz rendelése Ezen 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(); 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; Készítette: Zsótér Csaba III. éves informatikus hallgató 150 C# Amit a C# tudni érdemes! 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(); tester.TestChar += new CharEventHandler(Drop A); tester.In Char = B; Console.WriteLine("{0}", testerIn Char); tester.In Char = r; Console.WriteLine("{0}", testerIn Char); tester.In Char = a; Console.WriteLine("{0}",
testerIn Char); tester.In Char = d; Console.WriteLine("{0}", testerIn Char); } static void Drop A(object source, CharEventArgs e) { if(e.CurrChar == a || eCurrChar == A ) { Console.WriteLine("Dont like a!"); e.CurrChar = X; } } } Több eseménykezelő egyetlen eseményhez Lehető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. Készítette: Zsótér Csaba III. éves informatikus hallgató 151 C# Amit a C# tudni érdemes!
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; 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}", testerCurr Char); tester.Curr Char = r; Console.WriteLine("{0}", testerCurr Char); tester.Curr Char = a; Console.WriteLine("{0}", testerCurr Char); tester.Curr Char = d;
Console.WriteLine("{0}", testerCurr Char); } static void Drop A(object source, CharEventArgs e) Készítette: Zsótér Csaba III. éves informatikus hallgató 152 C# Amit a C# tudni érdemes! { if(e.CurrChar == a || eCurrChar == A ) { Console.WriteLine("Dont like a!"); e.CurrChar = X; } } // új eseménykezelő. static void Change D(object source, CharEventArgs e) { if(e.CurrChar == d || eCurrChar == D ) { Console.WriteLine("Ds are good!"); e.CurrChar = Z; } } } Eseménykezelők eltávolítása Az 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; } } } } Készítette: Zsótér Csaba III. éves informatikus hallgató 153 C# Amit a C# tudni érdemes! 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}", testerCurr Char); tester.Curr Char = r; Console.WriteLine("{0}", testerCurr Char); tester.Curr Char = a; Console.WriteLine("{0}", testerCurr Char); tester.Curr Char = d; Console.WriteLine("{0}", testerCurr Char); Console.WriteLine(" Removing event handler"); tester.TestChar -= new CharEventHandler(Change D); tester.Curr Char = D;
Console.WriteLine("{0}", testerCurr Char); tester.Curr Char = a; Console.WriteLine("{0}", testerCurr Char); tester.Curr Char = d; Console.WriteLine("{0}", testerCurr Char); } static void Drop A(object source, CharEventArgs e) { if(e.CurrChar == a || eCurrChar == A ) { Console.WriteLine("Dont like a!"); e.CurrChar = X; } } static void Change D(object source, CharEventArgs e) { if(e.CurrChar == d || eCurrChar == D ) { Console.WriteLine("Ds 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 Készítette: Zsótér Csaba III. éves informatikus hallgató 154 C# Amit a C# tudni érdemes! 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ése Az 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 int int int int mymethod( mymethod( mymethod( mymethod( mymethod( int x, int y ); int x ); long x ); char x, long y, long z ); 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ése A 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. Készítette: Zsótér Csaba III. éves informatikus hallgató 155 C# Amit a C# tudni érdemes! Az alapvető kéttényezős matematikai műveletek túlterhelése A 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
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); Készítette: Zsótér Csaba III. éves informatikus hallgató 156 C# Amit a C# tudni érdemes! 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 result = new AChar(); result.ch = (char)(origch + return result; }
static public AChar operator- ( { AChar result = new AChar(); result.ch = (char)(origch return result; } AChar orig, int val ) val); AChar orig, int val ) val); } 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}", aaach, bbbch); aaa = aaa + 25; bbb = bbb - 1; Console.WriteLine("Final values: {0}, {1}", aaach, bbbch); } } Az alapvető egytényezős matematikai műveletek túlterhelése Az 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: + - ++ -- ! ~ Készítette: Zsótér Csaba III. éves informatikus hallgató true 157 false C# Amit a C# tudni érdemes! 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; } set{ this.private ch = value; } } static public AChar operator+ ( AChar orig ) { AChar result = new AChar(); if( orig.ch >= a && origch <=z ) result.ch = (char) (origch - 32 ); else result.ch = origch; return result; } static public AChar operator- ( AChar orig ) { AChar result = new AChar(); if( orig.ch >= A && origch <=Z ) result.ch = (char) (origch + 32 ); else result.ch = origch; 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}", aaach); Console.WriteLine("bbb value: {0}", bbbch); Console.WriteLine("ccc value: {0}", cccch); Készítette: Zsótér Csaba III. éves informatikus hallgató 158 C# Amit a C# tudni érdemes! Console.WriteLine("ddd value: {0}", dddch); aaa bbb ccc ddd = = = = +aaa; -bbb; +ccc; -ddd; Console.WriteLine(" FINAL:"); Console.WriteLine("aaa value: {0}", Console.WriteLine("bbb value: {0}", Console.WriteLine("ccc value: {0}", Console.WriteLine("ddd value: {0}", aaa.ch); bbb.ch); ccc.ch); ddd.ch); } } Az összehasonlító és logikai műveletek túlterhelése A 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: < <= > >= 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; } } Készítette: Zsótér Csaba III. éves informatikus hallgató 159 C# Amit a C# tudni érdemes! static public bool operator < ( Salary first, Salary second ) { bool retval; if ( first.amount < secondamount ) retval = true; else retval = false; return retval; } static public bool operator <= ( Salary first, Salary second
) { bool retval; if ( first.amount <= secondamount ) retval = true; else retval = false; return retval; } static public bool operator > ( Salary first, Salary second ) { bool retval; if ( first.amount > secondamount ) retval = true; else retval = false; return retval; } static public bool operator >= ( Salary first, Salary second ) { bool retval; if ( first.amount >= secondamount ) retval = true; else retval = false; return retval; } public override string ToString() { return( this.amountToString() ); } } public class myAppClass { public static void Main(String[] args) { Készítette: Zsótér Csaba III. éves informatikus hallgató 160 C# Amit a C# tudni érdemes! 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(" --------------------------- "); 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(" I make as much or more than a president"); else Console.WriteLine(" I dont make as much as a president"); } } A logikai műveletek túlterhelése Az „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: Készítette: Zsótér Csaba III. éves informatikus hallgató 161 C# Amit a C# tudni érdemes! 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; 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 == thisamount ) 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; } Készítette: Zsótér Csaba III. éves informatikus hallgató 162 C# Amit a C# tudni érdemes! static public bool operator != ( Salary first, Salary second ) { bool retval; retval = !(first.Equals(second)); return retval; } public override string ToString() { return( this.amountToString() ); } } public class myAppClass { public static void Main(String[] args) { 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(" My salary {0} your salary", tmpstring); if (mySalary == PresSalary) tmpstring = "equals"; else tmpstring = "does not equal"; Console.WriteLine(" My salary {0} a presidents 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 Készítette: Zsótér Csaba III. éves informatikus hallgató 163 C# Amit a C# tudni érdemes! 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örnyezetben A .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. 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 JScriptNET-ben is A típusok szervezése névterekkel A 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álata Nem
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, Készítette: Zsótér Csaba III. éves informatikus hallgató 164 C# Amit a C# tudni érdemes! 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 MicrosotfWin32 – 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.Microsoftcom/net/ecma címen Az ECMA mellett a .NET jelentős része az ISO
szabványnak is megfelel Tájékozódás a környezet osztályairól Az 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ése A 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}", EnvironmentCommandLine); Console.WriteLine("Curr Dir: {0}", EnvironmentCurrentDirectory); Console.WriteLine(" Sys Dir: {0}", EnvironmentSystemDirectory);
Console.WriteLine(" Version: {0}", EnvironmentVersion); Console.WriteLine(" OS Vers: {0}", EnvironmentOSVersion); Console.WriteLine(" Machine: {0}", EnvironmentMachineName); Console.WriteLine(" Memory: {0}", EnvironmentWorkingSet); 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]); } Készítette: Zsótér Csaba III. éves informatikus hallgató 165 C# Amit a C# tudni érdemes! Console.WriteLine("================================="); Console.WriteLine("Path: {0}", Environment.GetEnvironmentVariable("Path"));
Console.WriteLine("================================="); } } Matematikai eljárások használata Az 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 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éke Abs 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ény Floor 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ánya Round 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. Készítette: Zsótér Csaba III. éves informatikus hallgató 166 C# Amit a C# tudni érdemes! Sin Sinh Tan Tanh A megadott szög szinusza. A megadott szög szinusz hiperbolikusza. A megadott szög tangense. 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és A 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. 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ás AppendText 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ára Delete 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 { Készítette: Zsótér Csaba III. éves informatikus hallgató 167 C# Amit a C# tudni érdemes! 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 { File.Copy(origfile, newfile); } catch (System.IOFileNotFoundException) { Console.WriteLine(" {0} does not exist!", origfile); return; } catch
(System.IOIOException) { Console.WriteLine(" {0} already exists!", newfile); return; } catch (Exception e) { Console.WriteLine(" An exception was thrown trying to copy file."); Console.WriteLine(e); return; } Console.WriteLine("Done"); } } } A fájlok adatai A 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() Készítette: Zsótér Csaba III. éves informatikus hallgató 168 C# Amit a C# tudni érdemes! { string[] CLA = Environment.GetCommandLineArgs(); FileInfo fiExe = new FileInfo(CLA[0]); if ( CLA.Length < 2 ) { Console.WriteLine("Format: {0} filename", fiExeName); } else { try { FileInfo fiFile = new FileInfo(CLA[1]); if(fiFile.Exists) { Console.WriteLine("==================================="); Console.WriteLine("{0} -
{1}", fiFileName, fiFileLength ); Console.WriteLine("==================================="); Console.WriteLine("Last Access: {0}", fiFileLastAccessTime); Console.WriteLine("Last Write: {0}", fiFileLastWriteTime); Console.WriteLine("Creation: {0}", fiFile.CreationTime); Console.WriteLine("==================================="); } else { Console.WriteLine("{0} doesnt exist!", fiFileName); } } catch (System.IOFileNotFoundException) { Console.WriteLine(" {0} does not exist!", CLA[1]); return; } catch (Exception e) { Console.WriteLine(" An exception was thrown trying to copy file."); Console.WriteLine(e); return; } } } } Egyszerű adatfájlok kezelése Jó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. Készítette: Zsótér Csaba III. éves informatikus hallgató 169 C# Amit a C# tudni érdemes! Ismerkedés a folyamokkal A 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 menete Ha í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 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ása A 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ás AppendText 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 Készítette: Zsótér
Csaba III. éves informatikus hallgató 170 C# Amit a C# tudni érdemes! 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ása A 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"); } 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ól A 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 Készítette: Zsótér Csaba III. éves informatikus hallgató 171 C# Amit a C# tudni érdemes! { string buffer; StreamReader myFile = File.OpenText(args[0]); while ( (buffer = myFile.ReadLine()) != null ) { Console.WriteLine(buffer); } myFile.Close(); } } } Bináris adatok fájlba írása Ha 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 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(); } } } Készítette: Zsótér Csaba III. éves informatikus hallgató 172 C# Amit a C# tudni érdemes! A FileMode értékei Érték Leírása Append 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ól A 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; 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}> ", brFileReadInt32()); } Console.WriteLine("Done Reading"); brFile.Close(); myFile.Close(); } } } Készítette:
Zsótér Csaba III. éves informatikus hallgató 173 C# Amit a C# tudni érdemes! Windows alkalmazások készítése Windows ablakok készítése Napjainkban 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. 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 SystemWindowsForms névtérben található meg. Használatát egy igen egyszerű példán mutatjuk meg: 4.1 Egyszerű ablak using System.WindowsForms; 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ások Az 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.WindowsFormsdll 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 Készítette: Zsótér Csaba III. éves informatikus hallgató 174 C# Amit a C# tudni érdemes! 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.WindowsFormsdll Form1cs 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 11-es és későbbi változatainak C# fordítói automatikusan alkalmaznak egyes hivatkozásokat, köztük a System.WindowsFormsdll-t is A kapott eredmény a fordítás után: 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. Készítette: Zsótér Csaba III. éves informatikus hallgató 175 C# Amit a C# tudni érdemes! 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.WindowsFormsdll /t:winexe From1cs 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ése A 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 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. Készítette: Zsótér Csaba III. éves informatikus hallgató 176 C# Amit a C# tudni érdemes! 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ása Az 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. 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 HelpButton MaximizeBox MinimizeBox Text Meghatározza, hogy megjelenítsük-e a vezérlőmenüt. 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. Jelzi, hogy megjelenik-e a Maximize gomb. Jelzi, hogy megjelenik-e a Minimize gomb. 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.WindowsForms; 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 Forms Caption"; Application.Run(frmHello); } } Készítette: Zsótér Csaba III. éves informatikus hallgató 177 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ó 178 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ése Az 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ó 179 C# Amit a C# tudni érdemes!
SizeGripStyle StartPosition Width 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. 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. Az ablak szélessége. 4.3 Ablak átméretezése using System.WindowsForms; 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 = FormStartPositionManual;
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 Készítette: Zsótér Csaba III. éves informatikus hallgató 180 C# Amit a C# tudni érdemes! objektumra ilyen egyszerűen hivatkozzunk, használatba kell vennünk a System.Drawing névteret. Az ablak színeinek és hátterének megváltoztatása Az 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 =
ColorHotPink; 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.WindowsForms; using System.Drawing; public class PicForm : Form { public static void Main( string[] args ) { PicForm myForm = new PicForm(); myForm.BackColor = ColorHotPink; myForm.Text = "PicForm - Backgrounds"; if (args.Length >= 1) { myForm.BackgroundImage = ImageFromFile(args[0]); Size tmpSize = new Size(); tmpSize.Width = myFormBackgroundImageWidth; tmpSize.Height = myFormBackgroundImageHeight; myForm.ClientSize = tmpSize; myForm.Text = "PicForm - " + args[0]; } Application.Run(myForm); } } Készítette: Zsótér Csaba III. éves informatikus hallgató 181 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ása Az 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ása Fixed3D 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.WindowsForms; 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ó 182 C# Amit a C# tudni érdemes! myForm.BackColor = ColorSteelBlue; myForm.Text = "Borders"; myForm.FormBorderStyle = FormBorderStyleFixed3D; 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 ablakon Mindezidá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 ContainerControl Form Label MonthCalendar PrintReviewControl CheckBox DataGrid GroupBox LinkLabel NumericUpDown ProgressBar Készítette: Zsótér Csaba III. éves informatikus hallgató CheckedListBox DateTimePicker HScrollBar ListBox Panel ProperyGrid 183 ComboBox DomainUpDown ImageList ListView PictureBox RadioButton C# Amit a C# tudni érdemes! RichTextBox StatusBarPanel TextBox ToolTip UserControl ScrollableControl TabControl Timer TrackBar Splitter TabPage ToolBar TreeView StatusBar TabStrip ToolBarButton VScrollBar A
táblázatban látható vezérlők meghatározása a System.WindowsForms névtérben található. Címkék és szövegmegjelenítés A 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.ControlAdd(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.WindowsForms; using System.Drawing; public class ControlApp : Form { public static void Main( string[] args ) { ControlApp myForm = new ControlApp(); myForm.Text = EnvironmentCommandLine; myForm.StartPosition = FormStartPositionCenterScreen; 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ó 184 C# Amit a C# tudni érdemes! myDateLabel.Text = currDateToString(); myDateLabel.AutoSize = true; myDateLabel.Left = 50 + myLabelPreferredWidth + 10; myDateLabel.Top = 20; myForm.Width = myLabelPreferredWidth + myDateLabelPreferredWidth + 110; myForm.Height = myLabelPreferredHeight+ 100; myForm.ControlsAdd(myDateLabel); myForm.ControlsAdd(myLabel); Application.Run(myForm); } }
A program futásának eredménye: Gombok használata A 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ása BackColor 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ó 185 C# Amit a C# tudni érdemes! Left Right Text TextAlign Top Visible Width A gomb bal oldala helyzetének beállítása vagy kiolvasása. A gomb jobb oldalának beálltása vagy kiolvasása. A gomb feliratának beállítása vagy kiolvasása. A gomb felirata igazításának beállítása vagy kiolvasása. A gomb teteje helyzetének beállítása vagy kiolvasása. A gomb láthatóságát jelző érték beállítása vagy kiolvasása. 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ények Ne 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(thistagfü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.WindowsForms; using System.Drawing; Készítette: Zsótér Csaba III. éves informatikus hallgató 186 C# Amit a C# tudni érdemes! public class ButtonApp : Form { private Label myDateLabel; private Button btnUpdate; public ButtonApp() { InitializeComponent(); } private void InitializeComponent() { this.Text = EnvironmentCommandLine; this.StartPosition = FormStartPositionCenterScreen; this.FormBorderStyle = FormBorderStyleFixed3D; myDateLabel = new Label(); // Create label DateTime currDate = new DateTime(); currDate = DateTime.Now; myDateLabel.Text = currDateToString(); myDateLabel.AutoSize = true; myDateLabel.Location = new Point( 50, 20); myDateLabel.BackColor = thisBackColor; this.ControlsAdd(myDateLabel); // Add label to form this.Width = (myDateLabelPreferredWidth + 100); btnUpdate = new Button(); // Create a button btnUpdate.Text = "Update"; btnUpdate.BackColor = ColorLightGray; btnUpdate.Location = new Point(((thisWidth/2) - (btnUpdateWidth /
2)),(this.Height - 75)); this.ControlsAdd(btnUpdate); // Add button to form btnUpdate.Click += new SystemEventHandler(thisbtnUpdate Click); btnUpdate.MouseEnter += new System.EventHandler(thisbtnUpdate MouseEnter); btnUpdate.MouseLeave += new System.EventHandler(thisbtnUpdate MouseLeave); myDateLabel.MouseEnter += new System.EventHandler(thismyDataLabel MouseEnter); myDateLabel.MouseLeave += new System.EventHandler(thismyDataLabel MouseLeave); } protected void btnUpdate Click( object sender, System.EventArgs e) { DateTime currDate =DateTime.Now ; this.myDateLabelText = currDateToString(); } Készítette: Zsótér Csaba III. éves informatikus hallgató 187 C# Amit a C# tudni érdemes! protected void btnUpdate MouseEnter( object sender, System.EventArgs e) { this.BackColor = ColorHotPink; } protected void btnUpdate MouseLeave( object sender, System.EventArgs e) { this.BackColor = ColorBlue; } protected void myDataLabel MouseEnter(object sender, System.EventArgs e) {
this.BackColor = ColorYellow; } protected void myDataLabel MouseLeave(object sender, System.EventArgs e) { this.BackColor = ColorGreen; } public static void Main( string[] args ) { Application.Run( new ButtonApp() ); } } A kimenet pedig a következő lett. OK gomb készítése Szá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ó 188 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álata A 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.WindowsForms; using System.Drawing; public class GetName : Form { private Button btnOK; private private private private private Label Label Label Label Label lblFirst; lblMiddle; lblLast; lblFullName; 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ó 189 C# Amit a C# tudni érdemes! private void InitializeComponent() { this.FormBorderStyle = FormBorderStyleFixed3D; this.Text = "Get User Name"; this.StartPosition = FormStartPositionCenterScreen; lblInstructions = lblFirst = new lblMiddle = new lblLast = new lblFullName = new txtFirst txtMiddle txtLast new Label(); Label(); Label();
Label(); Label(); = new TextBox(); = new TextBox(); = 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" + " You will see your name appear as you type." + " For fun, edit your name after entering it."; lblInstructions.TextAlign = ContentAlignmentMiddleCenter;
lblInstructions.Location = new Point(((this.Width/2) - (lblInstructionsWidth / 2 )), 140); this.ControlsAdd(lblFirst); // Add label to form this.ControlsAdd(lblMiddle); this.ControlsAdd(lblLast); this.ControlsAdd(lblFullName); this.ControlsAdd(txtFirst); Készítette: Zsótér Csaba III. éves informatikus hallgató 190 C# Amit a C# tudni érdemes! this.ControlsAdd(txtMiddle); this.ControlsAdd(txtLast); this.ControlsAdd(lblInstructions); btnOK.Text = "Done"; btnOK.BackColor = ColorLightGray; btnOK.Location = new Point(((thisWidth/2) - (btnOKWidth / 2)), (this.Height - 75)); this.ControlsAdd(btnOK); // Add button to form btnOK.Click += new SystemEventHandler(thisbtnOK Click); txtFirst.TextChanged += new System.EventHandler(thistxtChanged Event); txtMiddle.TextChanged += new System.EventHandler(thistxtChanged Event); txtLast.TextChanged += new System.EventHandler(thistxtChanged Event); } protected void btnOK Click( object sender, System.EventArgs e) {
Application.Exit(); } protected void txtChanged Event( object sender, System.EventArgs e) { lblFullName.Text = txtFirstText + " " + txtMiddleText + " " + 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ó 191 C# Amit a C# tudni érdemes! Windows alkalmazások készítése Választógombok használata A 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.WindowsForms; 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.WindowsFormsRadioButton(); this.rdFemale = new SystemWindowsFormsRadioButton(); this.lblText1 = new SystemWindowsFormsLabel(); this.rdYouth = new SystemWindowsFormsRadioButton(); this.rdAdult = new SystemWindowsFormsRadioButton(); this.lblText2 = new SystemWindowsFormsLabel(); this.btnOK = new
System.WindowsFormsButton(); // Form1 this.ClientSize = new SystemDrawingSize(350, 225); this.Text = "Radio Buttons 1"; // rdMale this.rdMaleLocation this.rdMaleSize this.rdMaleTabIndex this.rdMaleText = = = = new System.DrawingPoint(50, 65); new Size(90, 15); 0; "Male"; Készítette: Zsótér Csaba III. éves informatikus hallgató 192 C# Amit a C# tudni érdemes! // rdFemale this.rdFemaleLocation this.rdFemaleSize this.rdFemaleTabIndex this.rdFemaleText = = = = new System.DrawingPoint(50, 90); new System.DrawingSize(90, 15); 1; "Female"; // lblText1 this.lblText1Location this.lblText1Size this.lblText1TabIndex this.lblText1Text = = = = new System.DrawingPoint(50, 40); new System.DrawingSize(90, 15); 2; "Sex"; // rdYouth this.rdYouthLocation this.rdYouthSize this.rdYouthTabIndex this.rdYouthText = = = = new System.DrawingPoint(220, 65); new System.DrawingSize(90, 15); 3; "Over 21"; // rdAdult this.rdAdultLocation
this.rdAdultSize this.rdAdultTabIndex this.rdAdultText = = = = new System.DrawingPoint(220, 90); new System.DrawingSize(90, 15); 4; "Under 21"; // lblText2 this.lblText2Location this.lblText2Size this.lblText2TabIndex this.lblText2Text // btnOK this.btnOKLocation = this.btnOKSize = this.btnOKTabIndex = this.btnOKText = this.btnOKClick += = = = = new System.DrawingPoint(220, 40); new System.DrawingSize(90, 15); 5; "Age Group"; new System.DrawingPoint(130, 160); new System.DrawingSize(70, 30); 6; "OK"; new System.EventHandler(thisbtnOK Click); this.ControlsAdd(rdMale); this.ControlsAdd(rdFemale); this.ControlsAdd(lblText1); this.ControlsAdd(rdYouth); this.ControlsAdd(rdAdult); this.ControlsAdd(lblText2); this.ControlsAdd(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ó 193 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álata Feladatunk 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.WindowsForms; public class GoodRadio : Form { private GroupBox gboxAge; private GroupBox gboxSex; private RadioButton rdMale; private RadioButton rdFemale; private RadioButton rdYouth; Készítette: Zsótér Csaba III. éves informatikus
hallgató 194 C# Amit a C# tudni érdemes! 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.gboxSexLocation this.gboxSexSize this.gboxSexTabStop this.gboxSexText // rdMale this.rdMaleLocation this.rdMaleSize this.rdMaleTabIndex this.rdMaleText = = = = = = = = // rdFemale this.rdFemaleLocation this.rdFemaleSize this.rdFemaleTabIndex this.rdFemaleText new Point(15, 30); new Size(125, 100); false; "Sex"; new Point(35, 35); new Size(70, 15); 0; "Male"; = = = = new Point(35, 60); new Size(70, 15); 1; "Female"; // gboxAge
this.gboxAgeLocation this.gboxAgeSize this.gboxAgeTabStop this.gboxAgeText = = = = new Point(200, 30); new Size(125, 100); false; "Age Group"; // rdYouth this.rdYouthLocation this.rdYouthSize this.rdYouthTabIndex this.rdYouthText = = = = new Point(35, 35); new Size(70, 15); 3; "Over 21"; // rdAdult this.rdAdultLocation this.rdAdultSize this.rdAdultTabIndex this.rdAdultText = = = = new Point(35, 60); new Size(70, 15); 4; "Under 21"; Készítette: Zsótér Csaba III. éves informatikus hallgató 195 C# Amit a C# tudni érdemes! // btnOK this.btnOKLocation = this.btnOKSize = this.btnOKTabIndex = this.btnOKText = this.btnOKClick += new Point(130, 160); new Size(70, 30); 6; "OK"; new System.EventHandler(thisbtnOK Click); this.ControlsAdd(gboxSex); this.ControlsAdd(gboxAge); this.gboxSexControlsAdd(rdMale); this.gboxSexControlsAdd(rdFemale); this.gboxAgeControlsAdd(rdYouth); this.gboxAgeControlsAdd(rdAdult); this.ControlsAdd(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 Mivel pedig ezek a csoportmezők az ablak (this) részei, a bennük található elemek is megjelennek az ablakon. Készítette: Zsótér Csaba III. éves informatikus hallgató 196 C# Amit a C# tudni érdemes! this.ControlsAdd(gboxSex); this.ControlsAdd(gboxAge); this.gboxSexControlsAdd(rdMale); this.gboxSexControlsAdd(rdFemale);
this.gboxAgeControlsAdd(rdYouth); this.gboxAgeControlsAdd(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álata Az ablakon gyakran használatos vezérlő a listamező (ListBox), amely lehetővé egy kisméretű mezőben sok elemet soroljunk fel. Beállíthatjuk úgy, hogy a görgethesse a tartalmát és akkor több elemet is kiválaszthasson. Mivel a tulajdonságainak beállítása kissé összetettebb feladat, mint a korábbiakban vezérlők esetében, érdemes elidőznünk itt egy kicsit. teszi, hogy felhasználó listamezők bemutatott 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.ControlsAdd(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ó 197 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.ItemsAdd("Első elem"); myListBox.ItemsAdd("Második elem"); myListBox.ItemsAdd("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.WindowsForms; 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 = FormBorderStyleFixed3D; this.Text = "Get User Info"; this.StartPosition = FormStartPositionCenterScreen; lblFullName txtFullName btnOK lblSex lboxSex lboxAge = = = = =
= new new new new new new Label(); TextBox(); Button(); Label(); ListBox(); 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ó 198 C# Amit a C# tudni érdemes! btnOK.Location = new Point(((thisWidth/2) - (btnOKWidth / 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 = SelectionModeOne; lboxSex.BeginUpdate(); lboxSex.ItemsAdd(" lboxSex.ItemsAdd(" Boy lboxSex.ItemsAdd(" Girl lboxSex.ItemsAdd(" Man lboxSex.ItemsAdd(" Lady lboxSex.EndUpdate(); "); "); "); "); "); lboxAge.Location = new Point(80, 100); lboxAge.Size = new Size(100, 60);
lboxAge.SelectionMode = SelectionModeOne; lboxAge.BeginUpdate(); lboxAge.ItemsAdd(" "); lboxAge.ItemsAdd(" Under 21 "); lboxAge.ItemsAdd(" 21 "); lboxAge.ItemsAdd(" Over 21 "); lboxAge.EndUpdate(); lboxAge.SelectedIndex = 0; this.ControlsAdd(btnOK); this.ControlsAdd(lblFullName); this.ControlsAdd(txtFullName); this.ControlsAdd(lboxSex); this.ControlsAdd(lblSex); this.ControlsAdd(lboxAge); btnOK.Click += new SystemEventHandler(thisbtnOK 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ó 199 C# Amit a C# tudni érdemes! A ListBox vezérlő kiválasztási módjai Mód Leírása SelectionMode.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 ablakokon Az 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.WindowsForms; using System.Drawing; public class Menu : Form { private Label myDateLabel; private MainMenu myMainMenu; Készítette: Zsótér Csaba III. éves informatikus hallgató 200 C# Amit a C# tudni érdemes! public Menu() { InitializeComponent(); } private void InitializeComponent() { this.Text = "STY Menus"; this.StartPosition = FormStartPositionCenterScreen; this.FormBorderStyle = FormBorderStyleFixed3D; myDateLabel = new Label(); // Create label DateTime currDate = new DateTime(); currDate = DateTime.Now; myDateLabel.Text = currDateToString(); myDateLabel.AutoSize = true; myDateLabel.Location = new Point( 50, 70); myDateLabel.BackColor = thisBackColor; this.ControlsAdd(myDateLabel); // Add label to form this.Width = (myDateLabelPreferredWidth + 100); myMainMenu = new MainMenu(); MenuItem menuitemFile =
myMainMenu.MenuItemsAdd("File"); menuitemFile.MenuItemsAdd(new MenuItem("Update Date", new EventHandler(this.MenuUpdate Selection))); menuitemFile.MenuItemsAdd(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.myDateLabelText = currDateToString(); } 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ó 201 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.MenuItemsAdd("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ó 202 C# Amit a C# tudni érdemes! Nézzük meg, hogy is néz ez ki: MenuItem menuitemFile = myMainMenu.MenuItemsAdd("&File"); menuitemFile.MenuItemsAdd(new MenuItem("Update &Date", new EventHandler(this.MenuUpdate Selection), Shortcut.CtrlD)); menuitemFile.MenuItemsAdd(new MenuItem("E&xit", new EventHandler(this.FileExit Selection), Shortcut.CtrlX)); MenuItem menuitemHelp = myMainMenu.MenuItemsAdd("&Help"); menuitemHelp.MenuItemsAdd(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üelemek A 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.WindowsForms; using System.Drawing; public class CheckedMenu : Form { Készítette: Zsótér Csaba III. éves informatikus hallgató 203 C# Amit a C# tudni érdemes! private Label myDateLabel; private MainMenu myMainMenu; private private private private private private MenuItem MenuItem MenuItem MenuItem MenuItem MenuItem menuitemFile; menuitemUD; menuitemActive; menuitemExit; menuitemHelp; menuitemAbout; public CheckedMenu() { InitializeComponent(); } private void InitializeComponent() { this.Text = "STY
Menus"; this.StartPosition = FormStartPositionCenterScreen; this.FormBorderStyle = FormBorderStyleSizable; myDateLabel = new Label(); // Create label DateTime currDate = new DateTime(); currDate = DateTime.Now; myDateLabel.Text = currDateToString(); myDateLabel.AutoSize = true; myDateLabel.Location = new Point( 50, 70); myDateLabel.BackColor = thisBackColor; this.ControlsAdd(myDateLabel); // Set width of form based on Labels width this.Width = (myDateLabelPreferredWidth + 100); CreateMyMenu(); } protected void MenuUpdate Selection( object sender, System.EventArgs e) { if( menuitemActive.Checked == true) { DateTime currDate = new DateTime(); currDate = DateTime.Now; this.myDateLabelText = currDateToString(); } else { this.myDateLabelText = "* " + this.myDateLabelText + " *"; } } 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ó 204 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.MenuItemsAdd("&File"); menuitemUD = new MenuItem(); menuitemUD.Text = "Update &Date"; menuitemUD.Shortcut = ShortcutCtrlD; menuitemUD.Click += new EventHandler(thisMenuUpdate Selection); menuitemFile.MenuItemsAdd( menuitemUD ); menuitemExit = new MenuItem(); menuitemExit.Text = "E&xit"; menuitemExit.Shortcut = ShortcutCtrlX; menuitemExit.ShowShortcut = false; menuitemExit.Click += new EventHandler(thisFileExit Selection); menuitemFile.MenuItemsAdd( menuitemExit ); // HELP MENU menuitemHelp = myMainMenu.MenuItemsAdd("&Help");
menuitemActive = new MenuItem(); menuitemActive.Text = "Active"; menuitemActive.Click += new EventHandler(thisActiveMenu Selection); menuitemActive.Checked = true; menuitemHelp.MenuItemsAdd( menuitemActive ); menuitemAbout = new MenuItem(); menuitemAbout.Text = "&About"; menuitemAbout.Shortcut = ShortcutCtrlA; menuitemAbout.ShowShortcut = false; menuitemAbout.Click += new EventHandler(thisFileAbout Selection); menuitemHelp.MenuItemsAdd( 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ó 205 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ése Az 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ó 206 C# Amit a C# tudni érdemes! 4.14 Helyi menü alkalmazása using System; using System.WindowsForms; using System.Drawing; public
class PopUp : Form { private ContextMenu myPopUp; public PopUp() { InitializeComponent(); } private void InitializeComponent() { this.Text = "STY Pop-up Menu"; this.StartPosition = FormStartPositionCenterScreen; CreatePopUp(); } protected void PopUp Selection( object sender, System.EventArgs e) { this.Text = ((MenuItem) sender)Text; } private void CreatePopUp() { myPopUp = new ContextMenu(); myPopUp.MenuItemsAdd("First Item", new EventHandler(this.PopUp Selection)); myPopUp.MenuItemsAdd("Second Item", new EventHandler(this.PopUp Selection)); myPopUp.MenuItemsAdd("-"); myPopUp.MenuItemsAdd("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ó 207 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ály A 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.WindowsForms; 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ó 208 C# Amit a C# tudni érdemes! private void InitializeComponent() { this.Text = "STY C# Pop-up Menu"; this.StartPosition = FormStartPositionCenterScreen; } protected void PopUp Selection( object sender, System.EventArgs e) { MessageBox.Show( ((MenuItem) sender)Text, thisText + " Msg Box"); } private void CreatePopUp() { myPopUp = new ContextMenu(); myPopUp.MenuItemsAdd("First Item", new EventHandler(this.PopUp
Selection)); myPopUp.MenuItemsAdd("Second Item", new EventHandler(this.PopUp Selection)); myPopUp.MenuItemsAdd("-"); myPopUp.MenuItemsAdd("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ó 209 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álata A 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.WindowsForms; using System.Drawing; public class Canned : Form { private MainMenu myMainMenu; public Canned() { InitializeComponent(); } private void InitializeComponent() { this.Text = "Canned Dialogs"; this.StartPosition = FormStartPositionCenterScreen; this.FormBorderStyle = FormBorderStyleSizable;
this.Width = 400; myMainMenu = new MainMenu(); MenuItem menuitemFile = myMainMenu.MenuItemsAdd("&File"); menuitemFile.MenuItemsAdd(new MenuItem("Colors Dialog", new EventHandler(this.Menu Selection))); menuitemFile.MenuItemsAdd(new MenuItem("Fonts Dialog", new EventHandler(this.Menu Selection))); menuitemFile.MenuItemsAdd(new MenuItem("Print Preview Dialog", new EventHandler(this.Menu Selection))); menuitemFile.MenuItemsAdd("-"); menuitemFile.MenuItemsAdd(new MenuItem("Exit", new EventHandler(this.Menu Selection))); Készítette: Zsótér Csaba III. éves informatikus hallgató 210 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ó 211 C# Amit a C# tudni érdemes! Saját párbeszédablak készítése Lehető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ó 212 C# Amit a C# tudni érdemes! 4.17 A Show és a ShowDialog tagfüggvény különbségei using System; using System.WindowsForms; using System.Drawing; public class MyForm : Form { private MainMenu myMainMenu; public MyForm() { InitializeComponent(); } private void InitializeComponent() { this.Text = "Canned Dialogs"; this.StartPosition = FormStartPositionCenterScreen; this.FormBorderStyle = FormBorderStyleSizable; this.Width = 400; myMainMenu = new MainMenu(); MenuItem menuitemFile =
myMainMenu.MenuItemsAdd("&File"); menuitemFile.MenuItemsAdd(new MenuItem("My Form", new EventHandler(this.Menu Selection))); menuitemFile.MenuItemsAdd(new MenuItem("My Other Form", new EventHandler(this.Menu Selection))); menuitemFile.MenuItemsAdd("-"); menuitemFile.MenuItemsAdd(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ó 213 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 = FormStartPositionCenterScreen; this.FormBorderStyle = FormBorderStyleFixedDialog; this.Width = 300; this.Height = 250; mySubMainMenu = new MainMenu(); MenuItem menuitemFile = mySubMainMenu.MenuItemsAdd("&File"); menuitemFile.MenuItemsAdd(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ó 214 C# Amit a C# tudni érdemes! Felhasznált irodalom 1. Programming C#, 2nd Edition Szerző: Jesse Liberty, Kiadó: O’Reilly, Kiadás éve: 2002. 2. C# Essentials, 2nd Edition
Szerző: Ben Albahari, Peter Drayton, Brad Merrill Kiadás éve: 2001. Kiadó: O’Reilly, 3. C#, Your visual blueprint for building NET applications by Eric Butow, Tommy Rayn Szerző: Eric Butow, Tommy Rayn Kiadó:Hungry Minds Inc. Kiadás éve: 2002. 4. C#NET Web Developer’s Guide Szerző:Adrian Turtschi DotThatCom.com Jason Werry Greg Hack Joseph Albahari Saurabh Nandu Technical Editor Wei Meng Lee Series Editor Kiadó: Syngress Publishing, Inc. Kiadás éve: 2002. 5. Designing Microsoft ASPNET Applications Szerző: 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 Days Szerző:Bradley L. Jones Kiadó: Pearson Education Inc. Kiadás éve: 2004. Készítette: Zsótér Csaba III. éves informatikus hallgató 215