46

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.

Community
  • 1
  • 1
James King
  • 6,233
  • 5
  • 42
  • 63
  • It should fail, but what's wrong with trying it out for yourself? – Pontus Gagge Jan 19 '11 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* – Cameron Jan 19 '11 at 16:36
  • Pontus> I thought Jon might need some more points : ) – James King Jan 19 '11 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. – James King Jan 19 '11 at 16:54
  • Thanks for the PS. Finally learned that there's names for indentation styles! – krlzlx Jan 16 '20 at 13:56

3 Answers3

107

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.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 9
    It's interesting that you catch only `TypeInitializationExceptions` when you explicitly throw `Exception`... does that `Exception` get swallowed? – James King Jan 19 '11 at 16:42
  • 11
    @James B: No, it's in the InnerException of the TypeInitializerException. – Jon Skeet Jan 19 '11 at 17:05
  • Ah! Hope you don't mind, I awarded the answer to Chris... Your question actually answers the question I was asking and was the most helpful in understanding the expected behavior, but I liked his solution to avoid the problems with my approach. Catching TypeInitializationExceptions everywhere I make static calls to my class doesn't sound like fun! Thanks for your help! – James King Jan 19 '11 at 18:27
  • 8
    @James: No, you shouldn't be catching TypeInitializationException anywhere. That should only occur if a type is *fatally* broken, basically. – Jon Skeet Jan 19 '11 at 18:33
  • Hmm, back to confused, then... If calling into my static class is going to potentially throw TypeInitializationExceptions, why wouldn't I want to trap for them? – James King Jan 19 '11 at 18:45
  • Jon - it would seem, per this behavior (especially the whole "dead for the lifetime of the AppDomain" statement) that it would make sense that any type that *might* fail on static initialization should be first "Test loaded" into a throwaway AppDomain; does this sound correct? – JerKimball Feb 12 '13 at 15:48
  • @JerKimball: Well, I think it's at least *unusual* that you'd be able to keep going sensible with one of your types being dead. It's even more unusual that the error would be transient, although I guess it's feasible. – Jon Skeet Feb 12 '13 at 15:53
  • Granted, it'd be an unusual situation to say the least; I imagine it'd be more common in "plug-in" type scenarios...I suppose it makes sense from the loader perspective; as you say, it's unlikely that a type load error would be transient, therefore the smartest course of action is to take the "I'm afraid I can't let you do that, Dave" approach...good to know behavior, tho! – JerKimball Feb 12 '13 at 15:56
  • 4
    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. – Doctor Jones Feb 13 '13 at 09:45
  • @JonSkeet - "The type is dead for the lifetime of the AppDomain" - what does it mean by lifetime of the appdomain? Lets say this type is in a WCF Service & IIS hosted, where multiple clients can be connected, but the very first client produces an error when calling the service in service's static constructor (specific to the first client only) making the type invalid, then after that no other client can make that WCF call. So we restart the IIS site or recycle app pool in IIS to force that a new appdomain is started? Or what kind of things determine that? – VoodooChild Mar 01 '13 at 19:14
  • 10
    According to http://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.` – myermian Mar 29 '13 at 15:33
  • @VoodooChild: Yes, in the case of a web-based application (like a WCF service) the app domain means the process of the IIS application pool. We had a similar issue with a custom SharePoint application, see https://pholpar.wordpress.com/2016/06/30/how-a-missing-permission-for-a-single-user-may-crash-a-whole-sharepoint-custom-web-application. A web application is a very special case of such potential issues, as it provides access for multiple user identities. If your application supports impersonation, the identity of the first user accessing the static constructor may affect all other users. – pholpar Jun 30 '16 at 07:37
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.

Chris B. Behrens
  • 6,255
  • 8
  • 45
  • 71
  • 1
    Jon actually answered my question most correctly, but this answer proposes a better solution than the one I have. – James King Jan 19 '11 at 18:22
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.

Wai Ha Lee
  • 8,598
  • 83
  • 57
  • 92
Michael Goldshteyn
  • 71,784
  • 24
  • 131
  • 181