You are on page 1of 58

Kivételkezelés

9. Kivételkezelés
 9.1. Mi a kivétel?
 A szoftverben előforduló problémák alapvetően három csoportba sorolhatók:
 Programhibák: a programozó által elkövetett hibák. Pl. C++-ban elfelejtjük felszabadítani a dinamikusan
lefoglalt memóriát.
 Felhasználói hibák: az alkalmazást futtató személy okozza, pl. nem olyan típusú értéket ír be, mint amit a
program vár.
 Kivételek: futásidejű rendellenességek, pl.: nem létezik az adatbázis, amihez csatlakozunk; nem sikerül egy
fájlt megnyitni, mert nem abban a könyvtárban van, ahol keressük; nem sikerül elérni a másik
számítógépet, mert éppen ki van kapcsolva, stb.
 A kivételkezelés olyan technika, amely a futásidejű rendellenességek kezelésére alkalmas.
Előfordulhat azonban az is, hogy programhiba vagy felhasználói hiba jelzésére a CLR
kivételt generál.
 Két nagy csoportja van:
 Alkalmazásbeli kivételek
 Rendszerkivételek

2 Programozási paradigmák és technikák Gurka Dezsőné Csizmás Edit


Példa egy felhasználó által okozott kivételre:
string s;
int x;
Console.WriteLine("Kérek egy egész számot: ");
s = Console.ReadLine(); ha szöveget ír be, nem lehet int-té
x = int.Parse(s); konvertálni

 Ilyenkor két eset lehetséges:


 1. F5-tel futtatunk
 2. ctrl+F5-tel futtatunk

3 Programozási paradigmák és technikák Gurka Dezsőné Csizmás Edit


 1. Ha F5-tel futtatjuk, vagyis debug módban, akkor a program leállása után kódszerkesztő nézetben a hibás
utasítást sárgával megjelölve látjuk, mellette egy ablakban megjelenik, hogy FormatException típusú
kezeletlen kivételt dobott a program. Ha a View Detail... feliratra kattintunk, további részleteket is
megtudhatunk a kivételobjektum mezőinek értékei alapján. A fejlesztőrendszer Locals és Watch ablakaiban
pedig a változók értékeit ellenőrizhetjük.

4 Programozási paradigmák és technikák Gurka Dezsőné Csizmás Edit


 2. Ha már kész a program és CTRL+F5-tel, vagyis nem debug módban
futtatjuk az alkalmazást, akkor először a kivétel típusáról és a kivétel kiváltás
pontos helyéről kapunk részletes szöveges információt, és egy figyelmeztető
ablakot kapunk:

Majd a Hibakeresés gombra kattintva a Yes választása után


eljutunk a kódszerkesztő nézetben a kivételablakba (mint F5-
nél).
5 Programozási paradigmák és technikák Gurka Dezsőné Csizmás Edit
 Mi történt itt valójában? Az s sztringben tárolt adatot az int Parse() metódusa
megpróbálta egész számmá alakítani, és a sikertelenséget egy kivétel előidézésével
jelezte a rendszer. Emellett egy objektumot is létrehozott, amelynek típusa és a benne
tárolt adatok a probléma jellegéről és részleteiről adnak felvilágosítást.

6 Programozási paradigmák és technikák Gurka Dezsőné Csizmás Edit


9.2. Kivételek kezelése
 A fenti esetet kezeletlen kivételnek nevezzük. Egy kész programban általában nem
megengedett az ilyesmi. Fejlesszük tovább a programunkat úgy, hogy ha a beírt szöveg nem
alakítható egész számmá, kérje be újra. Ehhez az adatbekérést rakjuk bele egy statikus
metódusba a Main()-en kívül: int szám = Beolvas();

static int Beolvas()


{
string s;
int x = 0;
Console.WriteLine("Kérek egy egész számot: ");
s = Console.ReadLine();
try
{ ha szöveget ír be, nem lehet int-
x = int.Parse(s); té konvertálni
}
catch (FormatException e)
{
Console.WriteLine("Hibásan adta meg a számot!");
Console.WriteLine(e.Message);
x = Beolvas();
}
return x;
}

7 Programozási paradigmák és technikák Gurka Dezsőné Csizmás Edit


 Try blokkba kell elhelyezni azt vagy azokat az utasításokat, amelyek végrehajtása során
számíthatunk kivétel bekövetkezésére.
 Ilyenek pl. az adatbeviteli részek, a konverziók, a tömbök indexelése, stb. Igazából az egész
programot belerakhatjuk egy try blokkba.
 A try-t követő catch blokkba (vagy blokkokba – lehet belőlük több is!) pedig a kivétel
bekövetkezésére reagáló programrészt rakjuk.
 A catch blokknak az esetek túlnyomó részében van paramétere, úgy, mint egy metódusnak, itt egy
