Exemple super simple d'observateur / observable C # avec des délégués
131
J'ai récemment commencé à creuser dans C # mais je ne peux pas de ma vie comprendre comment les délégués fonctionnent lors de l'implémentation du modèle observable / observable dans le langage.
Quelqu'un pourrait-il me donner un exemple très simple de la façon dont cela se fait? J'ai googlé, mais tous les exemples que je trouvais étaient trop de problèmes spécifiques ou trop « pléthorique ».
Le modèle d'observateur est généralement implémenté avec des événements .
Voici un exemple:
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();}}
Voir l'article lié pour plus de détails.
Notez que l'exemple ci-dessus utilise l' opérateur conditionnel null C # 6 pour implémenter en DoSomethingtoute sécurité pour gérer les cas où SomethingHappenedn'a pas été abonné et est donc null. Si vous utilisez une ancienne version de C #, vous aurez besoin d'un code comme celui-ci:
@Dinah: Cela n'évite pas la vérification de null. Vous pouvez toujours définir SomethingHappened = nullplus tard (un moyen pratique, bien que paresseux et non idéal de désabonner tous les gestionnaires), de sorte que la vérification de null est toujours nécessaire.
Dan Puzey
4
@DanPuzey: Vous pouvez dans la classe, mais vous pouvez également vous assurer de ne pas le faire - et d' autres codes ne peuvent pas le faire, car il ne peut que s'abonner et se désabonner. Si vous vous assurez de ne jamais le définir délibérément sur null dans votre classe, évitez la vérification de null.
Jon Skeet
2
@JonSkeet: bien sûr, j'oubliais que vous ne pouvez pas faire ça en dehors de la classe. Toutes mes excuses!
Dan Puzey
2
Je pense que vous pouvez remplacer tout le SomethingHappened?.Invoke(this, EventArgs.Empty);
Cela viole une règle en ce sens que je ne décroche pas l'observateur de l'observable, c'est peut-être suffisant pour cet exemple simple, mais assurez-vous de ne pas laisser les observateurs suspendus à vos événements comme ça. Un moyen de gérer cela serait de rendre ObserverClass IDisposable et de laisser la méthode .Dispose faire le contraire du code dans le constructeur
Aucune vérification d'erreur effectuée, au moins une vérification nulle doit être effectuée dans le constructeur de l'ObserverClass
Dans ce modèle, vous avez des éditeurs qui feront un peu de logique et publieront un "événement".
Les éditeurs enverront ensuite leur événement uniquement aux abonnés qui se sont abonnés pour recevoir l'événement spécifique.
En C #, n'importe quel objet peut publier un ensemble d'événements auxquels d'autres applications peuvent s'abonner.
Lorsque la classe de publication déclenche un événement, toutes les applications souscrites sont notifiées.
La figure suivante montre ce mécanisme.
Exemple le plus simple possible sur les événements et les délégués en C #:
le code est explicite, j'ai également ajouté les commentaires pour effacer le code.
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}}
Production:
Entendu par l'auditeur
Entendu par Auditeur2
Entendu par l'auditeur
Entendu par Auditeur2
Je l'ai entendu par l'auditeur. . . (temps infini)
J'ai lié quelques-uns des excellents exemples ci-dessus (merci comme toujours à M. Skeet et M. Karlsen ) pour inclure quelques observables différents et j'ai utilisé une interface pour les suivre dans l'observateur et permis à l'observateur de pour "observer" n'importe quel nombre d'Observables via une liste interne:
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);}}}
Je sais que c'est vieux, mais ... Cela semble sans danger pour les threads, mais ce n'est pas le cas. Dans Observer.Add et Observer.Remove, la vérification de null doit être à l'intérieur du verrou. Dispose doit également acquérir le verrou et définir un indicateur isDispised. Sinon, un bon exemple complet.
user5151179
5
L'application du modèle d'observateur avec des délégués et des événements dans c # s'appelle «modèle d'événement» selon MSDN qui est une légère variation.
Dans cet article, vous trouverez des exemples bien structurés de la façon d'appliquer le modèle en c # à la fois de manière classique et en utilisant des délégués et des événements.
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);}}
Je ne voulais pas changer mon code source pour ajouter un observateur supplémentaire, j'ai donc écrit l'exemple simple suivant:
//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
plus tard (un moyen pratique, bien que paresseux et non idéal de désabonner tous les gestionnaires), de sorte que la vérification de null est toujours nécessaire.SomethingHappened?.Invoke(this, EventArgs.Empty);
Voici un exemple simple:
Remarque:
la source
Dans ce modèle, vous avez des éditeurs qui feront un peu de logique et publieront un "événement".
Les éditeurs enverront ensuite leur événement uniquement aux abonnés qui se sont abonnés pour recevoir l'événement spécifique.
En C #, n'importe quel objet peut publier un ensemble d'événements auxquels d'autres applications peuvent s'abonner.
Lorsque la classe de publication déclenche un événement, toutes les applications souscrites sont notifiées.
La figure suivante montre ce mécanisme.
Exemple le plus simple possible sur les événements et les délégués en C #:
le code est explicite, j'ai également ajouté les commentaires pour effacer le code.
Production:
Entendu par l'auditeur
Entendu par Auditeur2
Entendu par l'auditeur
Entendu par Auditeur2
Je l'ai entendu par l'auditeur. . . (temps infini)
la source
J'ai lié quelques-uns des excellents exemples ci-dessus (merci comme toujours à M. Skeet et M. Karlsen ) pour inclure quelques observables différents et j'ai utilisé une interface pour les suivre dans l'observateur et permis à l'observateur de pour "observer" n'importe quel nombre d'Observables via une liste interne:
la source
L'application du modèle d'observateur avec des délégués et des événements dans c # s'appelle «modèle d'événement» selon MSDN qui est une légère variation.
Dans cet article, vous trouverez des exemples bien structurés de la façon d'appliquer le modèle en c # à la fois de manière classique et en utilisant des délégués et des événements.
Explorer le modèle de conception d'observateur
la source
la source
Je ne voulais pas changer mon code source pour ajouter un observateur supplémentaire, j'ai donc écrit l'exemple simple suivant:
la source
Quelque chose comme ça:
. modèle d'observateur C # avec événement . lien vers le référentiel
la source