我正在学习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);
}
}
有人可以解释为什么会发生这种情况吗?
答案 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
之前运行的。