【问题标题】:Object reference not set to an instance of an object.Why doesn't .NET show which object is `null`?对象引用未设置为对象的实例。为什么 .NET 不显示哪个对象是“空”?
【发布时间】:2013-01-25 02:31:06
【问题描述】:

关于这个 .NET 未处理的异常消息:

对象引用未设置为对象的实例。

.NET 为什么不显示哪个对象是null

我知道我可以检查 null 并解决错误。但是,为什么 .NET 不能帮助指出哪个对象具有空引用以及哪个表达式触发了NullReferenceException

【问题讨论】:

  • 当这种情况发生时,重写它发生的那一行,以便它首先检查每个可能的结果是否为 null - 然后你就会确切地知道它是什么。要么,要么附加 Visual Studio 令人惊叹的调试器,它会在发生异常的瞬间中断并让您看到什么是 null :)
  • 不是真的,他只是问为什么 .NET 框架不能帮助程序员显示 which 对象为空。我想这是性能损失(你需要反思)。但我也不确定。
  • @bas:虽然这是真的,但这个问题有点误导,因为它应该询问“表达式的一部分”,而不是“对象”。这也解释了为什么仅仅反射没有帮助,但需要一些广泛的调试信息。
  • 我仍然很好奇答案。这有点类似于 .net 异常,它无助于指出字典中不存在 which 键。另外,我不明白这个问题的奉献者。
  • 请使用术语:对象永远不会为空。 object reference 可能是。但是对象引用只是内存中的一个位置——它对你有什么帮助,除非你附加了一个调试器?

标签: c# .net


【解决方案1】:

不确定,但这可能是因为 .Net 不知道它是预定义的类还是用户定义的。如果它是预定义的,那么它可以为空(如占用 2 个字节的字符串),但如果它是用户定义的,那么我们必须创建它的一个实例,以便它知道这个对象将占用这么多内存。因此,它会在运行时引发错误。

【讨论】:

    【解决方案2】:

    (有关 Visual Studio 2017 中新的异常帮助器的信息,请参阅此答案的末尾)


    考虑这段代码:

    String s = null;
    Console.WriteLine(s.Length);
    

    这将在第二行抛出NullReferenceException,您想知道为什么.NET 没有告诉您在抛出异常时它是空的s

    要了解为什么您没有获得该信息,您应该记住执行的不是 C# 源代码,而是 IL:

    IL_0001:ldnull IL_0002: stloc.0 // 秒 IL_0003: ldloc.0 // 秒 IL_0004: callvirt System.String.get_Length IL_0009:调用 System.Console.WriteLine

    callvirt 操作码会抛出 NullReferenceException,当评估堆栈上的第一个参数是空引用(使用 ldloc.0 加载的引用)时,它会这样做。

    如果 .NET 应该能够判断它是 s,它是一个空引用,它应该以某种方式跟踪评估堆栈上的第一个参数源自 s。在这种情况下,我们很容易看到 s 为 null 但如果该值是来自另一个函数调用的返回值并且没有存储在任何变量中怎么办?无论如何,这类信息不是您希望在 .NET 虚拟机之类的虚拟机中跟踪的内容。


    为避免此问题,我建议您在所有公共方法调用中执行参数 null 检查(当然,除非您允许 null 引用):

    public void Foo(String s) {
      if (s == null)
        throw new ArgumentNullException("s");
      Console.WriteLine(s.Length);
    }
    

    如果将 null 传递给方法,您会得到一个异常,该异常准确地描述了问题所在(s 为 null)。


    四年后,Visual Studio 2017 现在有了一个新的异常帮助器,它会在抛出 NullReferenceException 时尝试判断什么是 null。当方法的返回值为 null 时,它甚至可以为您提供所需的信息:

    请注意,这只适用于 DEBUG 构建。

    【讨论】:

    • 行号和源文件名也不存储在 IL 代码本身中,或者是吗?然而,它们可以用于调试。
    • @MartinLiversage:没错。所以问题归结为:为什么符号文件中没有足够的信息来说明代码中的哪个表达式被评估为null
    • @MartinLiversage:符号文件可以通过某种方式访问​​,以便在发生异常时,连同异常消息,源文件和行号可以显示在调试器的输出中。所以,问题是,不包括更多关于确切返回的信息的原因是什么 null - 请注意,OP 并未声称他或她想知道对于发布版本,调试版本可能就足够了.
    • 嗯,我们不应该忘记实际执行的甚至不是 IL,而是在运行时由它构建的本机代码。
    • @MartinLiversage:这个问题中没有人声称我们希望在发布版本中得到完美支持。无论如何,我不太明白将使用对象引用(可能是null)的 IL 操作码与返回该对象引用的源文件行和列关联起来的问题。
    【解决方案3】:

    好问题。消息框简直没用。即使它距离引用定义一英里深,某些类或程序集或文件或其他信息也会比它们当前提供的更好(阅读:总比没有好)。

    您最好的选择是在带有调试信息的调试器中运行它,您的 IDE 将在有问题的行中断(更清楚地表明有用的信息实际上是可用的)。

    【讨论】:

      【解决方案4】:

      嗯,这由 Microsoft 的工程师来回答。但是您显然可以使用调试器并添加监视来找出其中哪些有问题。

      但是,NullReferenceException 是个例外,这意味着引用不存在。您根本无法获取尚未创建的对象。

      but why .NET don't tell us which object is null? 因为它不知道哪个对象是空的。该对象根本不存在!

      当我说 C# 被编译为 .NET IL 代码时也是如此。 .NET IL 代码不知道名称或表达式。它只知道引用及其位置。在这里,你也无法得到不存在的东西。表达式或变量名不存在。

      理念:如果一开始就没有鸡蛋,就不能做煎蛋。

      【讨论】:

      • 这也不是答案:)
      • 如果引用不存在,如何获取? @巴斯
      • “嗯,这由微软的工程师来回答。”。因此,让他们阐明这一点,而不是说显而易见的
      • @bas 我们至少可以决定什么是合乎逻辑的。逻辑上该对象不存在。你将如何用异常捕获它,然后打印出对象的名称?它只是不存在。甚至不在堆栈上..
      • 所以答案是几乎不可能指出哪个对象具有空引用?这也是一个答案。我并不是说我知道,我只是喜欢这个问题:)。为所有努力+1:p
      【解决方案5】:

      您希望以下情况下的错误消息是什么样的?

      AnyObject.GetANullObject().ToString();
      
      private object GetANullObject()
      {
        return null;
      }
      

      这里没有要报告的变量名!

      【讨论】:

      • 我怀疑 OP 正在寻找源代码中返回 null 的表达式,而不是对象。我已对该问题添加了相应的评论,并希望他或她能够澄清问题。如果我的怀疑是正确的,OP 会期望像 Object reference obtained from AnyObject.GetANullObject() not set to an instance of an object. 这样的错误消息。
      • @O.R.Mapper 我同意。如果我有足够的声望点来添加评论,我会把我的“答案”放在对 OP 的评论中!
      • “试图调用 XYZ 类的 ToString() 方法的空引用”会比我们现在得到的更有帮助。
      • 最有用的是堆栈跟踪,在每个调用级别显示究竟是哪一行、哪个文件中导致了错误。哦,等等……这就是它现在所做的!
      猜你喜欢
      • 2019-03-20
      • 1970-01-01
      • 2012-04-25
      • 2015-01-24
      • 2014-04-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多