C# 抛出新异常与捕获块

C# 抛出新异常与捕获块,c#,C#,以下各项之间是否存在行为差异: if (s == null) // s is a string { throw new NullReferenceException(); } 以及: 如果s为null,则两者都会引发null对象的异常。第一个示例更具可读性,因为它准确地显示了错误发生的位置(异常位就在导致异常的行旁边) 我在不同的博客上看到过很多这种编码风格,由不同技能水平的编码人员编写,但是为什么不通过检查s是否为null来执行主逻辑,从而避免异常被引发呢?这种方法有缺点吗 谢谢否,控制台

以下各项之间是否存在行为差异:

if (s == null) // s is a string
{
 throw new NullReferenceException();
}
以及:

如果s为null,则两者都会引发null对象的异常。第一个示例更具可读性,因为它准确地显示了错误发生的位置(异常位就在导致异常的行旁边)

我在不同的博客上看到过很多这种编码风格,由不同技能水平的编码人员编写,但是为什么不通过检查s是否为null来执行主逻辑,从而避免异常被引发呢?这种方法有缺点吗


谢谢

否,
控制台。WriteLine(null)
不会引发异常。它不会打印任何内容。现在假设你的意思是:

Console.WriteLine(s.Length);
那就有意义了。。。你应该使用第一种形式。当您无法使用当前信息提前预测异常时,应发生异常。如果你能很容易地判断出问题,那么尝试一个注定要失败的操作是没有意义的。这会导致代码更难理解,性能更差


因此,不应捕获
NullReferenceException
ArgumentNullException
等,除非它们是由于错误引起的。这就是为什么在代码契约中,失败契约的默认行为是抛出一个无法显式捕获的异常,而不是捕获所有内容(通常位于堆栈顶部)。

正如Jon Skeet已经提到的,Console.WriteLine(null)不会抛出异常

接下来,我想说你应该“快速失败”。这意味着您必须在方法中放入'guard'子句,并检查方法中给出的参数是否有效。
这允许您自己抛出一个异常,并给出一条在调试时有用的附加消息。这条信息可以清楚地指出问题所在,如果您面临的是一个NullReferenceException,它在消息属性中没有任何好信息的情况下抛出。

如果您采用按契约类型设计的方法,那么一段代码可以指定它抛出异常,以便指定它的契约并强制执行它。当然,另一半是调用代码识别合同并履行合同

在这种情况下,这意味着如果您知道一个方法在传入null时会抛出异常(即它的约定是您不传递null),那么您应该在调用它之前进行检查


Jon Skeet说该方法无论如何都不会抛出异常。这可能是真的,也可能不是真的,但保护方法契约的原则是正确的(我相信这是您的问题所在)。

如果您正在编写类库,您可能会知道,如果某个参数包含空值,这可能会进一步引起麻烦。在这些情况下,我通常认为抛出异常是一个好主意(即使我可能会使用
ArgumentNullException
来处理这种情况),以使类库的用户尽早清楚地意识到这一点


例外并不总是一件坏事。

Jon Skeet是对的,但更一般地说,这都是一个语义问题

如果该情况具有某种应用意义(数字超出范围、将来的出生日期等),则您可能希望在执行任何操作之前对其进行测试,并抛出一个自定义异常(该异常对您的应用程序具有意义)


如果情况确实是“异常的”,只需编写代码,就好像给定的值是正确的一样。看,如果您进行了测试,您每次都会进行测试,知道VM无论如何都会进行测试,以防它需要抛出异常。从性能的角度来看,如果错误恰好发生在统计上很小,则没有意义。

谢谢。我指的是使用变量(在方法/执行器中)但导致NullReferenceException或对象引用未设置为对象错误实例的任何情况。一位编写代码的朋友告诉我,在调用方捕获异常的情况下,应该使用第一个表单?因此,如果我有另一个方法调用第一个表单,并且有一个catch块用于抛出的异常行。您同意吗?如果有,对象引用未设置为对象实例与NRE之间的关系是什么?您如何知道调用方是否会捕获异常?当出现问题时显式抛出异常(信息量要大得多)和让.NET framework抛出一般的NullRef异常之间有什么区别?这一个?引用框架设计指南,“不允许公开调用的API显式或隐式抛出NullReferenceException…”另请参见
Console.WriteLine(s.Length);