2009-08-08 30 views
114

Recientemente comencé a profundizar en C#, pero a mi entender no sé cómo funcionan los delegados al implementar el patrón observador/observable en el idioma.Ejemplo super simple de C# observador/observable con delegados

¿Podría alguien darme un ejemplo súper simple de cómo se hace? I tengo en Google esto, pero todos los ejemplos que encontré fueron demasiado específicos del problema o demasiado "hinchado".

Respuesta

188

El patrón de observador generalmente se implementa con events.

He aquí un ejemplo:

using System; 

class Observable 
{ 
    public event EventHandler SomethingHappened; 

    public void DoSomething() 
    { 
     EventHandler handler = SomethingHappened; 
     if (handler != null) 
     { 
      handler(this, EventArgs.Empty); 
     } 
    } 
} 

class Observer 
{ 
    public void HandleEvent(object sender, EventArgs args) 
    { 
     Console.WriteLine("Something happened to " + sender); 
    } 
} 

class Test 
{ 
    static void Main() 
    { 
     Observable observable = new Observable(); 
     Observer observer = new Observer(); 
     observable.SomethingHappened += observer.HandleEvent; 

     observable.DoSomething(); 
    } 
} 

Véase el artículo enlazado por mucho más detalle.

+15

Para ahorrarse unas pocas líneas y evitar la verificación nula, inicialice su evento de esta manera: http://stackoverflow.com/questions/340610/create-empty-c-event-handlers-automatically/ 340618 # 340618 – Dinah

+1

@Dinah: Eso no evita la verificación nula. Todavía puede establecer 'SomethingHappened = null' más tarde (una forma práctica pero floja y no ideal para anular la suscripción a todos los manejadores), por lo que la verificación nula siempre es necesaria. –

+3

@DanPuzey: Puedes dentro de la clase, pero igualmente puedes asegurarte de que * no * lo haces, y * el otro código no puede hacerlo, ya que solo puede suscribirse y anular la suscripción. Si se asegura de que nunca lo configure como nulo deliberadamente dentro de su clase, está bien evitar la verificación nula. –

16

Aquí está un ejemplo sencillo:

public class ObservableClass 
{ 
    private Int32 _Value; 

    public Int32 Value 
    { 
     get { return _Value; } 
     set 
     { 
      if (_Value != value) 
      { 
       _Value = value; 
       OnValueChanged(); 
      } 
     } 
    } 

    public event EventHandler ValueChanged; 

    protected void OnValueChanged() 
    { 
     if (ValueChanged != null) 
      ValueChanged(this, EventArgs.Empty); 
    } 
} 

public class ObserverClass 
{ 
    public ObserverClass(ObservableClass observable) 
    { 
     observable.ValueChanged += TheValueChanged; 
    } 

    private void TheValueChanged(Object sender, EventArgs e) 
    { 
     Console.Out.WriteLine("Value changed to " + 
      ((ObservableClass)sender).Value); 
    } 
} 

public class Program 
{ 
    public static void Main() 
    { 
     ObservableClass observable = new ObservableClass(); 
     ObserverClass observer = new ObserverClass(observable); 
     observable.Value = 10; 
    } 
} 

Nota:

  • Esto viola una regla en la que no desenganchar el observador de lo observable, esto es tal vez lo suficientemente bueno para este ejemplo sencillo , pero asegúrate de no mantener a los observadores colgando de tus eventos de esa manera. Una forma de manejar esto sería hacer que ObserverClass IDisposable, y permitir que el método .Dispose haga lo contrario al código en el constructor
  • Sin verificación de errores realizada, al menos se debe hacer una verificación nula en el constructor del ObserverClass
5

Comprobar esta introducción al marco Rx que utiliza maravillosa IObserver-IObservable no bloqueante modelo de programación asíncrona introducing-rx-linq-to-events

+0

Algo bueno si usas Silverlight, desafortunadamente no es de mucha utilidad para el resto de nosotros. Es una pena. –

+0

Puede convertirlo a .net clr. Vea aquí http://evain.net/blog/articles/2009/07/30/rebasing-system-reactive-to-the-net-clr – Ray

+0

También será una parte de .NET 4.0 (y no solo para Silverlight) – Ray

5

he atado juntos un par de los grandes ejemplos anteriores (gracias como siempre a Mr. Skeet y Mr. Karlsen) para incluir un par de Observables diferentes y utilizar una interfaz para mantener t estante de ellos en el observador y permitió que el Observador a "observar" cualquier número de observables a través de una lista interna:

namespace ObservablePattern 
{ 
    using System; 
    using System.Collections.Generic; 

