繁体   English   中英

奇怪的异常行为......为什么?

[英]strange exception behaviour…why?

我创建了一个受C ++的std::numeric_limits启发的类,用于获取类型的最小值和最大值。 它使用反射填充两个静态只读成员,以读出该类型的MaxValueMinValue属性。 如果T没有该属性,它会抛出异常。

public class Limits<T>
{
    public static readonly T MaxValue = Read("MaxValue");
    public static readonly T MinValue = Read("MinValue");

    private static T Read(string name)
    {
        FieldInfo field = typeof(T).GetField(name, BindingFlags.Public | BindingFlags.Static);

        if (field == null)
        {
            throw new ArgumentException("No " + name + " property in " + typeof(T).Name);
        }

        return (T)field.GetValue(null);
    }
}

现在,当踩到下面的程序时,我看到了一些奇怪的行为。

    try
    {
        Console.WriteLine(Limits<int>.MaxValue);
        Console.WriteLine("1");
        Console.WriteLine(Limits<object>.MaxValue);
    }
    catch
    {
        Console.WriteLine("2");
    }

读取MaxValue属性时有一个断点。 单步执行Limits<int>将触发断点并读取属性。 然后在执行WriteLine("1") ,再次命中断点以读取Limits<object> 这会引发异常,因为object没有MaxValue因此可以预期异常会在Main中捕获。 但是这不会发生, WriteLine("1")被执行,只有异常被捕获....为什么会这样? 在执行实际行之前,CLR是否存储异常?

静态字段初始化的C#语言规范:

如果类中存在静态构造函数(第10.11节),则在执行该静态构造函数之前立即执行静态字段初始值设定项。 否则,静态字段初始化器在第一次使用该类的静态字段之前的实现相关时间执行。

这意味着:

  • 用户无法直接控制何时运行此初始化过程。
  • 保证在使用该字段之前运行一次。

如果一个异常在此过程中引发的类型变得不可用的AppDomain中的余生,并且每次尝试使用这种类型的,你会得到一个TypeInitializationException抛出(与内部的例外是原始异常)检查静态构造函数上的msdn 出于这些目的, Limits<int>Limits<object>被认为是不同的类型,因此您仍然可以使用Limits<int>

这就是为什么在尝试获取Limits<object>.MaxValue时会出现异常的Limits<object>.MaxValue ,因为clr会为您调用初始化代码并存储异常, TypeInitializationException每次使用它时都可以将其作为TypeInitializationException抛出。

同样重要的是要注意在第一次使用类型之前将初始化所有静态字段。 因此,您可以将以下静态属性添加到静态类:

public static T Default = default(T);

然后按如下方式更改测试程序:

static void Main(string[] args)
{
    for (int x = 0; x < 3; x++)
    {
        try
        {
            Console.WriteLine(Limits<int>.Default);
            Console.WriteLine("1");
            Console.WriteLine(Limits<object>.Default);
        }
        catch (TypeInitializationException e)
        {
            Console.WriteLine("TypeInitializationException: " + e.Message);
        }
    }
    Console.ReadKey();
}

您没有直接使用MaxValue静态字段,但由于您使用的是类型(通过访问默认值),所有静态字段在首次使用该类型之前仍在初始化。 您还会注意到,在Limits.Default和“1”之后,您将获得相同的异常3次。

ecma标准说:

17.4.5.1:“如果类中存在静态构造函数(第17.11节),则在执行该静态构造函数之前立即执行静态字段初始化程序。否则,静态字段初始化程序在执行相关时间之前执行。首次使用该类的静态字段。“

即它可以在一个单独的线程上运行初始化程序。

这个http://msdn.microsoft.com/en-us/library/aa664609(v=vs.71).aspx

•如果搜索匹配的catch子句到达静态构造函数(第10.11节)或静态字段初始化程序,则会在触发静态构造函数调用的位置抛出System.TypeInitializationException。

说是的,异常被保留并抛出导致初始化的代码行。 这是有道理的,否则你会在“随机”时间抛出异常。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM