当有Start和Stop方法时,使用Reactive Extensions包装事件

时间:2013-04-30 20:11:05

标签: c# events system.reactive

我正在尝试使用IObservable包装设备类。没有Rx就像这样使用:

device.IncomingData += data => { /* do something with float[] data */ };
device.Start(500);
// Something...
device.Stop(); 

到目前为止,我有一个类似下面的包装器类,它跟踪有多少观察者正在使用流并停止并相应地启动它。

是否没有内置的方法来跟踪Rx的观察者?

private class ObservableWrapper
{
  private int _observers;

  public ObservableStreamer(IDevice device)
  {
    Stream = Observable.FromEvent<float[]>(
      e =>
        {
          device.IncomingData += e;
          int obs = Interlocked.Increment(ref _observers);

          if (obs < 2)
            device.Start();
        },
      e =>
        {
          device.IncomingData -= e;
          int obs = Interlocked.Decrement(ref _observers);

          if (obs < 1)
            device.Stop();
        });
  }

  public IObservable<float[]> Stream { get; private set; }
} 

var wrap = new ObservableWrapper(device);
wrap.Stream.Subscribe(data => { /* do something with float[] data */ });

2 个答案:

答案 0 :(得分:3)

构建自定义observable时,不要费心添加引用计数或连接共享。如果您需要这些功能,可以分别使用RefCountPublish添加这些功能。您自己也应该几乎没有理由自己实施I(Connectable)Observable

至于您的具体用例,它可能是一个相当简单的扩展方法:

public static DeviceExtensions
{
    public static IObservable<float[]> AsObservable(this Device device)
    {
        return Observable.CreateWithDisposable<float[]>(obs =>
        {
            IDisposable disposable = Observable.FromEvent<float[]>(
                e => device.IncomingData += e,
                e => device.IncomingData -= e
                )
                .Finally(device.Stop)
                .Subscribe(obs);

            device.Start();

            return disposable;
        });
    }
}

现在你可以像这样使用它:

IObservable<float[]> observableData = device.AsObservable()
    .RefCount(); // If you need ref counting

observableData.Subscribe(data => {});

observableData.Subscribe(data => {});

答案 1 :(得分:2)

听起来你实际上映射的更好地使用较少的IConnectableObservable。 Connect方法将调用Start并返回一个调用Stop的一次性用法。 Subscribe方法将转发到Observable.FromEvent(没有所有引用计数)。然后,您可以使用RefCount将其重新转换为常规IObservable。与您当前的实现一样,您必须对所有订阅使用相同的实例,否则计数将无法正常工作。

例如(未编译的代码传入):

class ObservableDevice : IConnectableObservable
{
    public ObservableDevice(IDevice device)
    {
        _device = device;
        //not strictly necessary to cache this, but this way you only
        //create it once
        _stream = Observable.FromEvent<...>(...);
    }

    private IDevice _device;
    private IObservable _stream;

    public IDisposable Connect()
    {
        //it's up to you if you want/need to guard against multiple starts
        _device.Start();
        return Disposable.Create(() => { _device.Stop(); });
    }

    public IDisposable Subscribe(IObserver observer)
    {
        //error checking if you want, or just defer to 
        //_stream.Subscribe's error checking
        return _stream.Subscribe(observer);
    }
}