Exemplo super simples de observador de C # / observável com delegados
131
Recentemente, comecei a pesquisar em C #, mas na minha vida não consigo entender como os delegados funcionam ao implementar o padrão observador / observável no idioma.
Alguém poderia me dar um exemplo super simples de como isso é feito? Eu já pesquisei isso, mas todos os exemplos que eu encontrei eram ou problema específico demasiado ou demasiado "inchado".
O padrão de observador geralmente é implementado com eventos .
Aqui está um exemplo:
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();}}
Veja o artigo vinculado para muito mais detalhes.
Observe que o exemplo acima usa o operador condicional nulo do C # 6 para implementar DoSomethingcom segurança para lidar com casos em SomethingHappenedque não foi inscrito e, portanto, é nulo. Se você estiver usando uma versão mais antiga do C #, precisará de um código como este:
@ Dinah: Isso não evita a verificação nula. Você ainda pode definir SomethingHappened = nullmais tarde (uma maneira prática, embora preguiçosa e não ideal, de cancelar a inscrição de todos os manipuladores), para que a verificação nula seja sempre necessária.
Dan Puzey
4
@ DanPuzey: Você pode fazer parte da classe, mas também pode ter certeza de que não faz isso - e outro código não pode fazê-lo, pois só pode se inscrever ou se desinscrever. Se você garantir que você nunca o definirá nulo deliberadamente na sua classe, não há problema em evitar a verificação nula.
quer
2
@ JonSkeet: é claro, eu estava esquecendo que você não pode fazer isso fora da classe. Desculpas!
Dan Puzey
2
Eu acho que você pode substituir todo o material em DoSomething comSomethingHappened?.Invoke(this, EventArgs.Empty);
Isso viola uma regra em que eu não solto o observador do observável, talvez seja bom o suficiente para este exemplo simples, mas certifique-se de não manter os observadores pendurados em seus eventos como esse. Uma maneira de lidar com isso seria tornar ObserverClass IDisposable e deixar o método .Dispose fazer o oposto do código no construtor
Nenhuma verificação de erro realizada, pelo menos uma verificação nula deve ser feita no construtor do ObserverClass
Nesse modelo, você tem editores que farão alguma lógica e publicarão um "evento".
Os editores enviarão o evento apenas para assinantes que se inscreveram para receber o evento específico.
Em C #, qualquer objeto pode publicar um conjunto de eventos nos quais outros aplicativos podem se inscrever.
Quando a classe de publicação gera um evento, todos os aplicativos inscritos são notificados.
A figura a seguir mostra esse mecanismo.
Exemplo mais simples possível em eventos e delegados em C #:
O código é auto-explicativo. Também adicionei os comentários para limpar o código.
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}}
Eu juntei alguns dos grandes exemplos acima (obrigado como sempre ao Sr. Skeet e Karlsen ) por incluir alguns Observáveis diferentes e usei uma interface para acompanhá-los no Observador e permiti que o Observador para "observar" qualquer número de Observáveis por meio de uma lista interna:
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);}}}
Eu sei que isso é antigo, mas ... Parece seguro para tópicos, mas não é. Em Observer.Add e Observer.Remove, a verificação nula precisa estar dentro do bloqueio. Descarte também deve adquirir o bloqueio e definir um sinalizador isDispised. Caso contrário, um bom exemplo completo.
user5151179
5
A aplicação do Padrão do Observador com representantes e eventos em c # é denominada "Padrão do Evento" de acordo com o MSDN, o que é uma pequena variação.
Neste artigo, você encontrará exemplos bem estruturados de como aplicar o padrão no c #, tanto da maneira clássica quanto do uso de delegados e eventos.
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);}}
Eu não queria mudar meu código fonte para adicionar mais observadores, então escrevi o seguinte exemplo simples:
//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
mais tarde (uma maneira prática, embora preguiçosa e não ideal, de cancelar a inscrição de todos os manipuladores), para que a verificação nula seja sempre necessária.SomethingHappened?.Invoke(this, EventArgs.Empty);
Aqui está um exemplo simples:
Nota:
fonte
Nesse modelo, você tem editores que farão alguma lógica e publicarão um "evento".
Os editores enviarão o evento apenas para assinantes que se inscreveram para receber o evento específico.
Em C #, qualquer objeto pode publicar um conjunto de eventos nos quais outros aplicativos podem se inscrever.
Quando a classe de publicação gera um evento, todos os aplicativos inscritos são notificados.
A figura a seguir mostra esse mecanismo.
Exemplo mais simples possível em eventos e delegados em C #:
O código é auto-explicativo. Também adicionei os comentários para limpar o código.
Resultado:
Ouvido pelo ouvinte
Heard It por Listener2
Ouvido pelo ouvinte
Heard It por Listener2
Ouvido pelo ouvinte. . . (tempos infinitos)
fonte
Eu juntei alguns dos grandes exemplos acima (obrigado como sempre ao Sr. Skeet e Karlsen ) por incluir alguns Observáveis diferentes e usei uma interface para acompanhá-los no Observador e permiti que o Observador para "observar" qualquer número de Observáveis por meio de uma lista interna:
fonte
A aplicação do Padrão do Observador com representantes e eventos em c # é denominada "Padrão do Evento" de acordo com o MSDN, o que é uma pequena variação.
Neste artigo, você encontrará exemplos bem estruturados de como aplicar o padrão no c #, tanto da maneira clássica quanto do uso de delegados e eventos.
Explorando o padrão de design do observador
fonte
fonte
Eu não queria mudar meu código fonte para adicionar mais observadores, então escrevi o seguinte exemplo simples:
fonte
Algo assim:
. padrão de observador C # com evento . link para o repositório
fonte