kivétel objektumot veszünk át (a példában FormatException típusút).
 Try blokk nem állhat magában, legalább egy catch-nek követnie kell.
 Ha lépésenként futtatjuk ezt a programot, akkor láthatjuk, hogy a Parse() metódus meghívása
után a kivétel hatására a vezérlés a catch-re ugrik, annak a blokkja fut le. Ha érvényes értéket
kap a Beolvas() metódusban az x változó (vagyis a Parse() metódus sikeresen konvertált),
akkor a végrehajtás átugorja a catch blokkot és a return-nel ér véget.
8 Programozási paradigmák és technikák Gurka Dezsőné Csizmás Edit
9.3. Többfajta kivétel kezelése
 A Beolvas() metódusunkban igazából nem csak formátumhiba léphet fel. Ha túl nagy
egész számot próbálunk megadni, akkor túlcsordulási hiba is bekövetkezhet. Ha 2 147
483 648-at adunk meg, akkor OverflowException típusú kivételt dob a futtatórendszer
és ezt a catch águnk nem tudja elkapni, hiszen ő FormatException típusú objektumot
vár. Ismét kezeletlen kivételhez jutottunk.
 Egyáltalán, honnan lehet azt tudni, hogy a .NET-es osztályok metódusai milyen típusú
kivételt dobhatnak?
 Egyrészt meg lehet nézni a súgóban, másrészt ha a kódablakban a metódus vagy a
tulajdonság neve fölé visszük a kurzort, a leírás végén van egy Exceptions: rész, ott vannak
felsorolva a tag által kiváltható kivételek.

9 Programozási paradigmák és technikák Gurka Dezsőné Csizmás Edit


 Tehát ha a túl nagy számok problémáját is meg akarjuk oldani, akkor a programunkat a catch-ág után
egészítsük ki egy másik catch-blokkal:
catch (OverflowException e)
{
Console.WriteLine("Hibásan adta meg a számot!");
Console.WriteLine(e.Message);
x = Beolvas();
}

 Ebben az esetben mindkét hibafajtát tudja kezelni a programunk.


 Ha több catch ágat használunk, mindig a legspeciálisabb kerüljön előre.

10 Programozási paradigmák és technikák Gurka Dezsőné Csizmás Edit


Példa, másfajta kivételtípussal:
static void Main(string[] args)
{
int[] tomb = new int[5];
try
{
for (int i = 0; i < 10; i++)
{
tomb[i] = i;
Console.WriteLine("{0,3}", i);
}
}
catch (IndexOutOfRangeException e)
{
Console.WriteLine("Hiba: {0}!", e);
}
Console.WriteLine("itt a vége");
}
 A program 0-tól 4-ig kiírja a számokat, aztán a kivételobjektum tartalmát, majd
azt, hogy „Itt a vége”.

11 Programozási paradigmák és technikák Gurka Dezsőné Csizmás Edit


9.4. A kivételosztályok hierarchiája
 Eddig előforduló kivételek (kivételosztályok):
 FormatException (pl. int.Parse() nem tudja int-té konvertálni a
stringet)
 OverflowException (pl. túlcsordulás esetén)
 IndexOutOfRangeException (pl. tömb túlindexelés)
 A .NET-ben minden osztály az Object osztály leszármazottja.
 Ennek közvetlen utóda az Exception osztály. Az Exception osztálynak
két közvetlen leszármazottja van:
 SystemException
 ApplicationException
 A SystemException a rendszerkivételek ősosztálya. Ennek közvetlen
vagy közvetett leszármazottja a három előbb megnézett kivételtípus.
Ezeket a .NET futtatórendszer dobja.
 Az ApplicationException pedig az alkalmazásszintű kivételek
ősosztálya, ebből az osztályból kell majd származtatnunk a saját kivétel
osztályainkat.

12 Programozási paradigmák és technikák Gurka Dezsőné Csizmás Edit


 Miért fontos tudni, hogy melyik kivételosztály melyik másik őse
vagy leszármazottja?
 Azért, mert egy programban többféle kivételt is elkaphatunk, több
catch-ág is szerepelhet és ezek sorrendje nem tetszőleges. Mindig a
legspeciálisabb ággal kell kezdeni, és a végén legyen a legáltalánosabb,
mert a futtatórendszer sorban halad és a dobott kivételt a legelső
alkalmas catch ágnak adja át.
 Létezik még egy speciális catch-ág, az általános, ahol nem adunk át
kivételobjektumot:
catch
{
Console.WriteLine("Valami probléma lépett fel.");
}
 Ez nyilván nem túl informatív, hiszen nem tudunk meg a dobott
