Delegate och Event, ett exempel i C#
Förord
Har börjat min resa mot att Certifiera mig i MCTS och har börjat läsa i några böcker jag har här hemma. När jag repeterade avsnittet om Delegate och Event kom jag ihåg att jag tyckte det vara ganska rörigt att greppa. Därför tänkte skriva en artikel som kan hjälpa er som är nya inom C#.Innehåll
»»
»
»
»
»
Relaterade artiklar
Inledning
Anledningen till att man vill använda en egendefinierad delegate och event är att man vill att objekt ska kunna exekvera kod om en viss händelse har inträffat. När en händelse kastats i ett objekt kan det finnas situationer där andra objekt behöver exekvera kod. För att objektet, där händelsen kastats, inte ska behöva veta vilka objekt som lyssnar på denna händelse, har man frikopplat det med ett objekt som kallas en delegate. (Bygger på observer pattern)Jag kommer visa ett enkelt exempel där detta behandlas.
Här är några ord ni kommer behöva känna till:
- Event
En händelse, som andra objekt kan lyssna till.
- Delegate
Ett objekt som används för att koppla ett event till andra objekt som lyssnar och som vill att deras kod ska exekveras när eventet inträffat. Delegaten kommer att kunna lagra metoder från olika objekt. Dessa metoder (Event Handlers)kommer exekveras i tur och ordning när eventet kastats. De ligger i en lista hos delegaten och man kan addera metoder till listan om de har samma parameterlista som finns i delegate deklarationen.
(* En delegate i .NET beter sig egentligen så att när den deklareras så skapar .NET en klass som är sealed och så har samma namn som man angav delegate deklarationen. Klassen kommer automatiskt ha metoder för att hantera en lista av metoder dvs listan på metoder som ska exekveras i subscriber klasserna)
- Publisher class
En klassen från där eventet kastas
- Subscriber class
En klassen som lyssnar efter eventet. Den prenumererar på den och har metoder som kommer exekveras när eventet har kastas.
- EventHandler
En metod i subscriberklassen som exekveras när eventet kastats.
- EventArgs
En klass man ofta ärver från man använder parametrar till delegaten.
Exempel
Som exempel tänker jag beskriva en förenklad situation av man spelar brännboll. Tänk er att man vill skriva ett program där man simulerar bollen efter ett brännbolls slag. Vi tar händelsen som inträffar när bollen redan är slagen och är i luften. Brännaren är redo, domaren är redo. Om bollen är nära brännar vill han/hon naturligtvis fånga den. Om bollen slogs snett vill domaren blåsa i sin pipa. Nu har vi ett uppslag som kan användas för att skapa koden till exemplet.Vi har 3 objekt:
1. Boll: Bollen kommer vara objektet som kastar händelsen: dvs raise Event. Där den talar om att nu är jag i luften.
2. Brännare: är ett objekt som vill veta om bollen är i luften, därför vill det objektet subscribe på eventet.
3. Domare: Är också ett objekt som vill veta om bollen är i luften, därför vill det objektet subscribe på eventet.
För att domaren och brännaren ska veta var bollen befinner sig kommer vi skicka med lite information om bollens höjd och den vinkeln bollen har i förhållande till baslinjen (Naturligtvis räcker inte det egentligen men bara som exempel...). För att kunna skicka med det data kommer vi behöva använda parametrar som skickas med från Bollobjektet till Brännaren och Domaren. Vi använder oss av en klass med argument för att visa användning av det.
Koden till Exemplet:
Jag har valt att dela upp koden i 3 delar; event, delegate, main.
Event delen
Här finns klassen som är vår publisher, dvs Ball klassen. Här ligger även event deklarationen. I metoden RaiseEvent kommer man att kasta ett event som triggar igång hela kedjan med anrop till de Event Handlers som finns. Eftersom vi valde att ha parametrar som ska skickas med skapar vi en instans av en klass vi deklarerar i ”Delegete delen”. Det klass objektet kommer hålla data om höjd och vinkel.
///
/// denna klass är den klass som kastar eventet och även skapat objektsinstansen som håller typerna som är parametrar;
/// hight och angle som skickas med.
///
class Ball
{
//BallInAir är namnet på själva Eventet.
//MyDelegate är namnet på delegaten som används för att anropa Event Handler metoderna
public event MyDelegate BallInAir;
//Denna metod är bara för att ha en startpunkt
public void StartMe(int hight, int angle)
{
//Skapa en ny instans av klassen som lagrar parameter värdena för höjd o vinkel
MyEventArgs ArgsToSend = new MyEventArgs(hight, angle);
//Anropa metoden som kastar eventet och sen skickar vi med parametrarna via objectet ArgsToSend
RaiseEvent(ArgsToSend);
}
void RaiseEvent(EventArgs e)
{
if (BallInAir != null)
{
//Dax att kasta eventet
BallInAir(this, e);
}
}
}
Delegate delen
Här deklareras delegate med parametrarna. (observera att den ligger utanför klassendeklarationen.)Här ligger även klassen för hantering av parametern e. Den klassen ärver av EventArgs.
///
/// Detta är deklarationen på min delegate som hanterar eventet som kastas
/// (.NET kommer skapa en klass som är sealed baserat på deklarationen när delegate
/// instansieras, det förklarar delegates deklarations struktur)
///
public delegate void MyDelegate(object sender, EventArgs e);
///
/// En instans av denna klassen kommer användas för att skicka med när eventet triggas.
/// klassen ärver av EventArgs som ofta används, det är en fördefinerad klass.
///
class MyEventArgs : EventArgs
{
public int MyHight
{
get{ return myHight;}
}
public int MyAngle
{
get { return myAngle; }
}
int myHight;
int myAngle;
///
/// Denna konstruktor används för att sätta det data vi vill skicka med
/// när eventet kastas. Denna skapas i den klass som kastar eventet.
///
public MyEventArgs(int hight, int angle)
{
this.myHight = hight;
this.myAngle = angle;
}
}
///
/// Denna klass är en subscriber.
/// Den tillhandahåller EventHandler metoden TryToCatch.
///
class Catcher
{
///
/// Detta är EventHandler metoden dvs en metoden som kommer exekveras när eventet är kastat.
/// OBS!Parametrarna måste stämma med de som deklarerades med delegaten MyDelegate
///
public void TryToCatch(object sender, EventArgs e)
{
/* Här castar vi om in parametern ”e” till att vara en MyEventArgs. Då vi kan använda våra
egna uppsatta variabler i den klassen MyEventArgs som tex MyHight.
(Eftersom vi ärver av EventArgs är denna cast inga problem)*/
MyEventArgs myArgs = (MyEventArgs) e;
if (myArgs.MyHight < 2)
{
//ToDo:försök fånga bollen-kod :)
}
}
}
///
/// Denna klass en Umpire.
/// Den tillhandahåller EventHandler metoden CheckOutOfBounce.
///
class Umpire
{
///
/// Detta är Event Handler metdoden dvs en metoden som kommer exekveras när eventet är kastat.
/// OBS!Parametrarna måste stämma med de som deklarerades med delegaten MyDelegate
///
public void CeckOutOfBounce(object sender, EventArgs e)
{
/*Här castar vi om in-parametern ”e” till att vara en MyEventArgs, så vi kan använda våra
egna uppsatta variabler i den klassen MyEventArgs som tex MyAngle.
(Eftersom vi ärver av EventArgs är denna cast inga problem)*/
MyEventArgs myArgs = (MyEventArgs)e;
if (myArgs.MyAngle > 180)
{
//ToDo: Blås i pipan-kod
}
}
}
Main delen
Här är programstarten. Vi skapar instanser av alla objekt och sen kopplar vi event handler metoder till eventet via en delegate.
class MyProgram
{
///
/// The main entry point for the application.
///
[STAThread]
static void Main()
{
//Skapa instanser av de objekt vi skulle ha
Ball theBall = new Ball();
Catcher theCatcher = new Catcher();
Umpire theUmpire = new Umpire();
//Nu lägger vi till alla EventHandler metoder som ska exekveras. Det görs med "+="
//Kan göras med new eller direkt tilldelning
theBall.BallInAir += new MyDelegate(theCatcher.TryToCatch);
theBall.BallInAir += new MyDelegate(theUmpire.CeckOutOfBounce);
//Dax att kasta eventet, genom att anropa en start metod i Ball klassen
theBall.StartMe(15, 88);
}
}
Pelle Johansson
Keep up the good work och hoppas vi får se mer från dig!
Håkan Sjöberg
Väldigt bra exempel. Några frågor bara. 1: Vad gör [STAThread]? 2: Varför gör du inte delgaten direkt med din egna MyEventArgs så att du slipper kasta? mvh Håkan