[RxUI] 12 – Observable As Property Helper
ObservableAsPropertyHelper(OAPH)是一个类,它简化了IObservable和View Model上的属性之间的互操作。它允许你拥有一个属性,该属性反映通过IObservable流推送的最新值。
当可观察对象推送新值时,其将为View Model调用基于INotifyPropertyChanged
和INotifyPropertyChanging
的事件。ObservableAsPropertyHelper<T>
与Lazy非常相似,因为它提供了Value成员,该成员提供了Observable的最新值。它们通常是只读的,并反映IObservable流。通常将ObservableAsPropertyHelper与WhenAny
扩展组合使用。
示例
首先,使用ObservableAsPropertyHelper<T>
类声明一个Output Property:
readonly ObservableAsPropertyHelper<string> firstName;
public string FirstName => firstName.Value;
与读写属性类似,该代码应始终为100%样板。接下来,将在构造函数中使用辅助方法ToProperty
来初始化firstName
。ToProperty
有两个重载,一个重载直接返回OAPH,另一个重载在out
参数中返回OAPH。考虑到代码可读性,通常建议使用前一种方法:
firstName = this
.WhenAnyValue(x => x.Name)
.Select(name => name.Split(' ')[0])
.ToProperty(this, x => x.FirstName);
这里,ToProperty
创建了一个ObservableAsPropertyHelper
的实例,该实例将指示FirstName属性已更改。如果更喜欢在out
参数中返回OAPH的重载,可以使用下面的代码:
this.WhenAnyValue(x => x.Name)
.Select(name => name.Split(' ')[0])
.ToProperty(this, x => x.FirstName, out firstName);
ToProperty()
ToProperty
允许从IObservable<T>
构造一个ObservableAsPropertyHelper<T>
。当新值添加到IObservable<T>
时,它将使用IReactiveObject
接口中的重载方法来触发所需的事件。
ToProperty
是IObservable<T>
的扩展方法,在语义上类似于“Subscribe”。
public static ObservableAsPropertyHelper<TRet> ToProperty<TObj, TRet>(
this IObservable<TRet> target,
TObj source,
Expression<Func<TObj, TRet>> property,
TRet initialValue = default(TRet),
bool deferSubscription = false,
IScheduler? scheduler = null)
ToProperty
的参数允许指定属性的来源,通常指的是当前类。
- 公开属性的表达式或字符串。
- 可以选择在源可观察对象推送值之前为属性设置初始值。如果要推迟订阅,则此值可能就是第一个值,因为可观察对象不会立即订阅。
- 可以选择延迟订阅,直到用户访问该属性,该订阅才会订阅可观察对象。默认情况下,将立即订阅可观察对象,以便获取最新值。
- 可选的调度器,默认情况下将是调用位置的CurrentThreadScheduler。
Property与ObservableAsPropertyHelper
如果要更改值,应使用属性和RaiseAndSetIfChanged
。
如果属性的值仅是“计算”结果,例如,由其它属性的结果计算得来,那么ObservableAsPropertyHelper
属性就非常有用。当要公开IObservable<T>
的最新值时,也可以使用ObservableAsPropertyHelper
。
ObservableAsPropertyHelper
属性有助于删除在不同方法和组件中可能会存在多个位置的意大利面条代码。它们还可以清楚的定义在值的计算中使用哪些值,并帮助描述ObservableAsPropertyHelper
的从属属性,与可设置属性不同,在可设置属性中,必须进一步在代码库中搜索值发生改变的位置。
性能考量
nameof代替默认索引
对于有性能要求的解决方案,可以使用nameof
操作符重载版本的ToProperty
,该重载不使用表达式。
firstName = this
.WhenAnyValue(x => x.Name)
.Select(name => name.Split(' ')[0])
.ToProperty(this, nameof(FirstName));
延迟订阅
如果要创建大量的OAPH,请考虑延迟订阅。ToProperty
还允许使用deferSubscription来延迟订阅基础IObservable<T>
。延迟订阅,在没有访问Value属性之前不会有OAPH到基础IObservable<T>
的订阅。如果有更复杂的IObservable<T>
,这将十分有用,因为这种方法与Lazy<T>
所采用的方法非常接近。
// nameStatusObservable is IObservable<string>
var nameStatusObservable = this
.WhenAnyValue(x => x.Name)
.Select(name => GetLatestStatus(name));
// name is ObservableAsPropertyHelper<string>
name = nameStatusObservable
.ToProperty(this, nameof(FirstName), deferSubscription: true);
// nameStatusObservable won't be subscribed until the
// Name property is accessed.
private readonly ObservableAsPropertyHelper<string> name;
public string Name => name.Value;
延迟订阅的一个警告是,如果不小心,在将新值添加到IObservable<T>
之前,将有用无效的值。如果使用热
Observable,请考虑使用ReplaySubject<T>
并在构造函数中为ReplaySubject<T>
设置初始值1。
public class StatusViewModel : ReactiveObject
{
readonly ObservableAsPropertyHelper<string> status;
public string Status => status.Value;
public StatusViewModel()
{
var replayStatus = new ReplaySubject<string>(1);
// .OnNext() the 'replayStatus' subject somewhere...
// Note, that 'replayStatus' is also IObservable<string>,
// so we are allowed to add a call to .ToProperty() to
// convert it to ObservableAsPropertyHelper<string>.
status = replayStatus.ToProperty(this, nameof(Status), deferSubscription: true);
}
}
原文 https://www.reactiveui.net/docs/handbook/observable-as-property-helper