所以我可以试验反应式扩展,我想创建一个用户按下的键的 IObservable。我怎样才能做到这一点?
这适用于 C# 控制台应用程序
试试这个来获得一个可观察的读键序列:
IObservable<System.ConsoleKeyInfo> keys =
Observable
.Defer(() =>
Observable
.Start(() =>
Console.ReadKey()))
.Repeat();
我对此进行了测试,它就像一种享受。
ReadKey() 的阻塞版本有一个问题,如果你处理订阅,它仍然会提示你按下一个键。
如果您想彻底取消订阅,即能够取消提示,则(不幸的是)有必要采用轮询方法。
Observable.Interval(TimeSpan.FromMilliseconds(100))
.Where(_ => Console.KeyAvailable)
.Select(_ => (char)Console.ReadKey(false).Key)
你现在可以做一些很酷的事情,比如Amb
这个流Observable.Timer
来设置按键超时。
我看不出如何异步读取按键,所以我认为你必须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;
}
@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();