    internal static class Program 
    { 
     private static void Main() 
     { 
      var observable = new Observable(); 
      var anotherObservable = new AnotherObservable(); 

      using (IObserver observer = new Observer(observable)) 
      { 
       observable.DoSomething(); 
       observer.Add(anotherObservable); 
       anotherObservable.DoSomething(); 
      } 

      Console.ReadLine(); 
     } 
    } 

    internal interface IObservable 
    { 
     event EventHandler SomethingHappened; 
    } 

    internal sealed class Observable : IObservable 
    { 
     public event EventHandler SomethingHappened; 

     public void DoSomething() 
     { 
      var handler = this.SomethingHappened; 

      Console.WriteLine("About to do something."); 
      if (handler != null) 
      { 
       handler(this, EventArgs.Empty); 
      } 
     } 
    } 

    internal sealed class AnotherObservable : IObservable 
    { 
     public event EventHandler SomethingHappened; 

     public void DoSomething() 
     { 
      var handler = this.SomethingHappened; 

      Console.WriteLine("About to do something different."); 
      if (handler != null) 
      { 
       handler(this, EventArgs.Empty); 
      } 
     } 
    } 

    internal interface IObserver : IDisposable 
    { 
     void Add(IObservable observable); 

     void Remove(IObservable observable); 
    } 

    internal sealed class Observer : IObserver 
    { 
     private readonly Lazy<IList<IObservable>> observables = 
      new Lazy<IList<IObservable>>(() => new List<IObservable>()); 

     public Observer() 
     { 
     } 

     public Observer(IObservable observable) : this() 
     { 
      this.Add(observable); 
     } 

     public void Add(IObservable observable) 
     { 
      if (observable == null) 
      { 
       return; 
      } 

      lock (this.observables) 
      { 
       this.observables.Value.Add(observable); 
       observable.SomethingHappened += HandleEvent; 
      } 
     } 

     public void Remove(IObservable observable) 
     { 
      if (observable == null) 
      { 
       return; 
      } 

      lock (this.observables) 
      { 
       observable.SomethingHappened -= HandleEvent; 
       this.observables.Value.Remove(observable); 
      } 
     } 

     public void Dispose() 
     { 
      for (var i = this.observables.Value.Count - 1; i >= 0; i--) 
      { 
       this.Remove(this.observables.Value[i]); 
      } 
     } 

     private static void HandleEvent(object sender, EventArgs args) 
     { 
      Console.WriteLine("Something happened to " + sender); 
     } 
    } 
} 
0

Yo no he deseado cambiar el código fuente para agregar observador adicional, por lo que he escrito siguiente ejemplo sencillo:

//EVENT DRIVEN OBSERVER PATTERN 
public class Publisher 
{ 
    public Publisher() 
    { 
     var observable = new Observable(); 
     observable.PublishData("Hello World!"); 
    } 
} 

//Server will send data to this class's PublishData method 
public class Observable 
{ 
    public event Receive OnReceive; 

    public void PublishData(string data) 
    { 
     //Add all the observer below 
     //1st observer 
     IObserver iObserver = new Observer1(); 
     this.OnReceive += iObserver.ReceiveData; 
     //2nd observer 
     IObserver iObserver2 = new Observer2(); 
     this.OnReceive += iObserver2.ReceiveData; 

     //publish data 
     var handler = OnReceive; 
     if (handler != null) 
     { 
      handler(data); 
     } 
    } 
} 

public interface IObserver 
{ 
    void ReceiveData(string data); 
} 

//Observer example 
public class Observer1 : IObserver 
{ 
    public void ReceiveData(string data) 
    { 
     //sample observers does nothing with data :) 
    } 
} 

public class Observer2 : IObserver 
{ 
    public void ReceiveData(string data) 
    { 
     //sample observers does nothing with data :) 
    } 
} 
1
/**********************Simple Example ***********************/  

class Program 
     { 
      static void Main(string[] args) 
      { 
       Parent p = new Parent(); 
      } 
     } 

     //////////////////////////////////////////// 

     public delegate void DelegateName(string data); 

     class Child 
     { 
      public event DelegateName delegateName; 

      public void call() 
      { 
       delegateName("Narottam"); 
      } 
     } 

     /////////////////////////////////////////// 

     class Parent 
     { 
      public Parent() 
      { 
       Child c = new Child(); 
       c.delegateName += new DelegateName(print); 
       //or like this 
       //c.delegateName += print; 
       c.call(); 
      } 

      public void print(string name) 
      { 
       Console.WriteLine("yes we got the name : " + name); 
      } 
     } 
0

Algo como esto:

// interface implementation publisher 
public delegate void eiSubjectEventHandler(eiSubject subject); 

public interface eiSubject 
{ 
    event eiSubjectEventHandler OnUpdate; 

    void GenereteEventUpdate(); 

} 

