IObservable<T> 和 INotifyPropertyChanged - 是否有联系

12

我了解IObservable<T>IObserver<T>是观察者模式的实现,可以在类似于.Net事件的情况下使用。

我想知道它们与INotifyPropertyChanged是否有任何关系?

我目前在WinForms和WPF应用程序中使用INotifyPropertyChanged进行数据绑定,并想知道是否能够在UI数据绑定场景中使用IObservable?

干杯

AWC

6个回答

5
据我所了解,观察者模式和.NET事件是实现观察者/通知行为的两种方式,并不存在关联关系。
微软的答案是在.NET事件模式的基础上构建,而不是废弃手动注册观察者对象。
我对事件的一个最大的抱怨是无法按需清除委托链,这会导致许多受管理的内存泄漏情况。为此,微软引入了弱事件的概念,即解决可观察对象/观察者时间线不匹配问题。
您可以在这里阅读有关WeakEvent模式的更多信息。
Josh Smith发布了INotifyPropertyChanged的WeakEventManager实现,在此处提供了一种更安全(从内存角度)的方法来连接更改属性的对象及其观察者。

MVVM Foundation 项目看起来已经死了,2.5 年没有任何活动。 - BlackICE

3
除非WinForms和WPF绑定也支持IObservable,否则它不能帮助保持UI随模型更改而更新。使用INotifyPropertyChanged的原因是,WinForms和WPF中的绑定代码会寻找此接口,并在其实现时使用其事件来保持UI最新。

谢谢,但并没有真正帮到我,我知道如何将接口与绑定配合使用。 - AwkwardCoder
2
@AWC:相反,他的解释确实回答了你的问题,特别是你最后一个问题。 - Joel B Fant
1
让我们来看看 - 我声明我已经使用了INotifyPropertyChanged,因此我对它在Winforms\WPF中的工作原理有一些了解,我想知道IObservable是否在Winforms\WPF中有任何使用模式,这是最后一个问题。所以这并没有真正帮助到我... - AwkwardCoder
3
问:我目前在WinForms和WPF应用程序中使用INotifyPropertyChanged进行数据绑定,想知道是否可以在UI数据绑定场景中使用IObservable? 答:除非WinForms和WPF绑定也支持IObservable,否则它不能帮助将UI更新为模型中的更改。 之所以可以使用INotifyPropertyChanged是因为WinForms和WPF中的绑定代码寻找此接口,并在实现时使用其事件来保持UI更新。对我来说,问题的答案似乎很明显。 - Richard Anthony Freeman-Hein
2
我认为AWC想要的答案是关于Winforms或WPF是否支持IObservable接口在绑定能力方面的明确答案。在这方面,安迪提供了一些见解,指出了一个更基本的问题,但并没有真正给出明确的答案。 - jpierson

2
首先,我对Rx还有点陌生,所以请根据我的评论做出相应的考虑。
话虽如此,我认为INotifyPropertyChanged和Rx的IObservable之间存在很好的合作机会。目前,UI是建立在INPC之上的。但是,INPC也是检测变化和管理领域模型或视图模型中对象和属性之间相互依赖关系的主要方式。这些相互依赖关系似乎是Rx的良好候选者。
直接使用INPC有点棘手和痛苦。需要处理大量魔术字符串。同时,在对象树中多层次地观察一个对象的事件也有点困难。
但是,如果我可以“反应式地”对这些交互进行建模,那么我的视图模型和领域模型就会变得更加优雅。这在像Bindable Linq, Continuous Linq, Obtics等项目的优雅性中显而易见。这些库使创建“实时值”或“实时集合”变得简单,它们会自动更新(敢说“反应式”)以响应更改。连续的 Linq 甚至有一个"反应式对象"框架来进行反应式编程,尽管没有 Rx。
我认为,如果我们可以使用 Rx 来保持模型和视图模型的一致性,那么协同作用就会出现。然后,我们可以使模型/视图模型的“可绑定表面”遵守 INPC,通过根据需要继续引发 PropertyChanged 事件。我见过一些优雅的扩展方法,可以从 INotifyPropertyChanged 创建一个可观察对象。似乎另一半可能是创建一些基础设施,将 Rx 转换回 INPC。

+1 这正是我想要研究的内容。同时也在研究 ReactiveUI。 - Benjol

1
如果您所说的是由 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


0
通常,使用MVVM呈现IObservable<T>的最直接方式是创建一个传统的视图模型对象,如下所示,并手动订阅它到可观察对象。应该使用.ObserveOn(SynchronizationContext.Current)执行订阅以在UI线程中分派所有通知。反过来,同步上下文应该在那个时刻存在(SynchronizationContext.Currentnew Application().Run(mainWindow)之前为null)。请参见以下示例:
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();
}

这里的ObserveOnDispatcher()调用即使调度程序尚未启动并且SynchronizationContext.Current为空也可以工作。 调用者方法:

[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>

-1

我同意micahtanNathanAW的回答。我想简要总结并补充一下。严格来说,IObservable和INotifyPropertyChanged之间没有直接联系。Rx是从事件特定的抽象出发:它是处理任何类型事件的框架。RX以相同的方式处理所有类型的事件,所有的细节都在用户代码中。如果你想在WPF(Xamarin、Blazor)中确切地从INotifyPropertyChanged和INotifyCollectionChanged中获益,请看看我的ObservableComputations库。这个库是Bindable Linq、Continuous Linq、Optics库的生产就绪模拟版,NathanAW建议使用这些库。这些库可以单独使用,也可以与Rx协作。在这种情况下,Rx和INotifyPropertyChanged之间存在联系,但同样的联系也存在于Rx和任何其他事件之间。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接