Top Banner
Alapfogalmak CLR: Common Language Runtime: egy rendszer, amely egy „virtuális gépi kódú nyelv”-re fordított programokat (.exe) képes futtatni. BCL: Base Class Library: egy osztálygyujtemény, amely „kész”, általános célú objektumokat tartalmaz. Ezeket az objektumokat a .NET minden támogatott nyelvében el lehet érni. CLS: Common Runtime Specification: A „virtuális gépi kódú” nyelv, melyre le kell fordítani a .NET alatt futtatandó programokat. A .NET programok (bár .exe-k), nem közvetlenül futtatható programok, hanem egy köztes kódra fordított programok, amelyeknek az „eleje” normális gépi kód, de csak annyit „csinál”, hogy értesíti az operációs rendszert, hogy töltse be rá a CLR rendszert, amely átveszi a program futtatásának feladatát. A CLR, a BCL szabadon letöltheto (free software) a Microsoft honlapról .NET Framework” címszó alatt. Ezt (többek között) a “C:\WINDOWS\Microsoft.NET\Framework\v1.0.3705\” alkönyvtárba telepszik fel. Ebben található egy „CSC.EXE”, amely a C# (C-Sharp, ejtsd szí-sarp) programnyelv parancssori fordítója. A másik mód, hogy a „Microsoft Visual Studio v7.0” másik nevén „Microsoft Visual Studio .NET” fejlesztorendszert tesszük fel (5 cd). Ezt tartalmazza a Framework-t is. A Framework-nek készül a LINUX-os változata!!!! (http://www.go-mono.org) A C# fordító is készült LINUX-ra. A CLR interpreteres üzemmódban futtatja a .NET programokat (ez lassú). Ugyanakkor ezzel megvalósítható a kód futtatásának felügyeletét: - eroforrás-felügyelet: szabályozható, hogy a program milyen eroforrá sokhoz férhet hozzá (file-ok, internet, hálózati kapcsolatok, stb…) - a programok ezt az ellenorzést nem kerülhetik meg - minden eroforrásigénylésüket a CLR-en keresztül kell megvalósítaniuk - minden programhoz beállítható, hogy miket engedélyezünk neki: START menü / Beállítások / Vezérlopult / Felügyeleti eszközök / Microsoft .NET Framework Configuration - Ezzel megvédhetjük számítógépeinket, hogy pl. az Internet-rol letöltött, és elindított programok ne legyenek képesek „illegális” tevékenységeket folytatni (vírusok), ne olvashassák a file-okat, ne küldhessenek adatokat ki valamely hálózati kapcsolaton keresztül, … A CLR interpreteres üzemmódban futtatja a .NET programokat (ez lassú). - ugyanakkor nem érdekes, hogy melyik programnyelven írtuk az eredeti kódot, a számítógép a generált .exe-t fogja futtatni - ezért tetszoleges programnyelven meg lehet (majd) írni a programokat, csak a lényeges, hogy a fordító és szerkeszto programok ne „natív” windows-os .exe-t generáljanak, hanem .NET-es .exe-t. - a virtuális gépi kód OOP-t támogató kód! - ezért az általunk használt magasszintu programnyelveknek is OOP nyelveknek kell lenniük! - jelenleg is már 10 fölötti a .NET-et támogató nyelvek listája - a Microsoft a Visual Basic, C++, C#, Java Script, J# (Java-alapú) nyelvekhez elkészítette a fordítókat - egyéb nyelvekhez külso cégek készítik el a fordítókat (pl. Inprise-Delphi) A CLR interpreteres üzemmódban futtatja a .NET programokat (ez lassú). - az interpretert egy JIT (Just-In-Time) compiler támogatja, amely „dönthet” úgy, hogy a virtuális gépi kód bizonyos (surun hívott) részeit futtatás közben a memóriában natív kódra fordítja (sebességnövekedés) - ezzel pl. megoldható az, hogy a jövo processzorai új regisztereket is tartalmaznak majd, akkor ugyanaz az .exe kód ezeken ezt máris ki fogják használni - a processzor-specifikus gépi kódú utasításokat használja (AMD, Intel Pentium,…) - az adott operációs rendszer lehetoségeit messzemenokig kihasználja a program A CLR interpreteres üzemmódban futtatja a .NET programokat (ez lassú). Ugyanakkor az NGEN.EXE-vel a lefordított .NET programot natív kódra lehet fordítani. Ezzel a kód az adott processzorhoz és operációs rendszerhez lesz optimalizálva, futása ebben a környezetben 2x 3x gyorsabb lesz (lehet)! A CLR interpreteres üzemmódban futtatja a .NET programokat (ez lassú). - ezzel megszunnek az eredeti magasszintu nyelvek adatábrázolási különbségei: - sztringek tárolása (C és PASCAL stílus, ASCII és UNICODE, stb…) - számtípusú adatok tárolási különbsége (méret, pontosság, stb…) - tömbök indexelése (egységesen 0..n-1 indexelésú (Basic-esek sírnak!!!)) - stb… - a CLS-nek megfelelo nyelvek képesek együtt muködni: - a kész projekt egyik felét pl. C++-ban is meg lehet írni - a másik felét Visual Basic-ben - mindkét felét eloször CLS-nek megfelelo „virtuális gépi kódra” fordítják le - a kettobol a szerkeszto összeállítja a muködo kész programot. A CLR interpreteres üzemmódban futtatja a .NET programokat (ez lassú). - de a program futtatásához szükséges DLL (Dynamic Link Library = közös (shared) eljárás és fvgyujtemény) verzióit minden program magának specifikálhatja, azaz nem okoz problémát, hogy ha valamelyik program ugyanazon néven feltesz egy közös DLL-t, azt erre hivatkozó programok vagy „megbíznak” ebben a DLL-ben, és használják, vagy a saját régebbi verziót használják tovább - ennek kezelését a CLR végzi - A DLL-ek nem csak függvényeket exportálhatnak, hanem típusinformációval ellátott objektumosztályokat!!! - a programok védettek a buffer overflow vírustechnikával szemben. A CLR interpreteres üzemmódban futtatja a .NET programokat (ez lassú). - a CLR nyilvántartja a program eroforrás-felhasználását, és hiba esetén ter minálja a programot, és korrekt módon felszabadítja az eroforrásokat - ez kiterjed az objektumok destruktorainak automatikus meghívására is (garbage collector funkció) „normális” programmfuttatás közben is. 1 E jegyzet másolata nem használható fel szabadon,az előadás anyagának kivonata. Ezen teljes jegyzetről,vagy annak bármely részéről bármely másolat készítéséhez a szerző előzetes írásbeli hozzájárulására van szükség. A másolatnak tartalmaznia kell a sokszorosításra vonatkozó korlátozó kitételt is. A jegyzet kizárólag főiskolai oktatási vagy tanulmányi célra használható! A szerző hozzájárulását adja ahhoz, hogy az EKF számítástechnika tanári, és programozó matem- atikus szakján, a tárgyat az EKF TO által elfogadott módon felvett hallgatók bármelyike, kizárólag saját maga részére,tanulmányaihoz egyetlen egy példány másolatot készítsen a jegyzetből. A jegyzet e változata még tartalmazhat mind gépelési, mind helyességi hibákat. Az állítások nem mindegyike lett tesztelve tel jes körűen. Minden észrevételt,amely valamilyen hibára vonatkozik,örömmel fogadok. H E R N Y Á K Z O L T Á N [email protected] VB C++ JScript C# J# Visual Studio.NET Common Language Specification ASP .NET Web Forms Web Services Mobile Internet Toolkit Windows Forms ADO.NET and XML Common Language Runtime Operating System Base Class Library Programozási nyelvek II. C#
25

Programozási nyelvek II. C# - aries.ektf.huaries.ektf.hu/~shad/feltoltesek/download/Prognyelv_II/NET2_cou3.pdf · A „virtuális gépi kódú” nyelv, melyre le kell fordítani

Oct 24, 2019

Download

Documents

dariahiddleston
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: Programozási nyelvek II. C# - aries.ektf.huaries.ektf.hu/~shad/feltoltesek/download/Prognyelv_II/NET2_cou3.pdf · A „virtuális gépi kódú” nyelv, melyre le kell fordítani

AlapfogalmakCLR: Common Language Runtime:egy rendszer, amely egy „virtuális gépi kódú nyelv”-re fordított programokat(.exe) képes futtatni.BCL: Base Class Library:egy osztálygyujtemény, amely „kész”, általános célú objektumokat tartalmaz.Ezeket az objektumokat a .NET minden támogatott nyelvében el lehet érni.CLS: Common Runtime Specification:A „virtuális gépi kódú” nyelv, melyre le kell fordítani a .NET alatt futtatandóprogramokat.

A .NET programok (bár .exe-k), nem közvetlenül futtatható programok,hanem egy köztes kódra fordított programok, amelyeknek az „eleje” normálisgépi kód, de csak annyit „csinál”, hogy értesíti az operációs rendszert, hogytöltse be rá a CLR rendszert, amely átveszi a program futtatásának feladatát.

A CLR, a BCL szabadon letöltheto (free software) a Microsoft honlapról.NETFramework” címszó alatt. Ezt (többek között) a “C:\WINDOWS\Microsoft.NET\Framework\v1.0.3705\”alkönyvtárba telepszik fel.

Ebben található egy „CSC.EXE”, amely a C# (C-Sharp, ejtsd szí-sarp)programnyelv parancssori fordítója.

A másik mód, hogy a „Microsoft Visual Studio v7.0” másik nevén „MicrosoftVisual Studio .NET” fejlesztorendszert tesszük fel (5 cd). Ezt tartalmazza aFramework-t is.

A Framework-nek készül a LINUX-os változata!!!! (http://www.go-mono.org)A C# fordító is készült LINUX-ra.

A CLR interpreteres üzemmódban futtatja a .NET programokat (ez lassú).Ugyanakkor ezzel megvalósítható a kód futtatásának felügyeletét:

- eroforrás-felügyelet: szabályozható, hogy a program milyen eroforrásokhoz férhet hozzá (file-ok, internet, hálózati kapcsolatok, stb…)

- a programok ezt az ellenorzést nem kerülhetik meg- minden eroforrásigénylésüket a CLR-en keresztül kell megvalósítaniuk- minden programhoz beállítható, hogy miket engedélyezünk neki:

START menü / Beállítások / Vezérlopult / Felügyeleti eszközök / Microsoft.NET Framework Configuration

- Ezzel megvédhetjük számítógépeinket, hogy pl. az Internet-rol letöltött, és

elindított programok ne legyenek képesek „illegális” tevékenységeketfolytatni (vírusok), ne olvashassák a file-okat, ne küldhessenek adatokat ki valamely hálózati kapcsolaton keresztül, …

A CLR interpreteres üzemmódban futtatja a .NET programokat (ez lassú).- ugyanakkor nem érdekes, hogy melyik programnyelven írtuk az eredeti

kódot, a számítógép a generált .exe-t fogja futtatni- ezért tetszoleges programnyelven meg lehet (majd) írni a programokat,

csak a lényeges, hogy a fordító és szerkeszto programok ne „natív”windows-os .exe-t generáljanak, hanem .NET-es .exe-t.

- a virtuális gépi kód OOP-t támogató kód!- ezért az általunk használt magasszintu programnyelveknek is OOP

nyelveknek kell lenniük!

- jelenleg is már 10 fölötti a .NET-et támogató nyelvek listája- a Microsoft a Visual Basic, C++, C#, Java Script, J# (Java-alapú)

nyelvekhez elkészítette a fordítókat- egyéb nyelvekhez külso cégek készítik el a fordítókat (pl. Inprise-Delphi)

A CLR interpreteres üzemmódban futtatja a .NET programokat (ez lassú).- az interpretert egy JIT (Just-In-Time) compiler támogatja, amely

„dönthet” úgy, hogy a virtuális gépi kód bizonyos (surun hívott) részeit futtatás közben a memóriában natív kódra fordítja (sebességnövekedés)

- ezzel pl. megoldható az, hogy a jövo processzorai új regisztereket istartalmaznak majd, akkor ugyanaz az .exe kód ezeken ezt máris ki fogják használni

- a processzor-specifikus gépi kódú utasításokat használja(AMD, Intel Pentium,…)

- az adott operációs rendszer lehetoségeit messzemenokig kihasználja a program

A CLR interpreteres üzemmódban futtatja a .NET programokat (ez lassú).Ugyanakkor az NGEN.EXE-vel a lefordított .NET programot natív kódralehet fordítani. Ezzel a kód az adott processzorhoz és operációs rendszerhezlesz optimalizálva, futása ebben a környezetben 2x 3x gyorsabb lesz (lehet)!

A CLR interpreteres üzemmódban futtatja a .NET programokat (ez lassú).- ezzel megszunnek az eredeti magasszintu nyelvek adatábrázolási különbségei:

- sztringek tárolása (C és PASCAL stílus, ASCII és UNICODE, stb…)- számtípusú adatok tárolási különbsége (méret, pontosság, stb…)- tömbök indexelése (egységesen 0..n-1 indexelésú (Basic-esek sírnak!!!))- stb…

- a CLS-nek megfelelo nyelvek képesek együtt muködni:- a kész projekt egyik felét pl. C++-ban is meg lehet írni- a másik felét Visual Basic-ben- mindkét felét eloször CLS-nek megfelelo „virtuális gépi kódra” fordítják le- a kettobol a szerkeszto összeállítja a muködo kész programot.

A CLR interpreteres üzemmódban futtatja a .NET programokat (ez lassú).- de a program futtatásához szükséges DLL (Dynamic Link Library = közös

(shared) eljárás és fvgyujtemény) verzióit minden program magánakspecifikálhatja, azaz nem okoz problémát, hogy ha valamelyik program ugyanazon néven feltesz egy közös DLL-t, azt erre hivatkozó programok vagy „megbíznak” ebben a DLL-ben, és használják, vagy a saját régebbi verziót használják tovább - ennek kezelését a CLR végzi

- A DLL-ek nem csak függvényeket exportálhatnak, hanem típusinformációvalellátott objektumosztályokat!!!- a programok védettek a buffer overflow vírustechnikával szemben.

A CLR interpreteres üzemmódban futtatja a .NET programokat (ez lassú).- a CLR nyilvántartja a program eroforrás-felhasználását, és hiba esetén terminálja a programot, és korrekt módon felszabadítja az eroforrásokat

- ez kiterjed az objektumok destruktorainak automatikus meghívására is (garbage collector funkció) „normális” programmfuttatás közben is.

1

E jegyzet másolata nem használható fel szabadon,az előadás anyagának kivonata. Ezen teljes jegyzetről,vagy annak bármely részéről bármely másolatkészítéséhez a szerző előzetes írásbeli hozzájárulására van szükség. A másolatnak tartalmaznia kell a sokszorosításra vonatkozó korlátozó kitételt is. A jegyzetkizárólag főiskolai oktatási vagy tanulmányi célra használható! A szerző hozzájárulását adja ahhoz, hogy az EKF számítástechnika tanári, és programozó matem-atikus szakján, a tárgyat az EKF TO által elfogadott módon felvett hallgatók bármelyike, kizárólag saját maga részére,tanulmányaihoz egyetlen egy példánymásolatot készítsen a jegyzetből. A jegyzet e változata még tartalmazhat mind gépelési, mind helyességi hibákat. Az állítások nem mindegyike lett tesztelve tel

jes körűen. Minden észrevételt,amely valamilyen hibára vonatkozik,örömmel fogadok.

HH EE RR NN YY ÁÁ KK ZZ OO LL TT ÁÁ NN a r o a n @ e k t f . h u

VB C++ JScriptC# J#

VViiss

uuaa

ll SSttuu

ddiioo

..NNEE

TT

Common Language Specification

ASP .NETWeb Forms Web Services

Mobile Internet Toolkit

WindowsForms

ADO.NET and XML

Common Language Runtime

Operating System

Base Class Library

PPrrooggrraammoozzáássii nnyyeellvveekk IIII.. CC ##

Page 2: Programozási nyelvek II. C# - aries.ektf.huaries.ektf.hu/~shad/feltoltesek/download/Prognyelv_II/NET2_cou3.pdf · A „virtuális gépi kódú” nyelv, melyre le kell fordítani

object The ultimate base type of all other types object o = null;string String type; a string is a sequence of Unicode characters string s = "hello";sbyte 8- bit signed integral type sbyte val = 12;short 16- bit signed integral type short val = 12;int 32- bit signed integral type int val = 12;long 64- bit signed integral type long val1 = 12;

long val2 = 34L;byte 8- bit unsigned integral type byte val1 = 12;ushort 16- bit unsigned integral type ushort val1 = 12;uint 32- bit unsigned integral type uint val1 = 12;

uint val2 = 34U;ulong 64- bit unsigned integral type ulong val1 = 12;

ulong val2 = 34U;ulong val3 = 56L;ulong val4 = 78UL;

float Single- precision floating point type float val = 1.23F;double Double- precision floating point type double val1 = 1.23;

double val2 = 4.56D;bool Boolean type; a bool value is either true or false bool val1 = true;

bool val2 = false;char Character type; a char value is a Unicode character char val = 'h';decimal Precise decimal type with 28 significant digits decimal val = 1.23M;

Felsorolás típusenum Color { Red, Blue= 10, Green }

Color hatterszin = Color.Red;

Változók és adatmezõk• Ha egy fv belsejében van deklarálva -> változóint a;int b =1;int c, d = 1;int e = c + d; // hiba, ”c” még nem kapott értéket• Ha osztály szinten -> adatmező

o „static” módosítóval osztálymez őo „static” nélkül példánymező.

Tömbök

int[] a1 = {1, 2, 3};int[] x = new int[ 3] {0, 1, 2};int[] arr = new int[ 5]; //5 elemű vektorint[,] arr2 = new int[ 5,4]; // 5x4 elemű mátrixint [][] arr3 = new int[ 2]; // ”jagged” tömbarr3[0] = new int[ 5]; // arr3[0] legyen 5 eleműarr3[1] = new int[3]; // arr3[1] legyen 3 eleműfor (int i =0;i <arr.Length;i++) arr[i]=i;foreach(int x in arr ) Console.WriteLine(x );for (int i=0;i<arr3.Length;i++)for (int j=0;j<arr3[i].Length;j++)arr3[i][j]=0;

Konstansok deklarálásaconst double Pi = 3.14;(a konstansok mellé nem lehet “static” jelzőt írni, eleve úgy viselkedned)

„Property”(csak adatmezõ)

string IPCim {get { ...; return ”127.0.0.1”;}set { ...; elmenteni(value); }

}

Paraméterek módosítói

• A paraméterek is változóknak minősülneko Érték szerinti paraméter (bejövőadat) //nincs módosítóo Cím szerinti paraméter (átmen ő adat) // „ref” módosítóo Kimen ő paraméter (kimen ő adat) //„out” módosítóo Paraméter tömb (vált.param.szám) //„params” módosító

void F(int p) { p++; }void F( ref int p) { p++; }void F(out int p) { p=12; }static void F(params int[] args) { … }

Overloading támogatás

• A metódusoknak lehet ugyanaz a nevük, ha más a „szignatúrájuk”.• A formális paraméterezése más

o Paraméterek számábano Paraméterek típusao A paraméterek módosítói

static void F() { … }static void F( object o) { …}static void F( int value) { …}static void F(ref int value) { … }static void F(int a, int b) { … }static void F(int[] values) { … }

C# „goto”... goto done;... done: ...

C# egyszerû elágazás

• if ( logikai kif ) utasítás;• if ( logikai kif ) { utasítás1; ...; utasításN; }• if ( logikai kif ) utasítás;

else utasítás; • if (logikai kif ) {utasítás1; ...; utasításN;}

else utasítás;

C# többirányú elágazás

switch ( egész vagy string típusú kifejezés){case konstans1 : ...; break;case konstans2 : ...; break;default : ...; break;

}

C# „while” ciklus

while (logikai feltétel) utasítás;while (logikai feltétel) {utasítás1; ...; utasításN;}

C# „do- while” ciklus

do utasítás; while (logikai feltétel);do {utasítás1; ...;utasításN;} while (logikai feltétel);

C# „for” ciklus

for (utasítás1; feltétel; utasítás3){ …; }

utasítás1; while (feltétel) { ...; utasítás3; }

Pl: for (int i= 0; i< 10; i++) a[ i]= i;C# „foreach” ciklus• Ha egy vector minden elemén kívánunk m ű veletet végezni• deklarálunk egy olyan változót, mint a vector alaptípusa• A ciklusmagban ezen változóra hivatkozunk – ez a vector aktuális eleme

int[] tomb = new int[ 5];foreach (int x in tomb) Console. WriteLine( x);

// a ciklusmagban x csak olvasható !!!C# „break”• switch utasítás természetes része -> ugrás a switch utáni utasításra• ciklusok magjában használva a ciklusból kiugrásra alkalmas.

C# „continue”• Ciklus magjában használva “átugorja” a ciklusmag még maradék utasításait

o „while„ ciklusban ugrás a ciklus tesztfeltétel kiértékeléséreo „do- while” ciklusban szinténo „for” ciklusban ugrás a növel ő utasításra (utasítás3)o „foreach” ciklusban ugrás a következ ő elem kiértékelésére

C# „return”• A fv visszatérési értékét határozza meg• „return kifejezés” – a kifejezés eredménye lesz a fv visszatérési értéke• a „return” azonnal visszatér, a fv maradék utasításait átugorja• ha a fv void típusú, a return után nem lehet írni kifejezést!C# program kezdõ pontjapublic static int Main( string[] args){… a program itt kezd ő dik …for (int i= 0; i< args. Length; i++)

Console. WriteLine( args[i] );…}C# hatáskörök

