views:

189

answers:

3

I'm running this C# code in Visual Studio in debug mode:

public class MyHandlerFactory : IHttpHandlerFactory
{
  private static Dictionary<string, bool> myDictionary = new Dictionary<string, bool>();
  static MyHandlerFactory()
  {
    myDictionary.Add("someKey",true);
    myDictionary.Add("someKey",true); // fails due to duplicate key
  }
}

Outside of the static constructor, when I get to the line with the error Visual Studio highlights it and pops up a message about the exception. But in the static constructor I get no such message. I am stepping through line-by-line, so I know that I'm getting to that line and no further.

Why is this?

(I have no idea if that fact that my class implements IHttpHandlerFactory matters, but I included it just in case.)

This is VS2005, .Net 2.0

Edit: I just want to add, the fact that it's an HttpHandler does seem to matter. As the answers have indicated, the default behavior is to break on the TypeInitializationException rather than the inner exception. I tested another example without the HttpHandler and saw that this caused the program to break on the first line that used the class. But in this case there's no line in my code to break on, since the class was only called as an HttpHandler specified in my web.config file. Hence, it didn't break on the exception at all.

+4  A: 

The problem is that the exception thrown is actually a TypeInitializationException that wraps whatever exception is thrown. I'm not sure what design tradeoffs caused this, but IMO it's one of the most annoying things in .NET development, and I'm sad to see it's still around in .NET 4.

In VS, to catch the exception ASAP, you'll need to turn on first-chance exceptions. Go to Debug > Exceptions and check "Common Language Runtime Exceptions", which will break as soon as an exception is thrown.

(Note: if you're using the Dynamic Language Runtime, you'll need to be more choosy about what exceptions are caught, since that apparently uses exceptions for flow control).

Robert Fraser
Thanks for the answer. However, the choices I see in Debug > Exceptions... are "C++ Exceptions", "Common Language Runtime Exceptions", "Managed Debugging Assistants", "Native Run-Time Checks", and "Win32 Exceptions". Is ".Net Exceptions" under one of these? I couldn't find it.
Tim Goodman
@Tim Goodman -- OOPS! I meant "Common Language Runtime Exceptions". I'll update my answer.
Robert Fraser
Ah yes, making it break when Common Language Runtime Exceptions are thrown caused it to break on the inner exception. Thanks.
Tim Goodman
"since that apparently uses exceptions for flow control" - interesting. Do you mean at compile time? So, it works out Types by trial and error?
andy
@andy -- When you first reference a synamic member in the DLR, the .NET runtime throws a MissingMemberException, which I guess is caught by the DLR somewhere and causes it to be compiled... If you have first-chance exceptions on, though, it'll be pausing all the time on this.
Robert Fraser
ahhh.... interesting. would you consider this a design flaw in the compiler, or do you think it's a pretty clever way to integrate dynamic and static design?
andy
A: 

I tried your code and I got the TypeInitializationException as you expected. No problem in my VS...

But if you run this (or any other) application 'without debugging', you should always get an error massage for any unhandled exceptions - no VS settings will make a difference here.

Machta
On what line did you get the TypeInitializationException? I am thinking now the issue is that it would normally give an error on the first line to reference the class (the one which causes the static constructor to be called), but in this case it's being called as an HttpHandler so there's no line to go to.
Tim Goodman
The TypeInitializationException appears on the line where I create a new instance of the class: MyHandlerFactory factory = new MyHandlerFactory(); But I tried it without the interface that it derives from. Maybe you could extend the sample that we could test it exatly as it is in your code.
Machta
A: 

The static constructor gets run before your application is running in an appdomain. This is probably causing issues with the way VS is catching the exception, which from your description is quite non standard. It may be a limitation of 2005. That's why you should always catch within the body of the static constructor. There is an excellent discussion of this in the new book Effective C#

Steve