kivételről semmit. Ha mégis használni akarjuk, az összes kivétel
elkapására jó.
13 Programozási paradigmák és technikák Gurka Dezsőné Csizmás Edit
Példa kibővítve:
static int Beolvas()
{
string s;
int x = 0;
Console.WriteLine("Kérek egy egész számot: ");
s = Console.ReadLine();
try
{
x = int.Parse(s); //ha szöveget ír be, nem lehet int-té konvertálni
}
catch (FormatException e)
{
Console.WriteLine("Hibásan adta meg a számot!");
Console.WriteLine(e.Message);
x = Beolvas();
}
catch (OverflowException e)
{
Console.WriteLine("Túl nagy szám!"); csak a legvégén állhat, mert minden kivételt elnyel
Console.WriteLine(e.Message);
x = Beolvas();
}
catch (Exception e)
{
Console.WriteLine("Egyéb hiba történt.");
Console.WriteLine(e.Message); Ha az Exception catch-ágat előre rakom, a fordító
x = Beolvas(); eleve figyelmeztet, hibaüzenete küld, hogy az az
}
return x; ősosztály, a további catch ágakat már nem éri el a
} kivétel.
static void Main(string[] args)
{
int szám = Beolvas();
}

14 Programozási paradigmák és technikák Gurka Dezsőné Csizmás Edit


Programozási paradigmák és technikák
15 Gurka Dezsőné Csizmás Edit
Programozási paradigmák és technikák
16 Gurka Dezsőné Csizmás Edit
A finally blokk
 A finally blokk a try-catch szerkezet végén, az utolsó catch-ág után állhat.
 Ez a blokk mindig lefut, akár dobott kivételt a program, akár „normálisan” futott le,
kivételdobás nélkül.
 Abban az esetben érdemes használni, ha valamilyen erőforrást kell visszaadnunk, pl.: fájlt
bezárni, adatbázisról lecsatlakozni, rajzeszközt megszüntetni, stb.
 Igazából az erőforrások használatához kényelmesebb a using blokk, mivel a blokkból
való kilépéskor automatikusan lezárja azt, mivel megszünteti az objektumot. Fordítás
után pedig a using blokk try -finally szerkezetre fordul le.
using (Erőforrás er = new Erőforrás())
{ ... }
 Ebben az esetben az er objektum csak a using blokkon belül él, amint kilépünk a blokkból,
a rendszer lezárja a rajta keresztül elért erőforrásokat (a Dispose() metódus meghívásával)
és az er-t is megszünteti (hiszen lokális változó volt).
17 Programozási paradigmák és technikák Gurka Dezsőné Csizmás Edit
9.5. A kivételek továbbdobása
 A try-blokkban megengedett az elkapott kivétel továbbdobása.
 A throw kulcsszót kell használni.
 Ennek akkor van értelme, ha az egy szinttel feljebb lévő metódusban (ahová a kivételt
továbbdobjuk) jobban tudjuk kezelni a problémát, mint helyben.
 Arra kell figyelni, ha a Main()-ből is továbbdobjuk a kivételt, akkor azt már a CLR
kapja el, vagyis kezeletlen kivétel lesz belőle, leáll a program.

18 Programozási paradigmák és technikák Gurka Dezsőné Csizmás Edit


Példa kivétel továbbdobására
static int Beolvas()
{
string s;
int x = 0;
Console.WriteLine("Kérek egy egész számot: ");
s = Console.ReadLine();
try
{
x = int.Parse(s); ha szöveget ír be, nem lehet int-té konvertálni
}
catch (FormatException e)
{
Console.WriteLine("A Beolvasban vagyunk...");
throw;
}
return x; továbbdobjuk a kivételt
}
static void Main(string[] args)
{
try
{
int szám = Beolvas();
}
catch (Exception e)
{
Console.WriteLine("Mainben vagyunk - Hibásan adta meg a számot!");
Console.WriteLine(e.Message);
}
}
}

19 Programozási paradigmák és technikák Gurka Dezsőné Csizmás Edit


9.6. Saját kivételosztály készítése
 Tegyük fel, hogy a számbeolvasásnál külön kivételt akarunk dobni abban az esetben,
ha nem ír be semmit a felhasználó, csak ENTER-t üt (ez egyébként FormatException
kivételt vált ki).
 Ehhez írunk egy saját kivételosztályt. Ezt public hozzáféréssel kell megadni, azért, hogy ha
másik szerelvényben fordul elő ez a kivétel, ott is látható legyen az osztály.
 Másik elvárás, hogy az ApplicationException osztályból származzon, és az osztály neve
Exception-ra végződjön – ebből mindenki tudja, hogy ez egy kivételosztály.

20 Programozási paradigmák és technikák Gurka Dezsőné Csizmás Edit


