Let us take the rare case where I might want to implement IObservable<T> from scratch. I have a class below that generates random numbers, and I don't want to make it IDisposable because that'll be too much disposing for the client to do.
using System;
using System.Collections.Generic;
using System.Reactive.Disposables;
using System.Threading;
namespace ObservableNumberGenerator.UpFromScratch
{
// Cold
public class RandomNumbers : IObservable<int>
{
protected Random _random = null;
protected int _maxNumbersToGenerate;
protected int _startAfterMilliseconds = 1000;
protected int _generateEveryMilliseconds = 1000;
private List<ObserverContext<int>> _observers = new List<ObserverContext<int>>();
public RandomNumbers(int maxNumbersToGenerate,
int startAfterMilliseconds = 1000, int generateEveryMilliseconds = 1000)
{
_maxNumbersToGenerate = maxNumbersToGenerate;
_startAfterMilliseconds = startAfterMilliseconds;
_generateEveryMilliseconds = generateEveryMilliseconds;
_random = new Random();
}
protected virtual int GenerateNumber()
{
return _random.Next();
}
public IDisposable Subscribe(IObserver<int> observer)
{
if (observer == null) throw new ArgumentNullException("observer");
var observerContext = new ObserverContext<int>(observer);
observerContext.Counter = 0;
var timer = new Timer(ctx =>
{
var context = ctx as ObserverContext<int>;
if (context == null)
{
var msg = "Unable to start timer delegate. Supplied ObserverContext<int> is either null or is not of the correct type.";
var ex = new ArgumentNullException("context", msg);
observer.OnError(ex);
throw ex;
}
try
{
if (context.Counter == _maxNumbersToGenerate)
{
observer.OnCompleted();
}
var n = GenerateNumber();
++context.Counter;
observer.OnNext(n);
}
catch (Exception ex)
{
observer.OnError(ex);
}
} , observerContext, _startAfterMilliseconds, _generateEveryMilliseconds);
observerContext.Timer = timer;
var subscription = Disposable.Create(() =>
{
observerContext?.Timer?.Change(Timeout.Infinite, Timeout.Infinite);
observerContext?.Timer?.Dispose();
});
observerContext.Subscription = subscription;
_observers.Add(observerContext);
return subscription;
}
private class ObserverContext<T>
{
public ObserverContext(IObserver<T> observer)
{
Observer = observer;
}
public IObserver<T> Observer { get; private set; }
public Timer Timer { get; set; }
public IDisposable Subscription { get; set; }
public int Counter { get; set; }
}
}
}
So, in the finalizer for this class, I want to have a way to dispose off any undisposed subscriptions. If the AnonymouseDisposable was a public class, I could have used it like so:
~RandomNumbers()
{
_observers?.Select(o => o.Subscription).ToList().ForEach(subscription =>
{
var anonymousDisposable = subscription as AnonymousDisposable;
if (anonymousDisposable != null && !anonymousDisposable.IsDisposed)
{
anonymousDisposable.Dispose();
}
});
}
But I can't do that. Is there a reason that class is not public? It is presently declared as internal.
I realize it doesn't hurt to call Dispose on an IDisposable any number of times anyway, even if it had been previously disposed. But still.
Hi @Sathyaish,
Using finalizers in the way you describe is usually very bad. A finalizer should not "touch" any managed type and instead should only free unmanaged resources. That's one of the reasons why the Dispose pattern is typically implemented as
protected virtual void Dispose(bool disposing)
{
if(disposing)
{
// clean up managed resources, call dispose on contained disposables, etc
}
// Free up any unmanaged resources like OS handles
}
~MyClass()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
So short answer is that you cannot escape being disposable yourself if you contain member variables that are disposable.
For handling/dealing with disposables though, Rx does provide a Disposable class with several methods, including Create(...) that allows you to run a callback when Dispose is called. It also handles ensuring that the callback is only called once.
Also, the public contract for IDisposable explicitly allows Dispose to be called multiple times. It's up to the implementer to handle that safely, which the Disposable class does.
Thank you.
Most helpful comment
Hi @Sathyaish,
Using finalizers in the way you describe is usually very bad. A finalizer should not "touch" any managed type and instead should only free unmanaged resources. That's one of the reasons why the Dispose pattern is typically implemented as
So short answer is that you cannot escape being disposable yourself if you contain member variables that are disposable.
For handling/dealing with disposables though, Rx does provide a
Disposableclass with several methods, includingCreate(...)that allows you to run a callback when Dispose is called. It also handles ensuring that the callback is only called once.Also, the public contract for
IDisposableexplicitly allowsDisposeto be called multiple times. It's up to the implementer to handle that safely, which theDisposableclass does.