private Az elérés korlátozott az adott osztályraprotected Az elérés korlátozott az adott osztályra, és a

leszármazott osztályokrapublic Az elérés nincs korlátozvainternal Az elérés korlátozott az adott programraprotected internal A “protected” és az “internal” együtt

2

Page 3: Programozási nyelvek II. C# - aries.ektf.huaries.ektf.hu/~shad/feltoltesek/download/Prognyelv_II/NET2_cou3.pdf · A „virtuális gépi kódú” nyelv, melyre le kell fordítani

OOP történeteStruktúrált programozás:• Program = adat + algoritmus• 60- as években Böhm és Jacopini sejtése : bármely algoritmus

leírható az alábbi 3 vezérlési szerkezet véges sokszori alkalmazásával:- szekvencia- szelekció- iteráció

• A fenti sejtést Mills bizonyítottaObjetum orientált programozás:• Alan Kay diplomamunkája 1969• Tervek és elképzelések• SmallTalk programozási nyelv megtervezése• Meglév ö eljárásorientált nyelveket b ö vítik OOP lehet ö ségekkel

(Pascal,C)• Új, tisztán OO nyelveket terveznek (Java,C#)

Nyelvek osztályozása:• Hagyományos nyelvek (C)• OOP támogató nyelvek (Pascal, Delphi, C++)• Tisztán OOP nyelvek (C#, Java).OOP alapelvei1. Egységbezárás (encapsulation):az adatok és a hozzájuk tartozó eljárásokat egyetlen egységbenkezeljük (objektum-osztály)• Az osztály mezöi tárolják az információkat• A metódusok kommunikálnak a külvilággal• Az osztály változóit csak a metódusokon keresztül változtathatjuk meg• A valós világ szemléletesebb leírása.2. Öröklés (inheritance) :az objektum-osztályok továbbfejlesztéséneklehet ö sége.Ennek során a származtatott osztály örökli ösétöl azokattribútumait, és metódusait, de ezeket bizonyos szabályok mellettújakkal egészítheti ki, és meg is változtathatja• Az eredeti osztály ö sosztálynak nevezzük (szülö)• Az új, továbbfejlesztett osztályt származtatott osztálynak (gyerek)• Egy ösböl több leszármaztatott osztályt is készíthetünk• Egy származtatott osztálynak

- Legfeljebb egy szülöje lehet (pl.: Pascal, Java, C#)–öröklödési fa- Több szülöje is lehet (pl.:C++) –öröklödési gráf

• Metódusok törzsét megváltoztathatjuk• Mezök neveit, típusait általában nem változ tathatjuk meg• Új mezökkel, és metódusokkal egészíthetjük ki az osztályt.Sokalakúság (polymorphism):a származtatás során az ö s osztályok metódusai képesek legyenek azúj átdefiniált metódusok használatára újraírás nélkül is• Ezt virtuális metódusokon keresztül érhetjük el

- Egyes nyelvekben minden metódus virtuális (pl.: Java)- A metódusoknál külön jelezni kell, melyik a virtuális (pl.:Delphi, C#, C++)

1. Példa (C és C++) (csak egy verem)#include <stdio. h>struct TVerem{int vm;float tomb[ 100];

};struct TVerem V;void Init(){V. vm = 0;

}void Push( float x){V. tomb[ V. vm]= x;V. vm++;

}float Pop(){V. vm--;Return V. tomb[ V. vm];

}int main(){Init();Push( 12.5);Push( 16.3);Printf(”% f\ n”, Pop()); // 16.3Printf(”% f\ n”, Pop()); // 12.5}

1. Példa (Pascal) (csak egy verem)

type TVerem = recordvm: integer;tomb: array [1.. 100] of real;end;var V: TVerem;

procedure Init;beginV. vm := 0;

End;procedure Push( x: real);beginV. tomb[ V. vm]:= x;Inc( V. vm);

End;function Pop: real;begindec( V. vm);Pop := V. tomb[ V. vm];

End;BEGINInit;Push( 12.5);Push( 16.3);Writeln( Pop); // 16.3Writeln( Pop); // 12.5END.

2. Példa (C és C++) (több verem)#include <stdio. h>struct TVerem{int vm;float tomb[ 100];

};void Init( TVerem *V){V-> vm = 0;

}void Push( TVerem *V, float x){V-> tomb[ V-> vm]= x;V-> vm++;

}float Pop( TVerem *V){V-> vm--;Return V-> tomb[ V. vm];

}struct TVerem V1;struct TVerem V2;int main(){Init(& V1);Init(& V2);Push(& V1,12.5);Push(& V1,16.3);Push(& V2,11.3);Printf(”% f\ n”, Pop(& V1));Printf(”% f\ n”, Pop(& V2));Printf(”% f\ n”, Pop(& V1));

}2. Példa (Pascal) (több verem)type TVerem = recordvm: integer;tomb: array [1.. 100] of real;

end;procedure Init( var V: TVerem)beginV. vm = 0;

End;procedure Push( var V: TVerem; x: real)beginV. tomb[ V. vm]= x;Inc( V. vm);

End;Function Pop( var V: TVerem): real;begindec( V. vm);Pop := V. tomb[ V. vm];

End;var V1, V2: TVerem;BEGINInit( V1);Init( V2);Push( V1,12.5);Push( V1,16.3);Push( V2,11.3);Writeln( Pop( V1));Writeln( Pop( V2));Writeln( Pop( V1));END.

3

#1 (Bevezetés)

Page 4: Programozási nyelvek II. C# - aries.ektf.huaries.ektf.hu/~shad/feltoltesek/download/Prognyelv_II/NET2_cou3.pdf · A „virtuális gépi kódú” nyelv, melyre le kell fordítani

3. Példa (C++) (verem osztály)#include <stdio. h>class TVerem{ public:

int vm;float tomb[ 100];void Init();void Push( float x);float Pop();

};void TVerem:: Init(){vm = 0;

}void TVerem:: Push( float x){tomb[ vm]= x;vm++;

}float TVerem:: Pop(){vm--;return tomb[ vm];

}int main(){TVerem v1;TVerem v2;v1. Init();v2. Init();v1. Push( 12.5);v2. Push( 13.4);v1. Push( 16.3);printf("% f\ n", v1. Pop());printf("% f\ n", v1. Pop());printf("% f\ n", v2. Pop());return 0;

}

3. Példa (Pascal) (verem osztály)type TVerem= objectvm: integer;tomb: array [1.. 100] of real;procedure Init;procedure Push( x: real);function Pop: real;

end;procedure TVerem. Init;beginvm := 0;

end;procedure TVerem. Push( x: real);begintomb[ vm]:= x;inc( vm);

end;function TVerem. Pop: real;begin inc( vm);Pop := tomb[ vm];

End;var V1, V2: TVerem;BEGINv1. Init;v2. Init;v1. Push( 12.5);v2. Push( 13.4);v1. Push( 16.3);Writeln( v1. Pop);Writeln( v2. Pop);Writeln( v1. Pop);END.

AlapfogalmakOsztály:egy felhasználó által készített típus, mely összetett adatszerkezet – elvi-leg tartalmazza az adott objektum adatait, és az azokat kezel ö eljárá-sokat.Objektum:egy változó, melynek típusa valamely objektumosztály, vagyis az osztá-ly egy példánya.Attribútum (adatmezö): az osztály egy mezöje, konkrét adattárolási képes-ségû adattagja..-Metódus:olyan eljárás, mely része valamely objektumosztálynak, így az adottosztály attribútumaival végez valamilyen mûveletet.Példányosítás:egy objektumosztályból konkrét objektum készítése (objektum változódeklarálása)Inicializálás:az objektum attribútumainak kezdö értékbeállítása, általánosabban azobjektumpéldány alaphelyzetbe állítása

Adatrejtés (data hiding)Az adatrejtés az egységbezárás alproblémája.Cél:• Az objektum képes legyen adatokat tárolni• Azokat a külvilág képes legyen lekérdezni és megváltoztatni

• De csak ellenörzött módon!Az objektum belvilága – a saját metódusai.Külvilág (minden más)

• Közeli külvilág: az osztály leszármazottjai,s azok metódusai• Távoli külvilág: a példányokat készítö program modulok (pl.f ö program).

Attribútumokkal kapcsolatos- PRIVATE: a külvilág nem férhet ezen attribútumukhoz hozzá (osztály

„magánügye”). Ezek általában segédváltozók,segédmezök.- PROTECTED: olyan attribútumok, melyekhez a távoli külvilág nem

férhet hozzá (számára private), de a közeli külvilág, leszármazott osztályok metódusai hozzáférhetnek (számára public).ö bennük „megbízunk”!

- PUBLIC: olyan attribútumok, melyek jellegüknél fogva nem igényelnek speciális szabályozást, azok tetsz ö leges id ö ben és értékre történö megváltoztatása nem okoz, nem okozhat problémát az objektum mûködésében.

Metódusokkal kapcsolatos A metódusokhoz való hozzáférést is a fentiek szerint osztályozzuk, deitt az adott metódus meghívható-ságát jelöli:

- PRIVATE : ezen metódusokat a külvilág nem hívhatja meg, csak az adott osztály metódusai hívhatják meg

- PROTECTED : olyan metódusok, melyeket a távoli külvilág nem hívhat meg (számára private), de a közeli külvilág, a leszármazott osztályok metódusaiból meghívhatóak (számukra public)

- PUBLIC : ezen metódusokat a távoli külvilág meghívhatja.

Pl: C#

class TVerem{private int vm;private double[] tomb = new double[ 100];public void Push( double x){ ... }public double Pop() { ... }public void Init() { ... }

}–––––––––––––––––––––––––––––––––––––––––class TVerem{int vm; // alapból privatedouble[] tomb = new double[ 100];// alapból privatepublic void Push( double x) { ... }public double Pop() { ... }public void Init() { ... }

}Attribútumok nem elrejtése

class TVerem{public int vm;...

}...TVerem V = new TVerem();V. vm = 12;

Mivel a „vm” nem rejtett mezö, ezért a külvilág meg-változtathatja azt, ésezzel a fenti példában a LIFO adatelérési elvet megsértheti – és hibásértékre állítva futási hibát generálhat.Attribútumok elrejtése

class TVerem{private int vm;...

}...TVerem V = new TVerem();while (V.vm>0) //hibás,nincs „vm” mezö{Console.WriteLine(”Adat={0}”,V.Pop());

}

Mivel a „vm” mezöt elrejtettük,annak tartalmát kiolvasni sem lehetségesa továbbiakban – pedig az néha szükséges.Csak olvasható attribútumok készítése

class TVerem{private int vm;public int getVM() { return vm;}...

}... TVerem V = new TVerem();// V. vm = 12; // nem sikerülne !while (V.getVM()>0) // ez most működik !{Console.WriteLine(”Adat={0}”,V.Pop());

}4

#2 (Adatrejtés)

Page 5: Programozási nyelvek II. C# - aries.ektf.huaries.ektf.hu/~shad/feltoltesek/download/Prognyelv_II/NET2_cou3.pdf · A „virtuális gépi kódú” nyelv, melyre le kell fordítani

• Tegyük a mezöt rejtetté (private,protected)• Készítsünk egy „get” jelleg û fv-t (C++-ban leggtöbször inline fv).

Ellenörzött elérésû attribútumok készítése

class TVerem{private int vm;public int getVM() { return vm; }public void setVM(int uj){if (( 0<=uj)&&(uj<=vm))vm=uj;

}...

}...TVerem V = new TVerem();... V.setVM(1);while (V.getVM()>0) Console.WriteLine(”Adat={0}”,V.Pop());

• Lassú programfutás• Hosszabb kód• „Csúnya” programkód (csúnya szintaxis).

Property készítéseA property (jellemzö) egy virtuális attribútum

• Definiálása során nem keletkezik külön valóságos változó, amelyben a tényleges adatokat tárolni lehet.

• Meg kell adni, hogy kiolvasáskor (read) mi ezen mezö értéke• Meg kell adni,hogy íráskor (write) mi történjen a tárolandó értékkel

(ezt „value” néven lehet elérni)

class TVerem{private int fvm; // „fvm” tényleges mezöpublic int vm // „vm” virtuális mezö{ get { return fvm; }set {

if (( 0 <= value) &&(value <= fvm))fvm= value;}

}}

Használata:...TVerem V = new TVerem();V.vm =12; // -> V. vm. set( 12 ),így ”value”= 12while (V. vm> 0) // -> V.vm.get()Console.WriteLine(”Adat={0}”,V.Pop());

• Lassú programfutás• Hosszabb kód• „Szép” programkód• A külvilág azt hiszi, hogy a „vm” mezö fizikailag létezö, közönséges

„int” típusú mezö.Példa:// a téglalap tartalmaz bal_x,felso_y mezö ket a bal felsö sarok// koordinátáihoz valamint „széles” és „magas” adatokat, jelezvén hogy // milyen széles és magas a téglalap jobb széls ö koordinátájának // számítása (jobb_x) kiszámítható! ha valaki beállítja a jobb_x értékét, // akkor a szélesség változik!

class Teglalap{public int bal_x,felso_y;public int szeles,magas;public int jobb_x{get { return bal_x +szeles; }set {szeles = value - bal_x; }

}}Megjegyzések• A property belsejében természetesen lehet ivatkozni a „private”mezökre, hisz a „private” a külvilág felé takar, de a „set” és „get” belsejeegyben az osztály belseje is, ezért ott minden – az adott osztályban – definiáltprivate mezö teljesen hozzáférhetö• Ha csak a „get” részt adjuk meg a property-ben, ak-kor egy csak

olvasható mezöt kapunk• Ha csak a „set” részt adjuk meg, akkor egy csak írható mezöt kapunk.Turbo Pascal 6.0:• A TP 6.0-ban csak két szint létezik:private és public• De csak a private kulcsszó létezik• Ezért az osztály elején kell felsorolni a „public”-okat, majd a „private”kulcsszó után kell felsorolni a privát részeket:• Nincs lehetöség property kezelésérePl: type TVerem = object

procedure Push( X: integer);function Pop: integer;private:vm: integer;tomb: array [1..100] of integer;

end;

Turbo Pascal 7.0:• A TP 7.0- ban is csak két szint létezik: private és public• De bevezették a „public” kulcsszót, ezért az osztály tagjait tetszöleges sor

rendben lehet megadni• Nincs lehetöség property kezelésérePl:

type TVerem = objectprivate:vm: integer;tomb: array [1..100] of integer;public:procedure Push(X: integer);function Pop: integer;

end;

Delphi:• Van „protected” is, a „protected” kulcsszóval kell jelölni• Van „property”

- A „get” helyett a „read”, a „set” helyett a „write” kulcsszóval- A „read” után egy fizikai mezö nevét is lehet írni, vagy egy ugyanolyan típusú, paraméter nélküli fv nevét- A „write” után egy fizikai mezö nevét, vagy egy egyparaméteres eljárás nevét lehet írni (az egy paraméter típusa meg kell egyezzen a property alaptí pusával)- A property- t lehet indexelni is (tömbként kezelni)

