IObservable<T> 和 INotifyPropertyChanged - 是不是存在连接
Posted
技术标签:
【中文标题】IObservable<T> 和 INotifyPropertyChanged - 是不是存在连接【英文标题】:IObservable<T> and INotifyPropertyChanged - is there a connectionIObservable<T> 和 INotifyPropertyChanged - 是否存在连接 【发布时间】:2011-01-10 21:24:27 【问题描述】:我了解 IObservable<T>
和 IObserver<T>
是观察者模式的实现,可以在与 .Net 事件类似的情况下使用。
我想知道是否与INotifyPropertyChanged
有任何关系?
我目前在 winforms 和 WPF 应用程序中使用 INotifyPropertyChanged
进行数据绑定,我想知道我是否能够在 UI 数据绑定场景中使用 IObservable?
干杯
AWC
【问题讨论】:
【参考方案1】:我同意micahtan's 和NathanAW's 的回答。我只想总结和补充。严格意义上来说,IObservable 和 INotifyPropertyChanged 之间没有直接的联系。 Rx 是从特定事件中抽象出来的:它是处理任何类型事件的框架。 RX 以相同的方式处理所有类型的事件,所有细节仅在用户代码中。如果您想在 WPF(Xamarin、Blazor)体验中完全从 INotifyPropertyChanged 和 INotifyCollectionChanged 中受益,请查看我的 ObservableComputations 库。该库是 NathanAW 建议的 Bindable Linq、Continuous Linq、Optics 库的生产就绪模拟。这些库可以自己使用或与 Rx 合作。在这种情况下,存在 beetwen Rx 和 INotifyPropertyChanged 连接,但同样存在 beetwen Rx 和任何其他事件。
【讨论】:
【参考方案2】:通常,使用 MVVM 呈现IObservable<T>
的最直接方法是制作一个如下所示的常规视图模型对象,然后手动将其订阅到可观察对象。订阅应使用.ObserveOn(SynchronizationContext.Current)
执行,以在 UI 线程中分派所有通知。反过来,此时同步上下文应该存在(SynchronizationContext.Current
在new Application().Run(mainWindow)
之前为空)。请参阅下面的示例:
public class ViewModel : INotifyPropertyChanged
private int _property1;
public int Property1
get => _property1;
set
_property1 = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Property1)));
public event PropertyChangedEventHandler PropertyChanged;
观点:
<Window x:Class="ConsoleApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<TextBox Text="Binding Property1" />
</Window>
调用者方法:
[STAThread]
static void Main()
var model = Observable.Timer(TimeSpan.Zero, TimeSpan.FromSeconds(1)).Take(10);
var viewModel = new ViewModel();
var mainWindow = new MainWindow
DataContext = viewModel
;
IDisposable subscription = null;
mainWindow.Loaded += (sender, e) =>
subscription = model
.ObserveOn(SynchronizationContext.Current) // Dispatch all the events in the UI thread
.Subscribe(item => viewModel.Property1 = item);
new Application().Run(mainWindow);
subscription?.Dispose();
更新
另一种选择是使用 ReactiveUI.WPF 甚至更简洁 - ReactiveUI.Fody 在视图模型中生成自动属性。
视图模型:
public class ViewModel : ReactiveObject, IDisposable
private readonly IDisposable subscription;
public ViewModel(IObservable<long> source)
subscription = source
.ObserveOnDispatcher()
.ToPropertyEx(this, x => x.Property1);
// To be weaved by Fody
public extern long Property1 [ObservableAsProperty]get;
// Unsubscribe from the source observable
public void Dispose() => subscription.Dispose();
即使调度程序尚未启动且SynchronizationContext.Current
为空,此处ObserveOnDispatcher()
调用仍有效。
调用者方法:
[STAThread]
static void Main()
new Application().Run(
new MainWindow
DataContext = new ViewModel(
Observable.Timer(
TimeSpan.Zero,
TimeSpan.FromSeconds(1))
.Take(20))
);
解决方案文件附近的FodyWeavers.xml:
<?xml version="1.0" encoding="utf-8" ?>
<Weavers>
<ReactiveUI />
</Weavers>
【讨论】:
【参考方案3】:首先,我对 Rx 有点陌生,所以请相应地使用我的 cmets。
也就是说,我认为 INotifyPropertyChanged 和 Rx 的 IObservable 之间有很大的合作机会。我认为在这一点上 UI 是围绕 INPC 构建的相对明显。但是,INPC 也是检测更改和管理领域模型或视图模型在对象和属性之间具有相互依赖关系的场景的主要方式。正是这些相互依赖似乎是 Rx 的理想选择。
直接使用 INPC 有点棘手,也有点痛苦。很多魔术字符串要处理。在对象树中观察多个级别的对象上的事件也有点痛苦。
但是,如果我可以“响应式”地对这些交互进行建模,那么我的视图模型和域模型就会开始变得更加优雅。这在 Bindable Linq、Continuous Linq、Obtics 等项目的优雅中显而易见。这些库使创建自动更新的“实时值”或“实时集合”变得简单(我敢说“反应性”)去改变。 Continuous Linq 甚至有一个 "reactive object" framework 来进行响应式编程,尽管没有 Rx。
在我看来,如果我们可以使用 Rx 来保持模型和视图模型的一致性,就会产生协同效应。然后我们可以通过根据需要继续提高 PropertyChanged 来使模型/视图模型的“可绑定表面”尊重 INPC。我见过一对elegant extension methods 会从 INotifyPropertyChanged 创建一个 observable。似乎另一半可能是创建一些从 Rx 转换回 INPC 的基础设施。
【讨论】:
+1 这正是我打算看看的。也在研究ReactiveUI【参考方案4】:如果您指的是 Rx 扩展定义的 IObserver/IObservable:
http://channel9.msdn.com/shows/Going+Deep/Kim-Hamilton-and-Wes-Dyer-Inside-NET-Rx-and-IObservableIObserver-in-the-BCL-VS-2010/
和:
http://themechanicalbride.blogspot.com/2009/07/introducing-rx-linq-to-events.html
然后它就像苹果和橘子。
INotifyPropertyChanged 只是为数据绑定/等提供了一个通用事件连接,让控件知道何时更新其绑定值。
IObservable/IObserver 更像是“使用事件序列查询”,但即使这样描述也很糟糕。
嗯...好的,所以您知道如何将东西放入这个称为集合的“袋子”中,然后查询该集合(手动或使用 LINQ 语句)以提取值,对吧?有点像这样,但不是从“袋子”中“拉”出数据,而是将事件“推送”给您。
可能会有所帮助或进一步混淆的无耻插件: http://blog.lab49.com/archives/3252
【讨论】:
【参考方案5】:据我所知,没有任何关系。 Observers/.NET 事件是实现 Observer/Notification 样式行为的两种方式。
Microsoft 的答案是建立在 .NET 事件模式之上,而不是弃用它以支持手动注册的 Observer 对象。
我对事件最大的不满之一是无法按需清除委托链,这会导致很多托管内存泄漏情况。为此,微软引入了弱事件的概念,即解决 Observables/Observers 时间线不匹配的问题。
您可以阅读有关 WeakEvent 模式的更多信息here。
Josh Smith 已为 INotifyPropertyChanged here 发布了 WeakEventManager 的实现。这提供了一种更安全(从内存的角度来看)连接更改属性的对象及其观察者的方法。
【讨论】:
MVVM 基金会项目看起来已经死了,2.5 年内没有活动【参考方案6】:除非 WinForms 和 WPF 绑定也支持IObservable
,否则它不会帮助保持 UI 随着模型的变化而更新。您可以使用INotifyPropertyChanged
的原因是因为 WinForms 和 WPF 中的绑定代码会查找此接口,并在实现时使用其事件来保持 UI 最新。
【讨论】:
谢谢,但不是很有帮助,我知道界面在绑定方面是如何工作的 @AWC:相反,他的解释确实解决了你的问题,尤其是你的最后一个问题。 让我们看看 - 我声明我已经使用了 INotifyPropertyChanged,因此我将了解它如何与 Winforms\WPF 一起使用,我想了解 IObservable 以及这是否有任何使用模式Winforms\WPF 这是最后一个问题。所以不是真的有用吗... 问:“我目前在 winforms 和 WPF 应用程序中使用 INotifyPropertyChanged 进行数据绑定,我想知道我是否能够在 UI 数据绑定场景中使用 IObservable?” A: '除非 WinForms 和 WPF 绑定也支持 IObservable,否则随着模型的变化保持 UI 更新是没有帮助的。您可以使用 INotifyPropertyChanged 的原因是因为 WinForms 和 WPF 中的绑定代码会查找此接口,并在实现时使用其事件来使 UI 保持最新。对我来说,这个问题似乎得到了回答。 我认为 AWC 正在寻找的答案是关于 Winforms 或 WPF 是否会以绑定能力支持 IObservable 接口的明确答案。在这方面,安迪通过指出一个更基本的问题提供了一些见解,但并未真正给出明确的答案。以上是关于IObservable<T> 和 INotifyPropertyChanged - 是不是存在连接的主要内容,如果未能解决你的问题,请参考以下文章
创建一个 IObservable<T> 正确返回大量数据(异步?)