3

所以我可以试验反应式扩展,我想创建一个用户按下的键的 IObservable。我怎样才能做到这一点?

这适用于 C# 控制台应用程序

4

4 回答 4

4

试试这个来获得一个可观察的读键序列:

        IObservable<System.ConsoleKeyInfo> keys =
            Observable
                .Defer(() =>
                    Observable
                        .Start(() =>
                            Console.ReadKey()))
                .Repeat();

我对此进行了测试,它就像一种享受。

于 2012-05-21T00:18:42.323 回答
3

ReadKey() 的阻塞版本有一个问题,如果你处理订阅,它仍然会提示你按下一个键。

如果您想彻底取消订阅,即能够取消提示,则(不幸的是)有必要采用轮询方法。

Observable.Interval(TimeSpan.FromMilliseconds(100))
          .Where(_ => Console.KeyAvailable)
          .Select(_ => (char)Console.ReadKey(false).Key)

你现在可以做一些很酷的事情,比如Amb这个流Observable.Timer来设置按键超时。

于 2012-05-22T09:48:16.943 回答
0

我看不出如何异步读取按键,所以我认为你必须Console.ReadKey()在单独的线程上使用同步,以及Subject<T>. 就像是:

IObservable<ConsoleKeyInfo> ObserveKeysUntilEscape()
{
    var keys = new Subject<ConsoleKeyInfo>();

    Task.Run(
        () =>
        {
            ConsoleKeyInfo key;
            do
            {
                key = Console.ReadKey();
                keys.OnNext(key);
            } while (key.Key != ConsoleKey.Escape);
            keys.OnCompleted();
        });

    return keys;
}
于 2012-05-20T19:23:28.283 回答
0

@svick 方法的替代方法是将ReadKey循环作为一个Enumerable,然后转换为一个Observable. 这将其置于后台。

    static IEnumerable<ConsoleKeyInfo> KeyPresses()
    {
        ConsoleKeyInfo key;
        do
        {
            key = Console.ReadKey();
            yield return key;
        } while (key.Key != ConsoleKey.Escape);
    }

我们可以在线程池上生成 observables:

        var keys = KeyPresses().ToObservable(System.Reactive.Concurrency.Scheduler.ThreadPool);
        keys.Subscribe(key => Console.WriteLine("Pressed: {0}", key.Key));  

并在主线程上等待Escape密钥:

        keys.Where(key => key.Key == ConsoleKey.Escape).First();
于 2012-05-21T00:19:29.090 回答