public class SzámBeolvasásException : ApplicationException
{ átdefiniálhatjuk az
public override string Message{
get{ Exception ősosztály
string s = "Nem írt be semmit!"; virtuális Message
return s;}} tulajdonságát
}
class Program{
static int Beolvas(){
string s;
int x = 0;
Console.WriteLine("Kérek egy egész számot: ");
s = Console.ReadLine();
try{ kivétel dobása
if (s.Length == 0) throw new
SzámBeolvasásException();
x = int.Parse(s);}
catch (SzámBeolvasásException e){
Console.WriteLine(e.Message);}
return x;}
static void Main(string[] args)
{ int szám = Beolvas(); } }

21 Programozási paradigmák és technikák Gurka Dezsőné Csizmás Edit


Interfészek
10. Interfészek
 Az interfész az absztrakt tagok nevesített halmaza.
 Az interfész csak deklarációkat tartalmazhat:
 metódust
 tulajdonságot
 indexelőt
 eseményt
 Interfészt megvalósíthat osztály vagy struktúra.
 Az interfész referenciatípus, mint az osztály.
 Az interfész hasonló az absztrakt osztályhoz, mert ez is meghatározza az őt megvalósító osztályok
viselkedését.
 Az absztrakt osztályhoz hasonlóan nem példányosítható.
 Az absztrakt osztállyal szemben az interfész nem épít öröklési láncot.
 Az absztrakt osztály metódusait csak a leszármazott osztályai tudják megvalósítani, az interfészt
bármelyik osztály vagy struktúra megvalósíthatja.

Programozási paradigmák és technikák


23 Gurka Dezsőné Csizmás Edit
Mikor érdemes interfészt használni?
 Ha egy osztályhierarchiában a származtatott osztályoknak csak egy része rendelkezik
egyfajta közös tulajdonsággal, a többi viszont nem.
 Vagy pedig olyan közös viselkedést kell modellezni, ami több hierarchiában is
megtalálható és ezeknek a hierarchiáknak nincs közös őse az object-en kívül.

Programozási paradigmák és technikák


24 Gurka Dezsőné Csizmás Edit
10.1. Szintaktika
 Az interfész nevét megegyezés szerint nagy I-vel kezdjük.
 Az interfész elérhetősége alapértelmezetten public (ha nem írunk elé semmit, akkor is ez lesz),
esetleg public helyett írhatunk elé internal-t, de mást nem.
 Az interfész tagjai nem kaphatnak saját elérhetőséget, public-nak számítanak.
 Az interfészben nem lehet megvalósítás.
 VS: Projecten jobb klikk->Add->New item->Interface

public interface IPélda metódusdeklaráció


{
void kiír();
int Sorszám { get; set; } tulajdonságdeklaráció
}

Programozási paradigmák és technikák


25 Gurka Dezsőné Csizmás Edit
 Az interfészt megvalósító osztálynak vagy struktúrának kötelessége az interfész
minden tagját megvalósítani (mindent vagy semmit).
 Egy osztály vagy struktúra akárhány interfészt megvalósíthat, ekkor vesszővel
elválasztva kell őket felsorolni. Ha a megvalósító osztálynak van közvetlen őse, akkor
először azt az osztályt kell feltüntetni:
class Megvalósító : ős, IPélda {...}

Programozási paradigmák és technikák


26 Gurka Dezsőné Csizmás Edit
Az IPélda interfészt megvalósító osztály
class Megvalósító :IPélda
{
private int sorszám;
public void kiír()
{
Console.WriteLine("Itt a metódus megvalósítása.");
}
public int Sorszám
{
get { return sorszám; }
set { sorszám = value; }
}
}
static void Main(string[] args)
{
Megvalósító m = new Megvalósító();
m.kiír();
m.Sorszám = 10;
}
Programozási paradigmák és technikák
ea7_p1.txt
27 Gurka Dezsőné Csizmás Edit
10.2. A használata
 Akkor érdemes interfészt használni, ha egy osztályhierarchiában a származtatott
osztályoknak csak egy része rendelkezik egyfajta közös tulajdonsággal, a többi viszont
nem.
 Példa:
 absztrakt Alakzat osztály
 oldalak_hossza double tömb
 paraméteres konstruktor az oldalak számát veszi át
 Kerület virtuális metódus
 Terület absztrakt metódus
 Leszármazottjai
 Téglalap (vannak csúcsai)
 Kör (nincsenek csúcsai)
 Interfész ICsúcsok
 CsúcsokSzáma metódus (visszaadja a csúcsok számát)
ea7_p2.txt
Programozási paradigmák és technikák
28 Gurka Dezsőné Csizmás Edit
A példa osztálydiagramja

Programozási paradigmák és technikák


