User Tools

Site Tools


notes:csharp:exceptions

Exceptions in C#

A minimal custom exception definition:

public class CustomException : Exception // inherit from Exception rather than from ApplicationException
{
    public CustomException() { }
    public CustomException(string message) : base(message) { }
    public CustomException(string message, Exception innerException) : base(message, innerException) { }
}

A custom exception that overrides the Message property:

public class CustomException : Exception 
{
    // Override the Exception.Message property.
    public override string Message
    {
        get
        {
            StringBuilder sb = new StringBuilder();
            sb.Append("Time: " + DateTime.Now.ToString("f") + Environment.NewLine); // long date + short time
            sb.Append("Type: " + this.GetType().ToString() + Environment.NewLine);
            sb.Append("Message: " + base.Message + Environment.NewLine);
            if (this.InnerException != null) 
                sb.Append("Inner Message: " + this.InnerException.Message + Environment.NewLine);
            return sb.ToString();
        }
    }
 
    public CustomException() { }
    public CustomException(string message) : base(message) { }
    public CustomException(string message, Exception innerException) : base(message, innerException) { }
}

A method that iterates over all inner exceptions and returns their messages and types:

public string GetExceptionDesc(Exception exc)
{
    StringBuilder sb = new StringBuilder();
 
    sb.AppendLine("*** EXCEPTION ***");
    sb.AppendLine("DateTime: " + DateTime.Now.ToString());
    sb.AppendLine("Message: " + exc.Message);
    sb.AppendLine("Type: " + exc.GetType().ToString());
    sb.AppendLine("StackTrace: " + exc.StackTrace);
    AppendInnerExceptions(exc.InnerException, sb, 1);
 
    return sb.ToString();
}
 
private void AppendInnerExceptions(Exception exc, StringBuilder sb, int level)
{
    if (exc != null)
    {
        sb.AppendLine(String.Format("InnerException-{0} Message: {1}", level.ToString(), exc.Message));
        sb.AppendLine(String.Format("InnerException-{0} Type: {1}", level.ToString(), exc.GetType().Name));
 
        AppendInnerExceptions(exc.InnerException, sb, ++level);
    }
}

A custom exception that implements ISerializable:

// The SerializableAttribute ensures that the custom exception 
// can be serialized and works correctly across application domains 
// (for example, when a web service returns an exception).
[Serializable]
public class CustomException : Exception 
{
    // Custom properties for serialization.
    public int ExceptionNumber { get; private set; }
    public string MethodName { get; private set; }
 
    // A constructor accepting values for custom properties.
    public CustomException(string message, Exception innerException, int exceptionNumber, string methodName)
        : base(message, innerException)
    {
        this.ExceptionNumber = exceptionNumber;
        this.MethodName = methodName;
    }
 
    #region ISerializable Implementation
    protected CustomException(SerializationInfo info, StreamingContext context)
        : base(info, context)
    {
        // Get the custom property out of the serialization stream and set the object's property.
        ExceptionNumber = info.GetInt32("ExceptionNumber");
        MethodName = info.GetString("MethodName");
    }
 
    // The GetObjectData method is called  on serialization. 
    public override void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        // Add the custom property into the serialization stream.
        info.AddValue("ExceptionNumber", ExceptionNumber);
        info.AddValue("MethodName", MethodName);
 
        // Call the base exception class to ensure proper serialization.
        base.GetObjectData(info, context);
    }
    #endregion
}

An exception builder is a helper method that creates and throws exceptions. A method that calls the exception builder is inlined by the compiler:

public class CustomException : Exception
{
    public CustomException(string message) : base(message) { }
}
 
