Search code examples
c#reactive-programmingsystem.reactiverx.net

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


Having two properties, one of type public IObservable<bool> IsEnabled { get; set; } and the second public bool IsActive { get;set; } and I want to combine the two properties in one using System.Reactive like that 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);
    }
}

Solution

    • The code you've posted is not a good example of how use the Reactive-programming paradigm.

      • Please don't think of an "Observable" as a view over a single scalar-valued member property of a class a mutable object.
        • I blame Microsoft for people getting this misunderstanding because they added ObservableCollection to the .NET Framework ages ago, but their (mis)use of the term "Observable" in the context of ObservableCollection is completely unrelated to the meaning of Observable in Reactive programming.
    • When doing Reactive programming, an Observable is a source (or emitter) of a stream of data objects (note that a stream can be empty (never emitting anything) or only ever emit a single object before it closes itself, or emits thousands of objects every second).

    • So an IObservable is better thought of as an "IEnumerable<T>-that-pushes" (compared to the normal IEnumerable<T> which has "pull" semantics).
    • The other important thing is that the objects emitted by an Observable should (nay must) be immutable! - or at least, the objects emitted must not share mutable state with any other emitted object!
      • This is because if a value changes over time - or if writing a change to one object affects other objects - then other parts of your program that are using a different instance of a returned object will have their state mutated without them expecting it.

    I strongly recommend following a guide or tutorial on the concepts of Reactive programming before continuing further. Most of the guides and tutorials cover RxJS (Reactive Extensions for JavaScript, populised after Angular decided to use Observable<Response> instead of Promise<Response> for their built-in HTTP client librar. While many of the guides cover RxJS they are fully applicable to Reactive Programming for other platforms, such as .NET (while IObservable<T> is part of the .NET Framework, you will need the separate Reactive Extensions library to do reactive programming properly and without reinventing the wheel).

    Important note: Reactive Extensions, RxJS and Reactive programming have absolutely nothing to do with "React"/"ReactJS" and the React-pattern (there is a tangental relation, but they are completely separate things).

    Resources:

    As for your case, this is my recommendation:

    If class Manager must be mutable:

    • Remove public IObservable<bool> IsEnabled { get; set; }
    • Define a new immutable class ManagerState.
    • Extend class Manager : IObservable<ManagerState>
    • Every time a mutable property in class Manager changes, fire-off a new ManagerState (populated with a snapshot copy of Manager) to the subscribers.

    If class Manager can be made immutable:

    • Remove public IObservable<bool> IsEnabled { get; set; }
    • Define a new class ManagerSource which implements IObservable<Manager>
    • Every time a Manager is updated somehow, fire-off a new Manager object (populated with a snapshot copy of Manager) to the subscribers.

    Example (when class Manager is 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
    }
    

    Note that this can be combined with support for INotifyPropertyChanged like so (though I think this is a bad idea because supporting multiple ways to accomplish the same thing will end-up confusing your users/consumers/coworkers) - kinda like how System.Net.Sockets.Socket has Accept, AcceptAsync, BeginAccept/EndAccept because it supports synchronous, and the APM and EAP asynchronous paradigms (but not TPL for some reason).

    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
    }