A InfoWiki wikiből
WikiSysop (vita | szerkesztései)
(Új oldal, tartalma: „<cim cim3="#1 Bevezető Feladatok" cim2="OOP Gyakorlat" cim1="Magasszintű Programozási Nyelvek II" prev="mp4/Nyitolap" next="mp4/page02" kep="hz...”)
Újabb szerkesztés →
Aktuális változat
Tartalomjegyzék |
Téglalap
Készítsünk olyan programot, amely téglalapokat kezel. Egy téglalapot jellemez a 'szélessége', és a 'magassága', valamint a koordinátarendszerben a bal felső sarkának 'x','y' koordinátája. Ez gyakorlatilag egy 4 mezőt tartalmazó rekordot jelent. Számoljuk ki ezen téglalap kerületét. Oldjuk meg a téglalap adatainak kezelését biztonságosan.
Az alapfeladat
Készítsük el a téglalap adatait tartalmazó rekord deklarációját.
// Teglalap alap rekord class Teglalap { public int szelesseg; public int magassag; public int x; public int y; }
Készítsünk olyan programot, amely létrehoz egy ilyen téglalapot példányt, és a mezők értékét feltölti billentyűzetről. A program ezek után írja ki a téglalap kerületét.
// Teglalap kezelő főprogram class Foprogram { //.......................................................................... public static void Main() { Teglalap t; t = new Teglalap(); t.x = -20; t.y = +30; t.szelesseg = 10; t.magassag = 20; Console.WriteLine("A teglalap kerulete = {0}", (t.szelesseg+t.magassag)*2); } //.......................................................................... }
Alkalmazzunk a kerület kiszámítására függvényt:
// Teglalap kezelő főprogram + kerületszámító függvény class Foprogram { //.......................................................................... public static int Kerulet( Teglalap t ) { return 2*(t.szelesseg + t.magassag); } //.......................................................................... public static void Main() { Teglalap t; t = new Teglalap(); t.x = -20; t.y = +30; t.szelesseg = 10; t.magassag = 20; Console.WriteLine("A teglalap kerulete = {0}", Kerulet(t) ); } //.......................................................................... }
- Vegyük észre, hogy a rekord mezői előtt álló 'public' szó a mező védelmi szintje. Egyúttal megadja, hogy a program mely soraiban, mely területén lehet az adott mezőre hivatkozni. Mivel a főprogramban próbálunk a mezőkre hivatkozni, így ott csak a 'public' védelmi szintű mezőket érhetjük el. Amennyiben bármely mező védelmi szintjét módosítjuk ('protected', 'private'), úgy a főprogram fordításakor hibaüzenetet kapunk. Próbáljuk ki!
A rekord alapú vektor
Készítsünk a téglalapokból egy 30 elemű vektort, és töltsük fel a 30 téglalap rekord mezőit véletlen értékekkel. Határozzuk meg, melyik ezek közül a legnagyobb kerületűt, és irassuk ki a paramétereit.
// Teglalap kezelése vektorban class Foprogram { //.......................................................................... public static int Kerulet(Teglalap t) { return 2 * (t.szelesseg + t.magassag); } //.......................................................................... public static void Main() { Teglalap[] tomb = new Teglalap[30]; // --- rekordok létrehozása a memóriában --- for (int i = 0; i < tomb.Length; i++) tomb[i] = new Teglalap(); // --- rekordok mezőinek feltöltése --- Random rnd = new Random(); foreach (Teglalap t in tomb) { t.x = rnd.Next(-100, 100); t.y = rnd.Next(-100, 100); t.szelesseg = rnd.Next(10, 100); t.magassag = rnd.Next(10, 100); } // --- maximális elem sorszámának kiválasztása --- int maxI = 0; for (int i = 1; i < tomb.Length; i++) if (Kerulet(tomb[i]) > Kerulet(tomb[maxI])) maxI = i; // --- kiírás --- Console.WriteLine("A teglalap adatai: X={0}, Y={1}, Szelesseg={2}, Magassag={3}", tomb[maxI].x, tomb[maxI].y, tomb[maxI].szelesseg, tomb[maxI].magassag ); } //.......................................................................... }
- Vegyük észre, hogy maga a vektor deklarációja nem jelenti azt, hogy a 30 darab téglalap rekord be is került a memóriába. A vektor csak a későbbi téglalapok memóriacímét őrzi - ebben az állapotában azonban csak 30 darab 'null' értéket, jelezvén hogy még nem tartalmaz használható memóriacímet:
Teglalap[] tomb = new Teglalap[30];
- A téglalap rekordokat egyesével kell a memóriában kialakítani, helyüket lefoglalni, és a memóriacímeket bemásolni a vektor megfelelő helyére:
for (int i = 0; i < tomb.Length; i++) tomb[i] = new Teglalap();
A rekord alapú lista
Ismételten oldjuk meg a feladatot, de ezúttal ne vektort, hanem listát (ArrayList) használjunk fel. Ha ArrayList-et kívánunk használni egy programban, akkor a 'System.Collections' névteret kell a 'using' segítségével megnyitni!
// Teglalap kezelése ArrayList-ben class Foprogram { //.......................................................................... public static int Kerulet(Teglalap t) { return 2 * (t.szelesseg + t.magassag); } //.......................................................................... public static void Main() { ArrayList lista = new ArrayList(); // --- rekordok létrehozása a memóriában --- for (int i = 0; i < 30; i++) { Teglalap t = new Teglalap(); lista.Add( t ); } // --- rekordok mezőinek feltöltése --- Random rnd = new Random(); foreach (Teglalap t in lista) { t.x = rnd.Next(-100, 100); t.y = rnd.Next(-100, 100); t.szelesseg = rnd.Next(10, 100); t.magassag = rnd.Next(10, 100); } // --- maximális elem sorszámának kiválasztása --- int maxI = 0; for (int i = 1; i < lista.Count; i++) if (Kerulet(lista[i] as Teglalap) > Kerulet(lista[maxI] as Teglalap)) maxI = i; // --- kiírás --- Console.WriteLine("A teglalap adatai: X={0}, Y={1}, Szelesseg={2}, Magassag={3}", (lista[maxI] as Teglalap).x, (lista[maxI] as Teglalap).y, (lista[maxI] as Teglalap).szelesseg, (lista[maxI] as Teglalap).magassag ); } //.......................................................................... }
- Figyeljük meg, hogy 'tomb' helyett most 'lista' nevű változót használunk, amelynek típusa 'ArrayList'. Egy ilyen lista létrehozása után üres. Elemszámát nem a '.Length', hanem a '.Count' property írja le. Mivel induláskor üres a lista, az elemeket hozzá kell adni a listához az '.Add(...)' metódusával:
for (int i = 0; i < 30; i++) { Teglalap t = new Teglalap(); lista.Add( t ); }
- Ezt a fenti műveletet természetesen egy lépésben is elvégezhetjük:
for (int i = 0; i < 30; i++) lista.Add( new Teglalap() );
- A fenti ciklusban még nem használhatjuk a '.Count' tulajdonságot, mivel annak értéke (a lista létrehozása után) egyelőre '0'. Ezért a hozzáadandó elemek számát direktben kell megadni ('30').
- A 'foreach' ciklus nem változott, mivel annak jelen példában mindegy, hogy tömbbel vagy listával dolgozunk. Többek között ezért is érdemes használni.
- A további kódrészek a miatt módosultak, mivel az 'ArrayList' tetszőleges típusú elemek (Object alaptípus) tárolására képes, és e miatt az elemek kiolvasása után típuskényszerítéseket kell alkalmazni ('lista[i] as Teglalap)'. Az 'as' típuskényszerítés helyett természetesen alkalmazhatjuk a hagyományos módot is: '(Teglalap)lista[i]', de ekkor több zárójelet kell alkalmazni, pl: '((Teglalap)lista[i]).x' módon.
A rekord alapú típusos lista
Ismételten oldjuk meg a feladatot, de ezúttal ne vektort, hanem típusos listát (generic) használjunk fel.
// Teglalap kezelése generic listával class Foprogram { //.......................................................................... public static int Kerulet(Teglalap t) { return 2 * (t.szelesseg + t.magassag); } //.......................................................................... public static void Main() { List<Teglalap> lista = new List<Teglalap>(); // --- rekordok létrehozása a memóriában --- for (int i = 0; i < 30; i++) lista.Add( new Teglalap() ); // --- rekordok mezőinek feltöltése --- Random rnd = new Random(); foreach (Teglalap t in lista) { t.x = rnd.Next(-100, 100); t.y = rnd.Next(-100, 100); t.szelesseg = rnd.Next(10, 100); t.magassag = rnd.Next(10, 100); } // --- maximális elem sorszámának kiválasztása --- int maxI = 0; for (int i = 1; i < lista.Count; i++) if (Kerulet(lista[i]) > Kerulet(lista[maxI])) maxI = i; // --- kiírás --- Console.WriteLine("A teglalap adatai: X={0}, Y={1}, Szelesseg={2}, Magassag={3}", lista[maxI].x, lista[maxI].y, lista[maxI].szelesseg, lista[maxI].magassag ); } //.......................................................................... }
- A típusos lista használatához a 'System.Collections.Generic' névteret kell megnyitni. Ez a névtér csak a C# 2.0-ban, a Visual Studio 2005-ben létezik csak, korábbi verzióban nem elérhető.
- A generic lista egy típusos listát jelent, ahol a listában lévő elemek típusát meg tudjuk adni: 'List<Teglalap>'. Az elemek típusnevét a '<..>' jelek közé kell írni. Ennek két előnye van: egyrészt a fordítóprogram ellenőrzi, hogy ettől eltérő (ezzel nem kompatibilis) típusú elemet ne adhassunk a listához. Másrészt az elemek kiolvasásakor a fordító tudja, hogy az elem típusa milyen, ezért a további típuskényszerítésekre nincs szükség (sem az 'as', sem a hagyományos formájú típuskényszerítésre nincs szükség).
A kerület metódus bevezetése
A kerületszámító függvényt metódus formájában építsük be a téglalap osztályba.
// Teglalap osztály interface class Teglalap { public int szelesseg; public int magassag; public int x; public int y; public int kerulet() {...} }
// Teglalap osztály kidolgozva class Teglalap { public int szelesseg; public int magassag; public int x; public int y; //.......................................................................... public int kerulet() { return 2 * (szelesseg + magassag); } //.......................................................................... }
- A metódust az osztály belsejébe kell írni. A példányszintű metódusok előtt nem szerepel a 'static' szó. Paraméterre nincs szükség - hiszen a példányszintű metódusok az adott példány mezőivel kell hogy dolgozzon. Ezeket a mezőket viszont direkt módon éri el.
- A metódis védelmi szintjét jelen példában szintén 'public'-ra kell állítani, hiszen kívülről, a főprogramból fogjuk majd aktiválni.
Ismételten oldjuk meg a feladatokat, de ezúttal ezen metódus segítségével.
// Teglalap osztály kezelő főprogram class Foprogram { //.......................................................................... public static void Main() { Teglalap t; t = new Teglalap(); t.x = -20; t.y = +30; t.szelesseg = 10; t.magassag = 20; Console.WriteLine("A teglalap kerulete = {0}", t.kerulet()); } //.......................................................................... }
- a kerület metódus aktiválása a példányon keresztül történik: 't.kerulet()'.
// Teglalap osztály kezelése vektorban class Foprogram { //.......................................................................... public static int Kerulet(Teglalap t) { return 2 * (t.szelesseg + t.magassag); } //.......................................................................... public static void Main() { Teglalap[] tomb = new Teglalap[30]; // --- rekordok létrehozása a memóriában --- for (int i = 0; i < tomb.Length; i++) tomb[i] = new Teglalap(); // --- rekordok mezőinek feltöltése --- Random rnd = new Random(); foreach (Teglalap t in tomb) { t.x = rnd.Next(-100, 100); t.y = rnd.Next(-100, 100); t.szelesseg = rnd.Next(10, 100); t.magassag = rnd.Next(10, 100); } // --- maximális elem sorszámának kiválasztása --- int maxI = 0; for (int i = 1; i < tomb.Length; i++) if (tomb[i].kerulet() > tomb[maxI].kerulet()) maxI = i; // --- kiírás --- Console.WriteLine("A teglalap adatai: X={0}, Y={1}, Szelesseg={2}, Magassag={3}", tomb[maxI].x, tomb[maxI].y, tomb[maxI].szelesseg, tomb[maxI].magassag ); } //.......................................................................... }
- a kerület metódus aktiválása a példányon keresztül történik: 'tomb[i].kerulet()'.
// Teglalap osztály kezelése listában class Foprogram { public static void Main() { ArrayList lista = new ArrayList(); // --- rekordok létrehozása a memóriában --- for (int i = 0; i < 30; i++) { Teglalap t = new Teglalap(); lista.Add( t ); } // --- rekordok mezőinek feltöltése --- Random rnd = new Random(); foreach (Teglalap t in lista) { t.x = rnd.Next(-100, 100); t.y = rnd.Next(-100, 100); t.szelesseg = rnd.Next(10, 100); t.magassag = rnd.Next(10, 100); } // --- maximális elem sorszámának kiválasztása --- int maxI = 0; for (int i = 1; i < lista.Count; i++) if ((lista[i] as Teglalap).kerulet() > (lista[maxI] as Teglalap).kerulet()) maxI = i; // --- kiírás --- Console.WriteLine("A teglalap adatai: X={0}, Y={1}, Szelesseg={2}, Magassag={3}", (lista[maxI] as Teglalap).x, (lista[maxI] as Teglalap).y, (lista[maxI] as Teglalap).szelesseg, (lista[maxI] as Teglalap).magassag ); } //.......................................................................... }
- a metódus aktiválása most is a példányon keresztül történik, de itt a példányt a lista típustalansága miatt először típuskényszeríteni kell: '(lista[i] as Teglalap).kerulet()', vagy használhatnánk a hagyományos alakot is: '((Teglalap)lista[i]).kerulet()'. Ekkor azonban bonyolultabb a zárójelezés.
// Teglalap osztály kezelése típusos listában class Foprogram { public static void Main() { List<Teglalap> lista = new List<Teglalap>(); // --- rekordok létrehozása a memóriában --- for (int i = 0; i < 30; i++) lista.Add(new Teglalap()); // --- rekordok mezőinek feltöltése --- Random rnd = new Random(); foreach (Teglalap t in lista) { t.x = rnd.Next(-100, 100); t.y = rnd.Next(-100, 100); t.szelesseg = rnd.Next(10, 100); t.magassag = rnd.Next(10, 100); } // --- maximális elem sorszámának kiválasztása --- int maxI = 0; for (int i = 1; i < lista.Count; i++) if (lista[i].kerulet() > lista[maxI].kerulet() ) maxI = i; // --- kiírás --- Console.WriteLine("A teglalap adatai: X={0}, Y={1}, Szelesseg={2}, Magassag={3}", lista[maxI].x, lista[maxI].y, lista[maxI].szelesseg, lista[maxI].magassag ); } //.......................................................................... }
- a különbség csak abban van, hogy a metódushíváshoz sem kell a típuskényszerítést használni, mivel a lista elemeinek a típusa ismert a fordítóprogram által.
Szabályok a mezők értékeire - property
"Feladat": a téglalap mezőinek aktuális értékeire az alábbi megkötéseket tesszük:
- a szélesség értéke nem nulla, vagy negatív számok
- a magasság értéke nem nulla, vagy negatív számok
// Teglalap osztály kiegészítve property-kkel class Teglalap { //.......................................................................... protected int _szelesseg; //.......................................................................... public int szelesseg { get { return _szelesseg; } set { if (value <= 0) throw new Exception("Nem lehet negativ vagy nulla!"); else _szelesseg = value; } } //.......................................................................... //.......................................................................... protected int _magassag; //.......................................................................... public int magassag { get { return _magassag; } set { if (value <= 0) throw new Exception("Nem lehet negativ vagy nulla!"); else _magassag = value; } } //.......................................................................... public int x; public int y; //.......................................................................... public int kerulet() { return 2 * (szelesseg + magassag); } //.......................................................................... }
- amennyiben valamely mezőre szabály, megszorítás van, úgy az nem lehet 'public'. Ez esetben ugyanis a külső kód direkt módon írhatná a mező értékét, és lehetősége lenne hibás érték beírására is.
- a mező ilyenkor jellemzően 'protected' lesz, hogy a gyerekosztályok a mezőt képesek legyenek elérni direkt módon is (ez gyors kezelést tesz lehetővé). Csak nagyon indokolt esetben állítjuk a mezőt 'private'-ra.
- az elrejtett mező nevét névadási hagyomány szerint megváltoztatjuk, és egy '_' karakterrel egészítjük ki.
- a property neve így már megkaphatja az előbb használt, "szép" nevet.
- a property get része jellemzően egyszerű, visszaadja a háttérben működő, de a külvilág számára nem elérhető mezőben lévő értéket
- a property set részében a beírandó értéket a 'value' kulcsszó segítségével kapjuk meg
- a property set része legegyszerűbb esetben is legalább egy 'if'-et tartalmaz, mely ellenőrzi, hogy a beírandó érték megfelelő-e. Amennyiben nem, kivétel feldobásával jelzi. Amennyiben az érték megfelelő, eltárolja az elrejtett mezőben.
Szabályok a mezők kezdőértékeire - konstruktor
Alkalmazzunk konstruktort a mezők kezdőértékének beállításához. Alkalmazzunk property-ket a mezők értékeinek kiolvasásához, beállításához, miközben a szóban forgó szabályok sérthetetlenségét biztosítjuk.
// Teglalap osztály interface kiegészítve a konstruktorral class Teglalap { protected int _szelesseg; protected int _magassag; public int x; public int y; public int keruletSzamitas() {...} public Teglalap(int szelesseg, int magassag, int x, int y) {...} public int szelesseg { get {...} set {...} } public int magassag { get {...} set {...} } }
// Teglalap osztály kidolgozása kiegészítve a konstruktorral class Teglalap { //.......................................................................... protected int _szelesseg; //.......................................................................... public int szelesseg { get { return _szelesseg; } set { if (value <= 0) throw new Exception("Nem lehet negativ vagy nulla!"); else _szelesseg = value; } } //.......................................................................... //.......................................................................... protected int _magassag; //.......................................................................... public int magassag { get { return _magassag; } set { if (value <= 0) throw new Exception("Nem lehet negativ vagy nulla!"); else _magassag = value; } } //.......................................................................... public int x; public int y; //.......................................................................... public int kerulet() { return 2 * (szelesseg + magassag); } //.......................................................................... public Teglalap(int szelesseg, int magassag, int x, int y) { this.szelesseg = szelesseg; this.magassag = magassag; this.x = x; this.y = y; } //.......................................................................... }
- a konstruktor írásakor ügyeljünk, hogy ha úgyanúgy nevezzük el a paraméterváltozókat, mint amilyen nevű mezőnk, vagy property-nk van, akkor a 'this' segítségével kell hangsúlyoznunk, mikor gondolunk a property-re.
- a konstruktor a property-n keresztül menti el a paraméterben megkapott kezdőértékeket, mivel így az keresztülmegy a property ellenőrző mechanizmusán.
- a konstruktor biztosítja, hogy a mezők kezdőértéket kapjanak a példányosításkor. A property ugyanis csak a már létrehozott példányok esetén működik, nem garantálja önnmagában, hogy a példány létrehozásakor a mezők kezdőértéke eleve helyes lesz.