49

I've dug around SO for an answer to this, and the best one I can find so far is here, however that is geared toward instances with static constructors; I'm only using the class statically.

My code:

public static class MailHelper {

    private static string mailHost;

    static MailHelper() {

        var mailSettings = ConfigurationManager.GetSection("MailSettings") as NameValueCollection;
        if (null == mailSettings) {
            throw new ConfigurationErrorsException("Missing Mail Settings in the configuration file");
        }

        mailHost = ConfigurationManager.AppSettings["mailHost"];
        if (null == mailHost) {
            throw new ConfigurationErrorsException("Missing mailHost setting in the configuration file");
        }

    }

    public static void SendMail(MailMessage Message) {
        ...
    }

}


try {
    MailHelper.SendMail(Message);
}
catch (ConfigurationErrorsException exc) {
    ...
}

//  ???    
MailHelper.SendMail(Message);


.

So if the static constructor throws an exception the first time it's called, what happens the second time I try to access the static SendMail() method?

PS: Sorry if you don't like Stroustrup's version of K&R brace styling, but don't edit my post just to change the braces to your preferred Allman style. Thanks.

5
  • It should fail, but what's wrong with trying it out for yourself? Commented Jan 19, 2011 at 16:31
  • I don't think it really makes sense to throw from a (static) constructor, because then the class is in an unstable state (not fully initialized). How about creating an explicit Init() function which you call before use (it should do nothing if already initialized), and if it throws an exception, don't use the class Commented Jan 19, 2011 at 16:36
  • Pontus> I thought Jon might need some more points : ) Commented Jan 19, 2011 at 16:52
  • 6
    Cameron> I've never been a big fan of Init() methods... if Init() is required to make a class usable, why not just do it in the constructor? Otherwise you're passing control of your object to the caller, and you still have to test for validity in the called methods, because you can't trust that the caller did what he was supposed to. I like Chris' answer below, it sounds like a compromise between your approach and mine... set the config in the constructor, but don't throw exceptions, and check the settings in the static method instead. Commented Jan 19, 2011 at 16:54
  • Thanks for the PS. Finally learned that there's names for indentation styles! Commented Jan 16, 2020 at 13:56

3 Answers 3

111

Once a type initializer has failed once, it is never retried. The type is dead for the lifetime of the AppDomain. (Note that this is true for all type initializers, not just for types with static constructors. A type with static variables with initializer expressions, but no static constructors, can exhibit subtle differences in the timing of the type initializer execution - but it'll still only happen once.)

Demonstration:

using System;

public sealed class Bang
{
    static Bang()
    {
        Console.WriteLine("In static constructor");
        throw new Exception("Bang!");
    }

    public static void Foo() {}
}

class Test
{
    static void Main()
    {
        for (int i = 0; i < 5; i++)
        {
            try
            {
                Bang.Foo();
            }
            catch (Exception e)
            {
                Console.WriteLine(e.GetType().Name);
            }
        }
    }
}

Output:

In static constructor
TypeInitializationException
TypeInitializationException
TypeInitializationException
TypeInitializationException
TypeInitializationException

As you can see, the static constructor is only called once.

Sign up to request clarification or add additional context in comments.

12 Comments

It's interesting that you catch only TypeInitializationExceptions when you explicitly throw Exception... does that Exception get swallowed?
@James B: No, it's in the InnerException of the TypeInitializerException.
@James: No, you shouldn't be catching TypeInitializationException anywhere. That should only occur if a type is fatally broken, basically.
I think the takeaway from this is to not throw exceptions from within a Static Constructor, not to catch TypeInitialisationException everywhere, not to mess around with test AppDomains. If you have to start jumping through these weird hoops you've almost certainly missed something. The design of James' class clearly needs to change.
According to msdn.microsoft.com/en-us/library/bb386039.aspx ... it states: Throwing exceptions from a static constructor causes the type to be unusable in the current application domain. You should have a very good reason (such as a security issue) for throwing an exception from a static constructor.
|
26

The other two answers are good answers to your direct question - here's a metaanswer - you should be throwing the exception in the method when you detect that the configuration elements are not populated, rather than in the constructor. IMHO, "not configured" is a valid configuration state for those elements at the constructor phase, just not at SendMail time. That will sidestep this whole problem.

1 Comment

Jon actually answered my question most correctly, but this answer proposes a better solution than the one I have.
21

From the Microsoft Documentation (Static Constructors (C# Programming Guide)):

If a static constructor throws an exception, the runtime will not invoke it a second time, and the type will remain uninitialized for the lifetime of the application domain in which your program is running. Most commonly, a TypeInitializationException exception is thrown when a static constructor is unable to instantiate a type or for an unhandled exception occurring within a static constructor. For implicit static constructors that are not explicitly defined in source code, troubleshooting may require inspection of the intermediate language (IL) code.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.