简体   繁体   English

可观察到Rx中的回调

[英]Observable for a callback in Rx

I'm looking for an elegant way to create an Observable from a plain callback delegate with Rx, something similar to Observable.FromEventPattern ? 我正在寻找一种优雅的方法来创建一个带有Rx的普通回调委托的Observable ,类似于Observable.FromEventPattern

Say, I'm wrapping Win32 EnumWindows API which calls back the EnumWindowsProc I provide. 说,我正在包装Win32 EnumWindows API,它回调我提供的EnumWindowsProc

I know I could create a temporary C# event adapter for this callback and pass it FromEventPattern . 我知道我可以为这个回调创建一个临时的C#事件适配器并将其传递给FromEventPattern Also, I could probably implement IObservable manually, so it would call IObserver.OnNext from my EnumWindowsProc callback. 此外,我可能手动实现IObservable ,因此它将从我的EnumWindowsProc回调调用IObserver.OnNext

Is the there an existing pattern for wrapping a callback in Rx that I'm missing? 在Rx中包含回调的现有模式是否缺少?

You can use a Subject<T> which can be used to move from the imperative programming world into the functional world of Rx. 您可以使用Subject<T> ,它可用于从命令式编程世界转移到Rx的功能世界。

Subject<T> implements both IObservable<T> and IObserver<T> , so you can call its OnNext , OnError and OnCompleted methods and the subscribers will be notified. Subject<T>实现IObservable<T>IObserver<T> ,因此您可以调用其OnNextOnErrorOnCompleted方法,并通知订阅者。

If you want to expose the Subject<T> as a property then you should do so using .AsObservable() as this hides the fact that the IObservable<T> is in fact a Subject<T> . 如果要将Subject<T>公开为属性,则应使用.AsObservable()因为这隐藏了IObservable<T>实际上是Subject<T>事实。 It makes things such as ((Subject<string>) obj.Event).OnNext("Foo") impossible. 它使诸如((Subject<string>) obj.Event).OnNext("Foo")变得不可能。

Keep in mind that callbacks like the one used in EnumWindows are subtly different than Rx. 请记住,像EnumWindows中使用的回调与Rx略有不同。 Specifically, the callback can communicate back to the caller through its return value. 具体来说,回调可以通过其返回值与呼叫者通信。 Rx observers cannot do this. Rx观察员不能这样做。 Also, callbacks can receive multiple parameters, but Rx observers receive a single value. 此外,回调可以接收多个参数,但Rx观察者会收到一个值。 So you need to wrap the multiple parameters into a single object. 因此,您需要将多个参数包装到单个对象中。

With that in mind, an alternative to using a Subject is to use Observable.Create . 考虑到这一点,使用Subject的替代方法是使用Observable.Create This way you only register the callback when there is actually an observer and you unregister it if that observer unsubscribes. 这样,只有在实际存在观察者时才注册回调,如果该观察者取消订阅,则取消注册。

For the synchronous API you've used an example, you might do something like this. 对于您使用过的示例的同步API,您可能会这样做。 Note in this example there is not actually a way to unregister the callback mid-stream since it all happens synchronously before we can ever return the unsubscribe disposable. 请注意,在此示例中实际上没有办法取消注册回调中间流,因为它在我们可以返回取消订阅一次性之前同步发生。

public static IObservable<Foo> WrapFooApi(string arg1, string arg2)
{
    return Observable.Create<Foo>(observer =>
    {
        FooApi.enumerate(arg1, arg2, e =>
        {
            observer.OnNext(new Foo(e));
            return true;
        });

        // In your case, FooApi.enumerate is actually synchronous
        // so when we get to this line of code, we know
        // the stream is complete.
        observer.OnCompleted();
        return Disposable.Empty;
    });
}

// Usage
WrapFooApi("a", "b").Take(1).Subscribe(...); // only takes first item

We can fix the problem with being unable to stop early by introducing a little asynchronicity, which will give the observer time to get a disposable that it can dispose of to notify you. 我们可以通过引入一点异步性来解决问题,因为它无法提前停止,这将使观察者有时间获得可以处理的一次性用品以通知您。 We can use CreateAsync to get a CancellationToken that will cancel when the observer unsubscribes. 我们可以使用CreateAsync来获取一个CancellationToken ,它会在观察者取消订阅时取消。 And we can run the FooApi code inside Task.Run : 而我们可以运行FooApi内部代码Task.Run

public static IObservable<Foo> WrapFooApi(string arg1, string arg2)
{
    return Observable.CreateAsync<Foo>(async (observer, ct) =>
    {
        await Task.Run(() => FooApi.register_callback(arg1, arg2, e =>
        {
            observer.OnNext(e);

            // Returning false will stop the enumeration
            return !ct.IsCancellationRequested;
        }));
        observer.OnCompleted();
    });
}

In a more traditional asynchronous callback API, where you register at some point and unregister at some other point, you might have something more like this: 在更传统的异步回调API中,您可以在某个位置注册并在其他位置取消注册,您可能会有类似这样的内容:

public static IObservable<Foo> WrapFooApi(string args)
{
    return Observable.Create<Foo>(observer =>
    {
        FooToken token = default(FooToken);
        var unsubscribe = Disposable.Create(() => FooApi.Unregister(token));
        token = FooApi.Register(args, e =>
        {
            observer.OnNext(new Foo(e));
        });

        return unsubscribe;
    });
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM