这段代码为什么会抛出NullReferenceException异常?

4

我一定是疯了,因为我的单元测试失败了,因为以下代码抛出了空引用异常:

int pid = 0;
if (parentCategory != null)
{
    Console.WriteLine(parentCategory.Id);
    pid = parentCategory.Id;
}

导致此错误的代码行是:

pid = parentCategory.Id;

console.writeline仅用于在NUnit GUI中进行调试,但它输出一个有效的int。

编辑:它是单线程的,因此无法从其他线程分配为null,并且Console.WriteLine成功打印出值表明它不应该抛出异常。

编辑:类别类的相关片段:

public class Category
{
    private readonly int id;

    public Category(Category parent, int id)
    {
        Parent = parent;
        parent.PerformIfNotNull(() => parent.subcategories.AddIfNew(this));
        Name = string.Empty;
        this.id = id;
    }
    public int Id
    {
        get { return id; }
    }
}

如果任何人想查看完整的代码,可以在Google Code上查看:http://code.google.com/p/chefbook/source/checkout

我觉得我要尝试重新启动电脑......我看到一些比较奇怪的问题通过重新启动解决了。重启后会更新。

更新:谜团解开了。看起来NUnit将错误行显示为最后一个成功执行的语句......将测试复制/粘贴到新的控制台应用程序中并在VS中运行,显示if语句块之后的行(未显示)包含空引用。感谢大家提供的所有想法。+1给所有回答的人。


parentCategory和Id是什么类型?有任何隐式操作符吗? - Colin Burnett
有关系吗?它已经检查了 null,是单线程的,属性被正确访问,因为 Console.WriteLine 成功了。Id 的类型是 int。 - Davy8
1
这段代码应该能正常执行,除非您在 Id 属性中做了一些奇怪的事情。不过我感觉您可能没有告诉我们全部情况。 - Vadim
根据我的经验,“不可能”的事情通常是由于一些微妙的语法错误造成的,比如忘记了大括号、方括号或分号。 - Svante
1
@Svante 但是编译器应该能够抓住(大多数)这些东西,对吧? - Davy8
显示剩余5条评论
8个回答

7
根据目前所有信息,我认为要么“抛出错误的行”是错误的(你是怎么想的),要么你的“源代码”与你构建的程序集不同步。
这看起来是不可能的,因此某些“理所当然的假设”是错误的,很可能是“你查看的源代码与你调试的进程匹配”的假设。

@Brian,你不觉得如果他的代码不匹配,VS会给出警告吗? - Vadim
@Vadim:我可能错过了,但我在帖子中没有看到他是否一定在使用VS的证据。 - Brian
如果代码和汇编之间存在不匹配,他如何能够调试它? - Christian Hagelid

3
当表面看起来正确的时候,很可能是你会忽视的东西。您是否有350%的把握,您的DLL / PDB与源代码相匹配,从而给出正确的行号?
  • 尝试手动删除程序集并运行您正在运行的任何内容。它是否像应该失败一样?
  • 尝试清理解决方案并重新编译。将程序集复制到任何位置并再次运行它。这次可以工作吗?在同一地方出现空引用吗?
  • 调试周围行的值以查看您期望的值。parentCategory等应与您想象的相匹配。
  • 通过抛出异常修改代码。堆栈跟踪行是否完全匹配您代码中的行?
我曾经有过一些非常令人费解的经历,就像这样,因为我的某些假设是错误的。典型的是旧版本的程序集。质疑所有假设。

我以前确实见过这种情况。这次不是这种情况,但一定要检查一下。 - Davy8

2

然而,不合理的是,原帖中表示console.writeline输出了一个有效的int,并调用了与他指出错误所在行相同的属性。

有一个堆栈跟踪会很有帮助,或者能够查看实际的单元测试。


1

是的,我们能看到类别类的代码吗?特别是Id属性?假设Id属性是int类型(而不是可空int),那么get方法必须正在访问一个NULL对象。


1

Id可以是一个可空的int类型(int?)没有值,但我觉得属性Id必须做的不仅仅是返回一个int,而且其中的某些内容是null。

编辑您的编辑揭示了这更加奇怪,你能否提供给我们一个堆栈跟踪?


1

我不是C#的专家,也不确定这是否有所区别,但你是在Debug模式还是Release模式下进行调试?如果你在Release模式下,你的IDE指向的行和实际出现问题的行可能会不同(我知道在使用Visual Studio时,C++就是这种情况)


1
我必须同意Brian的观点... 也许您正在使用过时的 .pdbs 文件,而您在调试模式下看到的代码与实际被调试的代码不符。
尝试清理项目,并在调试模式下重新构建它。

0

看起来 NUnit 显示错误行是最后成功执行的语句... 将测试复制/粘贴到新的控制台应用程序中,并在 VS 中运行,发现 if 语句块之后的行(未显示)包含了 null 引用。感谢大家提供的所有想法。给所有回答的人点个赞。


然而,这仍然向我们展示了Console.WriteLine(parentCategory.Id);抛出了一个null。是什么导致了这个问题? - johnc
1
我理解“if语句后的一行”是指“if代码块结束括号后的一行”,例如OP从未向我们展示的某些代码。 - Brian
如果NUnit显示了最后一个成功的行,并且它正在显示pid=parentCategory.Id;,那么Console.WriteLine(parentCategory.Id);将运行良好。导致错误的行是pid=...之后的那一行,Davy8没有向我们展示它(并不重要)。 - Smashery
对,我的意思是 if 块后面没有显示的那一行。 - Davy8

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