Noch mehr erweitertes HelloWorld!

Wie versprochen, werde ich mit dem erweiterten HelloWorld noch Ereignisse erklären. Damit wir wissen, wie Ereignisse funktionieren, müssen wir zuerst wissen, was Delegates sind. Delegates kann man sich als Array von Methoden, die auszuführen sind, vorstellen. Ein Delegat (engl. Delegate – „Abgeordneter, Abgesandter“, also ein „Laufbursche“ der Arbeit verrichten soll) kann mit Verweisen auf Methodengefüllt werden. Wenn man das Delegat „ausführt“, werden alle darin befindlichen Verweise auf die Methoden ausgeführt. Wie Hilft uns das bei Ereignissen? Zunächst, Ereignisse treten in Programmen auf, wenn etwas besonderes passiert. Zum Beispiel wenn eine bestimmte Anzahl überschritten wurde, wenn etwas fertig ist usw. Und wenn solch ein Ereignis eintritt, wollen wir natürlich, dass etwas passiert. Das könnte man auch „hardcoden“, doch spätestens wenn es mehrere (nicht statische) Klassen in einem Projekt gibt, braucht man die Ereignisse. Wenn ich von Klasse A aus, eine Methode in Klasse B aufrufe, und in B ein Ereignis stattfindet, muss A das irgendwie mitbekommen. Und jetzt wird es interessant… Das geschieht nicht irgendwie, man muss es nur einmal richtig verstehen. Wenn ein Ereignis ausgelöst wird, löst das die Ausführung eines Delegates aus. Durch das Delegate können verschieden viele Anweisungen ausgeführt werden, die von mehreren Klassen aus hinzugefügt werden können. Das entsprechende Delegat wird als der EventHandler bezeichnet. Er wird in jedem Fall, beim auslösen des Ereignisses, ausgeführt. Will Klasse A also dass etwas getan wird, wenn das Ereignis in Klasse B eintritt, muss der EventHandler befüllt werden. Wie das konkret geht, sehen wir hier:

delegate void TestHandler();
static void DelegateTest()
{
 TestHandler th = new TestHandler(DoSomething);
 th += new TestHandler(DoSomething);
 th += new TestHandler(DoSomething);
 th();
}
static void DoSomething()
{
 Console.WriteLine("Something");
}

Führen wir DelegateTest() aus, Instanziieren wir ein neues Delegate TestHandlerDabei weisen wir auch direkt die erste Methode zu, die ausgeführt werden soll. Die hinzugefügten Methoden werden übrigens nach dem FIFO-Prinzip abgearbeitet – First In – First Out. Also die zuerst hinzugefügte Methode wird auch zuerst ausgeführt. So weit zu Delegates, wir können also einen Zeiger auf die Methode, die wir gerne ausgeführt hätten, zu einem Delegate hinzufügen. Sobald das Delegate ausgeführt wird, werden die Methoden darin chronologisch abgearbeitet. Jetzt, wo wir wissen, wie Delegates funktionieren, können wir uns wieder mit unserem HelloWorld beschäftigen. Ich habe eine Klasse User hinzugefügt (Rechtsklick oben rechts auf die Assembly (HelloWorld_TC) > hinzufügen > Klasse), damit das Objektmodell noch einmal klar wird, und Events auch Sinn machen. Nicht erschrecken, hier kommen 2 Codes: Program.cs

//Visit http://www.tacticalcode.de !
static bool EError = false;
static void Main(string[] args)
{
string name = String.Empty;
User usr = new User();
usr.IncorrectEntry += new User.IncorrectEntryEventHandler(usr_IncorrectEntry);

if (args.Length == 0)
{
Console.WriteLine("Please enter your name:");
name = Console.ReadLine();
}
else if (args.Length == 1)
{
name = args[0];
}
else
Console.WriteLine("Usage: HelloWorldTC [name]");

if (!String.IsNullOrEmpty(name))
{
usr.Name = name;
usr.Well = usr.GetWellBeing();
Console.Write("\n");

if (!EError && usr.Well)
Console.WriteLine("Oh, great!");
else if (!EError && !usr.Well)
Console.WriteLine("I'm sorry for you :'(");
else
Console.WriteLine("Incorrect entry!");
}
else
Console.WriteLine("Please specify a name!");

}

static void usr_IncorrectEntry()
{
EError = true;
}

User.cs

public bool Well = false;
public string Name = String.Empty;
public event IncorrectEntryEventHandler IncorrectEntry;
public delegate void IncorrectEntryEventHandler();

public bool GetWellBeing()
{
	Console.WriteLine("How are you, " + Name + "?");
	Console.WriteLine("[G]ood, [B]ad: ");
	Char c = Console.ReadKey().KeyChar;

	switch (c)
	{
		case (char)66:
		case (char)98:
		{
			return false;
		}
		case (char)71:
		case (char)103:
		{
			return true;
		}
		default:
		{
			IncorrectEntry();
			return false;
		}
	}
}