29 Gurka Dezsőné Csizmás Edit
class Program {
static void Main(string[] args) {
Alakzat []alakzatok=new Alakzat[3];
for (int i = 0; i < alakzatok.Length; i++) {
if (i%2 == 0) { alakzatok[i] = new Teglalap(4); }
else { alakzatok[i]=new Kor(); }
Console.WriteLine((i+1)+". alakzat: ");
Console.WriteLine(alakzatok[i]);
Console.WriteLine("kerülete: "+ alakzatok[i].Kerulet());
Console.WriteLine("területe: " + alakzatok[i].Terulet());
//3. is kulcsszóval:
if (alakzatok[i] is ICsucsok) {
Console.WriteLine("csúcsok száma: " +
Csucsok)alakzatok[i]).CsucsokSzama());
}
else { Console.WriteLine("nincsenek csúcsok"); }
Console.WriteLine();
}
Console.ReadLine();
} }
30 Programozási paradigmák és technikák Gurka Dezsőné Csizmás Edit
abstract class Alakzat
{
protected double[] oldalak;
protected static Random rnd=new Random();
public Alakzat(int n) {
oldalak=new double[n];
for (int i = 0; i < n; i++) { oldalak[i] = rnd.Next(1, 7); }
}
public virtual double Kerulet() {
double k = 0;
for (int i = 0; i < oldalak.Length; i++) { k += oldalak[i]; }
return k;
}
public override string ToString() {
string s = "oldalak: ";
for (int i = 0; i < oldalak.Length; i++) { s += oldalak[i] + " "; }
return s;
}
public abstract double Terulet();
}
31 Programozási paradigmák és technikák Gurka Dezsőné Csizmás Edit
interface ICsucsok
{
int CsucsokSzama();
}

class Teglalap : Alakzat,ICsucsok


{
public Teglalap(int n) : base(n) {
oldalak[0] = oldalak[2];
oldalak[1] = oldalak[3];
}
public int CsucsokSzama() {
return oldalak.Length;
}
public override double Terulet() {
return oldalak[0]*oldalak[1];
}
}

32 Programozási paradigmák és technikák Gurka Dezsőné Csizmás Edit


class Kor:Alakzat
{
private double sugar;
public Kor(int n=0) : base(n)
{
sugar = rnd.Next(10, 20);
}
public override double Kerulet()
{
return 2*sugar*Math.PI;
}
public override double Terulet()
{
return sugar*sugar*Math.PI;
}
public override string ToString()
{
return "a sugár: "+sugar;
}
}
33 Programozási paradigmák és technikák Gurka Dezsőné Csizmás Edit
 Az Alakzat osztály egyes leszármazottai (pl. téglalap, négyzet,
háromszög, stb.) rendelkeznek csúcsokkal, mások viszont nem
(pl. kör, ellipszis). Ezért a leszármazási lánctól függetlenül hoztuk
létre az ICsúcsok nevű interfészt, amit egyes alakzatok
megvalósítanak, mások pedig nem.
 Mi történik, ha a Main()-ben létrehozunk egy Alakzat típusú
tömböt és egy for ciklusban feltöltjük Téglalap és Kör típusú
objektumokkal?
Alakzat[] al = new Alakzat[10];
 Ebben az esetben már nem tudjuk ránézésre megmondani, hogy
a tömb 5-ik eleme megvalósítja-e az ICsúcsok nevű interfészt
vagy sem.
 Erre a problémára, vagyis, hogy futásidőben eldöntsük egy
objektumról vagy struktúráról, hogy megvalósít-e egy interfészt a
következő módszerek léteznek:

Programozási paradigmák és technikák


34 Gurka Dezsőné Csizmás Edit
1. explicit kasztolás, try/catch blokkal:
ICsúcsok i = null;
try
{
i = (ICsúcsok)al[0];
Console.WriteLine(i.CsúcsokSzáma());
}
catch (InvalidCastException e)
{
Console.WriteLine("Ennek az alakzatnak nincsenek csúcsai.");
}

Programozási paradigmák és technikák


35 Gurka Dezsőné Csizmás Edit
2. as kulcsszóval:
ICsúcsok i = al[0] as ICsúcsok;
if (i != null)
{
Console.WriteLine(i.CsúcsokSzáma());
}
else
{
Console.WriteLine("Ennek az alakzatnak nincsenek csúcsai.");
}

Programozási paradigmák és technikák


36 Gurka Dezsőné Csizmás Edit
3. is kulcsszóval:
if (al[0] is ICsúcsok)
{
Console.WriteLine(((ICsúcsok)al[0]).CsúcsokSzáma());
}
else
{
Console.WriteLine("Ennek az alakzatnak nincsenek csúcsai.");
}

Programozási paradigmák és technikák


37 Gurka Dezsőné Csizmás Edit
 Interfész lehet metódusparaméter is
 ebben az esetben minden olyan objektum vagy struktúra átadható a metódusnak, ami
megvalósítja az adott interfészt.
 Interfészt vissza is adhat metódus
 Interfészekből tömböt is lehet képezni
 Ebben az esetben olyan elemeket lehet belerakni ebbe a tömbbe, amelyek megvalósítják az