• Osztályt az „object” és „class” kulcsszóval is lehet jelölni• Az elözö TP kompatibilis objektum-osztály, az utóbbit a Delphireferencia

alapon kezeli.

Delphi Pl.:

type TVerem = classprivate:procedure SetVM( ujVM: integer);protected:fvm: integer;tomb: array [1.. 100] of integer;public:procedure Push( X: integer);function Pop: integer;property vm: integer read fvm write SetVM;end;

C++:• A C++-ban létezik a „private” , „protected”, és „public”• Nincs lehetöség property kezelésére, de az „inline” fv-ekkel property-

khez hasonló hatást lehet elérni• Az osztályokat a „class” és „struct” kulcsszavakkal is létrehozhatjuk.

Elöbbi esetben az alapértelmezett hozzáférés a „private” hacsak másképp nem jelöljük.A „struct” esetében az alapértelmezett hozzáférés a „public”.

class TVerem {private:int vm;int[ 100] tomb;public:void Push(int X);int Pop();

}

C#:• A C# -ban a private, protected, public- on kívül még kéthozzáférési

szint is be van vezetve: internal, és internal protected• Van property, bár nem kell használni ezt a kulcsszót

class TVerem {protected int fvm;protected int[] tomb = new int[ 100];public void Push( int X) { ...; }public int Pop() { ...; }public int vm {get { ...; }set { ...; }

}}JAVA:• A JAVA- ban háromszintű hozzáférés szabályozás van, bár csak két kulc-sszót ismer „private” és „public”. A jelöletlen tagokat „félnyilvános” tagok-nak nevezi, és elérése hasonló korlátozású, mintha protected lenne.• Nincs benne property kezelés.

class TVerem {int vm;int[ 100] tomb;public void Push( int X) { ...; }public int Pop() { ...; }}

5

Page 6: Programozási nyelvek II. C# - aries.ektf.huaries.ektf.hu/~shad/feltoltesek/download/Prognyelv_II/NET2_cou3.pdf · A „virtuális gépi kódú” nyelv, melyre le kell fordítani

AdattagokEgy objektumosztály kétfajta tárolási technikát támogat:· Osztály szintu adatok – osztálymezok („static” módosító)· Példány szintu adatok – példánymezok (nincs módosító)Osztálymezok: ezen „változók” egyetlen példányban lé-teznek a memóriában,akárhány objektumot is hozunk létre az adott osztályból.Példánymezok: ha újabb objektumot hozunk létre, akkor ezen „változóknak”külön memória foglalódik le.

BA : BenzinAlapAraDF : DollarForintny : nyeresegkulcs class TBenzinKut {static float BenyinAlapAra;// osztálymezostatic int DollarForint;// osztálymezofloat nyeresegkulcs; // példánymezoint literje { get { return

BenyinAlapAra*DollarForint*nyeresegkulcs; } }void Dragulas(float UjAlapAr) { BenyinAlapAra = UjAlapAr;}}TBenzinKut.BenyinAlapAra = 1.4; // 1 liter benzin 1.4$TBenzinKut.DollarForint = 276; // 1 $ = 276 FtTBenzinKut EgriMOLKut = new TBenzinKut();TBenzinKut Q8 = new TBenzinKut();

OsztálymezokAz osztálymezok kezelése:· Elérése (címzése) az osztály nevén keresztül történik· Nem címezheto meg egy példányon keresztülTBenzinKut.BenzinAlapAra = 1.4; // rendbenEgriMOLKut. BenzinAlapAra = 1.4; // nem jó

· Az osztály minden metódusán belül lekérdezheto, és beállítható közvetlenülint literje { get { return

BenyinAlapAra*DollarForint*nyeresegkulcs; } }void Dragulas(float noveles) {BenyinAlapAra += noveles;}Ha megváltoztatunk egy osztálymezot, akkor azt minden példány észleli (mertközös változó): TBenzinKut.BenyinAlapAra = 1.6; // rendbenUtán az EgriMOL és Q8 benzinára is megváltozik azonnal!

Ha ezen két példányunk van a TBenzinKut osztályból, összes memóriafel-használásunk:

1 float (BenzinAlapAra)1 int (DollarForint)2 db float (példányonként egy nyereségkulcs)minden új példány létrehozásakor +1 float.

KonstansokA konstans : · Értéke már fordítási idoben ismert

· Nem változhat meg a program futása során

Ilyen értelemben egy osztály konstansai hasonló viselkedést mutatnak, mint azosztálymezok:

· Értelmetlen lenne minden példány számára külön konstans példányt létrehozni· A konstans ugyanúgy közös a példányok között.

Pl:class Szogek {const double Pi = 3.14159265358979323846;double szog;double Sin() { return ...; }double Cos() { return ...; }

}Szogek alfa = new Szogek();alfa.szog = 48;Console.WriteLine(alfa.Sin() );

A „const” esetén nem kell (nem szabad) kiírni a „static” módosítót!Példánymezok

A példánymezok a példányokhoz kötodnek, új példány létrehozásakor újabbmemóriát igényelnek. Egy példánymezo értékének megváltoztatása csak azadott példányra fejti ki a hatását. Ilyen mezo a fenti példában „nyeresegkulcs”mezo, mely példányonként más-más lehet.PropertyA property egy virtuális adatmezo. Egyaránt lehet osztály-szintu, és példányszintu.

Osztályszintu property „get” és „set” része osztályszintümetódusnak tekinten-do, ezért a benne szereplo kifejezésekben csak

· osztálymezo,· konstans szerepelhet.

MetódusokMetódusok több fajtáját ismerjük, jelen esetben a fenti szemlélet szerint kéttípust emelünk ki:· Osztályszintu metódusok – („static” módosító)· Példány szintu metódusok – (nincs „static” módosító).

Az osztályszintu metódusok csak az osztálymezokkel és az osztály konstan-saival végezhetnek muveleteket (hivatkozhatnak rájuk), a példánymezokhöznem férhetnek hozzá!Egy példányszintu metódus hivatkozhat, végezhet muve-letet a konstansokkal,

az osztálymezokkel, és a példány-mezokkel is!Az osztálymetódus meghívása az osztály nevén keresztül történik. Apéldánymetódus meghívása a példány nevén keresztül történik. Az osztály-metódus meghívásához nincs szükség példányra!Pl:

class Szogek {const double Pi = 3.14159265358979323846;double DegToRad( double szog_fokban) {return szog_fokban/180*Pi;

}double RadToDeg( double szog_radianban) {return szog_radianban/Pi*180;

}}Console.WriteLine(„145 fok = {0} radián”, Szogek.DegToRad(145));

Osztálymetódusok az alábbi feladatot szokták ellátni (pl):· Olyan stílusú számítások, melyekhez minden változó információ

a metódus paraméterezésében vanPl:double A1 = System.Convert.ToDouble(”143.23”);· Olyan „osztályok” metódusai, melyekbol egy alkalmazásban úgyis csak egy

példány lesz (felesleges példányosítani)Pl: System.Console.WriteLine(”Hello World!”);· Az osztály tartalmaz osztályváltozókat. Ezekkel való muveletvégzéshez ne

kelljen példányokat létrehozni!static void Dragulas(float noveles) { BA += noveles; }AdatrejtésA ...

· példánymezokre és osztálymezokre,· a példánymetódusokra és osztálymetódusokra,· a példánypropertykre és osztályproperty-kre,· a konstansokra

... egyaránt alkalmazhatók a hozzáférési szint korlátozó módosítók:publicprivateprotected

SzármaztatásEgy objektumosztály fejlesztése két módon kezdodhet el:

· Abszolút nulláról· Egy már meglévo osztály továbbfejlesztése révén.

Abszolút nulláról· A valós életben ritkán kerül rá sor, mert a fejleszto rendszerekben sok

elore definiált osztály van, valószínu, hogy találunk egy jó kiindulási pontot· A legtöbb fejlesztoeszközben (pl. C#, Delphi) ekkor is van os, mely így

minden, az adott fejlesztorendszerben készített objektumosztály közös ose lehet (típuskompatibilitás)

· Ettol eltekintve az osztály „üres”, vagyis nincs sem mezoje, sem metódusa.Mindent a programozónak kell definiálni.

Továbbfejlesztés (1)Egy már meglévo (akár más programozó által elkészített) osztály továbbfe-jlesztése révén úgy kell tekinteni, hogy az általunk fejleszett osztály induláskornem „üres”, hanem eleve tartalmazza az „os” osztály minden mezojét és metó-dusát.

class TPont // nincs os, abszolút // nulláról indul{public int x,y;public TPont() { ... }public void Kirajzol() { ... }...

}class TKor:TPont // származtatás a TPont-ból{ ... }.

· Tartalmazza (örökli) a public mezoket és property-ket, azokat felis használhatja a saját metódusokban

· Tartalmazza (örökli) a protected mezoket és property-ket, és azokat fel is használhatja a saját metódusokban

· Tartalmazza (örökli) a private mezoket és property-ket is, de azokat nem tudja felhasználni, ha hivatkozna a metódusok törzsében ezekre a mezokre, a „azonosító nem létezik” fordítási üzenetet kapnjuk.

Ezen private mezok tartalmazása azt jelenti, hogy a memóriaigénybebeleszámít, de az új osztály belsejében nem lehet rá hivatkozni. Ugyanakkor azúj osztály metódusai meghívhatják az örökölt metódusokat, és azok természete-sen felhasználhatják ezeket a private mezoket, hiszen ok még az os osztályrészei, és számukra ezen private mezok hozzáférhetok (voltak).

class TPont {private int x,y;public void Balra_tol () { if (x>0) x--; }...

}class TKor:TPont { // származtatás a TPont-bólpublic void KorBalratol() {

Balra_tol(); // muködikx--; // nem muködik, nincs x!

}} 6

#3 (Adattagok)

#4 (Öröklõdés)

Page 7: Programozási nyelvek II. C# - aries.ektf.huaries.ektf.hu/~shad/feltoltesek/download/Prognyelv_II/NET2_cou3.pdf · A „virtuális gépi kódú” nyelv, melyre le kell fordítani

A származtatott osztályban (gyermekosztály)Lehetoség van:

