Skip to main content

Extension methods for the Exception class.

using System;
using System.Collections.Generic;
using System.Linq;

namespace Extensions
{
    public static class ExceptionExtensions
    {
        /// <summary>
        /// Gets the original Exception which is most inner Exception.
        /// </summary>
        /// <param name="self">The Exception</param>
        /// <returns>The original Exception</returns>
        public static Exception Unwrap(this Exception self)
        {
            if (self == null)
            {
                return null;
            }

            var next = self.GetBaseException();
            while (next.InnerException != null)
            {
                // On mono GetBaseException() doesn't seem to do anything so
                // just walk the inner exception chain.
                next = next.InnerException;
            }

            return next;
        }

        /// <summary>
        /// Gets all the error messages.
        /// </summary>
        /// <param name="self">The Exception</param>
        /// <note>
        /// The most inner self message is first in the list, and
        /// the most outer self message is last in the list
        /// </note>
        /// <returns>IEnumerable of message</returns>
        public static IEnumerable<string> Messages(this Exception self)
        {
            return self != null
                   ? new List<string>(self.InnerException.Messages()) { self.Message }
                   : Enumerable.Empty<string>();
        }

        public static string GetExceptionChain(this Exception self)
        {
            var message = new StringBuilder(self.Message);

            if (self.InnerException != null)
            {
                message.AppendLine();
                message.AppendLine(GetExceptionChain(self.InnerException));
            }

            return message.ToString();
        }

        ///<summary>
        /// Gets all the errors
        ///</summary>
        ///<param name="self">The Exception</param>
        ///<returns>IEnumerable of message</returns>
        /// <note>
        /// The most inner self is first in the list, and
        /// the most outer self is last in the list
        /// </note>
        public static IEnumerable<Exception> Exceptions(this Exception self)
        {
            return self != null
                   ? new List<Exception>(self.InnerException.Exceptions()) { self }
                   : Enumerable.Empty<Exception>();
        }

        /// <summary>
        /// Represents exceptions we should never attempt to catch and ignore.
        /// </summary>
        /// <param name="exception">The exception being thrown.</param>
        /// <returns></returns>
        public static bool IsCriticalException(this Exception exception)
        {
            if (exception == null)
            {
                throw new ArgumentNullException("exception");
            }

            return exception.IsFatalException()
                || exception is AppDomainUnloadedException
                || exception is BadImageFormatException
                || exception is CannotUnloadAppDomainException
                || exception is InvalidProgramException
                || exception is NullReferenceException
                || exception is ArgumentException;
        }

        /// <summary>
        /// Represents exceptions we should never attempt to catch and ignore
        /// when executing third party plugin code.
        /// This is not as extensive as a proposed IsCriticalException method
        /// that I want to write for our own code.
        /// </summary>
        /// <param name="exception">The exception being thrown.</param>
        /// <returns></returns>
        public static bool IsFatalException(this Exception exception)
        {
            if (exception == null)
            {
                throw new ArgumentNullException("exception");
            }

            return exception is StackOverflowException
                || exception is OutOfMemoryException
                || exception is ThreadAbortException
                || exception is AccessViolationException;
        }

        public static bool CanRetry(this Exception exception)
        {
            return !exception.IsCriticalException()
                && !(exception is ObjectDisposedException);
        }
    }
}