2013-03-05 31 views
7

Các Extensions phản ứng cho phép bạn dễ dàng đăng ký vào một sự kiện sử dụng Observable.FromEventPattern, nhưng tôi không thể tìm thấy bất cứ điều gì về cách bạn có thể thực hiện một sự kiện khi bạn có một IObservable.Làm thế nào để thực hiện một sự kiện sử dụng phản ứng Extensions

Tình huống của tôi là: Tôi cần triển khai giao diện chứa sự kiện. Sự kiện đó được cho là được gọi bất cứ khi nào một giá trị nhất định của đối tượng của tôi thay đổi và vì lý do an toàn chủ đề, tôi cần phải gọi sự kiện này trên một số SynchronizationContext nhất định. Tôi cũng phải gọi mỗi người xử lý sự kiện với giá trị hiện tại khi đăng ký.

public interface IFooWatcher 
{ 
    event FooChangedHandler FooChanged; 
} 

Bắt một quan sát được rằng những gì tôi muốn là khá dễ dàng với Rx sử dụng BehaviorSubject:

public class FooWatcher 
{ 
    private readonly BehaviorSubject<Foo> m_subject; 
    private readonly IObservable<Foo> m_observable; 

    public FooWatcher(SynchronizationContext synchronizationContext, Foo initialValue) 
    { 
     m_subject = new BehaviorSubject<Foo>(initialValue); 
     m_observable = m_subject 
      .DistinctUntilChanged() 
      .ObserveOn(synchronizationContext); 
    } 

    public event FooChangedHandler FooChanged 
    { 
     add { /* ??? */ } 
     remove { /* ??? */ } 
    } 
} 

Bây giờ tôi đang tìm kiếm một cách dễ dàng để có addremove chức năng đăng ký và huỷ đăng ký các thông qua FooChangedHandler dưới dạng Observer<Foo> trên m_observable. Triển khai hiện tại của tôi trông giống như sau:

add 
    { 
     lock (m_lock) 
     { 
      IDisposable disp = m_observable.Subscribe(value); 
      m_registeredObservers.Add(
       new KeyValuePair<FooChangedHandler, IDisposable>(
        value, disp)); 
     } 
    } 

    remove 
    { 
     lock (m_lock) 
     { 
      KeyValuePair<FooChangedHandler, IDisposable> observerDisposable = 
       m_registeredObservers 
        .First(pair => object.Equals(pair.Key, value)); 
      m_registeredObservers.Remove(observerDisposable); 
      observerDisposable.Value.Dispose(); 
     } 
    } 

Tuy nhiên, tôi hy vọng sẽ tìm thấy một giải pháp dễ dàng hơn, vì tôi cần triển khai một số sự kiện này (với các loại trình xử lý khác nhau). Tôi đã cố gắng tung ra giải pháp chung của riêng mình nhưng nó tạo ra một số vấn đề cần phải làm việc xung quanh (đặc biệt, cách bạn làm việc chung với một đại biểu có tham số T), vì vậy tôi muốn tìm giải pháp hiện có khoảng cách theo hướng này - cũng giống như FromEventPattern làm ngược lại.

Trả lời

2

Bạn có thể làm điều này:

public event FooChangedHandler FooChanged 
{ 
    add { m_observable.ToEvent().OnNext += value; } 
    remove { m_observable.ToEvent().OnNext -= value; } 
} 

Tuy nhiên, trên phương thức remove, tôi nghĩ có lẽ bạn chỉ có thể muốn vứt bỏ đăng ký ... hoặc có lẽ nhận được hành động từ ToEvent() và cửa hàng như một thành viên. Chưa được kiểm tra.

EDIT: Bạn sẽ phải sử dụng Hành động thay vì đại biểu FooChangedHandler, tuy nhiên.

EDIT 2: Đây là phiên bản thử nghiệm. Tôi cho rằng bạn cần phải sử dụng FooChangedHandler, tuy nhiên, kể từ khi bạn có một loạt các xử lý trước tồn tại?

void Main() 
{ 
    IObservable<Foo> foos = new [] { new Foo { X = 1 }, new Foo { X = 2 } }.ToObservable(); 
    var watcher = new FooWatcher(SynchronizationContext.Current, new Foo { X = 12 }); 
    watcher.FooChanged += o => o.X.Dump(); 
    foos.Subscribe(watcher.Subject.OnNext); 
} 