· újabb mezok definiálására (tetszolegesen lehet private, protected, public)(Általában) nincs lehetoség (C#-ban van!):

· mivel a származtatott osztályban eleve létezonek tekinthetoek a protected vagy public mezok, ugyanilyen nevu mezok definiálására nem lehetséges

Nincs lehetoség:· nincs lehetoség az örökölt mezok típusának (vagy nevének) megváltoz

tatására.

A private mezok a gyermekosztályban ebbol a szempontból nem létezonektekinthetok, ezért ugyanolyan nevu mezok definiálása lehetséges a gyer-mekosztályban is.

class elso{private int x;protected int y;public int z;

}class masodik:elso{int x; // muködik,mert X itt private (volt)int y; // nem muködik, mer elrejtené az örökölt y-tint z; // nem muködik, mert elrejtené az örökölt z-t

}

C#-ban lehetoség van ...... a származtatott osztályban eleve létezonek tekinthetoek a protected vagypublic mezok átdefiniálására. Ugyanilyen nevu mezok definiálására lehetséges– a „new” kulcsszóval kell jeleznünk a fordítóprogramnak, hogy szándé-kosan csináljuk! A new kulcsszó hiányában a C# fordítási hibaüzenettel jelez, ezzel figyelmeztet, hogy figyeljünk oda!

class masodik:elso{int x;new int y;new int z;

}C#-ban sincs lehetoség ...

· ... az örökölt mezok típusának megváltoztatására!· De van lehetoség, hogy a „new” kulcsszó révén újra bevezett mezok

típusa ne ugyanaz legyen, mint az örökölt mezo típusa

class masodik:elso{int x;new float y;new float z;

}

Ezen mezok használata esetén tudni kell, hogy az „elso” osztály metódusai arégi y és z mezoket használják, a „masodik”, és a belole származtatott osztályokmetódusai már csak az újonnan definiált mezoket látják. Viszont a memóriábamindketto bekerül.

Ezen átdefiniálás révén a mezok hatáskörét változtatjuk meg. Az örököltmezok hatásköre az os osztály metódusaira korlátozódik, a gyermekosztálybanújradefiniált mezok hatásköre a gyermekosztályra, és a belole származtatotttovábbi gyermekosztályokra terjed ki. Lehetoség van a régi mezo elérésére – azosztály nevének – mintminosített név - használata esetén:

class masodik:elso{...new float z;public akarmi() {z=1.1; // a float „z”elso.z = 1; // az int (egyébként elfedett) „z”

}}

Továbbfejlesztés (2)· A származtatott osztály tartalmazza (örökli) a protected és public

metódusokat, azokat meg is lehet hívni az új gyermekosztály metódusaiból

· Az os osztály private metódusait természetesen nem lehet meghívni a gyermekosztály metódusaiból, de meg lehet hívni egy olyan protected vagy public örökölt metódust, amely meghívja ezeket az „os”private metódusokat.

Lehetoség van (újradefiniálás révén):· Megváltoztatni egy örökölt metódus törzsét· Megváltoztatni egy örökölt metódus paraméterezését

Nincs lehetoség:· Megváltoztatni egy örökölt metódus hozzáférési

szintjét (private, protected, public).

Az elso eset azt jelenti, hogy a gyermekosztályban lehetoség van egyugyanolyan nevu metódus definiálására, amelynek a törzse (utasítás-része) ter-mészetesen más is lehet. Ezen metódus ekkor „elfedi” az örökölt metódust.

A második eset azt jelenti, hogy a gyermekosztályban le-hetoség van egyugyanolyan nevu, de más paraméterezésu metódus definiálására. Ez még azoverloading-ot nem támogató nyelvekben is muködik.

A különbség a következo:· Overloading-ot alapból nem támogató nyelvekben (pl. Delphi) innentol

„nem elérheto” a régi metódus (csak minosített névvel), még akkor sem, ha a paraméterezés megváltozott

· Overloading-ot támogató nyelveknél (C++, C#) a régi metódus meghívhatóa minosített név használata nélkül is, ha más a paraméterezése.

-- A nagy kérdés: az os osztály azon metódusai, ---- amelyek meghívják a szóban forgó metódust, --

-- vajon még a régi változatot, vagy az új ---- változatot fogják meghívni? --

(csak akkor kérdéses, ha egyforma a paraméterezés).

class elso{public int visszaad() { return 1; }public void kiir() {System.Console.WriteLine("Az

érték={0}",visszaad());}

}class masodik:elso{new public int visszaad() { return 2; }

}class Test{static void Main() {masodik a= new masodik();a.kiir();

}}

-- Kérdés: „1”-t vagy „2”-t fog kiírni? -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

class elso{public int visszaad() { return 1; }public void kiir() {Console.WriteLine("Az érték={0}"

,visszaad());}

}class masodik:elso{public int visszaad(int a) { return a*2; }

}

class Test{static void Main() {masodik a= new masodik();a.kiir();

}} -- Itt nem kérdéses, mivel más a paraméterezése! --

Örökölt metódusok C#· Egy objektumosztály az öröklés során metódusokat is örököl· Az örökölt metódusok közül most csak a protected vagy public metódu

sok az érdekesek· A gyermekosztályban lehet ugyanolyan nevu metódust létrehozni· Ha a paraméterezése is ugyanaz, használni kell a „new” kulcsszót· Ha más a paraméterezése, akkor nem kell a „new” használata

(az overloading miatt)· Az új metódust ekkor az os osztály metódusai „nem látják”!

class elso{public int visszaad() { return 1; }public void kiir() {Console.WriteLine("Az érték={0}",visszaad());

}}class masodik:elso{new public int visszaad() { return 2; }

}class Test{static void Main() {masodik a= new masodik();a.kiir();

}}

7

#5 (örökölt metódusok)

Page 8: Programozási nyelvek II. C# - aries.ektf.huaries.ektf.hu/~shad/feltoltesek/download/Prognyelv_II/NET2_cou3.pdf · A „virtuális gépi kódú” nyelv, melyre le kell fordítani

-- Ebben a példában a kiírás „1” lesz! --

class elso{public int visszaad() { return 1; }public void kiir() {Console.WriteLine("Az érték={0}",visszaad());

}}class masodik:elso{public int visszaad(int a) { return a*2; }

}class Test {static void Main(){masodik a= new masodik();a.kiir();

}}

-- Itt eleve nem is kérdés, hiszen más a paraméterezése ! --

class elso {public int visszaad() { return 1; }public void kiir() {Console.WriteLine("Az érték={0}",visszaad());

}}class masodik:elso {public int visszaad(int a) { return a*2; }new public void kiir() {Console.WriteLine("Az érték={0}",visszaad());}

}class Test {static void Main(){masodik a= new masodik();a.kiir();

}}

Itt a kiírás „2” lesz, de nem azért, mert az új „visszaad”-t használjaa régi „kiir”, hanem mer az új „kiir”-t hívtuk meg,

amely az új „visszaad”-t használja !

(nyilván nem megoldás a metódus lemásolása!)

class elso{public virtual int visszaad() { return 1; }public void kiir() {Console.WriteLine("Az érték={0}",visszaad());

}}class masodik:elso{public override int visszaad() { return 2; }

}class Test{static void Main(){masodik a= new masodik();a.kiir();

}}

Itt a kiírás „2” lesz, mert a régi „kiir” az új „visszaad”-t használja !!!

class elso{public virtual int visszaad() { return 1; }...

}class masodik:elso{public int visszaad() { return 2; } // (a)new public int visszaad() { return 2; } // (b)public override int visszaad() { return 2; } // (c)

}

· (a) nem helyes szintaktikailag· (b) muködik, de a kiírás „1” lesz, nem használja az új „visszaad”-t· (c) muködik, a kiírás „2” lesz.

Virtuális metódusok· A „virtual” kulcsszóval jelezzük a fordítónak, hogy az adott metódust

valószínuleg felül fogják majd definiálni a fejlesztés következo szintjein.· Ez csak akkor érdekes, ha a szóban forgó metódust más metódusból

meghívjuk – és azt akarjuk, hogy ezen metódusok képesek legyenek a továbbfejlesztett változatokat használni.

· Ezt az igényt a metódus elso bevezetésekor a „virtual” kulcsszóval kell jelölni – a felüldefiniálás során pedig az „override” kulcsszóval.

· Ha az „override”-t nem tesszük ki, a fordító hibát fog jelezni.· Ha az „override”-t nem akarjuk kitenni, a „new” kulcsszóval kell jelezni

azt, hogy „szándékosan” tesszük ezt.· Ekkor a metódus megszunik virtuális lenni!

Korai kötés

class Elso {void metodus_a() { ... }void metodus_c() {metodus_a(); // (a)}

}class Masodik:Elso {new void metodus_a() { ... }...

}class Test {static void Main() {masodik b= new masodik();b.metodus_c();

}}

Az (a) sorban a fordító program az „Elso” osztály „metodus_a()” metódusá-nak meghívása mellett „dönt”, mert a metodus_a() definiálásakor nemjelöltük a „virtual” kulcsszó segítségével, hogy ezen metódus (metodus_a) akésobbiek során valószínuleg felüldefiniálásra fog kerülni.

Ekkor az eljáráshívás sorát összeköti a konkrét eljárással:metodus_a() <=> Elso.metodus_a { ... } hívása

Ezt a kötést (döntést) korán hozza meg a rendszer, ezért ezt korai kötésnek(early binding) nevezzük.

Ezt a döntést a fordító még akkor meghozza, amikor a „class Elso” osztálytfordítja le. Ezt a döntést akkor meg kell hoznia! Ezen döntés meghozatalátkésobb már nem másítja meg, hiába definiáljuk felül a „metodus_a”-t!

A „b.metodus_c()” hívása során ezen programkód kerül végrehajtásra, amely ezendöntés értelmében egyértelmuen az „elso.metodus_a()” hívását fogja jelenteni!

Késöi kötés

class Elso {virtual void metodus_a() { ... }void metodus_b() { ... }void metodus_c() {metodus_a(); // (a)}

}class Masodik:Elso {override void metodus_a() { ... }...

}class Test {static void Main() {elso a= new elso(); a.metodus_c();masodik b= new masodik();b.metodus_c();

}}

Ez esetben a „virtual”-„override” kulcsszavakat használtuk, amely jelzés afordítónak, hogy a „metodus_a” nem közönséges metódus, újradefiniálásavalószínu, ezért az (a) sorban nem eldöntheto fordításkor, hogy melyik osztálymetódusát is kell majd meghívni.

Az elso esetben, az a.metodus_c() esetén elindul a „metodus_c()”, és meg kellhívni egy „metodus_a()”-t. Ez esetben ez csakis az „elso.metodus_a” lehet,mert az „a” példány az „elso” osztály egy példánya, és számára ezen metóduslegfrisebb verziója ez.

A második esetben a b.metodus_c() esetén is ugyanezen el-járás indul el, de a„metodus_a()” meghívásakor megkeresi a legfrisebb verziót ebbol a metódus-ból. Ez a „masodik. metodus_a()” lesz, hiszen a „b” példány a „masodik”osztály példánya, számára ez a legfrisebb verzió.

Az OOP ezen viselkedését, hogy egy metódus (metodus_c) belsejében szereplomásik metódushívás más-más viselkedést mutat (más-más tényleges eljárás-hívás történik a futtatás során) – sokalakúságnak (polymorphism) nevezzük.

Az, hogy konkrétan melyik metódus kerül „majd” meghívásra – ez csak futásközben derül ki. Ezért ezt a kötést késoi kötésnek (late binding) nevezzük.

Késoi kötésre csak a virtuális metódusok meghívásakor kerülhet sor. Ilyenmetódushívás esetén a fordító nem egy konkrét eljáráshívást fordít le (koraikötés) - hanem egy utasítássorozatot, amely futás közben egy „keresést” hajtvégre, hogy meghatározza, melyik metódusverziót kell konkrétan meghívni.

8

Page 9: Programozási nyelvek II. C# - aries.ektf.huaries.ektf.hu/~shad/feltoltesek/download/Prognyelv_II/NET2_cou3.pdf · A „virtuális gépi kódú” nyelv, melyre le kell fordítani

Virtuális Metódus Tábla• A késöi kötés egy „keresés”-t jelent, meg kell határozni a ténylegesen

meghívandó metódust• A cél az, hogy ez a keresés a lehet ö legegyszerűbb kóddal, a lehetö leg

gyorsabban megvalósuljon• Ehhez egy plusz táblázat felépítése és megtartása szükséges: a Virtuális

Metódus Táblázat (VMT). • Ezen táblázat mindig egy osztályhoz tartozik• A VMT tábla induláskor megegyezik az ösének a VMT-jével (ha nincs

ös, akkor induláskor üres)• Ha az osztályban bevezetünk egy új virtuális metódust a „virtual”

kulcsszóval, akkor ezen metódus bekerül a táblázatba (a végére)• Ha az osztályban felüldefiniáltunk egy már létezö virtuális metódust az

„override” kulcsszóval, akkor a táblázatba ezen bejegyzés kicserélödik az új metódusra

• A táblázatban a metódusok indításához szükséges információk vannak eltárolva (pl. a metódusok memóriacímei, amely alapján azokat el lehet inditani).

class elso {public virtual int metodus_ a() { ... } // a : új sorpublic virtual int metodus_ d() { ... } // d : új sorpublic void metodus_ c() { metodus_ a(); }// nem virtuális !

}class masodik: elso {public override int metodus_ a() { ... } // a : módosító sorpublic virtual int metodus_ b() { ... } // b : új sor

}

class elso VMTInt metodus_ a() elso. metodus_ aInt metodus_ d() elso. metodus_ d

class masodik VMTInt metodus_ a() masodik. metodus_ aInt metodus_ d() elso. metodus_ dInt metodus_ b() masodik. metodus_ b.

A késöi kötés fordítása során olyan programkód kerül fordításra, amely a VMTtáblázat alapján hozza meg a döntést, hogy melyik konkrét metódust kellmeghívni.

Az „e.metodus_c()” esetén az „e” példányhoz az „class elso” VMT tábla tar-tozik, hiszen az „e” példány az „elso” osztály egy példánya! Ezért a késöikötés a „class elso” VMT tábla szerint a „metodus_a()” hívás esetén az„elso.metodus_ a()” metódust kell meghívni.

A „m.metodus_c()” esetén az „m” példányhoz az „class masodik” VMTtábla tartozik,hiszen az „m” példány az „masodik” osztály egy példánya!Ezért a késöi kötés a „class masodik” VMT tábla szerint a „metodus_ a()”hívás esetén az „masodik.metodus_ a()” metódust kell meghívni.

Elönyök:• A késöi kötést feloldó programkód rövid, egyszerű ,gyors.

Hátrányok:• A VMT táblát el kell készíteni (fordítási idö)• A példányokhoz a VMT táblát hozzá kell rendelni (futási idö)• A VMT tábla túl sok memóriát köt le

o Mindig legalább annyi sora van, mint az ös VMT táblánako Újabb sorokkal bövül, ha új virtuális metódust vezetünk beo Akkor is öriz egy bejegyzést, ha annak átdefiniálása nem történt meg

(pl.„class masodik VMT – metodus_d()” sor!)

A VMT táblát a fordító program el tudja készíteni, és a VMT tábla eltárolásrakerül a futtatandó állományban.

A példányhoz rendelést a konstruktor teszi meg, automatikusan (generált pro-gramkód).

Dinamikus Metódus Tábla• A szerepe megfelel a VMT-nek (késöi kötés feloldását támogatni)• Kevesebb memóriaigénye van, mint a VMT-nek• Lassúbb a kezelése

Felépítése:• Hasonló, mint a VMT• Osztályhoz van hozzárendelve• A DMT induláskor üres• Ha az osztályban bevezetünk egy új virtuális metódust a „virtual”

kulcsszóval, akkor ezen metódus bekerül a táblázatba (a végére)• Ha az osztályban felüldefiniáltunk egy már létezö virtuális metódust az

„override” kulcsszóval, akkor ez is bekerül a táblázatba.• A DMT mindig tartalmaz egy bejegyzést, hogy hol van az ös osztály

DMT-je. Ha ilyen nincs, akkor azt a NIL jelzi.• A táblázatban a metódusok indításához szükséges információk vannak

eltárol-va (pl. a metódusok memóriacímei, amely alapján azokat el lehet indítani)

Lényeges különbség:a DMT induláskor nem tartalmazza az ösosztály DMT táblájának sorait.

class elso DMT öS DMT = NILint metodus_a() elso.metodus_aint metodus_d() elso.metodus_d

class masodik DMT öS DMT =elso DMTint metodus_a() masodik.metodus_aint metodus_b() masodik.metodus_b

Nem szerepel a „class masodik DMT”-ben a „int metodus_ d()” sora, mert aztnem definiáltuk felül a „masodik” osztályban.

A DMT alapján a kés ö i kötés feloldás kódvázlata:

P := példány. DMT kezdöcímeCiklus amíg P<>NILIF P-ben szerepel a keresett metódusMetódus meghívásaKilépés a ciklusbólENDIFP :=P.ö S_DMT tábla kezdöcímeCVÉGE

Vagyis a DMT táblákban keresés során ha a jelenlegi DMT táblában nincsbenne a keresett metódusról az információ, akkor visszalépünk az ö s metódusDMT-jébe (masodik -> elso), és ott folytatjuk a keresést.

Elönyök:• A DMT táblák kevesebb memóriát kötnek le

o Csak azon bejegyzések szerepelnek benne, amelyek ténylegesenváltozást szenvedtek el az ös DMT-hez képest

Hátrányok:• A késöi kötést feloldó generált programkód bonyolultabb és lassúb• A DMT táblát el kell készíteni (fordítási idö)• A példányokhoz a DMT táblát hozzá kell rendelni (futási idö)

Amely nyelvekben mindkettöt lehet használni, ott az a javaslat, hogy azonmetódusokat kezeljük VMT technikával, amelyek hívása gyors kell legyen(sokszor hívjuk meg, pl. egy ciklus belsejében).

Azokat tegyük DMT táblába, amelyeket az öröklés-továbbfejlesztés soránfelüldefiniálhatnak mások (bár erre elöre láthatólag ritkán kerül majd sor), vagya metódust ritkán hívják meg – ezért nem számottevö a lassúbb kezelésböladódó lassúbb programfutás.

Amely metódusokat nem szükséges, ne tegyük egyik táblába sem, mertfeleslegesen kötünk le memóriát,és feleslegesen lassítjuk a program futását.

ConstructorAz osztály inicializálása:

• az osztály mez ö inek alaphelyzetbe állítása• általánosan az osztály alaphelyzetbe állítása• megvalósítható egy szimpla metódussal is, de ...

o ... ez utóbbit a „felhasználó” elfelejtheti meghívni.

A konstruktor egy speciális metódus, melynek ...• ... kötött a neve –ugyanaz mint az osztály neve (C++, Java, C#)• ... nincs visszatérési típusa• ... fö feladata az objektum mez ö inek inicializálása• ... végrehajtódik, mielött bármely metódus meghívódhatna

class TVerem{ private int vm;private double[] tomb;...public TVerem() // konstruktor{vm = 0;}

}...TVerem V = new TVerem(); // konstruktor hívása kötelezöV.Push(12.3); // most már hívhatóak a metódusok.

9

#6 (VMT és DMT)

#7 (init, done)

Page 10: Programozási nyelvek II. C# - aries.ektf.huaries.ektf.hu/~shad/feltoltesek/download/Prognyelv_II/NET2_cou3.pdf · A „virtuális gépi kódú” nyelv, melyre le kell fordítani

Paraméteres konstruktorAz overloading- nak köszönhetöen egy osztálynak több konstruktora is lehet –különbözö paraméterezéssel :

class TKor{ ...public int x; TKor K1 = new TKor();public int y; TKor K2 = new TKor(8,7);... ...public TKor(){x = 0;y = 0;

}public TKor( int ax, int ay){x = ax;y = ay;

}}

Saját konstruktorokHa nem definiálunk konstruktort az osztályhoz, akkor egy üres paraméterezésűés törzs ű konstruktor „generálódik” az osztályhoz.

Ha készítünk saját konstruktortokat, de mindegyikük paramétert vár, akkorazok közül kell az egyiket alkalmazni.

Ha készítünk saját konstruktorokat, üres paraméterezésűt is, és paramétereseketis, akkor választhatunk, melyiket használjuk.

Metódus hívás konstruktorbólA konstruktor sok egyéb szempontból közönséges metódusnak minösül – pl.lehet belöle egyéb metódusokat is meghívni.class TKor{public TKor( int ax, int ay) // konstruktor!{x = ax;y = ay;Kirajzol(); // közönséges metódus hívása}

protected void Kirajzol(){...

}}

Konstruktor hívása konstruktorból• Az osztály konstruktorából lehetöség van az ös osztály valamely kon

struktorát meghívni (hogy az beállíthassa az örökölt mezök kezdö értékét), így ezen osztály ezen konstruktorának már csak az új mezök kezdö értékét kell beállítani

• Ha egy osztálynak több konstruktora is van, akkor lehetöség van az egyik konstruktorból (ugyanazon osztály) másik konstruktorát is meghívni.

ös osztály konstruktorának hívása:

class TPont {int x, y;public TPont( int ax, int ay) {x = ax;y = ay;

}}class TKor : TPont {int sugar;public TKor(int ax,int ay,int r):base( ax,ay){sugar = r;

}}TPont origo = new TPont (0,0);TKor buborek = new TKor(100,100,10);

Saját osztály másik konstruktorának hívása:

class TVerem{int vm;int[] tomb;void Kiir() { Console. WriteLine("Hi"); }public TVerem( int maxDb){vm = 0;tomb = new int[ maxDb];// ennyi elemű int tömb létrehozása

}public TVerem() :this(100){ // egyéb tennivalónk már nem is maradt}

}TVerem egyikverem = new TVerem(); // = new TVerem( 100)!TVerem masikverem = new TVerem(300); //konstruktor hívása.Örökölt constructor meghívásaKérdés: ha az ös osztálynak van konstruktora, és a gyer-mekosztálynak is van,és a gyermekböl példányosítunk, akkor meg fog-e hívódni az ös osztály kon-struktora?Ha mi nem hívjuk meg, akkor nem!

(Pascal, Delphi) // „a” variáció

type TElso = classpublicx: integer;constructor Create;procedure Kiir; virtual;

end;type TMasodik = class(TElso)publicconstructor Create;procedure Kiir; override;

end;constructor TMasodik.Create;begininherited Create; { explicit hívása az ö s konstruktorának { ha nem írjuk, nem hívódik meg!}x:= 2;

end;

Elkerülhetetlenül! (C++, C#, Java) // „b” variáció

#include <stdio. h>class TElso{ public:int x;TElso() { x= 0; }// nincs paramétere -> default constructor};class TMasodik: public TElso{ public:int y= 0;TMasodik() { y= 0;}// mire elindul, a TElso() már lefutott};int main(){ TMasodik m = TMasodik();}

Elkerülhetetlenül, de melyik?! (C++, C#, Java) // „b” variáció

#include <stdio.h>class TElso{int x;public TElso( int ax) { x= ax; }

}class TMasodik: TElso{int y;public TMasodik() { y= 0;}// hibás, nincs „default constructor”

}class TMasodik: TElso{int y;TMasodik(): base(0) { y= 0;}// jó, megjelölt konstruktor

}VMT beállítása mikor?Amennyiben meghívunk egy „hagyományos” metódust (korai kötés) a kon-struktorból, az mindig ugyanazon metódus lesz.

A kérdés: ha egy virtuális metódust hívunk meg, az melyik lesz? A válasz azonmúlik, hogy a VMT a konstruktor kódjának elején vagy a végén állítódik be!?

A végén! Ekkor a konstruktor belsejében még a korai kötés működik avirtuális metódusokra is! (C++)

class TElso{public:int x;TElso() { x= 1; kiir(); } // kiir meghívásavirtual void kiir() { printf(" elso. kiir\ n");}

};class TMasodik: public TElso{public:int y;TMasodik() { y= 2;}//az ös konstruktora automatikusan elindul

void kiir() { printf(" masodik. kiir\ n", x); }//autom.override!};int main(){TMasodik m= TMasodik();

// „elso. kiir” íródik ki!}

10

Page 11: Programozási nyelvek II. C# - aries.ektf.huaries.ektf.hu/~shad/feltoltesek/download/Prognyelv_II/NET2_cou3.pdf · A „virtuális gépi kódú” nyelv, melyre le kell fordítani

Az elején! Ekkor a konstruktorok belsejében már működik a késöi kötés!(C#, Delphi, Java)

class TElso{public int x;public TElso() { x= 1; kiir(); }public virtual void kiir() {

Console. WriteLine( ”elso. kiir”); }}class TMasodik:TElso{public int y;public TMasodik() { y=2;}// ös konstruktora automatikusan elindul!public override void kiir(){

Console.WriteLine("masodik.kiir”); }}static void Main(){TElso m = new TElso(); // ”elso. kiir”íródik kiTMasodik m = new TMasodik();// ”masodik.kiir” íródik ki

}

Statikus konstruktorA statikus konstruktornak ... (csak egy lehet belöle!)

• „static” módosítóval kell rendelkezni• nem lehet hozzáférési módosítója (public, private, ...)• nem lehet paramétere• nem lehet meghívni explicit módon• automatikusan kerül meghívásra „mire szükség van rá”!• csak a statikus mez ö knek adhat kezd ö értéket

class TVeletlenSzamok {static int RandSeed;static TVeletlenSzamok(){RandSeed = Timer_now();

}}

DestruktorEzen metódusok gondoskodnak arról, hogy az objektum használatának befe-jeztekor az objektum által lefoglalt eröforrások (memória, file- ok, háttértároló,csatolt eszközök, stb.) felszabadításra kerüljenek.

A destruktorok meghívásának három módja lehet:a. explicit módon (programozó által // desktruktor jelleg)b. implicit módon (objektum megszünésekor (ref. számláló))c. automatikusan (objektum nincs már használatban (garbage collector = gc))Explicit destruktor hívás (pl. Delphi)

type TAkarmi = class...constructor Create;destructor Done;end;procedure Valami;var A: TAkarmi;beginA := TAkarmi.Create;...A. Done; // a programozó meghívja a destruktortend;

Implicit destruktor hívás (1)Az objektum- példányokhoz a memóriában plusz-ban egy „referencia-számláló”készül, mely mutatja,hogy hány vál-tozón keresztül lehet ezt a példányt elérniközvetlenül. A példány megszűnik,ha a referencia-számláló 0-ra csökken.

type TAkarmi = class...constructor Create;destructor Done;

end;procedure Valami;var A: TAkarmi;beginA := TAkarmi.Create;// példány létrejön, ref.szamlalo=1...

end;//az „A”változó megszűnik (lokális változó)//a példány referencia-számlálója e miatt 0-ra csökken// meghívódik a destruktora automatikusan// a példány „kitakarítódik” a memóriábólImplicit destruktor hívás (2)

type TAkarmi = class...constructor Create;destructor Done;

end;var G: TAkarmi; // globális változóprocedure Valami;var A: TAkarmi;beginA := TAkarmi. Create; // példány létrejön, ref. szamlalo= 1...G :=A; // a ref. szamlalo 2 lesz !

end;// az „A” változó megsz ű nik (lokális változó) a példány// referencia- számlálója e miatt 1- re csökken, de még nem 0!

Implicit destruktor hívás (3 )Az ilyen nyelveknél megszüntették a „memória-felszaba-dítás”- szerű utasítá-sokat,a memória automatikusan felszabadítódik a ref. számláló kezelés mecha-nizmus révén.

type TLancoltListaElem = classpublic:kov: TLancoltListaElem;

end;var Fej: TLancoltListaElem;BEGIN... // a láncolt lista feltöltése elemekkelFej := nil; // Fej elem „végérték”- re állítása

END.

• Ekkor az 1. listaelem ref. számlálója 0-ra csökken -> megsz ű nik• Ekkor a 2. listaelem ref. számlálója csökken 0- ra -> megszűnik• ...• Az összes listaelem „kitakarítódik” a memóriából

A kétirányú láncolt listák esetén a mechanizmus nem működik!

type TKetiranyuLancoltListaElem = classpublic:kov : TKetiranyuLancoltListaElem;elozo : TKetiranyuLancoltListaElem;

end;var Fej: TKetiranyuLancoltListaElem;BEGIN... // a láncolt lista feltöltése elemekkelFej := nil; // Fej elem „végérték”- re állítása

END.

• A kétirányú láncolt listában az elemek ref. számlálója 2, mert mindkét szomszédos elemröl van rá hivatkozás (kivéve az utolsó elem esetén)

• Fej:= nil -> ekkor az 1. listaelem ref. számlálója 1- ra csökken -> megszűnik• A többi listaelem ref. számlálója nem változik• Az összes listaelem bent marad a memóriában!!• Pedig a listaelem többé már nem elérhetö a programból!

Garbage collector használata eseténA „garbage collector” egy eljárás (programocska)!

A GC algoritmus folyamatosan keresi a memóriában azokat az objektum-példányokat,akiknek a referencia-számlálója 0-ra csökkent, vagy elérhetetlennévált a programból (nincs olyan programváltozó, amelyböl kiindulva a pél-dány-hoz el lehetne jutni).

A GC amint ilyet felfedez, megszünteti azt.Mikor teszi ezt a GC?A program futása közben folyamatosan...

Nem explicit módon hívott destruktorokHa a destruktort a rendszer automatikusan hívja majd meg („ b” és „c” módsz-er), akkor a destruktoroknak ...

• ... nem lehet paraméterük (a rendszer nem fog kitalálni értékeket!)• ... csak egy lehet bel ö lük (a rendszer nem fog válogatni)• ... ennek „public” védelmi szintűeknek kell lennie

(a rendszer „kívülröl” hívja)

.Destruktor készítése (C#)• Kötött a neve (ugyanaz, mint az osztály, de ~ van elötte)• Nincs paramétere• Nincs hozzáférés- korlátozása (private, public,protected)• C#- ban nem tudhatjuk, mikor hívódik meg a destruktor (az automatikus

garbage collector miatt)• Az overloading mellett sem lehet több destruktor, hiszen kötött a név, és a

paraméterezés is• Kötött a neve (ugyanaz, mint az osztály, de ~ van elötte)• A garbage collector fogja meghívni!• Paramétere nem lehet!• Overloading mellett sem készíthet ö több destruktor!• Nem lehet védelmi szint módosítója (automatikusan public!)

11

Page 12: Programozási nyelvek II. C# - aries.ektf.huaries.ektf.hu/~shad/feltoltesek/download/Prognyelv_II/NET2_cou3.pdf · A „virtuális gépi kódú” nyelv, melyre le kell fordítani

This• Az osztály valamely metódusában szükség lehet saját

magára hivatkozni• Leggyakrabban akkor, ha egy „külsö” osztály metó

dusát hívjuk, és át kell adni „saját magát”• A C alapú nyelvekben a „this” változó a saját példányra

mutat• A Pascal alapú nyelvekben ezt „self”-nek nevezik,

funkciója ugyanaz• A „this” foglalt szó (nem használható fel saját célokra).

”this” mint automatikus paraméter

class Elso{public int x;public void beallit(int ujX)

// +1 „láthatatlan” paraméter: this{x = ujX;

} ...

}Elso e1 = new Elso(); e1.Beallit(10);Elso e2 = new Elso(); e2.Beallit(20);

class TKiirjaMagat{public string uzenet = ” ”;override public string ToString()

{ return ”Hello ”+ uzenet; }public void Kiir() {

Console. WriteLine( this ); //„saját maga”}

}class MainClass{static void Main(){TKiirjaMagat x = new TKiirjaMagat();TKiirjaMagat y = new TKiirjaMagat();x. uzenet = ”world!”; x.Kiir();// -> Console.WriteLine( x );y.uzenet = ”világ!”;y.Kiir();// -> Console. WriteLine(y );

}}class TPont{int x;public void setX_ 1( int x){x = x; // nem jó, ”x” a paraméter mindkétszer!

}public void setX_ 2( int x){this. x = x; // jó!

}public void setX_ 3( int aX){x = aX; // javasolt

}}”this”és az osztálymetódusok

• A „this” a példányt azonosítja• Az osztálymetódus nem példányon keresztül kerül meghívásra• Az osztálymetódus belsejében nincs „this”!• A „this”- t láthatatlan paraméternek is nevezik, amelyet a metódus

hívásakor a rendszer automatikusan ad át (aktuális paraméterlista), és vesz át (formális paraméterlista).

• Ezért „this” nevű paramétert mi már nem írhatunk! (Egyébként sem, mert foglalt szó !)

TípuskompatibilitásSzabály: az objektum (példány) típusa felül-röl kompatibilis minden ös típusával.• Ez logikus: egy „továbbfejleszett” gyermekosztály mindent tud, amit az ösei tudnak,mivel mindent örökölt tölük.• Egy gyermekböl származtatott példányminden olyan kifejezésben használható, aholaz öséböl származtatott példány használhatólenne.• Eljárások és fv-ek paramétereiben is hasz-nálhatóak ott, ahol valamely ös típus vanjelölve a paraméter típusának.

Pl (1/ a):class TElso{}class TMasodik{}class MainClass {static void Kiir( TElso x){Console. WriteLine( x);

}static void Main() {TElso e = new TElso();TMasodik m = new TMasodik();Kiir( e);Kiir( m); // nem jó, „m” nem kompatibilis TElso- vel

}}

Pl (1/ b):class TElso {public string nev;

}class TMasodik {}class MainClass {static void Kiir( TElso x){Console. WriteLine( x. nev);

}static void Main() {TElso e = new TElso();TMasodik m = new TMasodik();Kiir( e);Kiir( m); // nem lehet jó, „m. nev” nem létezik!

}}Pl (2):class TElso { public int szam = 10;

}class TMasodik: TElso { // származtatás !!}class MainClass {static void Kiir( TElso x){Console.WriteLine( x. szam );

}static void Main() {TElso e = new TElso(); Kiir( e);TMasodik m = new TMasodik();Kiir( m);// jó, „m. szam” létezik,„m” kompatibilis TElso- vel

}}

Két lépésben történ ö példánylétrehozás:TElso e;e = new TElso();

Típuskompatibilitás miatti értékadás:

TElso e; //nem jön létre még a változóTMasodik temp = new TMasodik();e = temp;

Ha a fenti helyes, akkor az alábbi is, mert ez ugyanaz, csak egy lépésben ...TElso e = new TMasodik(); //!!!!!jó

Nem megfelel ö típuskompatibilitás miatti hibás kód:

TMasodik e = new TElso (); // !!!!! nem jó

12

Destruktor példa

class TChat {Socket kapcsolat;public TChat( string ChatServerIP, int ChatPortNum){IPAddress ipcim=Dns.Resolve(ChatServerIP).AddressList[0];IPEndPoint szgep=new IPEndPoint(ipcim,ChatPortNum);kapcsolat = new Socket( AddressFamily. InterNetwork, SocketType.Stream,ProtocolType.Tcp );kapcsolat.Connect(szgep);

}~TChat() //a deskturktor lezárja a hálózati kapcsolatot megszűnés elött!{kapcsolat.Shutdown(SocketShutdown.Both);kapcsolat.Close();

}public void KuldUzenet(string Uzenet) { ...; }public string UzenetFogad() { ...; }...

}

public void beallit( int ujX, Elso this ){this.x = ujX;

}e1.Beallit( 10); -> Elso.Beallit(10,e1);e2.Beallit(20); -> Elso.Beallit(20,e2);

#8 („this” paraméter)

Page 13: Programozási nyelvek II. C# - aries.ektf.huaries.ektf.hu/~shad/feltoltesek/download/Prognyelv_II/NET2_cou3.pdf · A „virtuális gépi kódú” nyelv, melyre le kell fordítani

Pl (3):class TElso{public int szam() { return 10; }

}class TMasodik: TElso // származtatás !!{}class MainClass {static void Kiir( TElso x){Console. WriteLine( x. szam() );

}static void Main(){TElso e = new TElso();TMasodik m = new TMasodik();Kiir( e);Kiir( m); // jó, „m” kompatibilis TElso-vel

}}Pl (4):class TElso{public int szam() { return 10; }

}class TMasodik: TElso { // származtatás !!new public int szam() { return 20; }}class MainClass {static void Kiir( TElso x){Console. WriteLine( x. szam() );

}static void Main() {TElso e = new TElso();TMasodik m = new TMasodik();Kiir( e); // 10 Kiir( m); // ez is 10 !! (miért?!)

}}Pl (5):class TElso{public virtual int szam() { return 10; }

}class TMasodik: TElso {public override int szam() { return 20; }

}class MainClass {static void Kiir( TElso x) {Console. WriteLine( x. szam() );

}static void Main() {TElso e = new TElso();TMasodik m = new TMasodik();Kiir( e); // 10 Kiir( m); // 20 !! (miért?!)

}}

Pl (6):class TElso {public virtual int szam() { return 10; }

}class TMasodik: TElso { public override int szam() { return 20; }

} class MainClass { static void Kiir(TElso x) {Console. WriteLine( x. szam());} static void Main( string[] args) { TMasodik m = new TMasodik(); Kiir( m); // 20 (persze)TElso e = new TMasodik() ; Kiir( e); // 20 (miért?!) (mi lesz a VMT- je ?!)

} }Pl (7): class TElso {public int szam;public TElso() { szam= 10; }

} class TMasodik: TElso {public TMasodik() { szam= 20; }

}class MainClass{static void Kiir(TElso x){ Console.WriteLine(x.szam); }static void Main() {TElso e = new TElso ();TMasodik m = new TMasodik();TElso h = new TMasodik(); // ez pedig így jó! :)Kiir(e); // 10Kiir(m); // 20Kiir( h); //20 (miért?!)(melyik konstruktor fut le utoljára?)

}}

Pl (8):class TElso { public int szam; public TElso() { szam= 10; }

}class TMasodik: TElso { new public int szam; public TMasodik() { szam= 20; }

} class MainClass { static void Kiir( TElso x) { Console. WriteLine( x. szam); }

static void Main() { TElso e = new TElso(); TMasodik m = new TMasodik(); Kiir( e); // 10 (miért?!) // TElso. szam Kiir( m); // 10 (miért?!) // TElso. szam !

} }

Típuskompatibilitás• C#- ban ha nem jelölünk egy osztálynálöst,akkor automatikusan egy elöre

definiált „Object” osztály lesz az ö se.• Ha egy osztály öröklödési láncán elindulunk felfele, elöbb-utóbb el kell

jussunk a kiindulási ponthoz,amelynek a közvetlen ösemár az „Object”

Ezért minden osztály mindig kompatibilisaz Object típussal!

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Univerzális paramétertípus

class ArrayList // System.Collections. ArrayList{ ... public void Add( Object o) { ... }

}class Stack // System. Collections. Stack { ... public void Push( Object o) { ... } public Object Pop() { ... }

}class Console // System. Console { ... public void WriteLine( Object o) { ... }

}

Futás közbeni típusellenörzés• Mivel egy példány típusa nem egyértelm ű (lásd elözö példák), ezért

szükséges lehet a tényleges típus ellenörzésére• Ezen típusellen ö rzés biztonsági okok miatt mindenképpen szükséges

explicit típuskonverzió elött!• A legtöbb nyelv támogatja ezt valamilyen módon. A C#-ban az „is”

operátorral lehet elvégezni.• Az „is”igazi jelentése: „kompatibilis-e ?”• „is”általános alakja: példánynév is osztálynév• az „is” operátor logikai értéket ad vissza.

class TElso{public int szam;

} class TMasodik:TElso{ } class MainClass{static void Beallit(TElso x){if (x is Object) x.szam=10; //mindenre igaz!if (x is TElso) x.szam=20; //„e”és „m”-re is igazif (x is TMasodik) x.szam=30; //csak „m”-re igaz

} static void Main() {TElso e = new TElso(); Beallit(e);TMasodik m = new TMasodik(); Beallit (m);

}}

Futás közbeni típuskonverzió • Miután kiderült, hogy egy példány valójában más típussal is kompatibilis,

elöfordulhat, hogy ki akarjuk használni ezen másik típus speciális lehetöségeit.

• De ehhez a példány- ra típuskonverziót (típuskényszerítést) kell alkalmazni• A típuskényszerítés történhet „C”stílusban ((TMasodik)x).szam =10;• Az „as” operátorral (x as TMasodik).szam =10;• Módszert ö l függetlenül – ha a példány valójában nem kompatibilis a

szóban forgó típussal, akkor futás közbeni hibát fogunk kapni

13

#9 (típuskompatibilitás)

Page 14: Programozási nyelvek II. C# - aries.ektf.huaries.ektf.hu/~shad/feltoltesek/download/Prognyelv_II/NET2_cou3.pdf · A „virtuális gépi kódú” nyelv, melyre le kell fordítani

class TMasodik: TElso{public float jelzes; // +1 mezö

} static void Beallit(TElso x){if (x is TElso) x.szam=20; //„e”és „m”-re is igazif (x is TMasodik) x.jelzes=30.3; //hibás !!!(x as TMasodik).jelzes =30.3; //„e”-re hibás!!if (x is TMasodik)(x as TMasodik).jelzes=30.3; //jó !!!

} TElso e = new TElso(); Beallit(e);TMasodik m = new TMasodik(); Beallit(m);

Stack a = new Stack();TElso e = new TElso();a. Push ( e ); // odafele működik, TElso Y Object autom.e = a. Pop(); //visszafele nem, Pop() fv típusa Object !e = a. Pop() as TElso; // így már jóe = (TElso) a. Pop(); // így is jóObject o = a.Pop();if (o is TElso) e = o as TElso;

ArrayList l = new ArrayList();l.Add ( new TElso() );TElso e;e =l[0]; // nem megy, Object-t ad visszae =(TElso) l[ 0]; e =l[0] as TElso;

Absztrakt metódusokA fejlesztés során eljuthatunk egy olyan fázisba, hogy deklarálnunkkell egy olyan metódust, amelynek el tudjuk dönteni aparaméterezésének formáját, és a visszatérési típusát, de nemtudjuk megírni magának a metódusnak a kódját.

class TAltalanosGrafikusObjektum{public int x, y;public Mozgat( int eltolasX, int eltolasY){ Letorol();x = x + eltolasX;y = y + eltolasY;Kirajzol();

}public virtual void Letorol(){ // ??? ide mit írjunk ???}public virtual void Kirajzol(){ // ??? ide mit írjunk ???}

}

Megoldást jelenthet a problémára, hogy elkészítjük a metódusokat üres törz-zsel, virtuális metódusként, és rábízzuk a konkrétabb osztály tervez ő jére,hogy írja meg ő ket. Vegyük észre, hogy ezen metódusok mindig virtuálisaklesznek, mert az általunk megírt „Mozgat” metódus csak ekkor fogja tudnimeghívni a konkrétabb, kifejlesztett metódust.

A fenti megoldással két baj van• nem kényszeríti a továbbfejlesztő t a metódus felülírására (override),

mert a virtuális metódusok nem kötelezőenfelüldefiniálandók.• Meg kell írnom egy fv-t, amelyet igazából nem tudok megírni. A meg-

oldás, hogy az ilyen metódusokat „abstract” kulcsszóval megjelölöm:

public abstract void Letorol();public abstract void Kirajzol();• A megjelölés egy jelzés a C# fordító felé, jelezve, hogy engedje meg,

hogy ezen metódus törzsét ne írjam le.• Az abstract metódusok egyben virtuálisak is (virtual), ezért ezen

kulcsszót nem lehet együtt használni az abstract kulcsszóval.• Csak példányszint ű metódus lehet abstract, ezért nem szerepelhet együtt

a static kulcsszóval sem.• Lehet property is abstract.

abstract public int x{get ;set ;

}

Ha egy osztály tartalmaz abstract metódusokat, akkor magát az osztályt ismeg kell jelölni az abstract kulcsszóval!

abstract class TAltalanosGrafikusObjektum{public int x, y;public Mozgat( int eltolasX, int eltolasY){ Letorol(); x = x + eltolasX;y = y + eltolasY; Kirajzol();

}public abstract void Letorol(); public abstract void Kirajzol();

}

• Az abstract osztályból származtatás esetén a gyermekosztályban lehetőség van (természetesen) az örökölt abstract metódusok felüldefiniálására (konkretizálására) az override kulcsszó segítségével.

• De nem kötelez ő az összes abstract metódust kifejteni.• Ha egy ilyen gyermekosztályban még mindig van kifejtetlen abstract

metódus (nem mindet írtuk felül konkrét metódussal), akkor ezen osztálytis meg kell jelölni az abstract kulcsszóval.

• „abstract” jelzést olyan osztályra is rátehetek, amely nem tartalmaz abstract metódust.

• Ezen osztály gyermekosztályáról már le lehet venni az abstract kulcsszót (akkor is, ha a gyermekosztály semmi mást nem tartalmaz pluszban).

• „abstract” jelzésű osztályból példányosítani nem lehet, csak osztályszintű mez ő ket és metódusokat használhatok bel ő le.

abstract class Elso //ebből nem lehet példány{static public int x;static public void kiir() {}

} class Masodik:Elso // ebből lehet példány{ }

„sealed” osztályok • Az osztályt megjelölhetem „sealed” kulcsszóval, ez megakadályozza,

hogy ezen osztályból származtatás útján gyermekosztályt hozhassak létre (nem továbbfejleszthet ő osztály, nem választható „ ős”-nek).

• A „sealed” és az „abstract” nem szerepelhet együtt (értelemszerűen az abstract osztályokat kötelező továbbfejleszteni, mert nincsenek készen,a sealed-et nem lehettovábbfejleszteni =ellentmondás)„sealed =(levelet) lepecsétel, leplombál”.„sealed”

„Viktor probléma”: hogy lehet olyan osztályt készíteni (amelynek csak osztá-lymetódusai vannak) amelyből nem lehet példányosítani?

(a) Nem lehet ilyen osztályt létrehozni,a közös ős (Object) tartalmaz példányszint ű metódusokat, amelyeket mindenképpen örököl az adott „csak osztályszint ű ”objektumosztály

(b) Ha megjelöljük „abstract” kulcsszóval, akkor nem lehet belőle példá-nyosítani, de ez nem teljeskör ű védelem, mert készíthetünk belőle származtatott osztályt, amelyr ő l eltávolítjuk az „abstract” jelzést, és máris lehet példányosítani.

(c) Ha megjelöljük „sealed”- el, nem lehet bel ő le származtatni, depéldányosítani lehet.

(d) Az „abstract” és a „sealed” nem szerepelhet együtt.

Megoldás: constructorok elrejtése!

class NemPeldanyosithato{private NemPeldanyosithato() {} // fontos,hogy PRIVATE !

} NemPeldanyosithato nmp = new ???; // nem megy!class Proba: NemPeldanyosithato // nem megy!{ }

A fenti példában az osztály egyetlen konstruktora sem elérhető. Nemtudunk bel ő le példányt csinálni, mert a „new” után le kellene írni akonstruktor nevét! De a továbbfejlesztett osztályokból is meg kellhívni az ős konstruktorát (base!).

IndexelõAkkor használjuk, ha az osztályt mint egy tömböt akarjuk kezelni:

• Tömbszer ű szintaxissal• Ekkor valójában egy property-t írunk magához az osztályhoz• Ezen property neve kötelez ő en „this”

14

#10 (absztrakt metódusok)

#11 (indexelõk)

Page 15: Programozási nyelvek II. C# - aries.ektf.huaries.ektf.hu/~shad/feltoltesek/download/Prognyelv_II/NET2_cou3.pdf · A „virtuális gépi kódú” nyelv, melyre le kell fordítani

Pl. (vektor- szerű indexelő):

class Verem{object[]tomb =new object[100];public object this[int index]{ get { if (0<=index &&index<vm) return tomb[index];else throw new Exception("Index out of range.");

}set { if (0<=index &&index<vm)tomb[index]=value;else throw new Exception("Index out of range.");}

}...

}

Pl. (vektor- szerű indexelő ):

class LancoltLista{public TListElem this[int index]{get {

... a láncolt listában az „index”-edik elem meg-keresése return megtalalt_elem;

}set {

... a láncolt lista „index”- edik elemének javítása, vagy új elem felvétele a láncolt listába... (value)

}}

} class String{public char this[int index] {get { ... }set { ... }

Pl. (mátrix-szerű indexelő):

class THaromszogMatrix{public object this[int sor,int oszlop]{ get { ... }set { ... }} ...

}

Típuskompatibilitás!?

class TElso: TValamilyenOs{

public string kiir() { return ”Hello, én TElso vagyok!”; }}

class TMasodik{

public string kiir() { return ”Hello, én TMasodik vagyok!”; }}

void MeghivKiir( ??? x ){

string s = x.kiir();}

Mit válasszunk „x” típusának, hogy mindkét osztályból származópéldányra m û ködjön a fenti példa?

Ez csakis az Object lehet, azzal mindenki kompatibilis!(a)

void MeghivKiir( Object x){

x.kiir(); // nem m ű ködik, az Object- nek nincs ilyen metódusa!}

(b)void MeghivKiir(Object x ){

string s;if (x is TElso) s=( x as TElso).kiir(); //rendbenif (x is TMasodik) s=( x as TMasodik). kiir(); //rendben

}

De hogyan tovább!? Mi van, ha lesznek újabb osztályok is, akiknekszintén van „kiir” metódusuk? Hogy a „MeghivKiir” jól mûköd-

jönezekre a példányokra is, újabb if- eket kell írni! Ez nem jó!!Mi a teendő , ha két teljesen különböző objektumosztályom van, amelyek nemkompatibilisek egymással, de vannak közös vonásaik? Pl. mindegyiknek van„kiir” metódusa! Hogyan tudok olyan eljárást írni, amelyik minden olyanpéldányra működik, akinek van kiir metódusa?

A probléma bővebb: olyan kiir metódus kell, amelyik string- et ad vissza,és nincs paramétere!

Ötlet: csináljunk erre egy segéd- osztályt!

class TKiir{public virtual abstract string kiir();

}class TElso: TKiir// ( de mi legyen az eredeti TValamilyenOs- el? ){public override string kiir() { ...; }

}class TMasodik: TKiir{public override string kiir() { ...; }

}void MeghivKiir( Tkiir x){x. kiir();

}

Ötlet nem m ű ködik a gyakorlatban! A TElso- nek már van őse! Nemcserélhetem le ezt a fontos ő st a „TKiir”- ra! Egy osztálynakmárpedig csak egy őse lehet!

Interface = segéd osztály

interface IKiir{string kiir();

}class TElso: TValamilyenOs, IKiir{public string kiir() {return "Hello, én TElso vagyok"; }

}class TMasodik: IKiir{public string kiir() {return "Hello,én TMasodik vagyok";}

}void MeghivKiir( Ikiir x){x.kiir();

}

• Az interface belsejében csak fv-eket, és propertyket, és indexelőket lehet definiálni.

interface IGrafikusObjektum{void kirajzol(); // fvint x // property{ get;set;}

}

• Az interface belsejében definiált „dolgoknak” nem lehet elérhetőségi módosítójuk (private,stb.)

• Nem lehet sem „static”, sem „virtual” módosítóik• Automatikusan „abstract” módosítójuk van, tehát sosincsenek kidolgozva!

Egy osztály ha implementál egy interface- t, akkor az interface-ben feltüntetettminden fv- t, property- t, indexel ő t implementálnia kell! Ha bármitkihagyunk, hibajelzést kapunk!

• Az interface- t implementáló osztályban az a kidolgozott „dolgok”kötelez ő en „public” módosítóval kell rendelkezniük!

• Az interface olyan, mintha ő s lenne. Az ő t implementáló osztályinnent ő l kezdve típuskompatibilis az interface- el!

Interface-t támogató nyelvek

• Az egyébként egymással nem kompatibilis osztályok közötti „rokonsági”kapcsolatot írják le az interface-k (vannak bennük közös vonások, egyforma nevű és paraméterezésű metódusok).

• Egy osztálynak csak egy őse lehet,de tetszőleges számú interface-timplementálhat.

• Ilyen nyelvek:o Delphio Javao C#

15

#12 (interface)

Page 16: Programozási nyelvek II. C# - aries.ektf.huaries.ektf.hu/~shad/feltoltesek/download/Prognyelv_II/NET2_cou3.pdf · A „virtuális gépi kódú” nyelv, melyre le kell fordítani

Interface-t nem támogató nyelvek• Ezen nyelveken egy objektumosztálynak több ő se is lehet• Ekkor az adott osztály mindkett ő t ő l egyszerre örököl, és mindkettővel

kompatibilis• Ezen nyelveken az interface helyett ténylegesen osztályokat hozunk létre

A több ős problémákat is felvet, mert a több ősnél lehetnek egyforma nevűmezők, egyforma nevű és paraméterezésű metódusok. A gyermekosztályban,és a belőle származó osztályokban e pillanattól kezdve minősített nevet,vagy típuskényszerítést kell használni a konfliktusok kezelésére.

# include <stdio. h>class elso{ public:

int a, b;elso( void) {a= 1; b= 2;}int getb( void) {return b;}

};class masodik{ public:

int c;float b;masodik( void) {b= 1.0; c= 3;}float getb( void) {return b;}

};class harmadik: public elso, public masodik{ public:

int d;harmadik( void) {d= 4;}

};int main(){elso a1;masodik a2;harmadik a3;printf(" a1. a=% d a1. b=% d\ n ", a1. a, a1. b);printf(" a2. b=% f a2. c=% d\ n ", a2. b, a2. c);printf(" a3. a=% d a3. c=% d\ n ", a3. a, a3. c);printf(" a3. b=% d a3. b=% f\ n ", a3. elso:: b, a3.masodik::b);printf(" a3. b=% d a3. b=% f\ n ",( elso) a3.b, (masodik) a3.b);printf(" a3. get=% d\ n ", a3. elso:: getb());return 0;

}

NévtérNagyobb méret ű program esetén az azonosítók választása problémás lehetmár – elfogynak a jó nevek.Külső (third party) objektumok alkalmazása esetén névütközések szinteelkerülhetlenek.Pl: két különböz ő cég által megírt külső DLL- ben is van SuperStringnevű objektumosztály, amely valamilyen formában különleges string-eket kezel (pl. tömörítve tárolt extra hosszú string-eket). A programunkbanmindkett ő t használni akarjuk.

A probléma az, hogy a név mindkét esetben ugyanaz, de maga az osztály ter-mészetesen nem. Ekkor a két azonosító elfedi egymást, és nem egyértelmű ,mikor melyiket használjuk.

A probléma rokon azzal, ha van egy küls ő („ globális”) változónk, ésegy lokális (pl.paraméter) változó is, amelyeknek a neve elfedi egymást.

Az egyforma nev ű osztályok esetén a megoldást a névtér biztosítja.(A .NET- en belül pl. három timer osztály van:

System.Timers.Timer,System.Threading.Timer,System.Windows.Forms.Timer).

namespace TomoritoKFT{ class SuperString { ...

} } namespace HalozatokBT { class SuperString { ...

} } namespace SajatProgram { public static void Main() { TomoritoKFT.SuperString s1 =new TomoritoKFT.SuperString();

} }

A névtér nem csak egy darabból állhat:

namespace HalozatokBT.Internet.HTTP { class SuperString

{ ...

} }

Ekkor az osztály pontos neve:

namespace SajatProgram { public static void Main() { TomoritoKFT.Internet.HTTP.SuperString s1; }

}

A névtéren belül lehet beágyazott névtér:

namespace HalozatokBT{ namespace Internet { namespace HTTP { class SuperString {...

} }

} }

Ezen osztály teljes neve úgyanúgy

TomoritoKFT.Internet.HTTP.SuperString s1;

A gépelést rövidítendő : usingusing TomoritoKFT.Internet.HTTP;namespace SajatProgram{public static void Main(){SuperString s1; //itt már nem kell kiírni a teljes nevet

}}

Alias létrehozása:

using alias1 =TomoritoKFT.Internet.HTTP;{public static void Main(){alias1.SuperString s1; //itt az alias név is elég

}}

A névterek bővíthetők,vagyis egy már létez ő névteret újból használatba véveaz újonnan definiált osztályok pluszban b ő vítik a névtér tartalmát.

Ha egy objektumosztályt nem teszünk névtérbe (nem kötelez ő ), akkor az aglobális (névtelen) névtérbe kerül.

using System;class MyClass{...

}

A globális névtér mindig nyitott, könny ű használni, de nem tegyünk bele túlsok mindent, mert könnyen ütközések lehetnek.

Ha egy adott névtér belsejében a névtér „nyitott” mintha eleve kiiírtuk volna a„using nevter” sort.

namespace Sajat{class Elso { ... }

} namespace Sajat{class Masik: Sajat.Elso { ... } // nincs rá szükségclass Masodik:Elso { ... } // ennyi is elég

}

Ez akkor is működik, ha a fenti sorok egyetlen modul belül vannak (.cs file),illetve ha külön modulban vannak (kett ő db .cs file).

using Sajat; // erre itt nincs szükség igazábólnamespace Sajat{class Masodik:Elso { ... }

}

16

#13 (névterek)

Page 17: Programozási nyelvek II. C# - aries.ektf.huaries.ektf.hu/~shad/feltoltesek/download/Prognyelv_II/NET2_cou3.pdf · A „virtuális gépi kódú” nyelv, melyre le kell fordítani

Referencia-elv• Az objektum-példány definiálása önmagában nem jelent memóriafoglalást.• A példányhoz tartozó memóriafoglalás a „new” operátor hívásakor valósul meg.• A „new” nem a példány alaptípusának foglal helyet, hanem a konstruktor

típusa dönti el a memóriafoglalást!• A „new”-al kötelez ő megadni egy konstruktor-t is.• A példány e pillanattól kezdve ezen memóriaterületre vonatkozó referenciát

(hivatkozást) tartalmaz.• Ez nagyon hasonlít egy pointerre.• A különbség az, hogy az objektumhoz tartozó memóriaterületet az operá

ciós rendszer „elmozgathatja”, a referencia azonban továbbra is azonosítja a területet.

• A pointer a memória kezd ő címét tartalmazza, ha az operációs rendszer „elmozgatja” a foglalt területet, a pointer már rossz helyre mutatna.

• Ha két példány között értékadás m ű veletet hajtunk végre, akkor areferencia másolódik csak át:

TMasodik m1 = new TElso();TMasodik m2 = m1;• Ugyanez történik objektumokkal kapcsolatos paraméter-átadásakor is!

void Berak(TMasodik m) { m. szam = 20;}TMasodik x =new TMasodik();x.szam =10;Berak(x );Console.WriteLine(x.szam ); //20 !!!!• Az objektum típusú paraméterek ezért akkor is „ref” típusú paraméterek,

ha azt nem jelöljük a paraméter-módosítóval.

Null-referencia• A példányok valójában referencia-típusúak (reference type)

TMasodik m;

Ez egy „m” nev ű referenciát definiál, mely 4 byte- ot foglal el, ésinduláskor még nem tartalmaz referenciát egyetlen memóriaterületre sem:

• A nem létez ő referencia a „null” konstans. Pl:if (e== null) Console.WriteLine(”A példány még nem létezik”);

Referencia és típuskompatibilitás

TMasodik m = new TMasodik ();TElso e = m; // rendben, típuskompatibilise. szam = 10;Console. WriteLine( m.szam ); // 10-et ír ki

• „e” használható, de mivel az ő alaptípusa TElso, a fordító úgy tekint rá,mintha ő valódi TElso lenne, ezért „e”- nek csak olyan mezői és metódusai vannak (szerinte), amelyek a TElso- ben vannak definiálva.

• „e” ugyanaz mint az „m”, de visszabutítva TElso szintre, vagyis „e” mezőinek az értéke ugyanaz, mint „m”- nek, de a korai kötések a TElso szerint működnek.

• Az „e” VMT- je a TMasodik- é, ezért a kés ő i kötések a TMasodikszerint m ű ködnek!

TElso e; //nem jön létre még a változóTMasodik temp = new TMasodik();e = temp;

Összevonva egy sorba:

TElso e = new TMasodik(); //!!!!! jó.

Minden olyan „változó” referencia elven van kezelve, amelynek típusa class”.Ez nem minden esetben jó!

int a = 10;int b = a;a = 20; // b= 20 szintén !?! nem !!!

A nyelv építőkő jellegű alaptípusai nem lehetnek referencia-elvűek.

Ezen típusok érték típusú (value type) adatok. Értékadáskor csak a bennüktárolt érték másolódik át.

Az OOP nyelveken nem lehet olyan típus, amelyik nem OOP típus!

Ezért a nyelvek kétfajta objektum- típust támogatnak (kétféle módon lehetosztályt definiálni):• A „class” kulcsszóval -> referencia elv ű példány• A „struct”kulcsszóval -> érték típusú példány• Az enumeration típusú változók is érték típusúak.

Ha C#- ban a struct-al definiálunk egy példányt• Nem kell a new kulcsszót használni a példányosításhoz• Ha nem használjuk a new kulcsszót, akkor a struktúra mezőinek nem lesz

kezdőértéke (memóriaszemét)• Ilyen osztály mezőire nem használhatunk kezdőértékadást.• Ezért a struct- nak általában van konstruktora• A paraméter nélküli (default) konstruktort a nyelv automatikusan készíti.

Mi csak paraméteres konstruktort készíthetünk!• A struct típus korlátozott OOP tulajdonságokkal rendelkezik, pl. nem

működik rá az öröklődés semmilyen formában (egy struct nem lehet ő s,ő nem lehet gyermek)

• Ugyanakkor a struct őse is az Object• Viszont implementálhat interface-t• A példányosítás kevesebb memóriát igényel, mert a példány nem kerül +4

byte-ba a referencia tárolása miatt.

A nyelv alaptípusai (int, double, boolean, char, ...) mind struct típusúak,hogy a kifejezésekben a szokott módon használható legyenek.• Amíg a struktúra mez ő i nincsenek feltöltve, addig nem használható• Ezért az „ int a; ” deklaráció után az „a” változó még nem használható fel.

Az „ int a= 0; ” után már igen!• Ez akár az alábbi formában is írható: „ int a = new int();”

Referencia-elv el ő nyei:• garbage collector m ű ködését támogatja• nem pointer, ezért a memóriaterület áthelyezhet ő (windows memóri

akezelés mellett ez sűrűn elő fordul :) )Hátrányai:• Nem minden esetben egyértelm ű a viselkedés.

Pl:

class THallgato {public string nev;public int[] jegyek= new int[ 20];}class IskolaiOsztály{ private System.Collections.ArrayList tanulok; public THallgato this[ int index] { get { return (THallgato) tanulok[index]; } }

}

Ebben az esetben azt hihetnénk, hogy a csak olvasható property-ben visszaadott THallgato is csak olvasható lesz. De nem!

int[] vektor = new int[10];foreach( int a in vektor){a = 10; // nem megy, a ciklusváltozó csak olvasható!

} ------------------------------------------------------------THallgato[] vektor = new THallgato[10];foreach(THallgato a in vektor){a.nev = ”ismeretlen”;// m ű ködik, mert a ciklusváltozó csak egy ref.

}------------------------------------------------------------void akarmi( int a) // érték szerinti paraméterátadás{a = 10;

} int x = 20; akarmi( x);Console. WriteLine( x); // még mindig 20------------------------------------------------------------void akarmi( THallgato a)// érték szerinti paraméterátadás, de ref !{ a. nev = ”Jancsi”;

} THallgato x = new THallgato(); x. nev = ”Juliska”; akarmi( x);Console. WriteLine( x. nev); // ez már JancsiA string típus azonban referencia típusú, de az értékadás mindig új stringlétrehozását jelenti! ------------------------------------------------------------void akarmi(string a) // érték szerinti paraméterátadás,de ref !{a =”Jancsi”; // új string létrehozása !!!

} string x =”Juliska”;akarmi(x);Console.WriteLine(x.nev); //ez még mindig Juliska------------------------------------------------------------string a = ”Jancsi”;a =a. ToUpper();//ennek során valójában új string jön létre,//és a régi megszűnik (garbage collector !)

17

#14 (referencia-elv)

Page 18: Programozási nyelvek II. C# - aries.ektf.huaries.ektf.hu/~shad/feltoltesek/download/Prognyelv_II/NET2_cou3.pdf · A „virtuális gépi kódú” nyelv, melyre le kell fordítani

------------------------------------------------------------a=a+”és Juliska”; //ennek során új string jön létre, amely értéke

//a ”Jancsi és Juliska”,és a régi string megszűnik------------------------------------------------------------string a =”Jancsi”;Console.WriteLine(a+”és Juliska”);

A fv hívásakor (röptében) létrejön egy string, amely tartalmazza az összefűzöttszöveget, az „a” továbbra is csak a „Jancsi” szöveget tartalmazza a fv futásaután is (nem változott a referencia).

MemóriakezelésEgy számítógépes program alábbi változótípusokkal dolgozik:• statikus: egy „globális” memóriaterületen tárolódik az értéke, mely terület

a program indulásának pillanatában foglalódik le (allokálódik), és a pro-gram futásának befejésekor szabadítódik fel.o Ezen területen tárolt változók értékei a program futásának végéig

megőrződnek, ”biztonságban” vannak.o Ezen terület induló méretét a compiler számolja ki a programban definiált

statikus változók összmérete alapján.

• automatikusan dinamikus: a lokális változók és paraméterek. Ezen vál-tozók egy erre a célra fenntartott,szintén a program indulásának pillanatábanlefoglalt memóriaterületen a „stack” területen kerülnek elhelyezésre.o Ezen terület összmérete induláskor eldől,később nem növelhető.o Ha a terület elfogy, akkor „stack overflow error” futási hibával a program

kiakad (ez a rekurzív programoknál fordul elő gyakran)Az ilyen változók területfoglalását, és felszabadítását az alprogramba lépéskorés kilépéskor a programnyelv automatizmusa végzi el.• manuálisan dinamikus:A programozó pointereket vagy referencia típusú

változókat definiál, és a másodlagos adatterület lefoglalását és felszabadí-tását maga kezdeményezi megfelelő függvények hívásával(pl:malloc() és free() )o ennek hátránya, hogy

-- a programozó elfelejtheti lefoglalni a memóriát (ritkábban)-- elfelejti felszabadítani (sűrűbben)

• félmanuálisan dinamikus: A programozó referencia típusú változókat definiál, és a másodlagos adatterület lefoglalását maga kezdeményezi megfelelő függvények hívásával (pl: new), de a felszabadítását ő már nem kezdeményezheti. Erről a nyelv egy automatizmusa gondoskodik, agarbage collector.

Garbage collectorA garbage collector (szemétgyűjtő) egy algoritmus, amely megkeresi a memó-riában azokat az objektum-példányokat, amelyek a program egyetlen vál-tozójából kiindulva sem elérhetőek. Ezen memóriaterületek felkutatása nemminden esetben triviális feladat.Pl:

static void Kiiras(){Jatekos j = new Jatekos();...

}

A fenti sor hatására létrejön egy példány a memóriában, melynek a referenci-aszámlálója 1 lesz a fv futási ideje alatt. A fv- ből kilépéskora „j” változó meg-szűnik létezni, az általa hivatkozott példány referenciaszámlálója lecsökken 0-ra. Ezen esetekben a gc ezt a példány fel tudja szabadítani.

static void Kiiras(string s){...

} string nev = ”Jancsi”;kiiras(nev);

A fenti példában a fv futási idejére létrejön egy string változó (paraméter),mely szintén a „nev” változó területére vonatkozó referenciát tárol. A fvlefutása után ezen „s” megszűnik, de a példány nem felszabadítható, mert apéldányra van még referencia (a nev változó).

class Jatekosok{TJatekos[] vektor = new TJatekos[10];void JatekosHozzaad(){TJatekos j = new TJatekos();j.nev = ”Jancsi”;vektor[0] =j;

}}

Bár a fv lefutása végén a „j” megsz ű nik, de a példány nem felszabadítható,mert még mindig van rá referencia, a vektor 0. eleme.

void akarmi(){Jatekosok csapat = new Jatekosok();...

}

Ezen fv lefutása után megsz ű nik a csapat, ezzel együtt megsz ű nik avektor, amely több játékost tartalmaz. E miatt hirtelen n db Jatekos objektumpéldányra sz ű nik meg a referencia, ezeket mind fel lehet szabadítani.

static void Verem (){Object tomb = new Object[100]int vm = 0;void Push( object o) { tomb[ vm]= o; vm++; }object Pop() { vm--; return tomb[ vm]; }

}Verem v = new Verem();v. Push( new Jatekos() ); // tomb[ 0]- ba, ezért„megmarad”v.Pop(); // a tomb[ 0] ettől még őrzi !!!v.Push(new Jatekos()); // felülírja a tomb[ 0]- t,// a régi példány felszabadítható !object Pop() // javított POP(){vm--; object o = tomb[vm];tomb[vm] = null;return o;

}

---------------------------------------------------------------------------------------class TListaElem{TListaElem kov;string adat;

}

A fenti elemekből felépített láncolt lista elemeit azért nem szünteti meg a gc,mert az előző listaelem „köv” mezője hivatkozik rájuk.Egy láncolt lista törlésekor megtehetjük azt, hogy a listaelemek „köv” mezőjét„null”-ra állítjuk, mert ekkor megszüntetjük a következ ő listaelemre vonat-kozó referenciát, s ekkor a gc ezt észreveszi, és törli ezen elemeket.

A másik, egyszerűbb módszer, hogy a listafejnél töröljük az első elemre muta-tó referenciát „null” értékre állítással. Ekkor a lista első elemére már nem leszrámutató referencia, ezért ő t megszünteti a gc, de ekkor észereveszi, hogy alista második elemre mutató referencia is megsz ű nik, ezért ő t is törli, ...Ennek megfelelően a gc lassanként „megeszi” a teljes listát. Ezen módszernem kevésbé lesz hatékony, mint a „gondos programozó” módszere.

class TKetiranyuListaElem{TListaElem kov;TListaElem elozo;string adat;

}

A kétirányú láncolt lista elemei nemcsak előre, de vissza mutató referenciát istartalmaznak. Ekkor az utolsó elem kivételével minden listaelem hivatkozásiszámlálója 2, mert az i. elemre az i- 1 elem köv mezője, és az i+ 1 elem elozomezője is hivatkozik. A lista fej elemének „null”- ra állítása mellett az elemeknem megszütethetőek lesznek, mert az első listaelem referenciaszámlálójaekkor még csak 1- re csökken le, hiszen a 2. listaelemnek van egy elozomutatója, amely még hivatkozik rá.

A jó minőségű gc algoritmus képes az ilyen objektum-csoportok felkutatására,amely egy önnmagában zárt lánccsoportot alkot, de a program jelenleg élőváltozói közül egyik sem mutat a lánccsoport belsejébe. Ennek felfedezése ésa teljes csoport feltérképezése után a teljes csoport törölhető. A gc algoritmusbonyolultabb, sok változót tartalmazó programok esetén komoly keresztrefer-encia táblázatokat és összefüggőségi gráfokat épít fel futás közben, és kezel le.

Mikor mûködik a gc? Lehetőségek:• A gc akkor fut, amikor van rá egy kis id ő . Pl. a program jelenleg nem dol

gozik, mert eseményre vár (pl. adatbevitelre) (idle time).• A gc minden memóriafoglaláskor fut, a memória kiosztás előtt „szétnéz”,

hogy van-e felszabadítható hely.• A gc mindig fut, mert külön szálon indult el a program indulásának pil

lanatában, és folyamatosan gyüjtögeti az információkat.• A gc akkor fut, amikor a programozó elindítja// System.GC.Collect() metódus //.

Mit nem tud kezelni a gc?• Az érték típusú változókat (struct,...)• A pointereket

A pointerek használatára van lehet ő ség C#- ban, de ellentétes az gc elveivel.Ha a programozó C#- ban ragaszkodik a pointerek használatához, úgy magá-nak kell gondoskodni a memória felszabadításáról.

18

#15 (garbage collector)

Page 19: Programozási nyelvek II. C# - aries.ektf.huaries.ektf.hu/~shad/feltoltesek/download/Prognyelv_II/NET2_cou3.pdf · A „virtuális gépi kódú” nyelv, melyre le kell fordítani

Életciklus1. „live” státusz az objektumra a new operátor és a konstruktor hívása után2. „no longer in use” státusz, ha az objektum már soha többé nem lesz

elérhet ő a program futtatása során3. valamivel kés ő bb lefut a destruktor4. „inaccessible” státusz a destruktor lefutása után5. a gc felszabadítja a memóriát

using System; class A { ~A() { Console. WriteLine(" Destruct instance of A");

} } class B { object Ref; public B( object o) { Ref = o;

} ~B() { Console. WriteLine(" Destruct instance of B");

} }class Test{static void Main(){B b = new B( new A());b = null;GC.Collect();GC.WaitForPendingFinalizers();

}}A b=null hatására mind az „A” beli, mind a „B” beli példány„no longer in use” lesz. Hogy milyen sorrendben hívódik meg a destruk-toruk, az viszont nem egyértelmű (nem specifikált a nyelv leírásában).

using System; class A { ~A() { Console. WriteLine(" Destruct instance of A");

} public void F() { Console. WriteLine(" A. F"); Test. RefA = this;

} } class B { public A Ref; ~B() { Console. WriteLine(" Destruct instance of B"); Ref. F();

} }class Test{public static A RefA;public static B RefB;static void Main(){RefB = new B(); // B-re egy hivatkozásRefA = new A(); // A-ra egy hivatkozásRefB.Ref = RefA; // A-ra még egy hivatkozásRefB = null; // B-re nincs már hiv.RefA = null; // A-ra sincs már külső hiv!

//A and B now eligible for destructionGC.Collect();GC.WaitForPendingFinalizers();// B now eligible for collection,but A is

not if (RefA !=null)Console.WriteLine("RefA is not null");

}}

Az „A” és „B” destruktorok hívása csak „B” és „A” sorrendben jó, mert az„A”- ra van még m ű köd ő hivatkozás „B”- ből,melyet a „B” destruktorábanmég ki is használunk. Ha el ő ször az „A”- t szünteti meg a „gc”, akkor a „B”destruktorának közepén a Ref. F() fvhívás már futási hibát fog generálni !

Amennyiben az „A”- ban is van kereszthivatkozás „B”- re (alkalmasint az „A”destruktorában), akkor ezek megszüntetésének nincs jó sorrendje.

A programozási nyelv számára csak az van definiálva, hogy a memóriafel-szabadítást egyfajta garbage collector fogja végezni. A gc konkrét

mûködésérõl a nyelv maga nem rendelkezik!

Boxing és unboxing Ha van egy „value type” változónk (struct), és nekünk át kell alakítani referen-cia- típusúvá (mert olyan helyen akarjuk használni), akkor BOXING-t végzünk.

boxing = value -> reference

Pl:ArrayList r = new ArrayList(); int i= 10; r. Add( i);A fenti sor nem m ű ködik, mert az .Add metódus m ű ködését tekintvevalójában az átadott példány referenciáját menti el ... de az i változónak nincsreferenciája, mert ő nem referencia elv ű . A boxing során létrejön egy object osztályból származó példány, amely magá-ba foglalja (körülöleli) a struct-ban tárolt információt.

int i=10;object o = i; // boxingr.Add(o );

A létrejött példány lemásolja az i változó memóriaterületét, ezért az i értékekésőbb megváltoztatható, ez nem változtatja meg a létrejött object által tároltértéket:

ArrayList r = new ArrayList();int i=10;r.Add(i);i =20;... object o =r[0];i =(int)o; //i =10 újra //unboxing rövidebben írva:i = (int) r[0]; //i =10 újra //unboxing

UNBOXING a visszaalakítás, amikor az object-b ő l visszanyerjük a bennetárolt értéket. Természetesen ügyeljünk rá, hogy ugyanazon típusba kon-vertáljuk vissza a benne tárolt értéket.

object o =r[0];i =(int)o; //i =10 újra //unboxingi =(int)r[0]; //i =10 újra //unboxingMegj: A string típus nem value type!object = System.Objectint = System.Int32string = System.String...

EnumerationAz „enum” egyszerre állít el ő egy felhasználói típust, és konstansok soroza-tát: enum Color{Red, Green, Blue

}

Ezen típus felfogható úgy is, mint egy objektumosztály, és a benne lévő azo-nosítók az osztályon belül definiált konstansok lennének.

Ennek megfelelöen változót készíteni:

Color szin = Color.Red; //mintha static mez ő lenne !void Atszinez(Color ujszin){...

} ...Atszinez(Color.Green );...

