Up until now I have zealously kept every reference to the IDisposable
returned from any .Subscribe(...)
, .Connect(...)
, etc, method within Rx. I've done this because of my fear that a garbage collections will dispose the disposable if I don't keep the reference.
However, I did a test in LINQPad where I did some calls GC.Collect()
on a .Subscribe(...)
where I didn't keep the reference and guess what? The world didn't end and the subscription ran to completion.
In further testing I found that my subscription was disposed of immediately after .OnComplete()
without my intervention.
This leads me to understand that, at least for .Subscribe(...)
, that the only reason to keep a reference to the subscription is to force the subscription to end before its normal completion. It's more like a cancellation token.
So are all Rx disposables used for cancelling rather than keeping alive?
What, then, are the rules for hanging on to an IDisposable
?
No need to hold on to the IDisposable objects unless you want to unsubscribe from an observable source in the future. Similar for the Schedule methods on IScheduler, whose returned IDisposable objects can be used to cancel the scheduled action.
The garbage collector doesn't care about IDisposable object directly, nor do we implement finalizers on any of our objects, so basically lifetime management of subscriptions etc. is entirely up to you in the world of Rx. Compare it to Win32 handles if you want, with Dispose being the moral equivalent to CloseHandle.
Trivia: at some point during the design of Rx, the cancelable operations returned an Action whose invocation would cause the cancellation. Quite functionally inspired in nature, but less obvious to some. So, we decided to go with an interface that already represents a notion of resource management, and IDisposable was the obvious choice. This said, it departs a little from typical usage of the interface elsewhere in the .NET Framework:
Hope this helps,
-Bart (Rx team)
The IDisposable
returned by IScheduler.Schedule()
is only useful if you want to cancel the scheduled action (that is, before it's happened)
The IDisposable
returned by IObservable.Subscribe
and IConnectableObservable.Connect
are equivalent, in that disposing either will terminate the subscription to the source observable.
As for garbage collection, while Rx makes it a little more difficult to gauge, it's still bound by the rules of the GC. If the source of your observable is rooted (like the event of a UI control), then you don't need to worry about it being GC'd.
If your source is a Observable.Interval
or something else is basically a recursive IScheduler
call, then the scheduler should keep it alive (ie. the thread pool, task pool, or dispatcher) since the observable is rooted to the scheduler (via IScheduler.Schedule
).
Well, firstly I think its important to point out the garbage collection and disposal of objects / the IDisposable
interface are completely separate - in particular the garbage collector will never directly dispose of an object by calling the Dispose
method of an IDisposable
(unless the finalizer for that object does this itself).
As for when you should hang on to an IDisposable
, you should maintain a reference to any IDisposable
object that you need to dispose - it sounds like I'm stating the obvious and thats because I am! :-). Unless the disposable objects lifetime is longer than a single method the using
keyword is normally used:
using (var myDisposableObject = GetSomeDisposableObject())
{
myDisposableObject.DoThings();
}
This limits the scope of myDisposableObject
(which helps avoid attempting to use an object that has been disposed) and ensures that the object is correctly disposed of even if an exception is thrown.
It may be that for a certain class / API you don't need to (or shouldn't) dispose of an object that is returned to you, however that is entirely up to the API / class that returned that disposable object.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With