限制 BehaviorSubject 的值
Clamp the value of a BehaviorSubject
是否可以转换/验证 ISubject<T>
的值?
例如我有一个 BehaviorSubject<double> zoomFactor = new(1);
,我想夹在 0.1
和 10
之间。
- 应该可以调用
zoomFactor.OnNext(Math.Clamp(newZoomFactor, 0.1, 10))
,但这样做会将责任推给调用者,我希望避免这种情况。
- 我可以使用
zoomFactor.Select(newZoomFactor => Math.Clamp(newZoomFactor, 0.1, 10))
根据观察进行转换,但是:
- 这又是将责任外包给另一个来电者;
- 原始
zoomFactor
值不会在此处更改:想象一下用户缩小超过限制,然后再放大,但渲染的缩放在实际值在内部默默地回到限制范围内...
如何创建具有所需行为的自定义 ISubject<T>
实现?
class BehaviorTransformSubject<T> : ISubject<T>
{
private readonly BehaviorSubject<T> _subject;
private readonly Func<T, T> _transform;
public BehaviorTransformSubject(T value, Func<T, T> transform)
{
_subject = new BehaviorSubject<T>(value);
_transform = transform;
}
public void OnNext(T value) => _subject.OnNext(_transform(value));
public void OnCompleted() => _subject.OnCompleted();
public void OnError(Exception error) => _subject.OnError(error);
public IDisposable Subscribe(IObserver<T> o) => _subject.Subscribe(o);
}
用法示例:
ISubject<double> zoomFactor = new BehaviorTransformSubject<double>(1.0,
x => Math.Clamp(x, 0.1, 10.0));
替代实现: OnNext
方法可以这样替代实现:
public void OnNext(T value)
{
T newValue;
try { newValue = _transform(value); }
catch (Exception ex) { _subject.OnError(ex); return; }
_subject.OnNext(newValue);
}
这个函数以不同的方式处理 transform
函数可能出现的故障。它不会将错误直接抛回给调用 OnNext
方法的生产者,而是将错误传播给主题的消费者,从而导致其不可逆的终止(不再通过该主题传播任何值)。我想最初的 OnNext
实现具有您正在寻找的语义,但我可能错了。
公平地说,Math.Clamp
方法永远不会失败(根据文档),因此这种区别在您的案例中主要是学术性的。
是否可以转换/验证 ISubject<T>
的值?
例如我有一个 BehaviorSubject<double> zoomFactor = new(1);
,我想夹在 0.1
和 10
之间。
- 应该可以调用
zoomFactor.OnNext(Math.Clamp(newZoomFactor, 0.1, 10))
,但这样做会将责任推给调用者,我希望避免这种情况。 - 我可以使用
zoomFactor.Select(newZoomFactor => Math.Clamp(newZoomFactor, 0.1, 10))
根据观察进行转换,但是:- 这又是将责任外包给另一个来电者;
- 原始
zoomFactor
值不会在此处更改:想象一下用户缩小超过限制,然后再放大,但渲染的缩放在实际值在内部默默地回到限制范围内...
如何创建具有所需行为的自定义 ISubject<T>
实现?
class BehaviorTransformSubject<T> : ISubject<T>
{
private readonly BehaviorSubject<T> _subject;
private readonly Func<T, T> _transform;
public BehaviorTransformSubject(T value, Func<T, T> transform)
{
_subject = new BehaviorSubject<T>(value);
_transform = transform;
}
public void OnNext(T value) => _subject.OnNext(_transform(value));
public void OnCompleted() => _subject.OnCompleted();
public void OnError(Exception error) => _subject.OnError(error);
public IDisposable Subscribe(IObserver<T> o) => _subject.Subscribe(o);
}
用法示例:
ISubject<double> zoomFactor = new BehaviorTransformSubject<double>(1.0,
x => Math.Clamp(x, 0.1, 10.0));
替代实现: OnNext
方法可以这样替代实现:
public void OnNext(T value)
{
T newValue;
try { newValue = _transform(value); }
catch (Exception ex) { _subject.OnError(ex); return; }
_subject.OnNext(newValue);
}
这个函数以不同的方式处理 transform
函数可能出现的故障。它不会将错误直接抛回给调用 OnNext
方法的生产者,而是将错误传播给主题的消费者,从而导致其不可逆的终止(不再通过该主题传播任何值)。我想最初的 OnNext
实现具有您正在寻找的语义,但我可能错了。
公平地说,Math.Clamp
方法永远不会失败(根据文档),因此这种区别在您的案例中主要是学术性的。