Console.ReadLine()传递给C#事件

时间:2015-07-13 06:31:09

标签: events console system.reactive readline

我正在学习RX,并希望使用Console.ReadLine作为可观察序列的来源。

我知道我可以使用“yield return”创建“IEnumerable”,但对于我的具体用例,我决定创建一个C#事件,以便潜在的许多观察者能够共享相同的键盘输入。 / p>

这是我的代码:

class Program
{
    private delegate void OnNewInputLineHandler(string line);

    private static event OnNewInputLineHandler OnNewInputLineEvent = _ => {};

    static void Main(string[] args)
    {
        Task.Run((Action) GetInput);

        var input = ConsoleInput();
        input.Subscribe(s=>Console.WriteLine("1: " + s));

        Thread.Sleep(30000);
    }

    private static void GetInput()
    {
        while (true)
            OnNewInputLineEvent(Console.ReadLine());
    }

    private static IObservable<string> ConsoleInput()
    {
        return Observable.Create<string>(
        (IObserver<string> observer) =>
        {
            OnNewInputLineHandler h = observer.OnNext;
            OnNewInputLineEvent += h;
            return Disposable.Create(() => { OnNewInputLineEvent -= h; });
        });
    }
}

我的问题 - 当我运行如上所示的GetInput方法时,第一个输入行不会被发送到序列(但它被发送到事件处理程序)。

但是,如果我用以下版本替换它,一切都按预期工作:

private static void GetInput()
{
    while (true)
    {
        var s = Console.ReadLine();
        OnNewInputLineEvent(s);
    }
}

有人可以解释为什么会发生这种情况吗?

2 个答案:

答案 0 :(得分:5)

你正试图让自己的生活变得困难。几乎总有一种方法可以使用Rx简化操作。这只是学习在功能上而不是在程序上思考的问题。

这就是你所需要的:

class Program
{
    static void Main(string[] args)
    {
        var subscription = ConsoleInput().Subscribe(s => Console.WriteLine("1: " + s));
        Thread.Sleep(30000);
        subscription.Dispose();
    }

    private static IObservable<string> ConsoleInput()
    {
        return
            Observable
                .FromAsync(() => Console.In.ReadLineAsync())
                .Repeat()
                .Publish()
                .RefCount()
                .SubscribeOn(Scheduler.Default);
    }
}

这允许多个订阅者通过.Publish().RefCount()共享一个输入。并且.SubscribeOn(Scheduler.Default)将订阅推送到新线程 - 如果没有它,您就会阻止订阅。

答案 1 :(得分:1)

如果您在订阅后移动Task.Run((Action) GetInput);,您的代码将按照需要运行。这是因为在原始版本中,OnNewInputEvent(Console.ReadLine())的第一次调用是在您将OnNewInputLineEvent挂钩到observer.OnNext之前运行的。