IObservable <T> и INotifyPropertyChanged - есть ли соединение

Я понимаю, что IObservable<T> и IObserver<T> являются реализациями шаблона наблюдателя и могут использоваться в ситуациях, аналогичных событиям .Net.

Мне было интересно, есть ли какое-либо отношение к INotifyPropertyChanged?

В настоящее время я использую INotifyPropertyChanged для привязки данных в приложениях winforms и WPF, и мне было интересно, смогу ли я использовать IObservable в сценариях привязки данных пользовательского интерфейса?

ура

AWC

Ответ 1

Из того, что я могу собрать, нет никаких отношений. Наблюдения/события .NET - это два способа достижения стиля стиля Observer/Notification.

Ответ Microsoft должен был быть построен поверх шаблона событий .NET, а не обесценивать его в пользу зарегистрированных вручную объектов Observer.

Один из моих самых больших моментов с событиями - неспособность очистить цепочку делегатов по требованию, что приводит к довольно большому количеству сценариев утечки с управляемой памятью. С этой целью Microsoft представила концепцию слабых событий, то есть решить проблему несогласованных сроков наблюдения/наблюдателей.

Подробнее о шаблоне WeakEvent здесь.

Джош Смит выпустил внедрение WeakEventManager для INotifyPropertyChanged здесь. Это обеспечивает более безопасный (с точки зрения памяти) способ подключения объектов, которые изменяют свойства, и их наблюдателей.

Ответ 2

Если WinForms и привязки WPF не поддерживают IObservable, это не поможет обновить пользовательский интерфейс с изменениями в модели. Причина, по которой вы можете использовать INotifyPropertyChanged, состоит в том, что код привязки в WinForms и WPF ищет этот интерфейс, а когда он реализован, использует свое событие, чтобы обновить интерфейс.

Ответ 3

Во-первых, я немного новичок в Rx, поэтому сделайте мои комментарии соответственно.

Тем не менее, я думаю, что есть прекрасная возможность для сотрудничества между INotifyPropertyChanged и Rx IObservable. Я думаю, что относительно очевидно, что пользовательский интерфейс построен вокруг INPC на данный момент. Тем не менее, INPC также является основным способом обнаружения изменений и управления сценариями, когда модель домена или модель представления имеют взаимозависимости между объектами и свойствами. Именно эти взаимозависимости кажутся хорошими кандидатами на Rx.

Работа с INPC напрямую немного сложна и несколько болезненна. Множество магических струн, с которыми нужно иметь дело. Также немного больно наблюдать за событием на объекте на нескольких уровнях в дереве объектов.

Но если я смогу моделировать эти взаимодействия "реактивно", то мои модели взглядов и модели домена начинают чувствовать себя немного более элегантными. Это видно из элегантности проектов, таких как Bindable Linq, Непрерывный Linq, Obtics и т.д. Эти библиотеки упрощают создание "живых значений" или "живых коллекций", которые автоматически обновляются (смею сказать "реактивно" ) на изменения. Непрерывный Linq даже имеет "реактивный объект" framework для выполнения реактивного программирования, хотя без Rx.

Мне кажется, что синергии возникают, если мы можем использовать Rx, чтобы модель и модель были согласованы. Затем мы можем сделать "связующую поверхность" модели/модели зрения честью INPC, продолжая при необходимости увеличивать PropertyChanged. Я видел пару изящных методов расширения, которые создадут видимый из INotifyPropertyChanged. Похоже, что другой половине этого может быть создание некоторой инфраструктуры, которая переводится с Rx обратно в INPC.

Ответ 4

Если вы имеете в виду IObserver/IObservable, как определено расширениями Rx:

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 просто предоставляет общую привязку к событиям для привязки данных /etc, чтобы позволить элементам управления знать, когда обновлять связанные значения.

IObservable/IObserver больше похож на "запрос с последовательностями событий", но даже это плохое описание.

Хм... хорошо, так что вы знаете, как вы можете поместить вещи в эту "сумку", называемую коллекцией, и затем запросить эту коллекцию (вручную или с помощью оператора LINQ), чтобы вытащить значения, правильно? Это вроде как, но вместо того, чтобы "вытаскивать" данные из "мешка", вы делаете события "толкаемыми" к вам.

Бесстыдный плагин, который может помочь или еще больше запутать: http://blog.lab49.com/archives/3252

Ответ 5

Часто самый простой способ представить IObservable<T> с MVVM - это создать обычный объект модели представления, подобный приведенному ниже, и вручную подписать его на наблюдаемый. Подписка должна быть выполнена с использованием .ObserveOn(SynchronizationContext.Current) для отправки всех уведомлений в потоке пользовательского интерфейса. В свою очередь, контекст синхронизации должен существовать в этот момент (SynchronizationContext.Current равен null перед 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 для генерации авто-свойств в модели представления.

View-модель:

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 имеет значение null. Метод вызова:

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