用 Observable.Create 包裹 Observable.FromEventPattern

Wrap Observable.FromEventPattern with Observable.Create

这两者的区别是什么(取自RxCookbook on GitHub):

public static IObservable<TProperty> OnPropertyChanges<T, TProperty>(this T source, Expression<Func<T, TProperty>> property)
    where T : INotifyPropertyChanged
{
    return  Observable.Create<TProperty>(o=>
    {
        var propertyName = property.GetPropertyInfo().Name;
        var propertySelector = property.Compile();

        return Observable.FromEventPattern<PropertyChangedEventHandler, PropertyChangedEventArgs>(
                        handler => handler.Invoke,
                        h => source.PropertyChanged += h,
                        h => source.PropertyChanged -= h)
                    .Where(e => e.EventArgs.PropertyName == propertyName)
                    .Select(e => propertySelector(source))
                    .Subscribe(o);
    });
}

还有这个(我自己写的):

public static IObservable<TProperty> OnPropertyChanges<T, TProperty>(this T source, Expression<Func<T, TProperty>> property)
    where T : INotifyPropertyChanged
{
    var propertyName = property.GetPropertyInfo().Name;
    var propertySelector = property.Compile();

    return Observable.FromEventPattern<PropertyChangedEventHandler, PropertyChangedEventArgs>(
                    handler => handler.Invoke,
                    h => source.PropertyChanged += h,
                    h => source.PropertyChanged -= h)
                .Where(e => e.EventArgs.PropertyName == propertyName)
                .Select(e => propertySelector(source));
}

我认为在第二个代码块中,propertyNamepropertySelector 将在调用 OnPropertyChanges 时进行评估,而在第一个代码块中,每次有人对这些变量进行评估订阅可观察对象。但是,我无法弄清楚 if/why 第一个块比第二个块更可取,为什么第一个块的作者决定使用 Observable.Create.

GitHub第一段代码作者的回答:

My stance on this is that calling a method that returns an IObservable should do nothing. It is the subscription to that returned value that should invoke any side effects or processing.

If a subscription cost needs to be amortised across numerous subscriptions then the various multicast options in Rx should be applied.

In this specific case, the argument is weak, however it then muddies the pattern and opens the door for other methods to "do work" without a subscription. I see this as a very common mistake and a source of race conditions and leaking resources (think opening a connection, starting a timer etc)