Reactive: Why don't you make the AnonymousDisposable class public?

Created on 20 Jul 2016  路  2Comments  路  Source: dotnet/reactive

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.

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

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.

All 2 comments

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.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

clairernovotny picture clairernovotny  路  6Comments

fubar-coder picture fubar-coder  路  6Comments

JanEggers picture JanEggers  路  4Comments

17-09 picture 17-09  路  5Comments

clairernovotny picture clairernovotny  路  3Comments