Az enum típusú változó helyigénye automatikusan int. Ha ettől el akarunktérni, akkor meg kell adni az alaptípust:

enum Color:uint { Red ,Green , Blue }

Az azonosítók értékkel történ ő összerendelése automatikus:Red= 0, Green= 1, Blue= 2.Ez akkor derül ki, ha típuskényszerítjük:int kod = (int) szin;Az automatikus kiosztást meg lehet változtatni:enum Color enum Color{ {Red, Red, Green = 10, Green = 10,Blue red = Red} } 19

#16 (boxing és unboxing)

#17 (enumeration)

Page 20: Programozási nyelvek II. C# - aries.ektf.huaries.ektf.hu/~shad/feltoltesek/download/Prognyelv_II/NET2_cou3.pdf · A „virtuális gépi kódú” nyelv, melyre le kell fordítani

Kivételek A program bizonyos pontjain olyan szituációkkal találkozhatunk, amelyetakkor és ott nem tudunk lekezelni, de ugyanakkor a futást semtudjuk folytatni.Pl:

class Stack { ... object Pop() { if (vm> 0) { vm--; return tomb[ vm]; } else ??????; }

}

Ebben a kódrészletben megoldható lenne, hogy hangüzenetet adunk (beep),vagy kiírhatunk a konzolra egy hibaüzenetet (de mi van,ha windows- os a főprogram?), stb...

