Exception in static constructor

21,113

Solution 1

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.

Solution 2

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.

Solution 3

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.

Share:
21,113

Related videos on Youtube

James King
Author by

James King

Updated on March 02, 2021

Comments

  • James King
    James King about 3 years

    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.

    • Pontus Gagge
      Pontus Gagge over 13 years
      It should fail, but what's wrong with trying it out for yourself?
    • Cameron
      Cameron over 13 years
      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
    • James King
      James King over 13 years
      Pontus> I thought Jon might need some more points : )
    • James King
      James King over 13 years
      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.
    • krlzlx
      krlzlx over 4 years
      Thanks for the PS. Finally learned that there's names for indentation styles!
  • James King
    James King over 13 years
    It's interesting that you catch only TypeInitializationExceptions when you explicitly throw Exception... does that Exception get swallowed?
  • Jon Skeet
    Jon Skeet over 13 years
    @James B: No, it's in the InnerException of the TypeInitializerException.
  • James King
    James King over 13 years
    Jon actually answered my question most correctly, but this answer proposes a better solution than the one I have.
  • James King
    James King over 13 years
    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!
  • Jon Skeet
    Jon Skeet over 13 years
    @James: No, you shouldn't be catching TypeInitializationException anywhere. That should only occur if a type is fatally broken, basically.
  • James King
    James King over 13 years
    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?
  • JerKimball
    JerKimball about 11 years
    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?
  • Jon Skeet
    Jon Skeet about 11 years
    @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.
  • JerKimball
    JerKimball about 11 years
    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!
  • Doctor Jones
    Doctor Jones about 11 years
    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.
  • VoodooChild
    VoodooChild about 11 years
    @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?
  • myermian
    myermian about 11 years
    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.
  • pholpar
    pholpar almost 8 years
    @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 pholpar.wordpress.com/2016/06/30/…. 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.