Как объединить «IObservable <bool>IsActive» и «bool IsEnabled» в одной подписке - PullRequest
1 голос
/ 21 февраля 2020

Имеет два свойства, одно типа public IObservable<bool> IsEnabled { get; set; }, а второе public bool IsActive { get;set; }, и я хочу объединить два свойства в одно, используя System.Reactive вот так public bool IsActiveAndEnabled {get;set;}

public class BindableProperty<T> 
{
    T Value { get; }
}

public class Manager
{
    public IObservable<bool> IsEnabled { get; set; }

    public BindableProperty<bool> IsActiveAndEnabled { get; set; }


    private bool isActive;
    public bool IsActive
    {
        get { return isActive; }
        set
        {
            isActive = value;
            // Call OnPropertyChanged whenever the property is updated
            OnPropertyChanged(() => IsActive);
        }
    }

    private void OnPropertyChanged(Func<bool> property)
    {
        throw new NotImplementedException();
    }

    public Manager()
    {
        IsEnabled
            .And(/* accept only observable, but I want to join with IsActive*/)
            .Then((isEnabled,isActive) => IsActiveAndEnabled = isEnabled && isActive);
    }
}

1 Ответ

2 голосов
/ 21 февраля 2020
  • Код, который вы опубликовали, не является хорошим примером того, как использовать парадигму Reactive-программирования.

    • Пожалуйста, не думайте, что "Observable" как просмотр единственного скалярного свойства элемента класса изменяемый объект.
      • Я обвиняю Microsoft в том, что люди поняли это недоразумение, потому что они добавили ObservableCollection к. NET Framework стареет go, но их (неправильное) использование термина "Observable" в контексте ObservableCollection совершенно не имеет отношения к значению Observable в Реактивном программировании.
  • При выполнении Реактивного программирования Наблюдаемый является источником (или источником) ) потока объектов данных (обратите внимание, что поток может быть пустым (никогда ничего не излучать) или только когда-либо излучать один объект, прежде чем он закроется, или испускать тысячи объектов каждую секунду).

  • Таким образом, IObservable лучше рассматривать как "IEnumerable<T> -молкание" (по сравнению с обычным IEnumerable<T>, который имеет семантику "pull").
  • Другая важная вещь состоит в том, что объекты, испускаемые Наблюдаемой, должны (нет должны ) быть неизменными! - или, по крайней мере, испускаемые объекты не должны иметь изменяемое состояние с любым другим испускаемым объектом!
    • Это потому, что если значение изменяется со временем - или если запись изменения одного объекта влияет на другие объекты - тогда другие части вашей программы, использующие другой экземпляр возвращаемого объекта, будут мутировать без изменения они ожидают этого.

Я настоятельно рекомендую следовать руководству или руководству по концепциям реактивного программирования, прежде чем продолжать дальше. Большинство руководств и учебных пособий посвящено Rx JS (Reactive Extensions для JavaScript, распространённому после Angular, решившим использовать Observable<Response> вместо Promise<Response> для своего встроенного клиентского библиотеки librar HTTP. Хотя многие из руководств охватывают Rx JS они полностью применимы к реактивному программированию для других платформ, таких как. NET (хотя IObservable<T> является частью. NET Framework, вам потребуется отдельная библиотека Reactive Extensions для правильного выполнения реактивного программирования и не изобретая колесо).

Важное примечание: Reactive Extensions, Rx JS и Reactive программирования не имеют абсолютно никакого отношения к "React" / "ReactJS" и шаблону React (тангенциальные отношения существуют, но это совершенно разные вещи).

Ресурсы:

Что касается вашего случая, это моя рекомендация:

Если class Manager должен быть изменяемым:

  • Удалить public IObservable<bool> IsEnabled { get; set; }
  • Определить новый неизменный class ManagerState.
  • Расширить class Manager : IObservable<ManagerState>
  • Каждый раз, когда изменяемое свойство в class Manager изменяется, запускается новый ManagerState (заполненный копией снимка Manager) для подписчиков.

Если class Manager можно сделать неизменным:

  • Удалить public IObservable<bool> IsEnabled { get; set; }
  • Определить новый класс ManagerSource, который реализует IObservable<Manager>
  • Каждый раз, когда Manager обновляется каким-либо образом, запускается новый объект Manager (заполненный копией снимка Manager) для подписчиков.

Пример (когда class Manager mutable):

class Manager : IObservable<ManagerState>
{
    private Boolean isEnabled; // backing field
    public Boolean IsEnabled
    {
        get { return this.isEnabled; }
        set
        {
            this.isEnabled = value;
            this.NotifySubscribers();
        }
    }

    private Boolean isActive; // backing field
    public Boolean IsActive
    {
        get { return this.isActive; }
        set
        {
            this.isActive = value;
            this.NotifySubscribers();
        }
    }

    #region Observers (Subscribers) handling:

    private readonly ConcurrentBag<IObserver<ManagerState>> subscribers = new ConcurrentBag<IObserver<ManagerState>>();

    private void NotifySubscribers()
    {
        ManagerState snapshot = new ManagerState(
            this.IsEnabled,
            this.IsActive,
            // etc
        );

        foreach( IObserver<ManagerState> observer in this.subscribers )
        {
            observer.OnNext( snapshot );
        } 
    }

    #endregion
}

// Represents a snapshot of the state of a `Manager`
class ManagerState
{
    public ManagerState( Boolean isEnabled, Boolean isActive )
    {
        this.IsEnabled = isEnabled;
        this.IsActive  = isActive;
    }

    public Boolean IsEnabled { get; }
    public Boolean IsActive  { get; }
    // any other members, etc
}

Обратите внимание, что этот можно комбинировать с поддержкой INotifyPropertyChanged примерно так (хотя я думаю, что это плохая идея, потому что поддержка нескольких способов достижения sh то же самое приведет к тому, что вы запутаете своих пользователей / потребителей / коллег) - вроде как System.Net.Sockets.Socket имеет Accept, AcceptAsync, BeginAccept / EndAccept, поскольку поддерживает синхронный режим, а APM и EAP a синхронные парадигмы (но не TPL по некоторым причинам).

class Manager : IObservable<ManagerState>, INotifyPropertyChanged
{
    private Boolean isEnabled; // backing field
    public Boolean IsEnabled
    {
        get { return this.isEnabled; }
        set
        {
            this.isEnabled = value;
            this.OnPropertyChanged( nameof(this.IsEnabled) );
        }
    }

    private Boolean isActive; // backing field
    public Boolean IsActive
    {
        get { return this.isActive; }
        set
        {
            this.isActive = value;
            this.OnPropertyChanged( nameof(this.IsActive) );
        }
    }

    #region INotifyPropetyChanged and Observers (Subscribers) handling:

    private readonly ConcurrentBag<IObserver<ManagerState>> subscribers = new ConcurrentBag<IObserver<ManagerState>>();

    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged( String name )
    {
        // First, notify users of INotifyPropetyChanged:

        this.PropertyChanged?.Invoke( this, new PropertyChangedEventArgs( name ) );

        // Then notify users of IObservable:

        ManagerState snapshot = new ManagerState(
            this.IsEnabled,
            this.IsActive,
            // etc
        );

        foreach( IObserver<ManagerState> observer in this.subscribers )
        {
            observer.OnNext( snapshot );
        } 
    }

    #endregion
}
...