Az általánosan megírt objektum mit sem tud arról a környezetr ő l (a f ő pro-gramról), amibe beágyazva futtatják.Ezért mit sem tud a hibajelzés módjáról.

Ráadásul nagyon fontos, hogy az ő t futtató f ő program értesüljön a hibáról,mert lehet, hogy az számít a problémára, és fog is tudni vele mit kezdeni.

Ezen szituációk egy része a főprogramban megelőzhető lenne:

Stack v = new Stack();if (!v.Tele()) v.Pop();//csak akkor Pop(),ha van még elem!De némelyik nem (text file megnyitása):

using System.IO;StreamReader INI = new StreamReader( FName);A fenti példa megpróbál létrehozni egy StreamReader objektumot, amellyeljelen példában egy file-ból lehetne olvasni. De ha a file nem létezik, vagylétezik, de nem megnyitható (mert pl. nincs rá olvasási jogunk, vagy valakimás már megnyitotta), akkor a nyitás nem sikerülhet.

Hogyan jelezzen hibát a StreamReader konstruktora?

Megoldható lenne, hogy ekkor nem engedi létrehozni a példányt, és a futásután az „ INI ” változó „ null ” lesz.

De ez csak azt jelezné: „valami hiba történt”, és nem adna információt arról,hogy mi volt a hiba oka.