// class implementation publisher 
class ecSubject : eiSubject 
{ 
    private event eiSubjectEventHandler _OnUpdate = null; 
    public event eiSubjectEventHandler OnUpdate 
    { 
     add 
     { 
      lock (this) 
      { 
       _OnUpdate -= value; 
       _OnUpdate += value; 
      } 
     } 
     remove { lock (this) { _OnUpdate -= value; } } 
    } 

    public void GenereteEventUpdate() 
    { 
     eiSubjectEventHandler handler = _OnUpdate; 

     if (handler != null) 
     { 
      handler(this); 
     } 
    } 

} 

// interface implementation subscriber 
public interface eiObserver 
{ 
    void DoOnUpdate(eiSubject subject); 

} 

// class implementation subscriber 
class ecObserver : eiObserver 
{ 
    public virtual void DoOnUpdate(eiSubject subject) 
    { 
    } 
} 

. observer pattern C# with event . link to the repository

4

aplicar el patrón Observador con los delegados y eventos en C# se llama "Patrón de eventos" acuerdo con MSDN que es una ligera variación.

En este artículo encontrará ejemplos bien estructurados de cómo aplicar el patrón en C# tanto de forma clásica como utilizando delegados y eventos.

Exploring the Observer Design Pattern

public class Stock 
{ 

    //declare a delegate for the event 
    public delegate void AskPriceChangedHandler(object sender, 
      AskPriceChangedEventArgs e); 
    //declare the event using the delegate 
    public event AskPriceChangedHandler AskPriceChanged; 

    //instance variable for ask price 
    object _askPrice; 

    //property for ask price 
    public object AskPrice 
    { 

     set 
     { 
      //set the instance variable 
      _askPrice = value; 

      //fire the event 
      OnAskPriceChanged(); 
     } 

    }//AskPrice property 

    //method to fire event delegate with proper name 
    protected void OnAskPriceChanged() 
    { 

     AskPriceChanged(this, new AskPriceChangedEventArgs(_askPrice)); 

    }//AskPriceChanged 

}//Stock class 

//specialized event class for the askpricechanged event 
public class AskPriceChangedEventArgs : EventArgs 
{ 

    //instance variable to store the ask price 
    private object _askPrice; 

    //constructor that sets askprice 
    public AskPriceChangedEventArgs(object askPrice) { _askPrice = askPrice; } 

    //public property for the ask price 
    public object AskPrice { get { return _askPrice; } } 

}//AskPriceChangedEventArgs 
12

En este modelo, que tienen los editores que van a hacer algo de lógica y publicar un "evento".
Los editores luego enviarán su evento solo a los suscriptores que se hayan suscrito para recibir el evento específico.

En C#, cualquier objeto puede publicar un conjunto de eventos al que otras aplicaciones pueden suscribirse.
Cuando la clase de publicación plantea un evento, se notifican todas las aplicaciones suscritas.
La siguiente figura muestra este mecanismo.

enter image description here

ejemplo más simple posible en Eventos y delegados en C#:

código se explica por sí, también he añadido los comentarios para limpiar el código.

using System; 

public class Publisher //main publisher class which will invoke methods of all subscriber classes 
{ 
    public delegate void TickHandler(Publisher m, EventArgs e); //declaring a delegate 
    public TickHandler Tick;  //creating an object of delegate 
    public EventArgs e = null; //set 2nd paramter empty 
    public void Start()  //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! 
      } 
     } 
    } 
} 

public class Subscriber1    //1st subscriber class 
{ 
    public void Subscribe(Publisher m) //get the object of pubisher class 
    { 
     m.Tick += HeardIt;    //attach listener class method to publisher class delegate object 
    } 
    private void HeardIt(Publisher m, EventArgs e) //subscriber class method 
    { 
     System.Console.WriteLine("Heard It by Listener"); 
    } 

} 
public class Subscriber2     //2nd subscriber class 
{ 
    public void Subscribe2(Publisher m) //get the object of pubisher class 
    { 
     m.Tick += HeardIt;    //attach listener class method to publisher class delegate object 
    } 
    private void HeardIt(Publisher m, EventArgs e) //subscriber class method 
    { 
     System.Console.WriteLine("Heard It by Listener2"); 
    } 

} 

class Test 
{ 
    static void Main() 
    { 
     Publisher m = new Publisher();  //create an object of publisher class which will later be passed on subscriber classes 
     Subscriber1 l = new Subscriber1(); //create object of 1st subscriber class 
     Subscriber2 l2 = new Subscriber2(); //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 
    } 
} 

Salida:

lo oyó por Listener

lo oyó por listener2

lo oyó por Listener

lo oyó por listener2

lo oyó por Escucha er . . . (infinitas veces)

+1

Esto es genial, gracias por el simple y claro ejemplo. –

+1

Me salvaste el día :) Gracias ... –