【问题标题】:Exception in static constructor静态构造函数中的异常
【发布时间】:2011-06-11 21:26:10
【问题描述】:

我已经在 SO 中寻找答案,到目前为止我能找到的最好的答案是 here,但它适用于具有静态构造函数的实例;我只是静态地使用这个类。

我的代码:

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);


.

所以如果静态构造函数第一次调用时抛出异常,那么当我第二次尝试访问静态 SendMail() 方法时会发生什么?

PS:抱歉,如果您不喜欢 Stroustrup 版本的 K&R 牙套样式,但不要编辑我的帖子只是为了将牙套更改为您喜欢的 Allman 样式。谢谢。

【问题讨论】:

  • 应该会失败,但是自己试一下有什么问题?
  • 我认为从(静态)构造函数中抛出真的没有意义,因为那时该类处于不稳定状态(未完全初始化)。在使用之前创建一个显式的Init() 函数怎么样(如果已经初始化,它应该什么都不做),如果它抛出异常,不要使用类
  • Pontus> 我认为 Jon 可能需要更多积分:)
  • Cameron> 我从来都不是 Init() 方法的忠实拥护者……如果需要 Init() 才能使类可用,为什么不在构造函数中执行呢?否则,您会将对象的控制权传递给调用者,并且您仍然必须测试被调用方法的有效性,因为您不能相信调用者做了他应该做的事情。我喜欢下面 Chris 的回答,这听起来像是你的方法和我的方法之间的妥协......在构造函数中设置配置,但不要抛出异常,而是检查静态方法中的设置。
  • 感谢您的 PS。终于知道缩进样式有名字了!

标签: c# exception constructor static-classes


【解决方案1】:

一旦一个类型初始化器失败了一次,它就永远不会重试。该类型在 AppDomain 的生命周期内已失效。 (请注意,这对于所有类型初始化器都是如此,而不仅仅是对于具有静态构造函数的类型。具有带有初始化表达式但没有静态构造函数的静态变量的类型可能会在类型初始化器执行的时间上表现出细微的差异 - 但它会仍然只发生一次。)

演示:

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);
            }
        }
    }
}

输出:

In static constructor
TypeInitializationException
TypeInitializationException
TypeInitializationException
TypeInitializationException
TypeInitializationException

如你所见,静态构造函数只被调用一次。

【讨论】:

  • 有趣的是,当你明确抛出 Exception 时,你只捕捉到 TypeInitializationExceptions...Exception 会被吞没吗?
  • @James B:不,它在 TypeInitializerException 的 InnerException 中。
  • @James:不,您不应该在任何地方捕获 TypeInitializationException。基本上,只有在类型致命损坏时才会发生这种情况。
  • 我认为这样做的好处是不要从静态构造函数中抛出异常,不要到处捕捉TypeInitialisationException,不要乱用测试AppDomains。如果你必须开始跳过这些奇怪的箍,你几乎肯定错过了一些东西。詹姆斯班级的设计显然需要改变。
  • 根据msdn.microsoft.com/en-us/library/bb386039.aspx ...它声明: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.
【解决方案2】:

其他两个答案是您直接问题的好答案 - 这是一个元答案 - 当您检测到未填充配置元素时,您应该在方法中抛出异常,而不是在构造函数中。恕我直言,“未配置”是构造函数阶段这些元素的有效配置状态,而不是在 SendMail 时间。这将回避整个问题。

【讨论】:

  • Jon 实际上最正确地回答了我的问题,但这个答案提出了比我所拥有的更好的解决方案。
【解决方案3】:

来自the Microsoft Documentation(静态构造函数(C# 编程指南)):

如果静态构造函数抛出异常,运行时将不会再次调用它,并且该类型将在程序运行的应用程序域的生命周期内保持未初始化状态。最常见的是,当静态构造函数无法实例化类型或静态构造函数中发生未处理的异常时,会引发 TypeInitializationException 异常。对于未在源代码中明确定义的隐式静态构造函数,故障排除可能需要检查中间语言 (IL) 代码。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-02
    • 1970-01-01
    • 2023-04-01
    • 2011-05-06
    • 2020-05-05
    • 2023-03-29
    相关资源
    最近更新 更多