A legtöbb „régi” megoldás szerint majdnem mindent függvények formájábanírtak meg, és a függvény minden esetben egy egész számmal (egy hibakóddal)tért vissza.

Ez egy működő megoldás volt, de sok programozási pluszmunkát jelentett avisszaadott hibakódot „visszapasszolgatni” az egymást hívó függvényekmélységéből a problémát kezelő „felsõbb” szintû függvényig.

A megoldást a kivételek jelentik:• A kivétel egy jelzés, amelyet a programozó maga indít útnak.• A jelzés indítása után a függvények elkezdenek „visszatérni”

(terminálni) a hívó pontra (átugrován a még végrehajtatlan sorokat).• Ez a visszatérés mindaddig folytatódik, amíg valaki a jelzést el nem „fogja”.• Ha senki semfogja meg, akkor a program befejezheti a futását

(Console Application), vagy hibaüzenetet írhat ki, és folytathatja a futását (Windows Application).

throw - try - catch

double hanyados( int x, int y){if (y!= 0) return (double) x/ y;else throw new Exception(” Nullával nem tudok osztani!”);

}double sokhanyados(){double a = hanyados( 1,3);double b = hanyados( 2,0);double c = hanyados( 3,4); // ez már nem számolódik ki return (a+ b+ c)/ 3; // ez sem hajtódik végre

}void teszteles() {try {Console. WriteLine(” Átlaguk:{ 0}”, sokhanyados());

// ... ez itt már nem kerül végrehajtásra ...}catch (Exception e){Console. WriteLine(” Hiba:{ 0}”, e. Message;

}}

