2013-03-05 14 views
7

を使用してイベントを実装するためにどのように反応性の拡張機能を使用すると、簡単にObservable.FromEventPatternを使用してイベントをサブスクライブすることができ、私はあなたがIObservableを持っているときににイベントを実装する方法を上で何かを見つけることができません。反応性拡張

私の状況はこうです:私はイベントを含むインターフェースを実装する必要があります。このイベントは、オブジェクトの特定の値が変更されたときに呼び出されるはずです。スレッドの安全性の理由から、このイベントを特定のSynchronizationContextで呼び出す必要があります。私はまた、登録時に現在の値で各イベントハンドラを呼び出すことになっています。私が欲しいものをしても、その観察できるの取得

public interface IFooWatcher 
{ 
    event FooChangedHandler FooChanged; 
} 

は、Rxが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 { /* ??? */ } 
    } 
} 

今、私はaddremove機能が加入しており、合格を解除する簡単な方法を探していますFooChangedHandlerObserver<Foo>としてm_observableに設定します。私の現在の実装では、このようになります。

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

私は(異なるハンドラタイプの)これらのイベントのいくつかを実装する必要があるためしかし、私は、簡単に解決策を見つけることを願っています。私は自分のジェネリックソリューションを展開しようとしましたが、回避する必要があるいくつかの追加の問題が発生します(具体的には、代理人が一般的にTのパラメータを取る方法を一般的に扱う方法)。そのため、この方向の隙間 - ちょうどFromEventPatternと逆です。

答えて

2

あなたはこれを行うことができます:

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

ただし、削除の上に、私はあなただけのサブスクリプションを処分...または多分ToEvent()およびストアからのアクションを取得したいことがあり、おそらくだと思うとメンバー。テストされていない。

編集:ただし、FooChangedHandlerデリゲートの代わりにActionを使用する必要があります。

EDIT 2:テスト済みのバージョンです。しかし、あなたはこれらの既存のハンドラがたくさんあるので、FooChangedHandlerを使う必要があると思いますか?

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

ありがとうございました、私は前に 'ToEvent()'を見たことがありませんでした!これはすでにパズルの大きな部分です。 'Action 'と 'FooChangedHandler'の間の変換はまだ問題です(残念ながら、私たちはこれらに固執しています)。問題:デリゲートが同じターゲットとメソッドを持っている場合、デリゲートは等しいですが、この等価性は1つのレベルにしか達しません。デリゲートを持ち、2つの別々の 'Action 'インスタンスにラップすると、それらのインスタンスは等しくなります。 'アクション'インスタンスはそれぞれ別の 'アクション'にあります。*これらは*等しくありません。しかし、私はこれについての解決策を既に見たと思います... – Medo42

+1

ああ、ここにありました:http://stackoverflow.com/a/9290684/575615私はこの明日、大きな感謝を再びテストします! – Medo42

+0

すべてが期待通りに機能します。デリゲートを 'Action 'に変換するための上記のようなソリューションを使用すると、' ToEvent() 'で内部的に動作する短くて甘いジェネリックソリューションを書くことができました。そのクラスにはそれほど多くはありませんが、私は 'Subject'と' IEventSource'をひとつのオブジェクトにまとめたいと思っていました。 – Medo42

0

反応性コードと通常コードとの境界をすでに混在させているとすれば、反応性の低いバージョンを使用できます。起動するには、単に通常のイベントパターン

public event FooChangedHandler FooChanged; 

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

を宣言して、単に

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

コンストラクタで、それに観測可能に接続することは非常にRxのではないのですが、それは非常に簡単です。

+0

最初に 'delegate {}'を 'FooChanged'に代入することで' OnFooChanged'を完全に省略することができ、この解決法を2行に崩壊させることができます。ただし、イベントへの新規加入者が現在の状態を受信しないため、この方法は自分の要件を満たしません。さらに、私の 'SyncrhonizationContext'は基本的にイベントループにポストするので、新しいサブスクライバはサブスクリプションが確立される前に(Javaメモリモデルの用語を使用して)変更を受け取ることができます。これはおそらく問題ではありませんが、むしろそれを回避したいと思います。 – Medo42

関連する問題