SD Forum™
Септембар 03, 2010, 05:52:29 *
Добродошли, Гост. Молим вас пријавите се или се региструјте.
Да нисте изгубили свој активациони e-mail?

Пријавите се корисничким именом, лозинком и дужином сесије
Вести: Пре него напишете чланак, молимо проверите да ли се сећате српског правописа.
 
   Почетна   Помоћ Пријава Регистрација  
Странице: [1]   Иди доле
  Пошаљи ову тему  |  Штампај  
Аутор Тема: Objašnjenje: Eventi i Delegati  (Прочитано 1616 пута)
toxi
Гост
« послато: Април 13, 2007, 19:52:05 »

Rešio sam da napišem par tutoriala o naprednim( specializovanim) oblastima programiranja na .NET platformi( a ovo je prvi od njih), koristeći C sharp( C#) jezik. Samim tim za razumevanje ovih tutoriala biće vam potrebno osrednje znanje C# jezika ali prevashodno znanje o .NET platformi uopšte.

"Oblasti" koje ću objasniti su one koje su, u literaturi na Srpskom jeziku, vrlo šturo i nerazumljivo objašnjene( što zbog lošeg pristupa samih autora, što zbog katastrofalnog "književnog" prevoda tih knjiga.
Prvi u nizu tih tutoriala ipak nespada u "napredne" oblasti .NET programiranja, ali sam a napisao jer je "sistem" događaja( events) i delegata( delegates) obično vrlo nerazumljivo objašnjen u knjigama na našem jeziku, a od esencialne je važnosti za OO programiranje.





Eventi i Delegati

Eventi i delegati su međusobno u vezi, jer za pravljenje eventa je potreban neki delegat. Ako ste programirali bilo šta na jeziku C ili C++ onda vam je jasno šta je to pokazivač na funkciju. Na .NET platformi nepostoji( ni u jednom jeziku) takav pokazivač a zamena za njega je delegat. Pa čemu u stvari služi delegat? Pomoću delegata možete, posredno, pozvati jednu ili više funkcija. Možda ovo deluje kao bespotrebna stvar ali uopšte nije tako. Jednostavan primer je funkcija za na primer sortiranje nekog niza. Obično takve funkcije zahtevaju dva argumenta- prvi je niz koji treba da se sortira a drugi argument bi bila funkcija koja će određivati da li je jedan element niza veći od drugog( videćete primer za ovo kasnije). Da bi ste predali funkciju koja će vršiti poređenje morate koristiti delegate( pošto je nemoguće koristiti pokazivače na funkciju). Ako vam ipak nije jasno šta su to delegati, shvatićete u sledećem odeljku.

Kreiranje i korišćenje delegata
Jasno vam je da je, kod klasa, potrebno napisati definiciju klase da bi ste bili u mogućnosti da napravite objekte te klase. Iako delegati nisu "obične" klase, i za njih važi gornje pravilo. Dakle najpre je potrebno definisati klasu delegata, a opšti oblik za definisanje klase delegata je ovaj:

[oblast-dostupnosti] delegate povratni-tip  ime-klase-delegata ( [tip-parametra] [tip-parametra2] ***);

Objašnjenje ovoga:
 oblast-dostupnosti Kao i kod klasa, nivo do kojeg je dostupna na krišćenje klasa delegata. Npr. može biti private, public itd.

 delegate Ključna reč koja govori kompajleru da je u pitanju definicija klase delegata.

 povratni-tip Da bi neka funkcija bila pozvana pomoću određenog delegata, povratni tip te funkcije i povratni tip tog delegata moraju biti isti. Takođe lista parametara funkcije i delegata moraju biti isti( što je i logično).

 ime-klase-delegata Svoju klasu delegata možete nazvati kako vam volja. No, po standardu imena delegata bi trebalo da se završavaju rečju "Handler", pa bi ste to trebali da ispoštujete.
 
[tip-parametra1] [tip-parametra2] *** Svaka funkcija ima listu parametara koji moraju da joj se predaju pri njenom pozivu. Broj tih parametra naravno sami određujete. Isto to radite i ovde, kada definišete klasu delegata.

Dakle, da biste pozvali neku funkciju posredstvom delegata trebate uraditi sledeće:
  -definišete klasu delegata, u kojoj ćete navesti ime te klase, povratni tip i listu parametara koju mora da ima funkcija da bi bila pozvana pomoću tog delegata. To jest, njihov tkz."potpis" mora biti identičan.
  -napravite objekat( delegat) te klase delegata, pri tom predajući mu referencu funkcije koju bi trebao da pozove.
  -i zatim pozvati samu funkciju koristeći taj delegat.

Sledi demonstracija.
Код:
public delegate void Pr_Delegata( double br1, double br2);

//definisanje klase koja će imati dva metoda koji će biti pozivani pomoću delegata
class MojaKlasa
{
//funkcija koja sabira dva broja
public void Zbir(double a, double b)
{
Console.WriteLine(" Nalazimo se u funkciji Zbir()");
Console.WriteLine(a + " + " + b + " = " + (a*b) );
}

//funkcija koja racuna proizvod dva broja
public static void Proizvod(double a, double b)
{
Console.WriteLine(" Nalazimo se u funkciji Proizvod()");
Console.WriteLine(a + " + " + b + " = " + (a*b) );
}

//ovo funkcija nemože biti pozvana pomoću klase delegata "Pr_Delegata", jer im se "potpis" razlikuje
public void HelloWorld()
{
Console.WriteLine(" Nalazimo se u funkciji HelloWorld()");
}


static void Main(string[] args)
{
//napravi delegat i dodeli mu da poziva funkciju Zbir()
MojaKlasa bzw = new MojaKlasa();
Pr_Delegata prDel = new Pr_Delegata( bzw.Zbir );
//pozovi funkciju koristeci delegat
Console.WriteLine("Pozivam funkciju Zbir() koristeći delegat");
prDel( 12.789, 789);
//pozovi funkciju direktno
Console.WriteLine("Pozivam funkciju Zbir() direktno");
bzw.Zbir(1, 5);

Console.Read();
}
}

Razmotrimo kod u funkciji main(). Prvo smo napravili objekat klase MojaKlasa i nazvali ga "bzw". Sledećom linijom koda smo napravili delegat tipa "Pr_Delegata" nazvavši ga prDel i kao jedini parametar konstruktoru smo predali funkciju bzw.Zbir(). Primetićete da smo napisali "bzw.Zbir" - bez zagrada, na taj način se dobija referenca( adresa) funkcije, a upravo to je ono što konstruktor delegata očekuje( šta je referenca bi trebalo da znate pa to nećemo objašnjavati). Na ovaj način smo omogućili delegat prDel da poziva funkciju Zbir() objekta bzw. Linija koda:
Код:
prDel( 12.789, 789);
pretstavlja sam čin pozivanja funkcije na koju pokazuje delegat( u ovom slučaju to je funkcija bzw.Zbir() ).

Postoji još jedna bitna stvar, delegati mogu pozivati više od jedne funkcije! Niste ograničeni u broju funkcija koje će delegat pozvati. No, sve funkcije koje će određeni delegat pozvati sačinjavaju "listu poziva" tog delegata. Da biste dodali neku funkciju toj listi koristite operator +=, a za uklanjanje neke od funkcija iz te liste koristite operator -=. Za demonstraciju ovoga potrebno je modifikovati kod funkcije main() iz gornjeg primera( sve ostalo ostaje isto).
Код:
static void Main(string[] args)
{
//napravi delegat i dodeli mu da poziva funkciju Zbir()
MojaKlasa bzw = new MojaKlasa();
Pr_Delegata prDel = new Pr_Delegata( bzw.Zbir );
//listi poziva prDel dodaj i statičku funkciju MojaKlasa.Proizvod()
prDel += new Pr_Delegata( MojaKlasa.Proizvod);
//pozovi funkciju koristeci delegat
Console.WriteLine("Pozivam funkcije koje se nalaze na listi poziva delegata");
prDel( 12.789, 789);
//pozovi funkciju direktno
Console.WriteLine("\nPozivam funkciju Zbir() direktno");
bzw.Zbir(1, 5);

Console.Read();
}
Napominjem( mada je očigledno), linijom "prDel( 12.789,  789)" pozivaju se sve metode koje se nalaze na listi poziva prDel delegata( i to onim redom kojim ste ih dodavali) i svakoj se od tih metoda prosleđuju isi parametri( dakle 12.789 i 789). Ostali deo koda  bi trebalo da vam je jasan.

O event-ima( događajima) uopšte
Da biste bili u mogućnosti da implementirate evente potrebno je da prvo shvatite šta su oni uopšte. Na primer, u windows aplikacijama, kada kliknete na nekog dugme u prozoru vi aktivirate događaj "OnClick", a kao rezultat toga desiće se neka radnja( recimo prozor će se zatvoriti). A šta se događa "u pozadini" svega toga? Slikovito rečeno, aplikacija detektuje da ste pritisnuli određeno dugme, to dugme na koje ste kliknuli "podiže" događaj "OnClick", a taj događaj poziva neku metodu koja će uraditi nešto korisno( na primer može zatvoriti aplikaciju ili ispisati neki tekst i u opšte bilo šta). To koja će metoda biti pozvana određuje programer, kao i sam kod te metode. Pređimo na samo kreiranje događaja.

Pravljenje eventa
Da biste napravili event najpre vam je potreban delegat koji će biti korišćen za taj event, ali o tome ćemo kasnije. Za deklarisanje eventakoristi se ključna reč event a "šablon" za kreiranje eventa je:
[oblast-dostupnosti] event ime-klase-delegata  ime-eventa;
pri čemu je:
 [oblast-dostupnosti] isto kao i kod delegata
 event Ključna reč koja govori kompajleru da je u pitanju definicija eventa.
 ime-klase-delegata Potrebno je navesti ime klase delegata koja će biti korišćena za određeni event.
 ime-eventa Možete nazvati svoj event kako god vam volja ali po standardu se ispred imena event stavlja rec "On" a prvo slovo iza toga je veliko. Npr. "OnClick" ili "OnClosing".

Napravimo sada jedan event( najpre će nam biti potrebna neka klasa delegat), evo koda:
Код:
//klasa delegata
public delegate void NekiDelegatHandler(int x);

//event zasnovan na pređasnjoj klasi delegata
public event NekiDelegatHandler OnNekiEvent;

Sada znate da napravite event. Dalje njegovo korišćenje je gotovo identično načinu na koji se koriste delegati, a sada ćemo napisati jedan program koji koristi evente i objasniti njihovu upotrebu.
Kao primer napisaćemo jednostavnu klasu Automobil. Ono će imati dve metode- jednu koja će ispisivati koliko je benzina preostalo u rezervoaru i jednu kojom će automobil preći određeni broj kilometara. Definicija te klase izgleda ovako:
Код:
using System;

//klasa delegata
public delegate void FuelOutHandler(int x);


public class Automobil
{
//event zasnovan na "FuelOutHandler" klasi delegata
public event FuelOutHandler OnFuelOut;
//benzin u rezervoaru
private int benzin = 60;

public int Benzin
{
get
{
return benzin;
}
}
//funkcija kojom se automobil kreće nekoliko kilometara
public void VoziAuto( int duzina)
{
//ovde ćemo kasnije dodati kod
//...
}

public static void MojaFunkcija(int x)
{
//ovde ćemo kasnije dodati kod
//...
}

static void Main(string[] args)
{
Automobil auto = new Automobil();
Console.WriteLine("U automobilu ima " + auto.Benzin + " litara benzina");

Console.Read();
}
}


Ovde je sve jasno, napravili smo klasu delegata a zatim smo koristeći nju napravili public event "OnFuelOut" za klasu Automobil. Međutim, ova klasa neradi ništa korisno a i kod za funkciju VoziAuto() i MojaFunkcija() nemaju kod jer njega ćemo tek dodati, prvo da razjasnimo šta želimo da uradimo.
Klasa automobil ima privatni član "benzin" koji signalizira koliko goriva ima u rezervoaru. Funkcija "VoziAuto" će simulirati vožnju automobila, a parametar koji joj se predaje govori koliko kilometara auto treba da pređe. Pretpostavićemo da auto troši litar benzina po pređenom kilometru. S toga ako se zada da auto pređe recimo 100 kilometara, neće biti dovoljno goriva i u tom slučaju ćemo izazvati event( događaj) OnFuelOut. Event OnFuelOut ćemo prethodno iz main() funkcije podesiti da poziva neku funkciju, na primer funkciju MojaFunkcija().
Shodno tome prvo ćemo izmeniti kod u funkciji VoziAuto(), i sada on izgleda ovako:
Код:
public void VoziAuto( int duzina)
{
Console.WriteLine("Pokrenuli ste auto planirajuci da predjete " + duzina + " km");
if( duzina <= benzin)
{
benzin -= duzina; // izracunaj kolko je benzina ostalo
Console.WriteLine("Uspesno ste se prevezli, ostalo vam je jos " +
benzin + " litara benzina.");
}
else //nema dovoljno benzina
OnFuelOut( duzina - benzin); //samo proizvedi( podigni) događaj
}
Primetićete da se sistem poziva funkcija pomoću delegata i eventa uopšte nerazlikuje.

main() izmenite da izgleda ovako:
Код:
static void Main(string[] args)
{
//napravi objekat klase Automobil
Automobil auto = new Automobil();
Console.WriteLine("U automobilu ima " + auto.Benzin + " litara benzina");

//podesi koje funkcije treba da pozove OnFuelOut event
auto.OnFuelOut += new FuelOutHandler( Automobil.MojaFunkcija);

//prevezi auto 30 kilometara
auto.VoziAuto( 30);
//pokusaj ponovo da prevezes auto 50 kilometara
auto.VoziAuto( 50);

Console.Read();
}
Ovde bi trebalo da vam je sve jasno, uz napomenu da je u ovom slučaju funkcija koju će pozivati događaj "OnFuelOut" definisana u istoj klasi kao i sam događaj što naravno nemora da bude slučaj( šta više za ovim gotovo nikada nećete imati potrebe). Ostaje još da se izmeni kod za MojaFunkcija(), dodajte nešto poput ovoga:

Код:
public static void MojaFunkcija(int x)
{
Console.WriteLine("  Niste uspesno stigli na vasu destinaciju jer vam je zafalilo "+
x + " litara benzina");
}




Sada znate da koristite i pravite delegate i evente. Oni igraju veoma bitnu ulogu u OO programiranju, a primenljivaćete ih dosta kod pravljenja GUI aplikacija. Treba napomenuti da u .NETu postoji mnogo veće definisanih klasa delegata a osnovni je "EventHandler". Zajedničko za sve njih jeste da uzimaju dva parametra- prvi je tipa "object" i govori koji objekat je proizveo događaj a drugi parametar je objekat klase "EventArgs" ili klase izvedene iz nje i on poseduje informacije koje opisuju neki događaj. Vaši delegati bi trebali da poštuju ovaj "princip".
Na primer da bismo ispoštovali gore rečeno bilo bi potrebno da modifikujemo funkciju "MojaFunkcija()" da izgleda ovako:

public static void MojaFunkcija( object sender, FuelEventArgs e)
{
//kod...
}

gde je FuelEventArgs klasa izvedena iz klase System.EventArgs. Ova promena povlači i menjanje definicije klase delegata i još neke manje izmene. Predlažem vam da potrebne izmene uočite i da napišete ceo kod da radi u skladu sa njima, biće vam dobra vežba.

Patriota
Сачувана
Странице: [1]   Иди горе
  Пошаљи ову тему  |  Штампај  
 
Пребаци се на:  

Покреће MySQL Покреће PHP Powered by SMF 1.1.11 | SMF © 2006, Simple Machines LLC Исправан XHTML 1.0! Исправан CSS!
Страница је направљена за 0.101 секунди са 22 упита.

Google последњи пут посетио Август 30, 2010, 18:07:13