2009-08-08 62 views
114

Gần đây tôi đã bắt đầu đào sâu vào C# nhưng tôi không thể tìm ra cách các đại biểu làm việc khi triển khai mẫu quan sát/quan sát bằng ngôn ngữ.Ví dụ siêu đơn giản về quan sát C#/quan sát được với các đại biểu

Ai đó có thể cho tôi một ví dụ siêu đơn giản về cách thực hiện? Tôi đã giải quyết vấn đề này, nhưng tất cả các ví dụ tôi thấy là quá cụ thể hoặc quá "cồng kềnh".

Trả lời

188

Mẫu quan sát viên thường được triển khai với events.

Dưới đây là một ví dụ:

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(); 
    } 
} 

Xem bài viết liên quan cho một chi tiết hơn rất nhiều.

+15

Để tiết kiệm cho mình một vài dòng và tránh kiểm tra trống, hãy khởi tạo sự kiện của bạn như sau: http://stackoverflow.com/questions/340610/create-empty-c-event-handlers-automatically/ 340618 # 340618 – Dinah

+1

@Dinah: Điều đó không tránh được việc kiểm tra null. Bạn vẫn có thể thiết lập 'SomethingHappened = null' sau (một cách tiện dụng nếu không có lý do lười biếng và không lý tưởng để hủy đăng ký tất cả các trình xử lý), vì vậy việc kiểm tra null luôn luôn cần thiết. –

+3

@DanPuzey: Bạn có thể trong lớp học, nhưng bằng nhau bạn có thể chắc chắn rằng bạn * không * làm điều đó - và * mã khác * không thể làm điều đó, vì nó chỉ có thể đăng ký và hủy đăng ký. Nếu bạn đảm bảo rằng bạn không bao giờ đặt nó thành null một cách có chủ ý trong lớp của bạn, bạn nên tránh kiểm tra null. –

16

Dưới đây là một ví dụ đơn giản:

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; 
    } 
} 

Lưu ý:

  • này vi phạm một quy tắc trong mà tôi không mở móc người quan sát từ quan sát, điều này có lẽ là đủ tốt cho ví dụ đơn giản này , nhưng hãy chắc chắn rằng bạn không giữ cho các nhà quan sát treo các sự kiện của bạn như thế. Một cách để xử lý điều này là làm cho ObserverClass IDisposable, và để phương thức .Dispose làm ngược lại mã trong hàm khởi tạo
  • Không thực hiện kiểm tra lỗi, ít nhất một kiểm tra null phải được thực hiện trong hàm tạo của ObserverClass
5

Kiểm tra phần giới thiệu này để Rx Khung trong đó sử dụng tuyệt vời IObserver-IObservable non-blocking mô hình lập trình asyncronous introducing-rx-linq-to-events

+0

Một điều tốt nếu bạn sử dụng Silverlight - tiếc là không sử dụng nhiều cho phần còn lại của chúng tôi. Một sự xấu hổ. –

+0

Bạn có thể chuyển đổi nó thành .net clr. Xem tại đây http://evain.net/blog/articles/2009/07/30/rebasing-system-reactive-to-the-net-clr – Ray

+0

Ngoài ra, nó sẽ là một phần của .NET 4.0 (và không chỉ cho Silverlight) – Ray

5

tôi đã gắn bó với nhau một vài trong những ví dụ tuyệt vời ở trên (cảm ơn bạn như mọi khi để Mr. SkeetMr. Karlsen) để bao gồm một vài Đài quan sát khác nhau và sử dụng giao diện để giữ rack của chúng trong Observer và cho phép quan sát đến để "quan sát" bất kỳ số lượng quan sát thông qua một danh sách nội bộ:

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

tôi did't muốn thay đổi mã nguồn của tôi để bổ sung thêm quan sát viên, vì vậy tôi đã viết sau ví dụ đơn giản:

//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

Something như thế này:

// 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

Áp dụng Observer Pattern với các đại biểu và các sự kiện trong C# được đặt tên "Sự kiện Pattern" theo MSDN đó là sự thay đổi nhỏ.

Trong bài viết này, bạn sẽ tìm thấy các ví dụ có cấu trúc tốt về cách áp dụng mẫu trong C# cả cách cổ điển và sử dụng đại biểu và sự kiện.

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

Trong mô hình này, bạn có các nhà xuất bản sẽ làm một số logic và xuất bản một "sự kiện".
Sau đó, nhà xuất bản sẽ chỉ gửi sự kiện của họ tới những người đăng ký nhận sự kiện cụ thể.

Trong C#, bất kỳ đối tượng nào đều có thể xuất bản một tập hợp các sự kiện mà các ứng dụng khác có thể đăng ký.
Khi lớp xuất bản tăng sự kiện, tất cả các ứng dụng đã đăng ký sẽ được thông báo.
Hình dưới đây cho thấy cơ chế này.

enter image description here

đơn giản Ví dụ có thể trên sự kiện và đại biểu trong C#:

code đang tự giải thích, Ngoài ra tôi đã thêm những ý kiến ​​rõ ràng ra các mã.

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 
    } 
} 

Output:

Nghe Nó bởi Listener

Nghe Nó bởi Listener2

Nghe Nó bởi Listener

Nghe Nó bởi Listener2

Nghe Nó bởi Nghe er . . . (số lần vô hạn)

+1

Điều này thật tuyệt vời, cảm ơn ví dụ đơn giản và rõ ràng. –

+1

Bạn đã lưu ngày của tôi :) Cảm ơn ... –

Các vấn đề liên quan