IObservable of keys pressed

Deadly 提交于 2020-01-03 20:04:45

问题


So I can experiment with Reactive Extensions, I'd like to create an IObservable of keys pressed by the user. How can I do this?

This is for a C# console application


回答1:


Try this to get an observable sequence of read keys:

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

I tested this and it worked like a treat.




回答2:


The blocking versions of ReadKey() have a problem, in that if you dispose the subscription, it still prompts you to press a key.

If you want to have a clean unsubscription, i.e, be able to cancel the prompt, it's (unfortunately) necessary to go with a polling approach.

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

You can now do cool things like Amb this stream with Observable.Timer to set up a timeout for keypresses.




回答3:


I don't see any way how to read key presses asynchronously, so I think you have to use the synchronous Console.ReadKey() on a separate thread, along with Subject<T>. Something like:

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;
}



回答4:


An alternative to the method from @svick is to have the ReadKey loop as an Enumerable, and convert to an Observable. This puts it in the background.

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

We can generate the observables on the thread pool:

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

And wait on the main thread for the Escape key:

        keys.Where(key => key.Key == ConsoleKey.Escape).First();


来源:https://stackoverflow.com/questions/10675451/iobservable-of-keys-pressed

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!