如何使用无扩展

问题描述:

无功扩展可让您轻松地订阅使用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功能订阅和取消订阅传递FooChangedHandler作为的Observer<Foo>。我目前的实现看起来与此类似:

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恰恰相反。

你可以这样做:

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

然而,在删除,我想也许你只是可能要处置认购......或许得到ToEvent()和存储操作,作为成员。未经测试。

编辑︰你将不得不使用行动而不是FooChangedHandler委托,但是。

编辑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'之间的转换仍然是一个问题(是的,不幸的是我们被这些问题困住了)。问题:如果代表具有相同的目标和方法,那么代表是平等的,但是这种平等仅达到一个级别 - 如果您有一个代表并将其包装在两个单独的“Action ”实例中,那么它们将是平等的,但如果您将这两个'动作'在另一个'动作''实例'中,*这些*不会*相等。不过,我想我已经看到了这个解决方案...... – Medo42 2013-03-05 21:11:10

+1

啊,是的,这里是:http://*.com/a/9290684/575615我明天再测试一下,再次感谢! – Medo42 2013-03-05 21:18:14

+0

现在一切正常。使用类似上面链接的解决方案将代理转换为“Action ”的解决方案,使我可以在内部编写一个简短而通用的解决方案,该解决方案可与“ToEvent()”配合使用。这个类没有多少内容,但我想将'Subject'和'IEventSource'绑定在一个对象中。 – Medo42 2013-03-06 14:32:08

既然你已经在混合反应和更多的普通代码之间的界限,你可以做一个反应性较低的版本。要开始只是声明了一个正常的事件模式

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

您可以在开始时将'委托{}'分配给'FooChanged'完全省略'OnFooChanged',将此解决方案折叠为两行。但是,这种方法不符合我的要求,因为事件的新订户不会收到当前状态。此外,由于我的'SyncrhonizationContext'本质上是发布到一个事件循环中,所以新订阅者可以接收(使用Java内存模型条款)发生的更改 - 在订阅建立之前。这可能不是问题,但我宁愿避免它。 – Medo42 2013-03-06 09:08:09