Benutzer mit den meisten Antworten
Musterbeispiele für Events, Delegate, Action

Frage
-
Hallo,
ich habe von Golo Roden
http://www.guidetocsharp.de/StyleGuideDelegates.aspx#DefineDelegates
den Webcast angesehen.A) Was zu kurz kommt.
Wie löse ich den Event aus?
Das muss man halt so machen, wegen Threadsicherheit. Ist ja ok.
EventHandler<MyEventArgs> handler = _myEvent;
if(handler != null)
{
B) Kochrezept für Events, Delegate, Action ist offen.
Was verwende ich was am besten.Fazit
Kann mir jemand helfen, die Fragen beantworten. Danke.
Was suche ich?
Event -> Beispiel A
Delegate -> Beispiel B
Action -> Beispiel C
Somit hat man eine Art 'Tabellenbuch', SchemaF
Danke.
Grüße Sandrapublic class MyEventArgs : EventArgs { static new MyEventArgs Empty { // Empty wird von der Basis verwendet, override gibt es nicht, kein virtual get { return new MyEventArgs(); } } } public class Program { private EventHandler<MyEventArgs> _myEvent; private readonly object _lockObject = new object(); public event EventHandler<MyEventArgs> MyEvent { add { lock (_lockObject) { _myEvent += value; } } remove { lock (_lockObject) { _myEvent -= value; } } } protected virtual void OnMyEvent() { EventHandler<MyEventArgs> handler = _myEvent; if(handler != null) { handler(this, (MyEventArgs) MyEventArgs.Empty); } } public static void Main() { } }
Antworten
-
Hallo Sandra,
Fangen wir mit der Antwort auf meine Frage an:
Wichtiger ist das Warum und nicht was die Schlüsselwörter bewirken; die sind hier nur Mittel zum Zweck.Es gilt die Verwendung des Observer-Musters für sich zu "entdecken" und die Einsatzgebiete erkennen. Damit wird auch die Kapselung der Aufrufmethode klar: Ein stinknormales OOP-Paradigma und das Warum man eine Klasse braucht - nach der Du vermutlich gesucht hattest, wenn Du Dir die Verwendung nicht erklären konntest.
Nebenbei: Visual Basic widmet dem mit RaiseEvent ein Schlüsselwort - das im Hintergrund eben jenen Code produziert.Zu Delegaten:
Ich könnte vermutlich dutzende von Beispielen erfinden, nur hilft das wenig, glaube ich. Wie beim Ereignis muss man die Verwendbarkeit für sich erkennen.Wenn man sich den gezeigten Code von Main im Reflector oder ILSpy anschaut, sieht man etwas wie (hier ILSpy):
MyEventClass myClass = new MyEventClass(); myClass.MyEvent += delegate(object s, MyEventArgs e) { Console.WriteLine("<= Received MyEvent: " + e.Parameter); }; myClass.AMethodThatRaisesMyEvent(); myClass.AMethodThatUsesACallback(new Action<string>(MyProgram.ACallbackMethodWithString)); myClass.AMethodThatUsesACallback(new Action<string>(myClass.AnotherCallbackMethodWithString)); Action<string> callbackMethod = new Action<string>(MyProgram.ACallbackMethodWithString); myClass.AMethodThatUsesACallback(callbackMethod); myClass.AMethodThatUsesACallback(delegate(string c) { Console.WriteLine("<= Called back: " + c); });
Da gibt es bereits reichlich Action! Eine Func<...> ist ebenso Action, nur sagt der letzte Parameter, dass am Ende etwas herauskommen soll:
Func<string, string> aFunc = AMethodThatReturnsAString; string input = "Input"; Console.WriteLine("AMethodThatReturnsAString '{0}' => '{1}': ", input, aFunc(input)); static string AMethodThatReturnsAString(string parameter) { // kombiniert den Parameter und gibt ihn zurück return "<= AMethodThatReturnsAString: " + parameter; }
(und es wird nur anders und nicht besser, wenn ich die bereits gezeigten Methoden umgestalten würde ;-)
Fakt ist: Es gibt mittlerweile viele Varianten zum Schreiben eines Delegaten. Alles um es dem Entwickler leichter (beim Tippen) zu machen. Im Hintergrund wird (sehr oft) genau der gleiche Code erzeugt.
Und so ist beides auch bei Ereignissen vom Ergebnis gleich:
// beide sind identisch myClass.MyEvent += new EventHandler<MyEventArgs>(myClass_MyEvent); myClass.MyEvent += myClass_MyEvent;
Die erste Variante gab es ab C# 1.0, die zweite wurde später nachgereicht und der Compiler ergänzt den Rest - was er auch bei der Lambda Variante tut, die wie weiter oben zu sehen, ein delegate {} ist.
Falls es Dich beruhigt: Auch für mich war es ein schrittweiser Prozess, die jeweils aktuellste Syntax "auszunutzen", obwohl oder gerade weil ich bereits mit C# 1.0 angefangen habe.
Zu Ko- und Kontravarianz:
Das ist eines der schwierigeren Themen; das beste daran ist: Für 99,9 % der Aufgaben braucht es auch nicht zu verstehen. Einfache Regel hier: Meckert der Compiler so geht es nicht und man sollte seinen Entwurf überdenken.Willst Du es detailliert wissen, so hat Eric Lippert eine Serie (elfteilig und etwas mehr!) zu dem Thema und zwei Videos:
Part 1: http://msdn.microsoft.com/en-us/vcsharp/ee672314.aspx
Part 2: http://msdn.microsoft.com/en-us/vcsharp/ee672319.aspx
Was die Bücher angeht, hatte ich Dir bereits Jon Skeet empfohlen, der Stackoverflow-Legende, nicht nur dass er noch besser erklären kann als ich!
Das Buch C# in Depth zeigt nicht nur einfach die Sprachelemente auf (wie es hunderte andere tun), sondern die Entwicklung der Sprache und alles was hier nur angerissen wurde. Bei den Delegaten hat C# eine große Evolution seit Version 1.0 durchgemacht; was dort Schritt für Schritt durchgearbeitet wird (und soviel kann man hier einfach nicht schreiben).
Hinzu kommen weitere Themen wie LINQ, die intensiv von Delegaten Gebrauch machen und erst mit dem Verständnis dieser Themen wird der Gebrauch von Delegaten richtig interessant.
Und sollte englisch als Hürde erscheinen: Das Buch ist sehr gut geschrieben und da englisch für Entwickler heutzutage Plicht ist eine weitere Übung ;-). Falls es hilft: Meiner Meinung nach ist es das beste Programmierbuch seit langem - und ich bin da etwas anspruchsvoll ;-)
Gruß Elmar
- Als Antwort markiert Sandra Maier Mittwoch, 5. September 2012 04:25
-
Hallo Sandra,
das Problem mit solchen "Kochbüchern nach Schema F" ist, das man das Denken einstellt -
wie Du mit der Frage Wie löse ich den Event aus? "beweist" ;-)Wie schon bei den Attributen weise ich auf die Kapitel der MSDN hin:
Delegaten (C#-Programmierhandbuch)
Ereignisse (C#-Programmierhandbuch)
(und dort die Gewusst wie Themen für das "Schema F").
"Kurzfassung" zum Merken:
Ereignisse basieren auf Delegaten.
Sie haben eine spezifische Signatur, siehe dazu Ereignisentwurf (aus Entwurfsrichtlinien zum Entwickeln von Klassenbibliotheken), d. h. sie werden von EventHandler abgeleitet und die Methoden haben jeweils zwei Parameter (sender als Object und e als EventArgs).
Aufgerufen (siehe Frage) werden Ereignisse über die Methode On<Ereignisname>.
Im Gegensatz zu Delegaten können sie in Schnittstellen (interface) verwendet werden
und sind somit besser für öffentliche APIs als reine Delegaten geeignet.
Hinzu kommt, dass Abonnenten nur durch die Methoden (add/remove) verändert werden können,
wodurch ihr Verwendung durch den Abonnenten abgesichert wird.
Zum gezeigten Beispiel sei angemerkt:
Es ist es nicht zwangsweise notwendig, die add/remove-Methoden zu implementieren, mit dem event Schlüsselwort generiert der C# Compiler die notwendigen Methoden (ohne lock).
Die gezeigte threadsafe Implementierung (mit lock) nur in Klassen Sinn, die insgesamt als solche ausgelegt sind (und der Aufrufcode in OnMyEvent könnte das "_lockObject" verwenden).Zu Delegaten:
Sie werden mit dem Schlüsselwort delegate deklariert, womit die Signatur, d. h. die Parameter in Anzahl und Typ sowie optional der Rückgabetyp festgelegt werden.
Action<T>, Func<T> sind vom Framework vorgegebene generische Delegaten -
neben einigen weiteren wie Predicate<T> und auch EventHandler bzw. EventHandler<T>.Verwendet werden sie als Instanzvariablen wie andere Typen durch Angabe ihres Namens, z. B.:
EventHandler<MyEventArgs> handler = _myEvent;
von oben "geklaut" und hier als generische Delegat-Instanzvariable ;-).
Die vordefinierten Typen sollte man bevorzugt einsetzen, da der Compiler für jeden Delegaten eine neue Klasse generieren muss - die von System.MulticastDelegate abgeleitet ist.
Was zu einer unnötigen "Überflutung" mit trivialen Klassen führen würde, die nicht notwendig ist. Insbesondere da aktuelle Framework Versionen die Regeln für Delegaten (und somit auch Ereignisse) gelockert haben.
Mit anonymen Funktionen (C#-Programmierhandbuch) und Lambdaausdrücken stellt der C# Compiler vereinfachte Notationen bereit, um Delegaten leichter handhabbar und verwendbar zu machen.
Für eine detaillierte Beschreibung empfehle ich Dir Jon Skeet: C# in Depth,
ein Auszug davon wäre Delegates and Events
(Das Buch behandelt alle weiteren C# Features sehr ausführlich.)Gruß Elmar
- Als Antwort markiert Sandra Maier Dienstag, 4. September 2012 04:21
Alle Antworten
-
Hallo Sandra,
das Problem mit solchen "Kochbüchern nach Schema F" ist, das man das Denken einstellt -
wie Du mit der Frage Wie löse ich den Event aus? "beweist" ;-)Wie schon bei den Attributen weise ich auf die Kapitel der MSDN hin:
Delegaten (C#-Programmierhandbuch)
Ereignisse (C#-Programmierhandbuch)
(und dort die Gewusst wie Themen für das "Schema F").
"Kurzfassung" zum Merken:
Ereignisse basieren auf Delegaten.
Sie haben eine spezifische Signatur, siehe dazu Ereignisentwurf (aus Entwurfsrichtlinien zum Entwickeln von Klassenbibliotheken), d. h. sie werden von EventHandler abgeleitet und die Methoden haben jeweils zwei Parameter (sender als Object und e als EventArgs).
Aufgerufen (siehe Frage) werden Ereignisse über die Methode On<Ereignisname>.
Im Gegensatz zu Delegaten können sie in Schnittstellen (interface) verwendet werden
und sind somit besser für öffentliche APIs als reine Delegaten geeignet.
Hinzu kommt, dass Abonnenten nur durch die Methoden (add/remove) verändert werden können,
wodurch ihr Verwendung durch den Abonnenten abgesichert wird.
Zum gezeigten Beispiel sei angemerkt:
Es ist es nicht zwangsweise notwendig, die add/remove-Methoden zu implementieren, mit dem event Schlüsselwort generiert der C# Compiler die notwendigen Methoden (ohne lock).
Die gezeigte threadsafe Implementierung (mit lock) nur in Klassen Sinn, die insgesamt als solche ausgelegt sind (und der Aufrufcode in OnMyEvent könnte das "_lockObject" verwenden).Zu Delegaten:
Sie werden mit dem Schlüsselwort delegate deklariert, womit die Signatur, d. h. die Parameter in Anzahl und Typ sowie optional der Rückgabetyp festgelegt werden.
Action<T>, Func<T> sind vom Framework vorgegebene generische Delegaten -
neben einigen weiteren wie Predicate<T> und auch EventHandler bzw. EventHandler<T>.Verwendet werden sie als Instanzvariablen wie andere Typen durch Angabe ihres Namens, z. B.:
EventHandler<MyEventArgs> handler = _myEvent;
von oben "geklaut" und hier als generische Delegat-Instanzvariable ;-).
Die vordefinierten Typen sollte man bevorzugt einsetzen, da der Compiler für jeden Delegaten eine neue Klasse generieren muss - die von System.MulticastDelegate abgeleitet ist.
Was zu einer unnötigen "Überflutung" mit trivialen Klassen führen würde, die nicht notwendig ist. Insbesondere da aktuelle Framework Versionen die Regeln für Delegaten (und somit auch Ereignisse) gelockert haben.
Mit anonymen Funktionen (C#-Programmierhandbuch) und Lambdaausdrücken stellt der C# Compiler vereinfachte Notationen bereit, um Delegaten leichter handhabbar und verwendbar zu machen.
Für eine detaillierte Beschreibung empfehle ich Dir Jon Skeet: C# in Depth,
ein Auszug davon wäre Delegates and Events
(Das Buch behandelt alle weiteren C# Features sehr ausführlich.)Gruß Elmar
- Als Antwort markiert Sandra Maier Dienstag, 4. September 2012 04:21
-
Hallo Elmar,
Danke für die Antworten.
Kannst Du dennoch vorab mal zeigen, wie in dem obigen Beispiel der Event ausgelöst werden kann.
Grüße Sandra
protected virtual void OnMyEvent()
{
EventHandler<MyEventArgs> handler = _myEvent;
if(handler != null)
{
handler(this, (MyEventArgs) MyEventArgs.Empty);
}
}
-
Hallo Sandra,
damit ein Ereignis Sinn gibt, muss es nicht nur eine Klasse geben, die es implementiert,
sondern mindestens einen Abonnenten der es nutzt - wie es das Beobachter (Observer) Entwurfsmuster vorsieht, von dem es seinen Ursprung hat.
ich habe das (sog.) Beispiel mal etwas überarbeitet, so dass es zumindest eine Andeutung von Mehrwert hat. Und die fürs Verständnis redundanten Teile entfernt. (Das "leere" Argument habe ich drin gelassen, wenn auch nur dem bereits Geschriebenen zuliebe).
Und weil ich gerade dabei war, gibt es unten dran, einige Varianten von Delegaten
Für das weitere bitte ich Dich die Dokumentation zu studieren.
und ihre Verwendung.
Und als als kleine Aufgabe zu beantworten:
Warum ist die OnMyEvent-Methode protected und virtual?using System; namespace ElmarBoye.Samples.Code { /// <summary>Ereignis Argumente, ergänzt um ein Argument, um sie sinnvoll erscheinen zu lassen.</summary> public class MyEventArgs : EventArgs { /// <summary>Standard-Parameter mit "leerem" Inhalt.</summary> /// <remarks>Eher überflüssig, aber weil es im Ursprung vorkam...</remarks> public static readonly MyEventArgs MyEmpty = new MyEventArgs("Empty"); public MyEventArgs(string parameter) { this.Parameter = parameter; } /// <summary>Der Ereignisargument für den Empfänger.</summary> public string Parameter { get; private set; } } public class MyEventClass { public event EventHandler<MyEventArgs> MyEvent; protected virtual void OnMyEvent(string parameter = null) { EventHandler<MyEventArgs> handler = MyEvent; if (handler != null) { handler(this, (parameter == null) ? MyEventArgs.MyEmpty : new MyEventArgs(parameter)); } } /// <summary>Eine Methode die das Ereignis verwendet, d. h. aufruft.</summary> public void AMethodThatRaisesMyEvent() { Console.WriteLine("AMethodThatRaisesMyEvent called"); Console.WriteLine("=> Raise MyEvent Empty"); OnMyEvent(); Console.WriteLine("=> Raise MyEvent Parameter"); OnMyEvent("Parameter"); } public void AMethodThatUsesACallback(Action<string> callback) { string argument = "an argument"; Console.WriteLine("AMethodThatUsesACallback called with:\n=>" + argument); if (callback != null) callback(argument); else Console.WriteLine("=> Sorry no callback"); } public void AnotherCallbackMethodWithString(string parameter) { Console.WriteLine("<= MyEventClass.AnotherCallbackMethodWithString: " + parameter); } } static class MyProgram { static void Main() { MyEventClass myClass = new MyEventClass(); // Das Ereignis (mit Lambda) abonnieren um davon zu "profitieren". myClass.MyEvent += (s, e) => Console.WriteLine("<= Received MyEvent: " + e.Parameter); // und dann eine Methode aufrufen, die das Ereignis verwendet myClass.AMethodThatRaisesMyEvent(); // Alternative: Übergabe eines Delegaten, der von der Methode aufgerufen wird. // Entweder direkt über die Angabe einer Methode, die statisch... myClass.AMethodThatUsesACallback(ACallbackMethodWithString); // ... oder eine Instanzmethode (auch in einer anderen Klasse) sein kann myClass.AMethodThatUsesACallback(myClass.AnotherCallbackMethodWithString); // Übergabe einer Methode über eine Variable (in der Praxis für wechselnde Varianten) Action<string> callbackMethod = ACallbackMethodWithString; myClass.AMethodThatUsesACallback(callbackMethod); // und das gleiche noch mit einem Lambda, wenn es schnell gehen soll. myClass.AMethodThatUsesACallback((c) => Console.WriteLine("<= Called back: " + c)); } static void ACallbackMethodWithString(string parameter) { Console.WriteLine("<= ACallbackMethodWithString: " + parameter); } } }
Gruß Elmar
- Bearbeitet Elmar BoyeEditor Dienstag, 4. September 2012 07:22
-
Hallo Elmar,
Du könntest Tutor machen.
Ich gebe Dir 100% Recht, man muss es verstehen.
Mein Ansatz
Was will ich, was benötige ich, was geht heute?
Wenn ich es nicht weiß, kann ich ja nicht 100 Bücher lesen, dann komme ich nie ans Ziel.
>Warum ist die OnMyEvent-Methode protected und virtual?
Protected, nur innerhalb der Klasse anzusprechen bzw. von denen die erben.
Virtual, um bei Ableitung über override zu überschreiben.
Soweit habe ich das mal verstanden.AMethodThatRaisesMyEvent called
=> Raise MyEvent Empty
<= Received MyEvent: Empty
<= Received MyEvent 'normal abonniert': Empty
=> Raise MyEvent Parameter
<= Received MyEvent: Parameter
<= Received MyEvent 'normal abonniert': Parameter
AMethodThatUsesACallback called with:
=>an argument
<= ACallbackMethodWithString: an argument
AMethodThatUsesACallback called with:
=>an argument
<= MyEventClass.AnotherCallbackMethodWithString: an argument
AMethodThatUsesACallback called with:
=>an argument
<= ACallbackMethodWithString: an argument
AMethodThatUsesACallback called with:
=>an argument
<= Called back: an argumentWas nett wäre.
A) noch ein prägnantes Beispiel zu Delegate
B) das gleiche dann mit Action (Func), um zu sehen, was und wie es 'scheinbar' einfacher wird.
Ich finde die Beispiele sind nicht immer klar.
http://msdn.microsoft.com/de-de/library/dd465122.aspx
http://www.mycsharp.de/wbb2/thread.php?threadid=74181
Wenn ich mal die Grundsätze habe, dann doch noch was benötige kann ich mich weiter durcharbeiten.
Herzlichen Dank.
Viele Grüße Sandrastatic void Main() { MyEventClass myClass = new MyEventClass(); // Das Ereignis (mit Lambda) abonnieren um davon zu "profitieren". myClass.MyEvent += (s, e) => Console.WriteLine("<= Received MyEvent: " + e.Parameter); myClass.MyEvent += new EventHandler<MyEventArgs>(myClass_MyEvent);
-
Hallo Sandra,
Fangen wir mit der Antwort auf meine Frage an:
Wichtiger ist das Warum und nicht was die Schlüsselwörter bewirken; die sind hier nur Mittel zum Zweck.Es gilt die Verwendung des Observer-Musters für sich zu "entdecken" und die Einsatzgebiete erkennen. Damit wird auch die Kapselung der Aufrufmethode klar: Ein stinknormales OOP-Paradigma und das Warum man eine Klasse braucht - nach der Du vermutlich gesucht hattest, wenn Du Dir die Verwendung nicht erklären konntest.
Nebenbei: Visual Basic widmet dem mit RaiseEvent ein Schlüsselwort - das im Hintergrund eben jenen Code produziert.Zu Delegaten:
Ich könnte vermutlich dutzende von Beispielen erfinden, nur hilft das wenig, glaube ich. Wie beim Ereignis muss man die Verwendbarkeit für sich erkennen.Wenn man sich den gezeigten Code von Main im Reflector oder ILSpy anschaut, sieht man etwas wie (hier ILSpy):
MyEventClass myClass = new MyEventClass(); myClass.MyEvent += delegate(object s, MyEventArgs e) { Console.WriteLine("<= Received MyEvent: " + e.Parameter); }; myClass.AMethodThatRaisesMyEvent(); myClass.AMethodThatUsesACallback(new Action<string>(MyProgram.ACallbackMethodWithString)); myClass.AMethodThatUsesACallback(new Action<string>(myClass.AnotherCallbackMethodWithString)); Action<string> callbackMethod = new Action<string>(MyProgram.ACallbackMethodWithString); myClass.AMethodThatUsesACallback(callbackMethod); myClass.AMethodThatUsesACallback(delegate(string c) { Console.WriteLine("<= Called back: " + c); });
Da gibt es bereits reichlich Action! Eine Func<...> ist ebenso Action, nur sagt der letzte Parameter, dass am Ende etwas herauskommen soll:
Func<string, string> aFunc = AMethodThatReturnsAString; string input = "Input"; Console.WriteLine("AMethodThatReturnsAString '{0}' => '{1}': ", input, aFunc(input)); static string AMethodThatReturnsAString(string parameter) { // kombiniert den Parameter und gibt ihn zurück return "<= AMethodThatReturnsAString: " + parameter; }
(und es wird nur anders und nicht besser, wenn ich die bereits gezeigten Methoden umgestalten würde ;-)
Fakt ist: Es gibt mittlerweile viele Varianten zum Schreiben eines Delegaten. Alles um es dem Entwickler leichter (beim Tippen) zu machen. Im Hintergrund wird (sehr oft) genau der gleiche Code erzeugt.
Und so ist beides auch bei Ereignissen vom Ergebnis gleich:
// beide sind identisch myClass.MyEvent += new EventHandler<MyEventArgs>(myClass_MyEvent); myClass.MyEvent += myClass_MyEvent;
Die erste Variante gab es ab C# 1.0, die zweite wurde später nachgereicht und der Compiler ergänzt den Rest - was er auch bei der Lambda Variante tut, die wie weiter oben zu sehen, ein delegate {} ist.
Falls es Dich beruhigt: Auch für mich war es ein schrittweiser Prozess, die jeweils aktuellste Syntax "auszunutzen", obwohl oder gerade weil ich bereits mit C# 1.0 angefangen habe.
Zu Ko- und Kontravarianz:
Das ist eines der schwierigeren Themen; das beste daran ist: Für 99,9 % der Aufgaben braucht es auch nicht zu verstehen. Einfache Regel hier: Meckert der Compiler so geht es nicht und man sollte seinen Entwurf überdenken.Willst Du es detailliert wissen, so hat Eric Lippert eine Serie (elfteilig und etwas mehr!) zu dem Thema und zwei Videos:
Part 1: http://msdn.microsoft.com/en-us/vcsharp/ee672314.aspx
Part 2: http://msdn.microsoft.com/en-us/vcsharp/ee672319.aspx
Was die Bücher angeht, hatte ich Dir bereits Jon Skeet empfohlen, der Stackoverflow-Legende, nicht nur dass er noch besser erklären kann als ich!
Das Buch C# in Depth zeigt nicht nur einfach die Sprachelemente auf (wie es hunderte andere tun), sondern die Entwicklung der Sprache und alles was hier nur angerissen wurde. Bei den Delegaten hat C# eine große Evolution seit Version 1.0 durchgemacht; was dort Schritt für Schritt durchgearbeitet wird (und soviel kann man hier einfach nicht schreiben).
Hinzu kommen weitere Themen wie LINQ, die intensiv von Delegaten Gebrauch machen und erst mit dem Verständnis dieser Themen wird der Gebrauch von Delegaten richtig interessant.
Und sollte englisch als Hürde erscheinen: Das Buch ist sehr gut geschrieben und da englisch für Entwickler heutzutage Plicht ist eine weitere Übung ;-). Falls es hilft: Meiner Meinung nach ist es das beste Programmierbuch seit langem - und ich bin da etwas anspruchsvoll ;-)
Gruß Elmar
- Als Antwort markiert Sandra Maier Mittwoch, 5. September 2012 04:25