Thursday, April 21, 2011

How to properly implement IDisposable

A lot of junior developers don't know how to implement IDisposable properly. I've provided below an example base class and a possibly useful interface which can assist in this effort. It is the accumulation of knowledge I've gathered from reading books and blogs.

Disclaimer: The code below carries no guarantees-- heck, it may not even work-- so use at your own risk.



    public interface INotifyDisposed
    {
        event EventHandler Disposed;
    }


    public abstract class ScopeLimitedObject : IDisposable, INotifyDisposed
    {
        public event EventHandler Disposed = null;


        // We provide this version of the property name as it is most common in legacy .NET and applications.
        public bool Disposing
        { get { return IsDisposing; } }

        protected bool IsDisposing
        { get; private set; }

        public bool IsDisposed
        { get; protected set; }


        private void RaiseDisposed()
        {
            var @event = Disposed;
            if (@event != null)
                @event(this, null);
        }


        /// <summary>
        /// This is the dispose routine you want to normally handle.
        /// </summary>
        protected virtual void OnDisposeExplicit()
        { }

        /// <summary>
        /// This is the dispose routine that *typically* originates from garbage collection and where
        /// dispose was NEVER called explicitly.
        /// </summary>
        protected virtual void OnDisposeImplicit()
        { }

        /// <summary>
        /// This is a dispose routine that is always called, during both implicit and explicit calls to 
        /// Dispose.  WARNING: this method will likely (though not guaranteed) be executed twice, 
        /// so be careful of the side effects in your own overriding method.  You never need to provide
        /// an implementation of this method if you adequately provide implementations for both
        /// "OnDisposeExplicit" and "OnDisposeImplicit".
        /// </summary>
        protected virtual void OnDisposeRegardless()
        { }


        protected void Dispose(bool isDisposing)
        {
            if (!(IsDisposed || IsDisposing))
            {
                IsDisposing = true;

                if (isDisposing)
                {
                    IsDisposed = true;
                    OnDisposeExplicit();
                }
                else
                    OnDisposeImplicit();

                RaiseDisposed();
            }

            OnDisposeRegardless();
        }


        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }


        ~ScopeLimitedObject()
        { Dispose(false); }
    }


Tuesday, April 19, 2011

How to properly publish events

A lot of junior developers don't know how to publish events properly. I've provided an API below that can standardize this mundane task in your code. It is the accumulation of knowledge I've gathered from reading books and blogs.

Disclaimer: The code below carries no guarantees-- heck, it may not even work-- so use at your own risk.

    public static class Publish
    {
        public const bool CatchesExceptionsThrownByEventHandlersByDefault = false;


        static void Event(EventHandler eventPublishingDelegate, object sender, EventArgs eventArgs, bool catchExceptionsThrownByEventHandlers)
        {
            var eventPublishingDelegate_referenceHolder = eventPublishingDelegate;

            if (eventPublishingDelegate_referenceHolder != null)
            {
                if (!catchExceptionsThrownByEventHandlers)
                    eventPublishingDelegate_referenceHolder(sender, eventArgs);
                else
                    foreach (EventHandler eventHandler in eventPublishingDelegate_referenceHolder.GetInvocationList().Cast<EventHandler>())
                    {
                        try
                        { eventHandler(sender, eventArgs); }
                        catch
                        { }
                    }
            }
        }


        static void Event<TEventArgs>(EventHandler<TEventArgs> eventPublishingDelegate, object sender, TEventArgs eventArgs, bool catchExceptionsThrownByEventHandlers) where TEventArgs : EventArgs
        {
            var eventPublishingDelegate_referenceHolder = eventPublishingDelegate;

            if (eventPublishingDelegate_referenceHolder != null)
            {
                if (!catchExceptionsThrownByEventHandlers)
                    eventPublishingDelegate_referenceHolder(sender, eventArgs);
                else
                    foreach (EventHandler<TEventArgs> eventHandler in eventPublishingDelegate_referenceHolder.GetInvocationList().Cast<EventHandler<TEventArgs>>())
                    {
                        try
                        { eventHandler(sender, eventArgs); }
                        catch
                        { }
                    }
            }
        }

        static void Event<TEventArgs>(Delegate eventPublishingDelegate, object sender, TEventArgs eventArgs, bool catchExceptionsThrownByEventHandlers)
            where TEventArgs : EventArgs
        {
            if (!(eventPublishingDelegate is Delegate))
                return;

            var eventPublishingDelegate_referenceHolder = eventPublishingDelegate;
            var delegateInvocationArguments = new object[] { sender, eventArgs };

            if (eventPublishingDelegate_referenceHolder != null)
            {
                foreach (Delegate eventHandler in eventPublishingDelegate_referenceHolder.GetInvocationList())
                {
                    if (!catchExceptionsThrownByEventHandlers)
                        eventHandler.DynamicInvoke(delegateInvocationArguments);
                    else
                    {
                        try
                        { eventHandler.DynamicInvoke(delegateInvocationArguments); }
                        catch
                        { }
                    }
                }
            }
        }



        public static void Event(EventHandler eventPublishingDelegate, object sender, EventArgs eventArgs)
        { Event(eventPublishingDelegate, sender, eventArgs, CatchesExceptionsThrownByEventHandlersByDefault); }

        public static void Event<TEventArgs>(EventHandler<TEventArgs> eventPublishingDelegate, object sender, TEventArgs eventArgs) where TEventArgs : EventArgs
        { Event<TEventArgs>(eventPublishingDelegate, sender, eventArgs, CatchesExceptionsThrownByEventHandlersByDefault); }

        public static void Event<TEventArgs>(Delegate eventPublishingDelegate, object sender, TEventArgs eventArgs) where TEventArgs : EventArgs
        { Event<TEventArgs>(eventPublishingDelegate, sender, eventArgs, CatchesExceptionsThrownByEventHandlersByDefault); }


        public static void EventWithExceptions(EventHandler eventPublishingDelegate, object sender, EventArgs eventArgs)
        { Event(eventPublishingDelegate, sender, eventArgs, false); }

        public static void EventWithExceptions<TEventArgs>(EventHandler<TEventArgs> eventPublishingDelegate, object sender, TEventArgs eventArgs) where TEventArgs : EventArgs
        { Event<TEventArgs>(eventPublishingDelegate, sender, eventArgs, false); }

        public static void EventWithExceptions<TEventArgs>(Delegate eventPublishingDelegate, object sender, TEventArgs eventArgs) where TEventArgs : EventArgs
        { Event<TEventArgs>(eventPublishingDelegate, sender, eventArgs, false); }


        public static void EventWithoutExceptions(EventHandler eventPublishingDelegate, object sender, EventArgs eventArgs)
        { Event(eventPublishingDelegate, sender, eventArgs, true); }

        public static void EventWithoutExceptions<TEventArgs>(EventHandler<TEventArgs> eventPublishingDelegate, object sender, TEventArgs eventArgs) where TEventArgs : EventArgs
        { Event<TEventArgs>(eventPublishingDelegate, sender, eventArgs, true); }

        public static void EventWithoutExceptions<TEventArgs>(Delegate eventPublishingDelegate, object sender, TEventArgs eventArgs) where TEventArgs : EventArgs
        { Event<TEventArgs>(eventPublishingDelegate, sender, eventArgs, true); }
    }