DekGenius.com
[ Team LiB ] Previous Section Next Section

Recipe 5.16 Preventing the Nefarious TypeInitializationException

Problem

Problems can occur when initializing a class or a structure's static fields. Some of these problems are serious enough to raise a TypeInitializationException exception. Unfortunately, this exception can be hard to track down and can potentially shut down your application. You want to prevent this from occurring.

Solution

If you are initializing static fields to a value, null, or not initializing them at all, as is the case with the following class:

public class TestInit
{
    public static object one;
    public static string two = one.ToString( );
}

you should consider rewriting the class to include a static constructor that performs the initialization of the static fields. This will aid in the debugging of your static fields:

public class TestInit
{
    static TestInit( )
    {
        try
        {
            one = null;
            two = one.ToString( );
        }
        catch (Exception e)
        {
            Console.WriteLine("CAUGHT EXCEPTION IN .CCTOR: " + e.ToString( ));
        }
    }

    public static object one;
    public static string two;
}

Discussion

To see this exception in action, run the following method:

public static void Main( )
{
    // Causes TypeInitializationException
    TestInit c = new TestInit( );

    // Replacing this method's code with the following line 
    //    will produce similar results
    //TestInit.one.ToString( );
}

This code creates an instance of the TestInit class. We are assured that any static fields of the class will be initialized before this class is created, and any static constructors on the TestInit class will be called as well. The TestInit class is written as follows:

public class TestInit
{
    public static object one = null;
    public static string two = one.ToString( );
}

As you can see, a NullReferenceException should be thrown on the second static field, since it is trying to call ToString on an object set to null. If run from the development environment, you will see two message boxes pop up in sequence. The first is the message box depicted in Figure 5-4. The second message box shown is depicted in Figure 5-5. The application proceeds to shut down at this point.

Figure 5-4. An unhandled NullReferenceException
figs/cscb_0504.gif
Figure 5-5. An unhandled TypeInitializationException
figs/cscb_0505.gif

However, if this executable is run from outside the development environment, the message box shown in Figure 5-6 is displayed and the application shuts down.

Figure 5-6. An unhandled runtime exception
figs/cscb_0506.gif

Now, let's add a try-catch block around the Main method, as shown here:

public static void Main( )
{
    try
    {
        // Causes TypeInitializationException
        TestInit c = new TestInit( );
    }
    catch(Exception e)
    {
        Console.WriteLine("CAUGHT EXCEPTION IN CREATING METHOD: " + e.ToString( ));
    }
}

When this code is run inside the development environment, the message box in Figure 5-4 appears again, but the TypeInitializationException is caught by the new exception handler that we added to the Main method. The text displayed by the exception handler is shown here:

CAUGHT EXCEPTION IN CREATING METHOD: System.TypeInitializationException: 
     The type initializer for "TestInit" threw an exception. ---> 
     System.NullReferenceException: Object reference not set to an instance
                                    of an object.
   at Chapter_Code.TestInit..cctor( ) in c:\book cs cookbook\code\test.cs:line 200
   --- End of inner exception stack trace ---
   at Chapter_Code.TestInit..ctor( )
   at Chapter_Code.Class1.TypeinitExceptionPrevention( ) in c:\book cs cookbook\
     code\test.cs:line 175

The TypeInitializationException wraps the NullReferenceException that was the original exception thrown. The runtime provides the TypeInitializationException wrapper automatically.

A third method of trapping this exception is to use the exception event handler. This exception event handler is described in detail in Recipe 5.10. When only this exception handler is employed with no supporting try-catch or try-catch-finally blocks, the following events occur when running the executable in the development environment:

  1. The message boxes shown in Figures Figure 5-4 and Figure 5-5 are displayed in that order.

  2. The event exception handler intercepts the exception before the application is terminated. When the executable is run standalone, the message box in Figure 5-6 is displayed first. Then, the event exception handler intercepts the exception, and, finally, the application is terminated.

The second method seems to work best; use try-catch blocks at a minimum around code that will potentially cause static fields to initialize.

There is a way to eliminate the TypeInitializationException from the picture. We can simply initialize our class or structure's static fields within the appropriate static constructor(s), shown in the Solution section of this recipe. When this code is executed, the catch block captures the real exception and there is no fear of the application shutting down. The text displayed by the catch block is as follows:

CAUGHT EXCEPTION IN .CCTOR: System.NullReferenceException: Object reference not set to an instance of an object.
   at Chapter_Code.TestInit..cctor( ) in c:\book cs cookbook\code\test.cs:line 191

This is much cleaner and more elegant than the other solutions. In addition, tracking down the source of the bug is much easier. As a note, this exception now operates in the same manner regardless of whether the application is being run in the development environment.

See Also

See the "Error Raising and Handling Guidelines" and "TypeInitializationException Class" topics in the MSDN documentation.

    [ Team LiB ] Previous Section Next Section