如何在一个订阅中合并 "IObservable<bool> IsActive" 和 "bool IsEnabled"

How to combine "IObservable<bool> IsActive" and "bool IsEnabled" in one subsciption

有两个属性,一个是 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);
    }
}
  • 您发布的代码不是如何使用响应式编程范例的好例子。

    • 请不要将 "Observable" 视为 class 的单个标量值成员 属性 的视图 mutable 目的。
      • 我责怪微软让人们产生这种误解,因为他们很久以前就将 ObservableCollection 添加到 .NET Framework 中,但他们在 [=12] 的上下文中(错误)使用了术语 "Observable" =] 与 Reactive 编程中 Observable 的含义完全无关。
  • 在进行响应式编程时,Observable 是数据对象流的源(或发射器)(请注意,流 可以 为空(永远不会发出任何东西)或者在它自己关闭之前只发出一个对象,或者每秒发出数千个对象)。

  • 所以 IObservable 最好被认为是“IEnumerable<T>-that-pushes”(与具有 "pull" 语义的正常 IEnumerable<T> 相比)。
  • 另一个重要的事情是 Observable 发射的对象应该(不 必须 )是不可变的! - 或者至少,发出的对象不得与任何其他发出的对象共享可变状态!
    • 这是因为如果一个值随时间发生变化 - 或者如果对一个对象的更改会影响其他对象 - 那么你的程序中使用返回对象的不同实例的其他部分将在没有它们的情况下改变它们的状态期待中

我强烈建议在继续之前先阅读有关响应式编程概念的指南或教程。大多数指南和教程都涵盖了 RxJS([=134= 的响应式扩展],在 Angular 决定使用 Observable<Response> 而不是 Promise<Response> 作为其内置 HTTP 客户端库后得到普及。虽然许多指南涵盖了 RxJS,它们完全适用于其他平台的响应式编程,例如 .NET(虽然 IObservable<T> 是 .NET Framework 的一部分,您将需要单独的 Reactive Extensions 库来正确地进行响应式编程,并且无需重新发明轮子)。

重要提示:(有切线关系,但它们是完全不同的东西)

资源:

关于你的情况,我的建议是:

如果 class Manager 必须是可变的:

  • 移除public IObservable<bool> IsEnabled { get; set; }
  • 定义一个新的 immutable class ManagerState.
  • 延长class Manager : IObservable<ManagerState>
  • 每次 class Manager 中的可变 属性 更改时,都会向订阅者发送新的 ManagerState(填充有 Manager 的快照副本)。

如果class Manager可以不可变:

  • 移除public IObservable<bool> IsEnabled { get; set; }
  • 定义一个新的 class ManagerSource 实现 IObservable<Manager>
  • 每次 Manager 以某种方式更新时,向订阅者发送一个新的 Manager 对象(填充了 Manager 的快照副本)。

示例(当 class Manager 可变时):

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 的支持相结合(尽管我认为这是一个坏主意,因为支持多种方式来完成同一件事会最终混淆你的 users/consumers/coworkers) - 有点像 System.Net.Sockets.SocketAccept, AcceptAsync, BeginAccept/EndAccept 因为它支持同步,并且APM 和 EAP 异步范例(但出于某种原因不是 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
}