SerialDisposable<T>
05 May 2015Reactive Extensions includes a super useful class called SerialDisposable
. The idea is you can assign any IDisposable
to it and it automatically disposes of any previously assigned value. Suppose, for example, you have a need to create a UIPopoverController
whenever some user interaction occurs:
private readonly SerialDisposable popoverDisposable = new SerialDisposable();
private UIPopoverController searchPopover;
private UIPopoverController SearchPopover
{
get { ... }
set { ... }
}
// elsewhere in the code
someUserInteractionObservable
.Select(_ => new UIPopoverController(...))
.Subscribe(
x =>
{
popoverDisposable.Disposable = x;
this.SearchPopover = x;
});
Here we ensure that any previously created UIPopoverController
is disposed of by assigning the newly created one to the Disposable
property of our SerialDisposable
. But notice how we also assign the same value to our SearchPopover
property. That’s because SerialDisposable.Disposable
is of type IDisposable
. That is, by storing our UIPopoverController
inside a SerialDisposable
, we’ve lost some compile-time type information.
OK, so the example is contrived and incomplete but you get the point.
When faced with this situation of either doubling-up on backing fields versus sprinkling the code with casts, I figured I’d just create a generic SerialDisposable<T>
class instead:
namespace System.Reactive.Disposables
{
using System;
// generic variant of Rx's SerialDisposable class
public sealed class SerialDisposable<T> : ICancelable, IDisposable
where T : IDisposable
{
private readonly SerialDisposable disposable;
public SerialDisposable()
{
this.disposable = new SerialDisposable();
}
public bool IsDisposed => this.disposable.IsDisposed;
public T Disposable
{
get { return (T)this.disposable.Disposable; }
set { this.disposable.Disposable = value; }
}
public void Dispose()
=> this.disposable.Dispose();
}
}
SerialDisposable
is sealed, so we’re forced to use composition instead of inheritance, but we were going to do that anyway, right?
The upshot is that now we can just declare a single field of type SerialDisposable<UIPopoverController>
and utilize that wherever relevant:
private readonly SerialDisposable<UIPopoverController> searchPopover = ...;
private UIPopoverController SearchPopover
{
get { return this.searchPopover.Disposable; }
set
{
this.searchPopover.Disposable = value;
// plus raise property changed if necessary
}
}
// elsewhere in the code
someUserInteractionObservable
.Select(_ => new UIPopoverController(...))
.Subscribe(x => this.SearchPopover = x);
I’ve been pondering as well whether it would be a Good Idea™ to have SerialDisposable<T>
also implement IObservable<T>
, where each instance assigned to the Disposable
property ticks the observable. As yet, the jury’s out.