// Define other methods and classes here 

//public delegate void FooChangedHandler(Foo foo); 
public interface IFooWatcher 
{ 
    event Action<Foo> FooChanged; 
} 

public class Foo { 
    public int X { get; set; } 
} 
public class FooWatcher 
{ 

    private readonly BehaviorSubject<Foo> m_subject; 
    public BehaviorSubject<Foo> Subject { get { return m_subject; } } 
    private readonly IObservable<Foo> m_observable; 

    public FooWatcher(SynchronizationContext synchronizationContext, Foo initialValue) 
    { 
     m_subject = new BehaviorSubject<Foo>(initialValue); 

     m_observable = m_subject 
      .DistinctUntilChanged(); 
    } 

    public event Action<Foo> FooChanged 
    { 
     add { m_observable.ToEvent().OnNext += value; } 
     remove { m_observable.ToEvent().OnNext -= value; } 
    } 
} 
+0

Cảm ơn, tôi đã không nhìn thấy 'ToEvent()' trước đây! Đây là một phần lớn của câu đố. Việc chuyển đổi giữa 'Action ' và 'FooChangedHandler' vẫn là một vấn đề (vâng, tiếc là chúng ta đang mắc kẹt với những điều này).Vấn đề: Các đại biểu là bình đẳng nếu họ có cùng đích và phương pháp, nhưng bình đẳng này chỉ đạt tới một cấp - nếu bạn có một đại biểu và bọc nó trong hai trường hợp 'Action ' riêng biệt, chúng sẽ bằng nhau, nhưng nếu bạn quấn cả hai 'Hành động ' các trường hợp khác 'Hành động ' mỗi, * những * sẽ * không * bằng nhau. Tuy nhiên, tôi nghĩ rằng tôi đã thấy một giải pháp cho điều này rồi ... – Medo42

+1

Ah vâng, đây là: http://stackoverflow.com/a/9290684/575615 Tôi sẽ thử nghiệm vào ngày mai, cảm ơn một lần nữa! – Medo42

+0

Mọi thứ hoạt động như mong đợi ngay bây giờ. Sử dụng một giải pháp giống như một giải pháp được liên kết ở trên để chuyển đổi các đại biểu thành 'Action ' cho phép tôi viết một giải pháp chung ngắn gọn và ngọt ngào làm việc với 'ToEvent()' nội bộ. Không có nhiều thứ trong lớp đó, nhưng tôi muốn gộp cả 'Subject' và' IEventSource' lại thành một đối tượng. – Medo42

0

Vì bạn đã trộn ranh giới giữa mã phản ứng và mã bình thường hơn, bạn có thể làm phiên bản ít phản ứng hơn. Để bắt đầu đơn giản tuyên bố một sự kiện mô hình bình thường

public event FooChangedHandler FooChanged; 

protected void OnFooChanged(Foo) 
{ 
    var temp = FooChanged; 
    if (temp != null) 
    { 
    temp(new FooChangedEventArgs(Foo)); 
    } 
} 

và sau đó chỉ cần kết nối các quan sát để nó trong constructor

m_Observable.Subscribe(foo => OnFooChanged(foo)); 

Đó là không phải là rất Rx nhưng nó là vô cùng đơn giản.

+0

Bạn có thể bỏ qua 'OnFooChanged' hoàn toàn bằng cách gán một' delegate {} 'thành' FooChanged' ngay từ đầu, thu gọn giải pháp này thành hai dòng. Tuy nhiên, cách tiếp cận này không đáp ứng yêu cầu của tôi, bởi vì người đăng ký mới cho sự kiện không nhận được trạng thái hiện tại. Bên cạnh đó, vì 'SyncrhonizationContext' của tôi về cơ bản gửi đến một vòng lặp sự kiện, các thuê bao mới có thể nhận được các thay đổi (sử dụng các thuật ngữ mô hình bộ nhớ Java) đã xảy ra trước khi đăng ký được thiết lập. Điều này có lẽ không phải là một vấn đề, nhưng tôi thà tránh nó. – Medo42

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