Код, который вы опубликовали, не является хорошим примером того, как использовать парадигму 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
}