ObjectDisposedException

Jan 8, 2015 at 3:52 PM
If I inherit from Framework.ComponentModel.Disposable, I get an ObjectDisposedException on any objects I dispose manually (either from a direct call to Dispose() or via a Using statement.

The exception is thrown when the 'whenDisposed' event is raised using the following code:
 // Raise the WhenDisposed event.
this.whenDisposedSubject.OnNext(null);
I am using Elysium.Extra version 1.1.6


My original plan was to inherit from Framework.ComponentModel.NotifyPropertyChanges for my viewmodels. I have some event handlers I wanted to remove as soon as the viewmodel is no longer in use.

Any ideas?

Regards,
Jason Booth
Jan 8, 2015 at 8:28 PM
I think I figured it out, the reactor is obviously getting disposed before the OnNext() method is called, but this only happens if disposed explicitly either from the public Dispose() method or a Using statement.

I can only think of two solutions, one of which requires whenDisposedSubject to be writable:
    private void Dispose(bool disposing)
        {
            // Check to see if Dispose has already been called.
            if (!this.isDisposed)
            {
                // If disposing managed and unmanaged resources.
                if (disposing)
                {
                    this.whenDisposedSubject.Dispose();
                    this.whenDisposedSubject = null;
                    this.DisposeManaged();
                }

                this.DisposeUnmanaged();

                this.isDisposed = true;

                // Raise the WhenDisposed event.
                if (this.whenDisposedSubject != null)
                {
                    this.whenDisposedSubject.OnNext(null);
                }
            }
        }
or possibly this?
   private void Dispose(bool disposing)
        {
            // Check to see if Dispose has already been called.
            if (!this.isDisposed)
            {
                // If disposing managed and unmanaged resources.
                if (disposing)
                {
                    this.whenDisposedSubject.Dispose();
                    this.DisposeManaged();
                }

                this.DisposeUnmanaged();

                this.isDisposed = true;
                                
                if (!disposing)
                {
                    // Raise the WhenDisposed event.
                    this.whenDisposedSubject.OnNext(null);
                }
            }
        }
I am not sure of the best approach?
Coordinator
Jan 20, 2015 at 10:57 AM
Thanks for posting this. I'm not sure how this happened, but the Disposable implementation is not the one I've implemented elsewhere. Here is the full implementation which is more efficient and correct when it comes to Reactive Extensions. I hope to get this fix released soon.
    /// <summary>
    /// Base class for members implementing <see cref="IDisposable"/>.
    /// </summary>
    public abstract class Disposable : IDisposable
    {
        #region Fields

        private bool isDisposed;
        private Subject<Unit> whenDisposedSubject;

        #endregion

        #region Constructors

        /// <summary>
        /// Initialises a new instance of the <see cref="Disposable"/> class.
        /// </summary>
        public Disposable()
        {
            this.whenDisposedSubject = new Subject<Unit>();
        }

        #endregion

        #region Desctructors

        /// <summary>
        /// Finalizes an instance of the <see cref="Disposable"/> class. Releases unmanaged 
        /// resources and performs other cleanup operations before the <see cref="Disposable"/> 
        /// is reclaimed by garbage collection. Will run only if the 
        /// Dispose method does not get called.
        /// </summary>
        ~Disposable()
        {
            this.Dispose(false);
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets the when errors changed observable event. Occurs when the validation errors have changed for a property or for the entire object. 
        /// </summary>
        /// <value>
        /// The when errors changed observable event.
        /// </value>
        public IObservable<Unit> WhenDisposed
        {
            get
            {
                this.ThrowIfDisposed();

                if (this.whenDisposedSubject == null)
                {
                    this.whenDisposedSubject = new Subject<Unit>();
                }

                return this.whenDisposedSubject.AsObservable();
            }
        }

        /// <summary>
        /// Gets a value indicating whether this <see cref="Disposable"/> is disposed.
        /// </summary>
        /// <value><c>true</c> if disposed; otherwise, <c>false</c>.</value>
        public bool IsDisposed
        {
            get { return this.isDisposed; }
        }

        #endregion

        #region Public Methods

        /// <summary>
        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        public void Dispose()
        {
            // Dispose all managed and unmanaged resources.
            this.Dispose(true);

            // Take this object off the finalization queue and prevent finalization code for this 
            // object from executing a second time.
            GC.SuppressFinalize(this);
        }

        #endregion

        #region Protected Methods

        /// <summary>
        /// Disposes the managed resources implementing <see cref="IDisposable"/>.
        /// </summary>
        protected virtual void DisposeManaged()
        {
        }

        /// <summary>
        /// Disposes the unmanaged resources implementing <see cref="IDisposable"/>.
        /// </summary>
        protected virtual void DisposeUnmanaged()
        {
        }

        /// <summary>
        /// Throws a <see cref="ObjectDisposedException"/> if this instance is disposed.
        /// </summary>
        protected void ThrowIfDisposed()
        {
            if (this.isDisposed)
            {
                throw new ObjectDisposedException(this.GetType().Name);
            }
        }

        #endregion

        #region Private Methods

        /// <summary>
        /// Releases unmanaged and - optionally - managed resources.
        /// </summary>
        /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; 
        /// <c>false</c> to release only unmanaged resources, called from the finalizer only.</param>
        private void Dispose(bool disposing)
        {
            // Check to see if Dispose has already been called.
            if (!this.isDisposed)
            {
                // If disposing managed and unmanaged resources.
                if (disposing)
                {
                    this.DisposeManaged();
                }

                this.DisposeUnmanaged();

                this.isDisposed = true;

                if (this.whenDisposedSubject != null)
                {
                    // Raise the WhenDisposed event.
                    this.whenDisposedSubject.OnNext(Unit.Default);
                    this.whenDisposedSubject.OnCompleted();
                    this.whenDisposedSubject.Dispose();
                }
            }
        }

        #endregion
    }
Marked as answer by rehansaeed on 1/20/2015 at 4:57 AM