我试图遵循乔纳森·沃辛顿(Jonathan Worthington(在《事件驱动和反应式未来》中的机场公告示例
它编译。
问题是:SayGateChange
永远不会被调用。我是Rx的新手。我一定遗漏了什么。我这里有他的代码,就像我可以转录的那样。可悲的是,网上没有可用的资源。
AddGateChange
应该将一个新项目推到EventStreams.GateChanged
上,而Announcer.Announcements
又应该被监视,而SayGateChange
应该被监视。
我使用的是Windows表单,而不是WPF,如果这有所作为的话。
如果这样可以使其正常工作,我很乐意将其放入控制台应用程序或 LinqPad 中。
using Microsoft.VisualBasic;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Reactive.Concurrency;
using System.Reactive.Linq;
using System.Reactive.Subjects;
using System.Threading;
public class frmAnnouncements
{
Announcer _Announcer = new Announcer();
ObservableCollection<string> Announcements = new ObservableCollection<string>();
private void frmRx_Load(System.Object sender, System.EventArgs e)
{
PopulateAnnouncements();
AddGateChange();
}
private void AddGateChange()
{
EventStreams.GateChanged.OnNext(new GateChanged {
Destination = "DAL",
FlightCode = 1503
});
}
private void PopulateAnnouncements()
{
_Announcer.Announcements.ObserveOnDispatcher().Subscribe(SayGateChange);
}
private void SayGateChange(string Message)
{
Interaction.MsgBox(Message);
}
public class GateChanged
{
public string FlightCode;
public string Destination;
}
public class EventStreams
{
public static Subject<GateChanged> GateChanged = new Subject<GateChanged>();
}
public class Announcer
{
public Announcer()
{
this.Announcements = EventStreams.GateChanged.Select(e => string.Format("gate change {0} to {1} ", e.FlightCode, e.Destination));
}
public IObservable<string> Announcements;
}
public frmAnnouncements()
{
Load += frmRx_Load;
}
}
正如@Enigmativity所说,使用ObserveOnDispatcher()
是一个问题 - 尽管不看Interaction.MsgBox
很难 100% 确定这是整个故事 - 我想它可能在视频中,但它相当长,我没有全部看完。
使用 ObservableOnDispatcher()
表明您为 Rx 拉入了错误的 nuget 包:
- 对于 WPF 应用程序,请使用
rx-xaml
(已弃用的同义词rx-wpf
(,它提供了扩展方法ObserveOnDispatcher()
- 对于 Winforms 应用程序,请使用
rx-winforms
,它提供了扩展方法重载ObserveOn(Control)
Winforms和WPF都有类似的设计,其中用户界面在专用线程上运行。在Winforms中,这称为"UI 线程",在WPF中称为"调度程序"。虽然概念非常相似,但实现却大不相同。
WPF 中的ObserveOnDispatcher
将导致在调度程序线程上调用观察者通知OnXXX
。
在WinForms中,使用ObserveOn(this)
,this
通常是表单本身。对于任何 WinForms 控件,这将找到控件的SynchronizationContext
并将OnXXX
通知发布到该控件。
这两种重载都是聪明的,因为如果您碰巧已经在正确的调度程序线程或 UI 线程上,则调用是直接的。
我似乎记得WinForms对从UI线程更新UI的容忍度要高得多 - 尽管此问题也发生在WPF中。这不是一件好事,因为它可能导致难以调试的不可预测的结果。例如,我注意到 WinForms MessageBox.Show
方法并不关心在哪个线程上调用它,因为它创建了自己的窗口。通常,始终建议在UI方案中使用某种形式的ObserveOn
/ObserveOnDispatcher
。
出于这个原因,最好详细了解这些工作原理。为此,要了解相关SubscribeOn
,请查看此问题。
我很惊讶您没有收到信息丰富的InvalidOperationException
,说明"当前线程没有与之关联的调度程序"。我只能认为您的代码的其他部分正在吞噬异常,或者您在应用程序中也使用了 WPF 代码,并且已经创建了与 Winforms UI 线程关联的Dispatcher
。Interaction.MsgBox
背后的代码可能是吞下错误的罪魁祸首。无论哪种方式,我都建议删除rx-xaml
以避免混淆。