问题
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