User Tools

Site Tools


notes:csharp:exceptions

This is an old revision of the document!


Exceptions in C#

Exception types suitable for use in custom applications:

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 applications:

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.

Example: 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) { }
}

Example: 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) { }
}

Example: A method that traverses 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);
    }
}

Example: 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
}

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

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);
    // ...
}

Example: 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();
    }
}

Example: Environment.FailFast and ExceptionDispatchInfo.Throw:

Preventing the finally block from running can be achieved by using Environment.FailFast. This method has two overloads:

  • one that only takes a string
  • another one that also takes an exception

When this method is called, the message (and optionally the exception) are written to the Windows application event log, and the application is terminated.

ExceptionDispatchInfo.Throw is an option for re-throwing an exception introduced in C# 5. It can be used to throw an exception and preserve the original stack trace. It can be used to throw an exception in one thread and catch it in another one. The .NET Framework uses this feature when dealing with async/await. An exception that is thrown on an async thread is captured and re-thrown on the executing thread.

ExceptionDispatchInfo exceptionInfo = null;
 
try
{
    string s = Console.ReadLine(); 
    int.Parse(s);
}
catch (FormatException exc)
{
    exceptionInfo = ExceptionDispatchInfo.Capture(exc);
}
 
if (exceptionInfo != null)
{ 
    // It is possible to use the ExceptionDispatchInfo.Throw method
    // outside of a catch block. 
    exceptionInfo.Throw();
}

References

notes/csharp/exceptions.1468862530.txt.gz · Last modified: 2016/07/18 by admin