在C#中处理NullReferenceExceptions的更好方法

时间:2012-04-12 16:52:02

标签: c# exception-handling

我最近有一个编码错误,在某些情况下,变量没有被初始化,我得到了NullReferenceException。这需要一段时间来调试,因为我必须找到生成此数据的数据位以重新创建错误,因为异常不提供变量名称。

显然我可以在使用之前检查每个变量并抛出一个信息性异常但是有更好的(读取更少编码)方式吗?我的另一个想法是发送pdb文件,以便错误信息包含导致错误的代码行。其他人如何避免/处理这个问题?

由于

5 个答案:

答案 0 :(得分:11)

首先:不要在一个声明中做太多。如果你在一行中有大量的解除引用操作,那么很难找到罪魁祸首。很多Law of Demeter对此也有帮助 - 如果你有order.SalesClerk.Manager.Address.Street.Length这样的东西,那么当你得到异常时,你有很多选择可以解决。 (我关于得墨忒耳法则的教条,但一切都在适度......)

其次:更喜欢使用as进行强制转换,除非它的有效对象是另一种类型,这通常会在之后立即进行空检查。所以这里:

// What if foo is actually a Control, but we expect it to be String?
string text = foo as string;
// Several lines later
int length = text.Length; // Bang!

这里我们得到一个NullReferenceException并最终追溯到text为空 - 但是你不知道那是因为foo是null,还是因为它是一个意外的类型。如果它应该真的,真的string,那么转换为:

string text = (string) foo;

现在,您将能够分辨出两种情况之间的区别。

第三:正如其他人所说,验证您的数据 - 通常是公共和可能内部API的参数。我在Noda Time的足够多的地方这样做,我有一个实用工具类来帮助我整理检查。例如(来自Period):

internal LocalInstant AddTo(LocalInstant localInstant,
                            CalendarSystem calendar, int scalar)
{
    Preconditions.CheckNotNull(calendar, "calendar");
    ...
}

你应该记录什么可以也可以不是null。

答案 1 :(得分:3)

在很多情况下,几乎不可能计划和解释在应用程序执行流程中任何给定点可能发生的每种类型的异常。防御性编码仅对某一点有效。诀窍是在您的应用程序中集成一个可靠的诊断堆栈,可以为您提供有关未处理错误和崩溃的有用信息。在app-domain级别拥有一个好的顶级(最后一个沟渠)处理程序将对此有很大帮助。

是的,运送PDB(即使使用发布版本)是获得完整堆栈跟踪的好方法,可以精确定位错误的确切位置和原因。但无论您选择哪种诊断方法,都需要将其纳入应用程序的设计(理想情况下)。改造现有的应用程序可能很繁琐且时间/金钱密集。

答案 2 :(得分:2)

很抱歉,我会一直检查以确认我在特定方法中使用的任何对象都不为空。

就像

一样简单
if( this.SubObject == null )
{
    throw new Exception("Could not perform METHOD - SubObject is null.");
}
else
{
...
}

否则我想不出任何彻底的方法。对我来说,不管怎么说都不要做这些检查。我觉得这只是一种很好的做法。

答案 3 :(得分:2)

首先,您应始终验证您的输入。如果不允许null,请抛出ArgumentNullException

现在,我知道这会是多么痛苦,所以你可以查看为你做这件事的程序集重写工具。这个想法是你有一种属性可以标记那些不能null的参数:

public void Method([NotNull] string name) { ...

重写者将填补空白......

或者简单的扩展方法可以使其更容易

name.CheckNotNull();

答案 4 :(得分:2)

如果您只是想寻找一种更紧凑的方法来对空引用进行编码,请不要忽略空合并运算符?? MSDN

显然,这取决于你在做什么,但它可以用来避免额外的if语句。