我在ViewModels(Silverlight和/或Wp7应用程序)中使用Reactive Extensions来轻松处理事件。为了简单起见,假设我在VM的ctor:中有一行这样的代码
Observable.FromEvent<PropertyChangedEventArgs>(
h => MyObject.PropertyChanged += h,
h => MyObject.PropertyChanged -= h)
.Where(e=>e.PropertyName == "Title")
.Throttle(TimeSpan.FromSeconds(0.5))
.Subscribe(e=>{/*do something*/});
这将返回一个IDisposable对象,如果该对象被释放,将取消订阅(我的假设是对的吗?)
如果我没有引用它,它迟早会被收集,我的处理程序也会被取消订阅。
我通常在我的VM中有一个List<IDisposable>
,我会向它添加订阅,但我对此感到恶心,好像我没有以正确的Rx方式做一些事情。
在这种情况下,最佳实践和推荐模式是什么
您的第一个假设是正确的,IDisposable用于取消订阅。但是,您不需要持有对IDisposable的引用来防止您的观察者被收集。IOobservable需要保留对观测者的引用,以便能够调用其方法,从而只要可观察对象是活动的,就保持观测者是活动的。
敏锐的读者会意识到,我在某种程度上乞求这个问题,因为我已经假设观察到的东西不会被收集。为了解决这个问题,让我们对幕后发生的事情进行一些合理的猜测。第一个可观察到的是订阅一个事件。这意味着,从我们订阅到取消订阅,带有事件的对象都引用了我们的观察者。由于Rx操作符必须在某个点订阅其源,因此可以假设事件观测器引用了Where观测器,Where观测器引用了Throttle观测器,Throttle观察器当然是指我们的最终观察器。
由于我们无法取消订阅,这将观察者的生活与事件对象的生活联系起来。在不完全了解你的程序的情况下,我可以走得更远,但我认为这应该足以让你确定可观测的生命周期。
这实际上指出了标准.NET事件可能存在的潜在"内存泄漏",即对象保持其所有事件订阅者的活动,这就引出了第二个问题。如果您不保留对IDisposable的引用,您将永远无法取消订阅该事件,并且您的对象将继续收到通知,即使在您关闭与其相关的视图之后也是如此。如果事件源的寿命不超过视图的寿命,这可能不是问题,但我建议使用一次性。
这并没有什么"取消Rx"的地方,Rx甚至包括一个很好的类,如果你愿意的话,可以作为列表的替代System.Reactive.Disposables.CompositeDisposable
。
Gideon在这里不正确。Rx如何使用IDisposable的语义与典型的.NET不同。从Subscribe返回的IDisposaable是如果您想在IObservable结束之前取消订阅。如果您不想这样做,那么不需要使用所有额外的一次性管理来使代码复杂化。
常见的做法是实现IDisposable接口并在此处处理您的一次性成员。