¿Existe una implementación de Subject en Rx.NET que se parezca funcionalmente a BehaviorSubject pero emita el siguiente valor solo si ha cambiado?
Soy bastante nuevo en Reactive Extensions y parece que no puedo encontrar nada de eso, aunque este patrón se siente como un reemplazo natural para INotifyPropertyChanged .
Mi implementación ingenua es encapsular BehaviorSubject<T> como se muestra a continuación. ¿Hay alguna desventaja en esto, en comparación con la creación de un observable componible con Observable.DistinctUntilChanged ?
public class DistinctSubject<T> : SubjectBase<T> { private readonly BehaviorSubject<T> _subject; public DistinctSubject(T initialValue) => _subject = new BehaviorSubject<T>(initialValue); public T Value { get => _subject.Value; set => this.OnNext(value); } public override bool HasObservers => _subject.HasObservers; public override bool IsDisposed => _subject.IsDisposed; public override void Dispose() => _subject.Dispose(); public override void OnCompleted() => _subject.OnCompleted(); public override void OnError(Exception error) => _subject.OnError(error); public override void OnNext(T value) { if (!EqualityComparer<T>.Default.Equals(value, _subject.Value)) { _subject.OnNext(value); } } public override IDisposable Subscribe(IObserver<T> observer) => _subject.Subscribe(observer); }Después de mirar un poco el código fuente de la clase BehaviorSubject<T> , parece que su implementación de DistinctSubject<T> se comportará de manera diferente en caso de que un OnError sea seguido por un OnNext :
var subject = new DistinctSubject<int>(2021); subject.OnError(new ApplicationException()); subject.OnNext(2022); // throws ApplicationException Esto arrojará, mientras que hacer lo mismo con BehaviorSubject<T> no arrojará (el OnNext simplemente se ignora).
Mi sugerencia es usar el operador DistinctUntilChanged en la implementación, así:
public class DistinctSubject<T> : ISubject<T>, IDisposable { private readonly BehaviorSubject<T> _subject; private readonly IObservable<T> _distinctUntilChanged; public DistinctSubject(T initialValue, IEqualityComparer<T> comparer = default) { _subject = new BehaviorSubject<T>(initialValue); _distinctUntilChanged = _subject.DistinctUntilChanged( comparer ?? EqualityComparer<T>.Default); } public T Value => _subject.Value; public void OnNext(T value) => _subject.OnNext(value); public void OnError(Exception error) => _subject.OnError(error); public void OnCompleted() => _subject.OnCompleted(); public IDisposable Subscribe(IObserver<T> observer) => _distinctUntilChanged.Subscribe(observer); public void Dispose() => _subject.Dispose(); }Si le preocupa la asignación innecesaria de un objeto, entonces aún no está familiarizado con el espíritu de Rx. ¡Esta biblioteca se trata de funciones y facilidad de uso, no de rendimiento o eficiencia!