为什么在静态方法中使用'{'会抛出NullReferenceException?

6

这个问题有点难以理解。我在尝试打开一个在Visual Studio 2008中的winforms项目(在winforms设计器中)中的表单时遇到了一个NullReferenceException异常。堆栈跟踪指向以下代码的第四行:

public static class Logger
{
    public static void LogMethodEnter()
    {
        var frame = new StackFrame(1);
        var method = frame.GetMethod();
        Trace.TraceInformation("{0}.{1}.{2}()", method.DeclaringType.Namespace, method.DeclaringType.Name, method.Name);
        Trace.Indent();
    }

    public static void LogMethodExit()
    {
        Trace.Unindent();
    }
}

...意思是指带有左花括号的那一行。我在其他项目中也遇到了同样的问题(但不涉及winforms设计器),我认为这是一个线程相关的问题,但我没有代码来复制它。

为什么会发生这种情况,为什么异常堆栈跟踪指向带有花括号的行?

澄清:空引用异常只发生在winforms设计器中。当应用程序运行时,它不会抛出该错误。


1
你尝试过查看反汇编吗?您可能能够在那里看到它正在做什么(虽然我自己在反汇编视图中没有成功运行符号)。还有一个微小的可能性,即您正在运行与源代码不匹配的代码。 - Rup
2
你尝试过清理解决方案并重新构建吗? - FrustratedWithFormsDesigner
1
@Rup:同意关于VS抱怨代码与文件不同步的问题 - 可能是在使用缓存的内容? - FrustratedWithFormsDesigner
1
避免在设计时运行此类代码。请使用Control.DesignMode属性。 - Hans Passant
1
您还需要使用[MethodImpl]属性来禁止内联。 - Hans Passant
显示剩余3条评论
5个回答

4

我猜想你在类中的某个地方对静态成员进行了初始化,而该初始化程序抛出了NullReferenceException。此外,我猜想你没有静态构造函数,因此你的对象被标记为beforefieldinit,因此在JIT编译使用它的方法时,会抛出NullReferenceException

类似于:

public static class Logger
{
    private static object x = InitObjectX();
    private static object InitObjectX() {
        x.GetHashCode(); // Will throw since x is null.
    }

    public static void LogMethodEnter() 
    { 
        var frame = new StackFrame(1); 
        var method = frame.GetMethod(); 
        Trace.TraceInformation("{0}.{1}.{2}()", method.DeclaringType.Namespace, method.DeclaringType.Name, method.Name); 
        Trace.Indent(); 
    } 

    public static void LogMethodExit() 
    { 
        Trace.Unindent(); 
    } 
} 

哦,有几个静态字段,但它们是这样初始化的: static string Name = "xyz"; 这会导致相同的问题吗? - Zachary Yates
1
我也是这么想的,但是静态类初始化期间出现异常通常会导致TypeInitializationException(内部异常为NRE)。 - Dirk Vollmar

4

我猜测行号有误(实际原因不是很重要),异常实际上是由这个表达式抛出的:

method.DeclaringType.Namespace

您可能会看到一个NullReference异常的原因是由于前面几行中的new StackFrame(1)表达式有时会返回一个空帧。空帧意味着调用.GetMethod()将返回null,这就是问题所在。

您有时会得到一个空帧的原因是即时编译器可以选择内联像您代码中的短小且被重复调用的方法。这将扰乱您的调用堆栈,最好情况下,您会得到比预期更高级的方法,或者(在您的Main方法中)没有更高级的方法,您将得到null。


有趣。那么这是否意味着应用程序在运行时抛出该异常?我没有看到这种行为,我想我应该澄清一下,异常仅在我尝试在winforms设计器中打开表单时发生。 - Zachary Yates
@Zach 我认为在运行时可能会出现异常,但您很可能永远不会在WinForms中看到它,因为您不太可能从堆栈顶部的Main方法中调用它。相反,您可能会在内联此处的某些位置看到一些不正确的日志条目。您会在设计器中看到它,因为Visual Studio运行代码以确定设计器的行为/外观方式。突然间,一个内联调用正在堆栈的顶部运行。 - Joel Coehoorn
1
谢谢你的见解,我不知道有内联这个东西 - 我会尝试使用 [MethodImpl] 属性和一些空值检查。 - Zachary Yates
我也有同样的想法,但是如何添加静态构造函数来解决这个问题呢?而且我怀疑设计师会内联代码... - Dirk Vollmar
我通过结合大家的答案解决了这个问题 - 我选择了这个答案,因为它提供了最多的信息。我使用了 @Hans Control.DesignMode 的建议,@0xA3 .pdb 过期的建议来在我改变代码时改变错误,还有 @Joel 的空值检查和编译器内联建议。 - Zachary Yates

3

可能是包含行信息的.pdb文件已过期

要解决此问题,请重新构建您的项目,并确保在项目设置中启用了创建.pdb文件。对于C#项目,可以通过将高级->调试信息设置为fullpdb-only来在生成选项卡上进行配置。


好的建议,我之前遇到过这个问题。但是我已经尝试过了,没有效果。 - Zachary Yates

1

我认为问题与在静态对象构造之前调用静态方法有关。我通过在winforms项目中添加静态构造函数来解决这个问题。

如果我没记错的话,静态构造函数在执行时会锁定整个对象。


你的方法是否访问了类的任何静态字段? - Dirk Vollmar
不,该方法仅使用 StackFrame 和 Trace 对象,它们都不是类的静态字段。 - Zachary Yates
还有你的类里有什么别的吗?我只是好奇为什么静态构造函数解决了这个问题。这似乎是因为添加静态构造函数改变了静态类初始化行为的缘故。 - Dirk Vollmar
我认为这可能与从访问方法隐式调用静态构造函数创建StackFrame相关,当您创建显式静态构造函数时,这可能导致类更早地初始化。您可以尝试的另一个实验是在静态类上添加一个不使用StackFrame的不同方法,然后在调用LogMethodEnter()之前调用该方法。 - Dan Bryant

0

有时会指向花括号/看似不正确的代码行。我认为这只是因为异常发生在上一行代码上,而Visual Studio出于某种原因突出显示了下一行。

猜测程序可能不会在异常发生的确切行上中断,这可能由许多内部和外部因素造成。

很抱歉我无法很好地解释这个问题。


当执行到上一行但还没有到达下一行时,Visual Studio 会突出显示下一行。当 Visual Studio 以绿色突出显示该行时,表示错误发生在由上一行调用的方法内部。 - Pierre-Alain Vigeant

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接