Super einfaches Beispiel für C # Observer / Observable mit Delegierten
131
Ich habe kürzlich angefangen, mich mit C # zu beschäftigen, aber ich kann nicht anhand meines Lebens herausfinden, wie Delegierte arbeiten, wenn sie das Beobachter- / Beobachtungsmuster in der Sprache implementieren.
Könnte mir jemand ein supereinfaches Beispiel geben, wie es gemacht wird? Ich habe dies gegoogelt, aber alle Beispiele, die ich gefunden habe, waren entweder zu problemspezifisch oder zu "aufgebläht".
Das Beobachtermuster wird normalerweise mit implementiert Ereignissen .
Hier ist ein Beispiel:
using System;classObservable{publiceventEventHandlerSomethingHappened;publicvoidDoSomething()=>SomethingHappened?.Invoke(this,EventArgs.Empty);}classObserver{publicvoidHandleEvent(object sender,EventArgs args){Console.WriteLine("Something happened to "+ sender);}}classTest{staticvoidMain(){Observable observable =newObservable();Observer observer =newObserver();
observable.SomethingHappened+= observer.HandleEvent;
observable.DoSomething();}}
Weitere Informationen finden Sie im verlinkten Artikel.
Beachten Sie, dass im obigen Beispiel der nullbedingte C # 6 - Operator verwendet wird, DoSomethingum Fälle sicher zu implementieren , in denen er SomethingHappenednicht abonniert wurde und daher null ist. Wenn Sie eine ältere Version von C # verwenden, benötigen Sie folgenden Code:
Um sich ein paar Zeilen zu sparen und die Nullprüfung zu
Dinah
1
@ Dinah: Das vermeidet nicht die Nullprüfung. Sie können SomethingHappened = nullspäter noch festlegen (eine praktische, wenn auch faule und nicht ideale Methode zum Abbestellen aller Handler), sodass die Nullprüfung immer erforderlich ist.
Dan Puzey
4
@DanPuzey: Sie können innerhalb der Klasse, aber Sie können auch sicherstellen, dass Sie das nicht tun - und anderer Code kann es nicht, da er nur abonnieren und abbestellen kann. Wenn Sie sicherstellen, dass Sie es in Ihrer Klasse niemals absichtlich auf null setzen, ist es in Ordnung, die Nullprüfung zu vermeiden.
Jon Skeet
2
@ JonSkeet: Natürlich habe ich vergessen, dass du das nicht außerhalb der Klasse machen kannst. Entschuldigung!
Dan Puzey
2
Ich denke, Sie können alle Sachen in DoSomething durchSomethingHappened?.Invoke(this, EventArgs.Empty);
Dies verstößt gegen eine Regel, da ich den Beobachter nicht vom Beobachtbaren aushake. Dies ist vielleicht gut genug für dieses einfache Beispiel, aber stellen Sie sicher, dass Sie die Beobachter nicht so von Ihren Ereignissen abhalten. Eine Möglichkeit, dies zu handhaben, besteht darin, ObserverClass IDisposable zu machen und die .Dispose-Methode das Gegenteil des Codes im Konstruktor tun zu lassen
Es wurde keine Fehlerprüfung durchgeführt, zumindest sollte im Konstruktor der ObserverClass eine Nullprüfung durchgeführt werden
In diesem Modell haben Sie Publisher, die Logik ausführen und ein "Ereignis" veröffentlichen.
Publisher senden ihre Veranstaltung dann nur an Abonnenten, die sich für den Empfang der jeweiligen Veranstaltung angemeldet haben.
In C # kann jedes Objekt eine Reihe von Ereignissen veröffentlichen, die andere Anwendungen abonnieren können.
Wenn die Veröffentlichungsklasse ein Ereignis auslöst, werden alle abonnierten Anwendungen benachrichtigt.
Die folgende Abbildung zeigt diesen Mechanismus.
Einfachstes Beispiel für Ereignisse und Delegaten in C #:
Code ist selbsterklärend. Außerdem habe ich die Kommentare hinzugefügt, um den Code zu löschen.
using System;publicclassPublisher//main publisher class which will invoke methods of all subscriber classes{publicdelegatevoidTickHandler(Publisher m,EventArgs e);//declaring a delegatepublicTickHandlerTick;//creating an object of delegatepublicEventArgs e =null;//set 2nd paramter emptypublicvoidStart()//starting point of thread{while(true){System.Threading.Thread.Sleep(300);if(Tick!=null)//check if delegate object points to any listener classes method{Tick(this, e);//if it points i.e. not null then invoke that method!}}}}publicclassSubscriber1//1st subscriber class{publicvoidSubscribe(Publisher m)//get the object of pubisher class{
m.Tick+=HeardIt;//attach listener class method to publisher class delegate object}privatevoidHeardIt(Publisher m,EventArgs e)//subscriber class method{System.Console.WriteLine("Heard It by Listener");}}publicclassSubscriber2//2nd subscriber class{publicvoidSubscribe2(Publisher m)//get the object of pubisher class{
m.Tick+=HeardIt;//attach listener class method to publisher class delegate object}privatevoidHeardIt(Publisher m,EventArgs e)//subscriber class method{System.Console.WriteLine("Heard It by Listener2");}}classTest{staticvoidMain(){Publisher m =newPublisher();//create an object of publisher class which will later be passed on subscriber classesSubscriber1 l =newSubscriber1();//create object of 1st subscriber classSubscriber2 l2 =newSubscriber2();//create object of 2nd subscriber class
l.Subscribe(m);//we pass object of publisher class to access delegate of publisher class
l2.Subscribe2(m);//we pass object of publisher class to access delegate of publisher class
m.Start();//starting point of publisher class}}
Ich habe einige der oben genannten großartigen Beispiele (wie immer vielen Dank an Herrn Skeet und Herrn Karlsen ) zusammengefügt, um ein paar verschiedene Observables aufzunehmen, und eine Schnittstelle verwendet, um sie im Observer zu verfolgen, und dem Observer erlaubt, dies zu tun um eine beliebige Anzahl von Observablen über eine interne Liste zu "beobachten":
namespace ObservablePattern{
using System;
using System.Collections.Generic;internalstaticclassProgram{privatestaticvoidMain(){var observable =newObservable();var anotherObservable =newAnotherObservable();
using (IObserver observer =newObserver(observable)){
observable.DoSomething();
observer.Add(anotherObservable);
anotherObservable.DoSomething();}Console.ReadLine();}}internalinterfaceIObservable{eventEventHandlerSomethingHappened;}internalsealedclassObservable:IObservable{publiceventEventHandlerSomethingHappened;publicvoidDoSomething(){var handler =this.SomethingHappened;Console.WriteLine("About to do something.");if(handler !=null){
handler(this,EventArgs.Empty);}}}internalsealedclassAnotherObservable:IObservable{publiceventEventHandlerSomethingHappened;publicvoidDoSomething(){var handler =this.SomethingHappened;Console.WriteLine("About to do something different.");if(handler !=null){
handler(this,EventArgs.Empty);}}}internalinterfaceIObserver:IDisposable{voidAdd(IObservable observable);voidRemove(IObservable observable);}internalsealedclassObserver:IObserver{privatereadonlyLazy<IList<IObservable>> observables =newLazy<IList<IObservable>>(()=>newList<IObservable>());publicObserver(){}publicObserver(IObservable observable):this(){this.Add(observable);}publicvoidAdd(IObservable observable){if(observable ==null){return;}lock(this.observables){this.observables.Value.Add(observable);
observable.SomethingHappened+=HandleEvent;}}publicvoidRemove(IObservable observable){if(observable ==null){return;}lock(this.observables){
observable.SomethingHappened-=HandleEvent;this.observables.Value.Remove(observable);}}publicvoidDispose(){for(var i =this.observables.Value.Count-1; i >=0; i--){this.Remove(this.observables.Value[i]);}}privatestaticvoidHandleEvent(object sender,EventArgs args){Console.WriteLine("Something happened to "+ sender);}}}
Ich weiß, das ist alt, aber ... Das sieht threadsicher aus, ist es aber nicht. Sowohl in Observer.Add als auch in Observer.Remove muss sich die Nullprüfung innerhalb der Sperre befinden. Dispose sollte auch die Sperre erwerben und ein isDispised-Flag setzen. Ansonsten ein gutes, vollständiges Beispiel.
user5151179
5
Das Anwenden des Beobachtermusters mit Delegaten und Ereignissen in c # wird benannt laut MSDN "Ereignismuster" bezeichnet was eine geringfügige Abweichung darstellt.
In diesem Artikel finden Sie gut strukturierte Beispiele für die Anwendung des Musters in c # sowohl auf klassische Weise als auch unter Verwendung von Delegaten und Ereignissen.
publicclassStock{//declare a delegate for the eventpublicdelegatevoidAskPriceChangedHandler(object sender,AskPriceChangedEventArgs e);//declare the event using the delegatepubliceventAskPriceChangedHandlerAskPriceChanged;//instance variable for ask priceobject _askPrice;//property for ask pricepublicobjectAskPrice{set{//set the instance variable
_askPrice =value;//fire the eventOnAskPriceChanged();}}//AskPrice property//method to fire event delegate with proper nameprotectedvoidOnAskPriceChanged(){AskPriceChanged(this,newAskPriceChangedEventArgs(_askPrice));}//AskPriceChanged}//Stock class//specialized event class for the askpricechanged eventpublicclassAskPriceChangedEventArgs:EventArgs{//instance variable to store the ask priceprivateobject _askPrice;//constructor that sets askpricepublicAskPriceChangedEventArgs(object askPrice){ _askPrice = askPrice;}//public property for the ask pricepublicobjectAskPrice{get{return _askPrice;}}}//AskPriceChangedEventArgs
/**********************Simple Example ***********************/classProgram{staticvoidMain(string[] args){Parent p =newParent();}}////////////////////////////////////////////publicdelegatevoidDelegateName(string data);classChild{publiceventDelegateName delegateName;publicvoid call(){
delegateName("Narottam");}}///////////////////////////////////////////classParent{publicParent(){Child c =newChild();
c.delegateName +=newDelegateName(print);//or like this//c.delegateName += print;
c.call();}publicvoid print(string name){Console.WriteLine("yes we got the name : "+ name);}}
Ich wollte meinen Quellcode nicht ändern, um zusätzlichen Beobachter hinzuzufügen, daher habe ich folgendes einfaches Beispiel geschrieben:
//EVENT DRIVEN OBSERVER PATTERNpublicclassPublisher{publicPublisher(){var observable =newObservable();
observable.PublishData("Hello World!");}}//Server will send data to this class's PublishData methodpublicclassObservable{publiceventReceiveOnReceive;publicvoidPublishData(string data){//Add all the observer below//1st observerIObserver iObserver =newObserver1();this.OnReceive+= iObserver.ReceiveData;//2nd observerIObserver iObserver2 =newObserver2();this.OnReceive+= iObserver2.ReceiveData;//publish data var handler =OnReceive;if(handler !=null){
handler(data);}}}publicinterfaceIObserver{voidReceiveData(string data);}//Observer examplepublicclassObserver1:IObserver{publicvoidReceiveData(string data){//sample observers does nothing with data :)}}publicclassObserver2:IObserver{publicvoidReceiveData(string data){//sample observers does nothing with data :)}}
SomethingHappened = null
später noch festlegen (eine praktische, wenn auch faule und nicht ideale Methode zum Abbestellen aller Handler), sodass die Nullprüfung immer erforderlich ist.SomethingHappened?.Invoke(this, EventArgs.Empty);
Hier ist ein einfaches Beispiel:
Hinweis:
quelle
In diesem Modell haben Sie Publisher, die Logik ausführen und ein "Ereignis" veröffentlichen.
Publisher senden ihre Veranstaltung dann nur an Abonnenten, die sich für den Empfang der jeweiligen Veranstaltung angemeldet haben.
In C # kann jedes Objekt eine Reihe von Ereignissen veröffentlichen, die andere Anwendungen abonnieren können.
Wenn die Veröffentlichungsklasse ein Ereignis auslöst, werden alle abonnierten Anwendungen benachrichtigt.
Die folgende Abbildung zeigt diesen Mechanismus.
Einfachstes Beispiel für Ereignisse und Delegaten in C #:
Code ist selbsterklärend. Außerdem habe ich die Kommentare hinzugefügt, um den Code zu löschen.
Ausgabe:
Vom Zuhörer gehört
Habe es von Listener2 gehört
Vom Zuhörer gehört
Habe es von Listener2 gehört
Vom Zuhörer gehört. . . (unendlich oft)
quelle
Ich habe einige der oben genannten großartigen Beispiele (wie immer vielen Dank an Herrn Skeet und Herrn Karlsen ) zusammengefügt, um ein paar verschiedene Observables aufzunehmen, und eine Schnittstelle verwendet, um sie im Observer zu verfolgen, und dem Observer erlaubt, dies zu tun um eine beliebige Anzahl von Observablen über eine interne Liste zu "beobachten":
quelle
Das Anwenden des Beobachtermusters mit Delegaten und Ereignissen in c # wird benannt laut MSDN "Ereignismuster" bezeichnet was eine geringfügige Abweichung darstellt.
In diesem Artikel finden Sie gut strukturierte Beispiele für die Anwendung des Musters in c # sowohl auf klassische Weise als auch unter Verwendung von Delegaten und Ereignissen.
Erkunden des Observer-Entwurfsmusters
quelle
quelle
Ich wollte meinen Quellcode nicht ändern, um zusätzlichen Beobachter hinzuzufügen, daher habe ich folgendes einfaches Beispiel geschrieben:
quelle
Etwas wie das:
. Beobachtermuster C # mit Ereignis . Link zum Repository
quelle