C# Exception Handling

Array Collections File String Windows VB.NET Algorithm ASP.NET Cast Class Compression Convert Data Delegate Directive Enum Exception If Interface Keyword LINQ Loop Method .NET Number Regex Sort StringBuilder Struct Switch Time Value

Exception: concept drawing

Things go wrong in computer programs. But if you check for every possible error at every step, your program soon becomes very complex. Exception handling in the C# language isolates these checks in a separate part of your program. It simplifies control flow. It renders programs robust and easy to maintain.

Exceptions in C# provide a structured, uniform, and type-safe way of handling both system-level and application-level error conditions. Hejlsberg et al., p. 599

Example

In the C# language, you can throw exceptions with a throw statement. Just as important, exceptions can be thrown automatically by the runtime because of the values of the variables in your code. In this program we divide by zero. This results in a DivideByZeroException, which is reported.

Overview: These examples cover C# exception types in depth. They also cover try and catch statements.

Program that throws an exception [C#]

using System;

class Program
{
    static void Main()
    {
	try
	{
	    int value = 1 / int.Parse("0");
	    Console.WriteLine(value);
	}
	catch (Exception ex)
	{
	    Console.WriteLine(ex.Message);
	}
    }
}

Output

Attempted to divide by zero.

Properties

Property (Icon copyright Microsoft)

Next, we see examples of some of the exception type's properties in the C# programming language. These properties include HelpLink, Message, Source, StackTrace, and TargetSite. This program creates an exception by dividing by zero. Then, it catches the exception instance and writes five properties to the Console.

Program that shows exception properties [C#]

using System;

class Program
{
    static void Main()
    {
	try
	{
	    int value = 1 / int.Parse("0");
	}
	catch (Exception ex)
	{
	    Console.WriteLine("HelpLink = {0}", ex.HelpLink);
	    Console.WriteLine("Message = {0}", ex.Message);
	    Console.WriteLine("Source = {0}", ex.Source);
	    Console.WriteLine("StackTrace = {0}", ex.StackTrace);
	    Console.WriteLine("TargetSite = {0}", ex.TargetSite);
	}
    }
}

Output
    (StackTrace was abbreviated for display.)

HelpLink =
Message = Attempted to divide by zero.
Source = ConsoleApplication1
StackTrace =    at Program.Main() in C:\...\Program.cs:line 9
TargetSite = Void Main()

HelpLink. The HelpLink here is empty because it was not defined on the exception; the Message is a short description of the exception's cause; the Source is the application name; the StackTrace is the path through the compiled program's method hierarchy that the exception was generated from; and the TargetSite is the name of the method where the error occurred.

TargetSite. The TargetSite property is one of the most important ones here. If you simply need to know what method an error occurred in, as is often the case with short programs, this property can be used to simplify what part of the errors you record.

Data

It is possible to store structured data on an exception that is thrown in one part of your program, and then later read in this data. Using the Data dictionary on the Exception instance, you can store associative keys and values.

Program that uses Data property [C#]

using System;
using System.Collections;

class Program
{
    static void Main()
    {
	try
	{
	    // Create new exception.
	    var ex = new DivideByZeroException("Message");
	    // Set the data dictionary.
	    ex.Data["Time"] = DateTime.Now;
	    ex.Data["Flag"] = true;
	    // Throw it!
	    throw ex;
	}
	catch (Exception ex)
	{
	    // Display the exception's data dictionary.
	    foreach (DictionaryEntry pair in ex.Data)
	    {
		Console.WriteLine("{0} = {1}", pair.Key, pair.Value);
	    }
	}
    }
}

Output

Time = 6/21/2010 11:17:41 AM
Flag = True

Overview. In this example, we employ the alternative control structure of exception handling in the try/catch blocks. In the try block, a new exception is allocated. Next, the Data property is assigned to. It can be used as a Hashtable or Dictionary. The keys and values are represented by the object type, which means any value or class instance can be used. Finally, the exception instance is thrown and in the catch block we display the Data contents.

Programming tip

Analysis. The usefulness of the Data property on the Exception type largely depends on the complexity of your program. If you have a simple program, it probably won't be useful. But when you have a complex system with a lot of different logical dependencies, you can record important information in the Data storage and then use it later to diagnose problems.

Constructs

Try keyword

We describe the keywords you need to use to add exception handling control to your programs. The main topics include how to throw, catch, and use finally constructs.

Try Catch Finally Throw

Checked/unchecked

Check illustration

The C# language provides the checked and unchecked contexts that you can use to specify whether exceptions are caused when value types overflow. This can result in more reliable code in some cases, as we point out.

Checked Unchecked

Types

There are many different derived exception types in the .NET Framework. Here, we look at various exception types and demonstrate how they are caused and what you can do to prevent them from occurring.

This is a warning ArgumentException ArgumentNullException ArgumentOutOfRangeException ArrayTypeMismatchException DirectoryNotFoundException DivideByZeroException FileNotFoundException FormatException IndexOutOfRangeException InvalidCastException InvalidOperationException KeyNotFoundException NotImplementedException NullReferenceException OutOfMemoryException OverflowException StackOverflowException TypeInitializationException

Note: There are several specific types of database exceptions that you may encounter, including SqlException, SqlCeException, and OdbcException. More information about SQL database usage is available in the Data category.

Benchmark

Performance optimization

To test how fast exception handling is, we benchmark a method that uses try/catch against a method that carefully tests for null to avoid failures. Both methods—GetA and GetB—return the first array element if the array is not null.

Program that shows exceptions [C#]

using System;

class Program
{
    static void Main()
    {
	int[] arr = new int[] { 1, 2, 3 };

	Console.WriteLine(GetA(arr));
	Console.WriteLine(GetB(arr));

	Console.WriteLine(GetA(null));
	Console.WriteLine(GetB(null));
    }

    /// <summary>
    /// Get the first element, checking for null.
    /// </summary>
    static int GetA(int[] arr)
    {
	if (arr != null)
	{
	    return arr[0];
	}
	else
	{
	    return 0;
	}
    }

    /// <summary>
    /// Get the first element, catching exceptions.
    /// </summary>
    static int GetB(int[] arr)
    {
	try
	{
	    return arr[0];
	}
	catch
	{
	    return 0;
	}
    }
}

Output

1
1
0
0

Results. Let's look at the performance results. The error cases never occur in the benchmark: the catch statement is never executed and the parameter is never null. This shows us the cost of null checks versus exceptions that are never thrown.

Data used in benchmark [C#]

int[] arr = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };

Code tested in tight loops [C#]

int v = GetA(arr);
if (v == 5)
{
    i++;
}

int v = GetB(arr);
if (v == 5)
{
    i++;
}

Results
    Iterations: 100000000

GetA - null checks:  95 ms
GetB - uses try:    607 ms

Analysis. We saw that when an exception is not thrown in the C# code, the try/catch block nevertheless has a negative effect on performance. When an exception is actually thrown—which wasn't tested in the benchmark—performance suffers even more.

Optimization tip

Because even exception handling constructs that are not executed and have no effect will cause a performance loss, you can change how your code is structured to improve performance. It is best to use try/catch at the outermost block of loops if correctness is preserved.

Exception Optimization

Tester-doer

.NET Framework information

The .NET Framework makes extensive use of a pattern called the Tester-Doer pattern. This refers to functions that do not throw exceptions on errors; instead, these functions return an error code (such as False) and take no action. This pattern improves the performance of certain important functions by avoiding exception handling.

Tester-Doer Pattern

Summary

The C# programming language

Exposed through an alternative control flow, exception handling facilitates a robust error-handling approach. You can indicate the type of the error by using a derived Exception type. With Exception properties, you can store or read information about errors as they occur. Correct use of exceptions facilitates fast debugging of those errors that always occur in new code.

Dot Net Perls