如何将阻塞事件转换为Observable?

时间:2015-04-04 13:33:49

标签: c# system.reactive observable rx.net

我正在学习.net Rx(Reactive Extensions)库并尝试创建一个从Console中读取用户输入的正确Observable。

到目前为止,我来到这里:

    public static IObservable<string> ConsoleInputObservable()
    {
        return Observable.Create<string>(observer =>
        {
            var cancelable = new BooleanDisposable();
            while(!cancelable.IsDisposed)
            {
                observer.OnNext(Console.ReadLine());
            }
            observer.OnCompleted();
            return cancelable;
        });
    }

不幸的是,这个实现至少有一个问题 - 没有办法取消订阅它。

所以我的问题是:如何正确地将一系列阻塞事件转换为Observable?

感谢。

编辑:拼写错误

1 个答案:

答案 0 :(得分:3)

你走了。

请注意以下几点:

  • 这允许用户提供适当的调度程序来控制运行并发的位置,并产生每个循环以防止它过于混乱(尽管等待控制台显然是非常粘性的......)
  • 我们不应该在此示例中调用OnCompleted,因为终止的唯一方法是取消订阅 - 并且您努力在取消后不再发送消息
  • 我们也不会在此处发送OnNext帖子取消。

以下是代码:

public static IObservable<string> ConsoleInputObservable(
    IScheduler scheduler = null)
{
    scheduler = scheduler ?? Scheduler.Default;
    return Observable.Create<string>(o =>
    {
        return scheduler.ScheduleAsync(async (ctrl, ct) =>
        {
            while(!ct.IsCancellationRequested)
            {                    
                var next = Console.ReadLine();
                if(ct.IsCancellationRequested)
                    return;

                o.OnNext(next);
                await ctrl.Yield();
            }
        });
    });
}

附录

@MartinLiversage评论说,多个订阅者的行为是不可取的 - 这促使了这个附录。您可以简单地Publish()上面的代码,但鉴于控制台的性质是应用程序只有一个,并且一次只有一个线程可以读取它,因此需要采用不同的方法。

我忽略了上述内容,因为我觉得问题可能更多是关于线程方面而不是控制台的本质。如果您真的有兴趣报告在控制台输入的行,那么某些类似主循环可能更实用 - 这代表Subject的合理使用。

static void Main()
{
    Subject<string> sc = new Subject<string>();

    // kick off subscriptions here...
    // Perhaps with `ObserveOn` if background processing is required
    sc.Subscribe(x => Console.WriteLine("Subscriber1: " + x));
    sc.Subscribe(x => Console.WriteLine("Subscriber2: " + x));

    string input;
    while((input = Console.ReadLine()) != "q")
    {
        sc.OnNext(input);
    }
    sc.OnCompleted();

    Console.WriteLine("Finished");
}