将任务前置到IObservable <t>(最后的对称性)

时间:2016-06-19 16:29:31

标签: c# async-await task system.reactive observable

我希望在第一个客户端连接时向我的服务器发送一个启动脉冲,并在最后一个客户端断开连接时发送一个完成脉冲。

public class MyAdapter : IObservable<MyType> {

    IObservable<MyType> MyObservable = BuildMyObservable()
        .Initially(Start) // <- this method doesn't exist
        .Finally(Stop).Publish().RefCount().Subscribe(observer);

    public IDisposable Subscribe(IObserver<MyType> observer);
        return MyObservable.Subscribe(observer)
    }

    async Task Start() { /* start UDP stream */ }
    async Task Stop() { /* stop UDP stream */ }
    IObservable<MyType> BuildMyObservable() { /* wire up stuff */ }

}

在上面的方法中,我是否正在寻找一个不存在的函数Initially,或者我只是忽略它?

3 个答案:

答案 0 :(得分:3)

您可以像处理任何其他序列一样处理任务,并将其链接到查询

Start().ToObservable()
  .SelectMany(_=>MyObservable)
  .Finally(Stop)

作为单独的说明,我建议您避免制作采用格式为IDisposable Subscribe(IObserver<MyType> observer)的方法的API。这消除了消费者对Rx的影响。相反,只需公开IObservable<T>,因为它已经有Subscribe方法。现在,您的消费者可以链接您的序列,组合它,选择正确的并发/线程模型(使用ObserveOn / SubscribeOn),并应用它们的错误处理要求。

另外作为最后一点,发布-refcount一个方法调用结果的序列有点奇怪。当您的方法仅允许消费者提供一个消费者时,发布-refcount更加奇怪。假设您将方法签名更改为推荐/标准方法,那么我还建议您删除Publish().Refcount()代码,因为消费者极不可能缓存结果并重新使用它,v。回想一下这个方法。或者您可以保留方法(甚至更好地将其更改为属性),然后在内部缓存已发布的序列。

public class MyServiceThing
{
    private readonly IObservable<MyType> _myObservable;

    public MyServiceThing()
    {
        _myObservable = Start().ToObservable()
            .SelectMany(_=>/*The thing that defines your observable sequence...*/)
            .Finally(Stop)
            .Publish().RefCount();
    }

    public IObservable<MyType> MyObservable()
    {
        return _myObservable;
    }
    //OR
    //public IObservable<MyType> MyObservable() { get { return _myObservable; } }

    private async Task Start() {}
    private async Task Stop() {}
}

答案 1 :(得分:2)

我猜你正在寻找来自RxJava的.Net等价的doOnSubscribe。它不是开箱即用的。

您可以做的是将MyObservable包裹在Observable.Defer函数中,并在Defer内调用您的服务器。您可以使用以下代码来查看我的意思:

class Program
{
    static void Main(string[] args)
    {
        var source = Observable.Interval(TimeSpan.FromSeconds(1));

        var published = Observable.Defer(() =>
        {
            Console.WriteLine("Start"); // Here, you post "Start" to server
            return source;
        })
        .Finally(() => Console.WriteLine("End")) // Here, you post "End"
        .Publish()
        .RefCount();

        Console.ReadLine();
        var disposable = published.Subscribe(x => Console.WriteLine("First " + x));
        Console.ReadLine();
        var disposable2 = published.Subscribe(x => Console.WriteLine("Second " + x));
        Console.ReadLine();
        disposable.Dispose();
        Console.ReadLine();
        disposable2.Dispose();
        Console.ReadLine();
        published.Subscribe(x => Console.WriteLine("Third " + x));
        Console.ReadLine();
    }
}

有关延期的更多说明,请参阅this excellent blogpost

答案 2 :(得分:0)

使用pmbanka的正确答案,我为此添加了一个Observable扩展名。

public static IObservable<T> Initially<T>(this IObservable<T> resultingObservable, Func<Task> initialAction) {
    return Observable.Defer(async () =>
    {
        await initialAction();
        return resultingObservable;
    });
}

public static IObservable<T> Initially<T>(this IObservable<T> resultingObservable, Action initialAction) {
    return Observable.Defer(() =>
    {
        Action();
        return resultingObservable;
    });
}

它尚未经过测试,可能不是最佳的async / task lambdas。

我想以下内容也是如此:

public static IObservable<T> Initially<T>(this IObservable<T> resultingObservable, Func<Task> initialAction) {
    return Observable.Create(async (IObserver<T> observer) =>
    {
        await initialAction();
        return resultingObservable.Subscribe(observer);
    });
}