adott interfészt.
 Az interfészek között létezik öröklés kapcsolat, sőt többszörös öröklés is (egy
interfésznek két vagy több közvetlen őse is lehet).

Programozási paradigmák és technikák


38 Gurka Dezsőné Csizmás Edit
10.3. Explicit interfészimplementáció
 Tegyük fel, hogy egy osztály két különböző interfészt is megvalósít és ebben a két különböző
interfészben van azonos nevű metódus:
interface IRajzFormra
{
void Rajzol();
}
interface IRajzNyomtatóra
{
void Rajzol();
}
 Nyilvánvalóan eltérő kód kell a képernyőre illetve a nyomtatóra való kirajzoláshoz, de a szokásos
megoldással csak egy Rajzol() metódust lehet megvalósítani:
class Téglalap : Alakzat, ICsúcsok, IRajzFormra, IRajzNyomtatóra
{
public void Rajzol()
{
//valamit csinál
}
}

Programozási paradigmák és technikák


39 Gurka Dezsőné Csizmás Edit
 Ilyenkor a névütközés feloldására az explicit interfészimplementáció nevű módszert
használjuk.
 Ilyenkor nem adunk meg a metódusokhoz hozzáférés módosítót, tehát az interfész
láthatósága érvényes ezekre a tagokra.
class Téglalap : Alakzat, ICsúcsok, IRajzFormra, IRajzNyomtatóra
{
void IRajzFormra.Rajzol()
{
//valamit csinál
}
void IRajzNyomtatóra.Rajzol()
{
//valamit csinál
}
}

Programozási paradigmák és technikák


40 Gurka Dezsőné Csizmás Edit
 a megvalósító osztály objektumán keresztül nem hívhatjuk meg őket.
 A Téglalap típusú objektumnál az IntelliSense sem mutatja a Rajzol() metódusokat. Ezért
csak kasztolás segítségével érhetjük el őket:
static void Main(string[] args)
{
Téglalap t = new Téglalap();
IRajzFormra formra = (IRajzFormra) t;
formra.Rajzol();
//vagy
((IRajzFormra)t).Rajzol();
//vagy
if (t is IRajzFormra)
((IRajzFormra)t).Rajzol();
}
Programozási paradigmák és technikák
41 Gurka Dezsőné Csizmás Edit
10.4. A .NET könyvtárak gyakran használt interfészei
 Nem csak mi magunk hozhatunk létre egyedi interfészeket, hanem felhasználhatjuk a
.NET könyvtárakban lévőket is.
 Pl. a System.Windows.Forms névtérben van a Control osztály, ez az ősosztálya több
Windows Forms vezérlőelemnek (pl. Label, TextBox, GridView, stb.)
 A Control osztály megvalósítja az IDropTarget interfészt, amely az alapvető „húzd és dobd
(drag and drop)” funkciókat definiálja.
interface IDropTarget
{
void OnDragDrop(DragEventArgs e);
void OnDragEnter(DragEventArgs e);
void OnDragLeave(EventArgs e);
void OnDragOver(DragEventArgs e);
}
Programozási paradigmák és technikák
42 Gurka Dezsőné Csizmás Edit
10.4.1. Felsorolható típusok készítése
 A foreach ciklus olyan típusokon képes végiglépkedni, amelyek támogatják a
GetEnumerator() nevű metódust.
 A tömbök és a gyűjteményosztályok ilyenek,
 de a saját osztályunkat is alkalmassá tehetjük a foreach általi végigjárásra.
 Ehhez az IEnumerable interfészt kell neki megvalósítania, ami a System.Collections
névtérben található.
 Ez az interfész tartalmaz többek között egy:
IEnumerator GetEnumerator(); metódusdeklarációt.
 Az IEnumerator szintén egy interfész, amely 3 tagot tartalmaz:
bool MoveNext(); //a következő elemre való léptetés
object Current {get;} //az aktuális elem beolvasása, tulajdonság
void Reset(); //az első elem elé állítja a kurzort,
//ezt -1-gyel szoktuk jelölni

Programozási paradigmák és technikák


43 Gurka Dezsőné Csizmás Edit
Példa
class Autó
{
//típusa, sebessége, rendszáma, tulajdonosa, stb.
}
class Garázs
{
private Autó[] autótömb;
public Garázs()
{
autótömb = new Autó[5];
for (int i = 0; i < 5; ++i)
autótömb[i] = new Autó();
}
} a fordító kiírja, hogy a Garázs típus nem
static void Main(string[] args)
{ definiálja a GetEnumerator() metódust,
Garázs g = new Garázs(); és enélkül a foreach nem tudja
foreach (Autó a in g)
{ végigjárni a benne lévő elemeket.
//kiírja az autó adatait
}
}
Programozási paradigmák és technikák
44 Gurka Dezsőné Csizmás Edit
A Garázs osztály helyes megvalósítása
class Garázs : IEnumerable
{
private Autó[] autótömb;
public Garázs()
{
autótömb = new Autó[5];
for (int i = 0; i < 5; ++i)
autótömb[i] = new Autó();
}
public IEnumerator GetEnumerator()
{
return autótömb.GetEnumerator();
}
}
 Mivel az autótömb nevű változó egy tömb, az pedig a System.Array osztály leszármazottja,
ami megvalósítja a GetEnumerator() metódust, elég erre hivatkozni, nem kell saját
megvalósítást írni.
Programozási paradigmák és technikák
45 Gurka Dezsőné Csizmás Edit
 A .NET 2.0-tól kezdve van egy másik lehetőségünk is olyan típusok létrehozására,
amelyek a foreach-csel működnek, ez az iterátor.
 Az iterátor olyan tag, amely meghatározza, hogy egy tároló belső elemeit hogyan kell egy
foreach feldolgozás során visszaadni.
 Ennek a neve mindig GetEnumerator(), és a visszatérési értéke is IEnumerator kell legyen,
de az osztálynak nem kell egyik interfészt sem megvalósítania:

Programozási paradigmák és technikák


46 Gurka Dezsőné Csizmás Edit
class Garázs : IEnumerable
{
private Autó[] autótömb;
public Garázs()
{
autótömb = new Autó[5];
for (int i = 0; i < 5; ++i)
autótömb[i] = new Autó();
}
public IEnumerator GetEnumerator()
{
foreach (Autó a in autótömb)
yield return a;
}
}
 A yield kulcsszót arra használjuk, hogy megadjuk azt az értéket, amelyet a hívó foreach szerkezetnek
vissza kell adnunk.
 A yield működési elve: a legelső metódushívásnál a ciklus megtesz egy lépést, ezután „kilépünk” a
metódusból – de annak állapotát megőrizzük, azaz a következő hívásnál nem újraindul a ciklus, hanem onnan
folytatja, ahol legutóbb abbahagytuk.

Programozási paradigmák és technikák


47 Gurka Dezsőné Csizmás Edit
 Az iterátormetódusnak nem kell feltétlenül a foreach-et használnia. Lehet, hogy
egyszerűen csak felsorolja a visszaadandó elemeket:
public IEnumerator GetEnumerator()
{
yield return autótömb[0];
yield return autótömb[1];
yield return autótömb[2];
yield return autótömb[3];
yield return autótömb[4];
}
 Ennek csak akkor van értelme, ha az autótömb biztosan nem bővül a program futása
során.

48 Programozási paradigmák és technikák Gurka Dezsőné Csizmás Edit


10.4.2. Klónozható objektumok készítése
 Ha ismerjük a referenciatípusok működését, akkor tudjuk, hogy ha referenciaváltozót
adunk értékül egy másiknak, akkor a két referencia azonos objektumra mutat a
memóriában.
class Pont
{…}
Main()-ben:
Pont p1 = new Pont(5,6);
Pont p2 = p1;
 Ebben az esetben p1 és p2 ugyanazt az objektumot jelenti.
 Ezt elkerülendő, az objektumoknál kétfajta másolás is van: sekély és mély.

Programozási paradigmák és technikák


49 Gurka Dezsőné Csizmás Edit
 Sekély másolás
 Ha az objektum nem tartalmaz belső referenciát (másik objektumot).
 Az objektum tartalmával megegyező értékű, de a halomban máshová kerülő másik
objektum készül.
 Mély másolás
 Ha az objektum tartalmaz referenciatípusú tagváltozókat.
 Az objektumok belső referenciatípusai is teljesen új objektumot képeznek, azonos
állapottal.
 A klónozható objektumoktól általában azt várjuk, hogy mély másolatot hozzanak létre
magukról, bár ezt a szabvány nem rögzíti.

Programozási paradigmák és technikák


50 Gurka Dezsőné Csizmás Edit
Ha egy osztály objektumait klónozhatóvá akarjuk tenni, akkor az ICloneable interfészt kell
megvalósítania:
class Pont : ICloneable
{
private int x, y;
public int X
{
set { x = value; }
}
public Pont(int x0, int y0) { x = x0; y = y0; }
public Pont() { }
public override string ToString()
{
return string.Format("x = {0}; y = {1}", x, y);
}
public object Clone() az ICloneable interfész egyetlen
{ metódusa
return new Pont(x, y);
}
}
 A Pont esetében elég a sekély másolás, hiszen nincs benne referenciatípusú rész.

51 Programozási paradigmák és technikák Gurka Dezsőné Csizmás Edit