A System. Exception az általános hibajelenségek leírásakor használjuk.

A hiba okait típusuk szerint csoportosíthatjuk (pl.):System. ArithmeticExceptionSystem. ArrayTypeMismatchExceptionSystem. DivideByZeroExceptionSystem. IndexOutOfRangeExceptionSystem. InvalidCastExceptionSystem. NullReferenceExceptionSystem. OutOfMemoryExceptionSystem. StackOverflowExceptionSystem. TypeInitializationException.

Mindegyik speciálizált Exception osztálynak közösőse a System.Exception!De további ős-gyermek viszony is megfigyelhet ő a kivétel-osztályok között(kivétel-hierarchia)!

System.Object System.Exception

System.SystemException System.IO.IOException

System.IO.DirectoryNotFoundException.

Ha jobban akarjuk specifikálni a hibát, akkor specifikáltabb hibát dobhatunk!throw new FileNotFoundException(” A program INI file- jasehol!”);...catch (FileNotFoundException e) { ... }

Az „e” változó felveszi a létrehozott FileNotFoundException- ból létrehozottpéldány referenciáját, így az e. Message a szóban forgó üzenet lesz.

A metódusok leírásánal olvasható a HELP- ben, hogy hiba esetén azt hogyanjelzik:

public StreamReader( string path );

ExceptionsException Type ConditionArgumentException path is an empty string ("").ArgumentNullException path is a null reference (Nothing

in Visual Basic).FileNotFoundException The file cannot be found.DirectoryNotFoundException The directory cannot be found.IOException path includes an incorrect or

invalid syntax for file name,directory name, or volume label.

Ha egy metódus kivételt dob, akkor elkaphatjuk csak a bennünket érdeklőtípusú kivételeket, a többit továbbengedhetjük:

try{StreamReader r = new StreamReader(” program. ini”);

}catch (FileNotFoundException e) { ... }catch (DirectoryNotFoundException e) { ...}

Ekkor már működik a típuskompatibilitás:System.Object

System.ExceptionSystem.SystemException

System.IO.IOExceptionSystem.IO.DirectoryNotFoundExceptionSystem.IO.EndOfStreamExceptionSystem.IO.FileLoadExceptionSystem.IO.FileNotFoundExceptionSystem.IO.PathTooLongException

A catch (System.IO.Exception e) el fog kapni bármilyen olyan kivételt,amely kivételosztály bel ő le lett származtatva:

Ha elkaptunk egy kivételt, de később kiderül, hogy mégsem kellett volnaelfogni, akkor újra feldobhatjuk:

catch (FileNotFoundException e){ ...if ( ... ez nem ránk tartozik ... ) throw (e);

}

Ha nem akarjuk felhasználni a kivétel példányt, akkor nem muszáj változótdeklarálnunk:

catch (FileNotFoundException e){Console. WriteLine(” ajjaj!”); //itt szólni fog a C# fordító,

} // hogy az „e” változót nem használtuk felcatch (FileNotFoundException){Console. WriteLine(” ajjaj!”); // itt már nem

} 20

#18 (kivételek)

Page 21: Programozási nyelvek II. C# - aries.ektf.huaries.ektf.hu/~shad/feltoltesek/download/Prognyelv_II/NET2_cou3.pdf · A „virtuális gépi kódú” nyelv, melyre le kell fordítani

Befejezõ utasítások:Ha a metódus már belekezdett valamibe, és a metódus közepén keletkezett egykivétel, akkor elképzelhető , hogy a metódus nem engedheti el szó nélkül ezt akivételt:

void FTP_DOWNLOAD( string file_url){ ... hálózati kapcsolat kiépítése ...try{ ... ciklus amíg file nem nem tölt ő dött ...... file rész fogadása ...}catch (Exception e){ ... hálózati kapcsolat zárása ... //ha hiba volt, akkor is!throw (e); // a kivétel továbbdobása, a fv terminál}... hálózati kapcsolat zárása ...

}

try - finally

void FTP_DOWNLOAD(string file_url){ ... hálózati kapcsolat kiépítése ...try{ ... ciklus amíg file nem nem tölt ő dött ...... file rész fogadása ...}finally{ ... hálózati kapcsolat zárása ... //ha hiba volt, ha nem !} // a hiba ezek után magától továbbdobódik !

}

OperátorokAz objektum- osztályokhoz operátorokat is definiálhatunk. Ezzel a kidolgozás-sal közelíthetjük az objektum- osztályainkat a beépített típusok használ-hatóságához. Három fajta operátort adhatunk az objektum- osztályhoz:

- Unáris (egyoperandusú)- Bináris (kétoperandusú)- Konverziós (egyoperandusú)

public static MyClass operator+( MyClass a, MyClass b){return new MyClass( a. MyField + b. MyField);

}

Unáris operátorok

public static result-type operator unary-operator (op-type operand)

„result-type” kötelezően ugyanaz,mint az osztály típusa„unary-operátor” a következők egyike:+ - ! ~ ++ --„op-type” kötelezően ugyanaz, mint az osztály típusa„operand” a paraméter neve (szabadon választott)

class Datum{private int ev, honap, nap;

// (A) megoldás public static Datum operator ++(Datum x){return new Datum(x.ev,x.honap,x.nap+1);

}//(B)megoldás (még op.overloading mellett sem lehet mindkettõ

public static Datum operator ++(Datum x){x.nap++; return x;

}} Datum d = new Datum();d++; uaz mint d = Datum.++(d);

Bináris operátorok

public static result-type operator binary-operator(op-type operand,op-type2 operand2);

„result type” a művelet eredménye (’bármilyen’típus lehet)„binary operator” + - * / % & | ^ << >> == != > < >= <=„op-type”„op-type2” a két operandus típusai. Legalább az egyiknek az

osztály alaptípusának kell lennie„operand”„operand2” a két paraméter neve (tetszőleges).

class Datum{...public static Datum operator - (Datum d, int n){ return ...;}public static int operator - (Datum a,Datum b){ return ...;}

}

A fenti példa az operátor overloading miatt egy időben is létezhet egy osztá-lyon belül.

Konverziós operátorok

Az implicit konverziós operátort a C# fogja meghívni automatikusanha típusillesztésre (típuskonverzióra) van szükség

- függvény híváskor- értékadáskor- kifejezésekben- ...

Az explicit konverziós operátort a programozó által leírt típuskonverziókesetén használt.

public static implicit operator conv- type- out ( conv- type- in operand )public static explicit operator conv -type -out ( conv -type -in operand )

A konverzió a „conv- type- in” típusról „conv- type- out” típusra történik.Értelemszerűen a két típus közül az egyiknek az osztály típusának kell lennie.

class Datum{...public static implicit operator string(Datum d)

//Datum ->string{ return d.ToString();}public override string ToString()//egyébként is hasznos !!!!{ return ev.ToString()+"."+honap.ToString()+"."+nap.ToString();}

}

Datum d = new Datum();// implicit típuskonverzióstring s = d;

Ha „explicit” operátort írtunk volna, akkor a fenti sor:// explicit típuskonverzióstring s = (string) d;

// t. ToString() hívásaConsole. WriteLine( d);

// explicit típuskonverzió hívása,// majd a kapott típusra a ToString() hívásaConsole. WriteLine(( string) d);

DLLA DLL a Dynamic-Link Library a Windows alatt egy eljárás és függvény-gyűjteményt jelentett. Egy ilyen DLL több alkalmazáshoz is hozzátartozott.Ez helytakarékossági megfontolások, és update megfontolások miatt volt (aDLL-ben lévő hiba javítását minden őt használó alkalmazás megérezte).

.NET alatt ennél több: objektumok gyűjteménye.

DLL készítése: File / New Project / Class Library.

Egy DLL- ben nem lehet Main függvény.

A DLL-ben jellemző en osztályokat készítünk. De nem minden osztály leszmajd látható a DLL-en kívül:

public class Sakkfigura { ... }

A nem public class-ok a DLL-en kívül nem lesznek láthatóak.

class Sakkfigura { ... }public class Kiralyno:Sakkfigura { ... }

Ellentmondás: a Sakkfigura nem public, de a Királynő örököl(het) tőle publikusmezőket és metódusokat.Ezért ez nem megengedett.Vagy mindkettőpublikus, vagy egyik sem! 21

#20 (operátorok)

#21 (DLL írása)

Page 22: Programozási nyelvek II. C# - aries.ektf.huaries.ektf.hu/~shad/feltoltesek/download/Prognyelv_II/NET2_cou3.pdf · A „virtuális gépi kódú” nyelv, melyre le kell fordítani

public class Sakktabla{public Sakkfigura[,] tabla = new Sakkfigura[8,8];...

}

Szintén ellentmondás: ha a „Sakktabla” publikus, és a „tabla” is az,akkor a „Sakkfigura”- nak is annak kell lennie!

Ezen túl a védelmi szinteket ki lehet (kellett) egészíteni újakkal:internal: a projekten (DLL- en) belül úgy viselkedik, mintha publiclenne, de a DLL- en kívül nem látszik (mintha private lenne)

internal protected: az „internal” és a „protected” együtt, vagyisa projekten belül public, de kívülre csak akkor látszik, ha azttovábbfejlesztik.

Pl:

public class Kiralyno{internal int x;internal int y;internal protected void Athelyez( int ujx, int ujy){...

}}

DLL tesztelése (a)

A DLL-t nem lehet elindítani (nincs Main fv- e), de le lehet fordítani(BUILD / BUILD SOLUTION).Kell készíteni egy kis teszt programot, amely a DLL- ből exportált osztálybólkészít példányt,és azt működteti.

FILE /NEW /PROJECT /CONSOLE APPLICATION

Majd ezen APPLICATION-nak jeleznie kell,hogy az osztályt ebből a DLL-ből kell kivenni (ezt reference-nek nevezi a .NET):

PROJECT / ADD REFERENCEJobb oldalt fent:BROWSE gomb.

A megtalált DLL-t a .NET IDE bemásolja a Console Application forráskódjá-nak ./bin/Debug alkönyvtárába (ahova az .EXE-t is fordítja).

Ezek után már lehet is használni.

Nem elfelejtendő, hogy a DLL-ben a public class is egy namespace-ban van!!

DLL:

namespace MyDLL{public class Sakkfigura{...

}}

APP: BB

MyDLL.Sakkfigura bastya = new MyDLL.Sakkfigura();

vagy (nem kötelező – az Add Reference-n múlik!)using MyDLL;

DLL tesztelése (b)

SOLUTION: „megoldás” – projektek összessége (EXE+ DLL-ek)több projektből állhat

PROJECT: vagy egy DLL vagy egy EXEtöbb forráskódból állhat

Tesztelési javaslat:FILE /NEW /BLANK SOLUTIONFILE /NEW /PROJECT /CLASS LIBRARYFILE /NEW /PROJECT /CONSOLE APPLICATION (add to solution)Ekkor a Cons.App.-hoz szintén hozzá kell adni a DLL-t mint reference:PROJECT /ADD REFERENCE,de ekkor kattintsunk a Projects fülre (3. fülfent),itt a listában kiválaszthatjuk a DLL- t (a solution- n belülről).

Ekkor azt érjük el, hogy felváltva írhatjuk a teszt programot, és a DLL kódját(használva jobb oldalt a solution explorer-t), és amikor futtatni akarjuk a tesztprogramot,akkor előtte a fordító a DLL-t is újrafordítja.

A solution-hoz újabb ilyen kis teszprogramokat is adhatunk még hozzá,amelyek a DLL más-más képességeit tesztelik.

Ez esetben be kell állítani, hogy a solution két (vagy több) projektjébőlmelyiket akarjuk indítani akkor, amikor F5-t nyomunk (Debug/Start). Ezt úgykell megandi,hogy a solution explorerben a project-re kattintunk, és aPROJECT /SET AS STARTUP PROJECT menüpontra kattintunk. A solutionexplorer-ben a startup projectneve mindig vastag betűkkel van szedve (kiemelt).

Ha egy ilyen összetett solution-t készítünk, és ’másnap’folytatni akarjuk amunkát,a solution-t kell betölteni, nem valamelyik projectet! Az elkészültmunka már önállóan is futtatható: Másoljuk ki egy külön alkönyvtárba a DLL- t és a teszt EXE-t, és egyszerűenműködni fog. Ugyanis az EXE-ben el van tárolva, hogy neki még kell egyDLL is, és ezt a futtató rendszer (.NET framework) első esetben ugyanabbanaz alkönyvtárban keresi majd,ahol az EXE is van.

DELEGATEAz OOP-ban van mód CALLBACK típusú fv-ek készítésére. A CALLBACKfv azt jelenti, hogy idejekorán szólunk, hogy ha valamielőre látható eseménybekövetkezik,akkor indítsák el egy saját függvényünket.

Pl:van egy file-tömörítő objektum, amely hosszan fut egy filetömörítéseközben. Azt szeretnénk, hogy miközben fut a tömörítés,egy progress bar(folyatamjelző) jelezzen a képernyőn, hogy hol tarta tömörítés (hány száza-léknál). Két megoldás van: virtual-override,és a callback fv.

class ZIP{ public virtual void ProgressBar( int Percent) {}public void Becsomagol( string inputfile, string zipfile){...ciklus amíg nincs készProgressBar( szazalek );

...cvége...

}...

}/// és nem abstract a ProgressBar fv !!és ha mi azt akarjuk, hogy a százalék látszódjon:

class MyZIP: ZIP{ public override void ProgressBar( int Percent){ Console.WriteLine(”A csomagolás {0}%- nál jár!”,Percent);}

}// és utána a MyZIP- et használni csomagolásra!Ennek a megoldásnak az a hátránya, hogy folyton származtatni kell, és renge-teg VMT tábla készül, sokat kell programozni.

delegate void PercentCallback( int);// a callback fv prototípusaclass ZIP{ public PercentCallback callbackproc = null;public void Becsomagol( string inputfile, string zipfile){...ciklus amíg nincs kész

if (callbackproc!= null) callbackproc( szazalek );...cvége

...}...

}

majd: (példány szintű callback)

class AkarmilyenOsztaly:AkarmilyenOs{ public void KijelezSzazalek(int szazalek){ Console.WriteLine(”A csomagolás {0}%-nál jár!”,Szazalek);}

} végül pedig:AkarmilyenOsztaly a =new AkarmilyenOsztaly();ZIP z =new Zip();

z.callbackproc =new PercentCallback(a.KiejelezSzazalek);Erről a Garbage Collector-nak is tudnia kell,mert ha egy példány kellős köze-pére van egy callback visszahívás kérés, akkor a példányt nem szabad egszün-tetni még akkor sem, ha a példányra egyébként nincs hivatkozás (pl.a „a”változó már megszűnt, de a „z” még él, és a „z”-ből van callback az „a”példány egyik metódusára!!!)

22

#21 (delegate-callback)

Page 23: Programozási nyelvek II. C# - aries.ektf.huaries.ektf.hu/~shad/feltoltesek/download/Prognyelv_II/NET2_cou3.pdf · A „virtuális gépi kódú” nyelv, melyre le kell fordítani

Callback fv lehet osztály-szintű metódus is:

class AkarmilyenOsztaly:AkarmilyenOs{ public static void KijelezSzazalek(int szazalek){Console.WriteLine(”A csomagolás {0}%-nál jár!”,Szazalek);

}} //ilyenkor a GC-nek nem kell figyelni ...végül pedig:

ZIP z = new Zip();

// callback eljárás regisztrálásaz. callbackproc =new PercentCallback( AkarmilyenOsztaly.KijelezSzazalek);z. Becsom(” c:\\ nagyfile. dat”,” c:\\ nagyfile. zip”);

// callback eljárás unregisztrálásaz. callbackproc = null;z. Becsom(” c:\\ nagyfile. dat”,” c:\\ nagyfile. zip”);

class ZIP{ public ArrayList procs = new ArrayList();public void Becsomagol( string inputfile, string zipfile){...ciklus amíg nincs készforeach (PercentCallback p in procs) procs( szazalek );...cvége

...}

...}

ZIP z = new Zip();

//callback eljárások regisztrálásaz.procs.Add(new PercentCallback(AkarmilyenOsztaly.KijelezSzazalek));z.procs.Add(...);//a felsorolt eljárások mindegyike meg fog hívódni a foreach ciklusban (eztmajd később eseménykezelésnek hívjuk!)

// probléma: az ArrayList nem ellenőrzi, hogy valóban csak PercentCallbacktípusú dolgokat adtunk-e hozzá,ami típus- inkompatibiltási kivételt fog dobnia foreach ciklusban!

class ZIP{ private ArrayList procs = new ArrayList();public RegisterCallback(PercentCallback proc){ procs.Add(proc);}

} z.RegisterCallback(

new PercentCallback(AkarmilyenOsztaly.KijelezSzazalek));

E jegyzet másolata nem használható fel szabadon,az előadásanyagának kivonata. Ezen teljes jegyzetről,vagy annak bárme-ly részéről bármely másolat készítéséhez a szerző előzetesírásbeli hozzájárulására van szükség. A másolatnak tartalmaz-nia kell a sokszorosításra vonatkozó korlátozó kitételt is.A jegyzet kizárólag főiskolai oktatási vagy tanulmányi célrahasználható! A szerző hozzájárulását adja ahhoz, hogy az EKFszámítástechnika tanári, és programozó matematikus szakján, atárgyat az EKF TO által elfogadott módon felvett hallgatókbármelyike, kizárólag saját maga részére,tanulmányaihozegyetlen egy példány másolatot készítsen a jegyzetből. Ajegyzet e változata még tartalmazhat mind gépelési, mindhelyességi hibákat. Az állítások nem mindegyike letttesztelve teljes körűen. Minden észrevételt,amelyvalamilyen hibára vonatkozik,örömmel fogadok.

Hernyák Zoltá[email protected]

23

Page 24: Programozási nyelvek II. C# - aries.ektf.huaries.ektf.hu/~shad/feltoltesek/download/Prognyelv_II/NET2_cou3.pdf · A „virtuális gépi kódú” nyelv, melyre le kell fordítani

#

Page 25: Programozási nyelvek II. C# - aries.ektf.huaries.ektf.hu/~shad/feltoltesek/download/Prognyelv_II/NET2_cou3.pdf · A „virtuális gépi kódú” nyelv, melyre le kell fordítani

#