// Enum values representing errors.
public enum ErrorCode
{
    NotInitialized,
    InvalidArgument,
    TimeOut
}
...
// An exception builder method that creates and throws CustomException.
private static void ThrowException(ErrorCode code)
{
    string message = "";
 
    switch (code)
    {
        case ErrorCode.NotInitialized:
            message = "Component is not initialized ...";
            break;
        case ErrorCode.InvalidArgument:
            message = "Invalid argument passed ...";
            break;
        case ErrorCode.TimeOut:
            message = "Time-out ocurred ...";
            break;
        default:
            message = "Generic exception.";
            break;
    }
 
    throw new CustomException(message);
}
...
// This method builds exceptions using the exception builder.
// As it does not use 'throw' to throw exceptions, it is inlined by the compiler.
public void DoSomething()
{
    // ...
    ThrowException(ErrorCode.NotInitialized);
    // ...
}

An example of implementing a SoapException wrapper:

using System;
using System.Text;
using System.Web.Services.Protocols;
using System.Xml;
...
public class SoapExceptionWrapper
{
    private long code;
    private string description;
    private string type;
 
    public long Code { get { return code; } }
    public string Description { get { return description; } }
    public string Type { get { return type; } }
 
    public SoapExceptionWrapper(SoapException exc)
    {
        if (exc.Detail != null && exc.Detail.InnerXml != "")
        {
            XmlDocument doc = new XmlDocument();
            doc.LoadXml(exc.Detail.InnerXml);
 
            if (doc.DocumentElement != null && doc.DocumentElement.Name == "error")
            {
                this.code =
                    Convert.ToInt64(doc.DocumentElement.SelectSingleNode("/error/code").InnerText, 16);
                this.description = doc.DocumentElement.SelectSingleNode("/error/description").InnerText;
                this.type = doc.DocumentElement.SelectSingleNode("/error/type").InnerText;
            }
            else
            {
                this.code = 0;
                this.description = "No error";
            }
        }
        else if (exc.GetBaseException() != null)
        {
            this.code = 0;
            this.description = exc.GetBaseException().Message;
            this.type = exc.GetBaseException().GetType().ToString();
        }
    }
 
    public override string ToString()
    {
        StringBuilder sb = new StringBuilder();
 
        sb.Append("SoapException Details:" + Environment.NewLine);
        sb.Append("    Code: " + this.code + Environment.NewLine);
        sb.Append("    Description: " + this.description + Environment.NewLine);
        sb.Append("    Type: " + this.type + Environment.NewLine);
 
        return sb.ToString();
    }
}

Exception types that are suitable to throw in custom code:

Exception The base class for all exceptions. Throw a more specific type of exception rather than this one.
ArgumentException Throw this exception when an argument to your method is invalid.
ArgumentNullException A specialized form of ArgumentException that you can throw when one of your arguments is null and this isn't allowed.
ArgumentOutOfRangeException A specialized form of ArgumentException that you can throw when an argument is outside the allowable range of values.
FormatException Throw this exception when an argument does not have a valid format.
InvalidOperationException Throw this exception when a method is called that is invalid for the object's current state.
NotImplementedException This exception is often used in generated code where a method has not been implemented yet.
NotSupportedException Throw this exception when a method is invoked that you don't support.
ObjectDisposedException Throw when a user of your class tries to access methods when Dispose has already been called.

Exception types that should not be thrown in custom code:

ArithmeticException A base class for other exceptions that occur during arithmetic operations.
ArrayTypeMismatchException Thrown when you want to store an incompatible element inside an array.
DivideByZeroException Thrown when you try to divide a value by zero.
IndexOutOfRangeException Thrown when you try to access an array with an index that is less than zero or greater than the size of the array.
InvalidCastException Thrown when you try to cast an element to an incompatible type.
NullReferenceException Thrown when you try to reference an element that is null.
OutOfMemoryException Thrown when creating a new object fails because the CLR doesn't have enough memory available.
OverflowException Thrown when an arithmetic operation overflows in a checked context.
StackOverflowException Thrown when the execution stack is full.
TypeInitializationException Thrown when a static constructor throws an exception that is unhandled.
notes/csharp/exceptions.txt · Last modified: 2016/11/28 by admin