News & UpdatesProgrammingWeb programming StoreMy Projects
Links
Affiliates

C# Tutorial – 22 – Exception Handling

Exception handling allows programmers to deal with unexpected situations that may occur in programs. As an example, consider opening a file using the StreamReader class in the System.IO namespace. To see what kinds of exceptions this class may throw, you can hover the cursor over the class name in Visual Studio. For instance, the System.IO exceptions FileNotFoundException and DirectoryNotFoundException. If anyone of those exceptions occurs, the program will terminate with an error message.

using System;
using System.IO;
 
class ErrorHandling 
{
  static void Main()
  { 
    StreamReader sr = new StreamReader("missing.txt"); // run-time error
  }
}

Try-catch statement

To avoid crashing the program the exceptions must be caught using a try-catch statement. This statement consists of a try block containing the code that may cause the exception, and one or more catch clauses. If the try block successfully executes, the program will then continue running after the try-catch statement. However, if an exception occurs the execution will then be passed to the first catch block able to handle that exception type.

try
{
  StreamReader sr = new StreamReader("missing.txt"); 
}
catch 
{ 
  Console.Write("File not found"); 
}

Catch block

Since the catch block above is not set to handle any specific exception it will catch all of them. This is equivalent to catching the System.Exception class, because all exceptions derive from this class.

catch (Exception) {}

To catch a more specific exception the catch block needs to be placed before more general exceptions.

catch (FileNotFoundException) {}
catch (Exception) {}

The catch block can optionally define an exception object that can be used to obtain more information about the exception, such as a description of the error.

catch (Exception e) 
{
  Console.Write("Error: " + e.Message);
}

Finally block

As the last clause in the try-catch statement, a finally block can be added. This block is used to clean up certain resources allocated in the try block. Typically, limited system resources and graphical components need to be released in this way once they are no longer needed. The code in the finally block will always execute, whether or not there is an exception. This will be the case even if the try block ends with a jump statement, such as return.

In the example used previously, the file opened in the try block should be closed if it was successfully opened. This is done properly in the next code segment. To be able to access the StreamReader object from the finally clause it must be declared outside of the try block. Keep in mind that if you forget to close the stream the garbage handler will eventually close it for you, but it is a good practice to do it yourself.

StreamReader sr = null;
try
{
  sr = new StreamReader("missing.txt"); 
}
catch (FileNotFoundException) {}
finally 
{
  if (sr != null) sr.Close();
}

The statement above is known as a try-catch-finally statement. The catch block can be left out to create a try-finally statement. This statement will not catch any exceptions. Instead, it will ensure the proper disposal of any resources allocated in the try block. This can be useful if the allocated resource does not throw any exceptions. For instance, such a class would be Bitmap, in the System.Drawing namespace.

using System.Drawing;
// …
Bitmap b = null;
try
{
  b = new Bitmap(100, 100);
  System.Console.WriteLine("Width: " + b.Width + ", Height: " + b.Height);
}
finally 
{
  if (b != null) b.Dispose();
}

Note that when using a Console Project a reference to the System.Drawing assembly needs to be manually added for those members to be accessible. To do so right click the References folder in the Solution Explorer window and select Add Reference. Then from the .NET tab select the System.Drawing assembly and click OK to add its reference to your project.

Using statement

The using statement provides a simpler syntax for writing the try-finally statement. This statement starts with the using keyword followed by the resource to be acquired, specified in parentheses. It then includes a code block in which the obtained resource can be used. When the code block has finished executing, the Dispose method of the object is automatically called to clean it up. This method comes from the System.IDisposable interface, so the specified resource must implement this interface. The code below performs the same function as the one in the previous example, but with fewer lines of code.

using System.Drawing;
// …
using (Bitmap b = new Bitmap(100, 100))
{
  System.Console.WriteLine("Width: " + b.Width + ", Height: " + b.Height);
}

Throwing exceptions

When a situation occurs that a method cannot recover from it can generate an exception to signal the caller that the method has failed. This is done using the throw keyword followed by a new instance of a class deriving from System.Exception.

static void MakeError()
{
  throw new System.DivideByZeroException("My error message");
}

The exception will then propagate up the caller stack until it is caught. If a caller catches the exception but is not able to recover from it, the exception can be re-thrown using only the throw keyword.

static void Main()
{
  try { MakeError(); } catch { throw; }
}

If there are no more try-catch statements the program will stop executing and display the error message.