static void Main(string[] args)
Az a1 és a2 {
objektumoknál Pont a1= new Pont(1,1);
egyszerűen csak két Pont a2 = a1;
referencia is mutat a2.X = 2;
ugyanarra a Console.WriteLine(a1.ToString()); //2;1
memóriaterületre. Console.WriteLine(a2.ToString()); //2;1

Pont a3 = new Pont(3, 3);


Pont a4 = (Pont) a3.Clone();
Az a4-et viszont már a4.X = 4;
klónoztuk, így Console.WriteLine(a3.ToString()); //3;3
független lett az az Console.WriteLine(a4.ToString()); //4;3
a3-tól. }

52 Programozási paradigmák és technikák Gurka Dezsőné Csizmás Edit


Mélymásolás megvalósítása
class Pont : Icloneable {
public int X { set { k[0] = value; } }
private int[] k;
public Pont(int[] k) { this.k = k; }
public Pont(int x, int y) {
k = new int[2];
k[0] = x; k[1] = y;
}
public Pont() { }
public override string ToString() {
return string.Format("x = {0}; y = {1}", k[0], k[1]);
}
public object Clone() {
int[] ujk = new int[2];
ujk[0] = k[0]; ujk[1] = k[1];
return new Pont(ujk);
//return new Pont(k); //ez így rossz!!!
//return new Pont(k[0], k[1]); //ez egy jó megoldás
}
53 Programozási paradigmák és technikák Gurka Dezsőné Csizmás Edit
10.4.3. Összehasonlítható objektumok készítése
 Tegyük fel, hogy a Pont típusú objektumokat az alapján akarjuk sorba rendezni, hogy milyen
messze vannak az origótól. Ehhez az osztálynak az IComparable nevű interfészt kell
megvalósítania:
class Pont : IComparable
{
private int x, y;
public int X { set { x = value; } }
public Pont(int x0, int y0) { x = x0; y = y0; }
public Pont() { }
public override string ToString()
{
return string.Format("x = {0}; y = {1}", x, y);
}
public int CompareTo(object obj) az IComparable interfész egyetlen
{ metódusa
Pont Másik = (Pont) obj;
double SajátTávolság = Math.Sqrt(x *x + y *y);
double MásikTávolság = Math.Sqrt(
Másik.x * Másik.x + Másik.y * Másik.y);
if (SajátTávolság > MásikTávolság) return 1;
if (SajátTávolság < MásikTávolság) return -1;
else return 0;
}
}
Programozási paradigmák és technikák
54 Gurka Dezsőné Csizmás Edit
static void Main(string[] args)
{
Pont [] tmb = new Pont[10];
Random r = new Random();
for (int i = 0; i < 10; i++)
{
tmb[i] = new Pont(r.Next(5), r.Next(5));
}
Array.Sort(tmb); növekvő sorba rendezzük a pontokat a
foreach (Pont a in tmb) CompareTo metódus alapján
{
Console.WriteLine(a.ToString());
}
}

Programozási paradigmák és technikák


55 Gurka Dezsőné Csizmás Edit
 Az összehasonlításnál felhasználhatjuk azt is, hogy minden .NET ValueType
rendelkezik a CompareTo() metódussal, így a double is:

public int CompareTo(object obj)


{
Pont Másik = (Pont) obj;
double SajátTávolság = Math.Sqrt(x *x + y *y);
double MásikTávolság = Math.Sqrt(Másik.x *
Másik.x + Másik.y * Másik.y);
return SajátTávolság.CompareTo(MásikTávolság);
}

Programozási paradigmák és technikák


56 Gurka Dezsőné Csizmás Edit
 Hogyha ugyanazt a típust több, különböző szempont szerint is szeretnénk
rendezni (vagy szeretnénk a rendezési lehetőségek közül választani),
 akkor a System.Collections névtérben lévő IComparer interfészt kell megvalósítani.
 Ezt az interfészt nem a rendezendő típus osztálya, hanem segédosztályok valósítják meg.
 Ennek az az előnye, hogy ha nem férünk hozzá a rendezendő osztály kódjához, akkor is
írhatunk ilyen segédosztályokat:
class XSzerintRendez : IComparer
{
public int Compare(object a, object b)
{
if ( ((Pont)(a)).X > ((Pont)(b)).X) return 1;
if ( ((Pont)(a)).X < ((Pont)(b)).X) return -1;
else return 0;
}
}

57 Programozási paradigmák és technikák Gurka Dezsőné Csizmás Edit


 A Pontot bővítsük ki két lekérdező tulajdonsággal, hogy a másik osztály hozzáférjen a
pontok x koordinátájához:
public int X
{
set { x = value; }
get { return x; }
}
public int Y
{
get { return y; }
}
 A Main()-ben pedig az előző rendezést megtartva, használhatjuk az újat is:
sorba rendezzük
Array.Sort(tmb, new XSzerintRendez());
foreach (Pont a in tmb) az x koordináták
{ szerint
Console.WriteLine(a.ToString());
}
Programozási paradigmák és technikák
58 Gurka Dezsőné Csizmás Edit

You might also like