Das sieht doch schon mal etwas anders aus als vorher. Was wurde geändert? Wir haben jetzt eine Klasse User. Diese Klasse fasst wichtige Methoden, Eigenschaften usw. zusammen, die einem User zugeordnet werden können. Dadurch, dass wir eine Klasse haben, können wir beliebig viele User-Objekte erstellen. Somit könnte in unserem Programm nicht nur ein User, sondern mehrere User gleichzeitig „behandelt“ werden. Also Schaffen wir uns zunächst einen neuen User, indem wir den Konstruktor der User-Klasse aufrufen. Danach weisen wir dem Event eine Methode zu, die ausgeführt werden soll, wenn das Event ausgelöst wurde. Dann fragen wir wie gehabt nach dem Namen, und setzen diesen Namen als Name Eigenschaft unseres User-Objektes. Dieses stellt nun auch die Methode bereit, den Anwender zu fragen, wie es ihm geht. Methoden mit Rückgabewert haben wir im ersten Teil des HelloWorld besprochen, die Funktionsweise dürfte also klar sein. Was in der GetWellBeing() Methode passiert, ist schon viel interessanter. Sie wurde etwas aufgeräumt, aber das Augenmerk liegt auf dem Auslösen unseres Events.

default:
{
	IncorrectEntry();
	return false;
}

Gibt der Anwender etwas falsches ein, Lösen wir zunächst das Event aus, danach geben wir false zurück. Warum wieder false zurückgeben? Unser switch-case Konstrukt braucht weiterhin eine Abbruch-Anweisung. Durch die Auslösung des Events ist nämlich noch nicht sichergestellt, dass überhaupt etwas geschieht. Dass der EventHandler befüllt wird, ist optional. Die andere Klasse kann auch einfach weiter verfahren, ohne sich um das Ereignis zu kümmern. In unserem Fall, kümmern wir uns um das Ereignis, wir haben direkt nach dem Instanziieren unseres User-Objektes festgelegt, was passieren soll, wenn das IncorrectEntryEvent ausgelöst wird. Hier hilft uns VisualStudio freundlicher Weise, sobald wir einem Event etwas zuweisen wollen (Ereignissen kann nur mit „+=“ oder „-=“ etwas zugewiesen werden), schlägt uns IntelliSense vor, einen neuen EventHandler dafür zu erzeugen, und fügt auf Wunsch auch gleich eine passende Methode in unsere Klasse ein (Wir erinnern uns, der EventHandler ist ein Delegat, welchem wir (eine) Methode(n) zuweisen, die nach dem Auslösen des Events ausgeführt werden sollen). Ein Kleiner Tipp noch zur Definition von Events und EventHandlern: Ich lege immer zuerst den EventHanlder an. Will ich ein Event „Test“ in die Klasse einfügen, definiere ich also erst ein Delegat mit dem Namen „TestEventHandler“. Delegates mögen auf den ersten Blick komisch erscheinen, denn der Typ eines Delegates wird durch seinen Namen bestimmt. Ist das Delegate angelegt, füge ich das Event „Test“ hinzu. Es ist vom gleichen Typ, wie das Delegate (also so, wie das Delegate heißt). Das muss man einfach ein paar mal üben, bis es klappt. Ihr könntet als Übung Events für alle Cases angeben, also für Gut, Schlecht und Error, und in der Hauptklasse die Antwort ausgeben, je nachdem welches Ereignis ausgelöst wurde. Zum Schluss, ein Resumée über den geänderten Programmablauf:

  • Ein User-Objekt wird angelegt
  • Wir geben eine auszuführende Methode für das Ereignis an
  • – Wurde das Ereignis ausgelöst, speichern wir das in einem bool EError
  • Dem User-Objekt wird ein Name zugewiesen
  • Ist der Name nicht leer:
  • – Fragen wir dem User, wie es ihm geht.
  • – Gibt er g/G/b/B für good/bad ein, liefern wir seinen Zustand zurück
  • – Gibt er etwas anderes ein, wird das Ereignis ausgelöst, und wir geben false zurück
  • – Wurde das Ereignis nicht ausgelöst, und fühlt sich der User gut/schlecht
  • — Entsprechende Antwort ausgeben
  • – In jedem anderen Fall, wurde das Ereignis also ausgelöst
  • — Fehler ausgeben
  • Ist der Name leer, Fehler ausgeben.

Somit haben wir unser Programm noch dynamischer gemacht. Es ist skalierbar – Es können Eigenschaften beliebig vieler User verwaltet werden. Dazu kommt dass Fehlerquellen beseitigt wurden. Hier ist der obligatorische Download der Projektmappe. Im nächsten Blog-Eintrag gehe ich noch auf Ausnahmen („Exceptions“) ein, danach werde ich ein paar Programme vorstellen, die ich geschrieben habe. Also, stay tuned!

Dieser Eintrag wurde veröffentlicht in C#
Bookmarken: Permanent-Link Schreibe einen Kommentar oder hinterlasse einen Trackback: Trackback-URL.
Achtung: Wordpress interpretiert bestimmte Zeichenfolgen als Markup und verändert diese. Nutzt für Programmcode lieber Gist oder PasteBin-Services und verlinkt die Code-Schnipsel.

Post a Comment

Ihre E-Mail wird niemals veröffentlicht oder verteilt. Benötigte Felder sind mit * markiert

*
*

Du kannst diese HTML Tags und Attribute verwenden: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>