我认为两者并无区别,因为您可以在if语句或catch块中处理空指针的逻辑。所以哪种做法更好呢?
我会说始终使用逻辑来捕获异常,而不是使用 try/catch。
当您进行验证但出现某些奇怪的情况导致错误时,应使用 try/catch 来处理错误以更妥善地处理它。
这里没有单一的答案,它取决于。
让我们举几个场景,这样你就能明白我的意思了。
场景:接受不了 null
的引用类型参数的方法
你正在定义一个方法,它接受一个引用类型参数,比如一个流对象,并且你不想接受 null
作为合法的输入参数。
在这种情况下,我会说,契约是null
不是一个有效的输入。如果某些代码确实使用null
引用调用该方法,则违反了协议。
这是一个异常,更具体地说,它是一个ArgumentNullException。
例如:
public void Write(Stream stream)
{
if (stream == null)
throw new ArgumentNullException("stream");
...
在这种情况下,我绝对不会让代码一直执行到尝试取消引用流,而是会崩溃并抛出NullReferenceException异常,因为此时我失去了所有能够在了解原因时做出反应的能力。
Q. 为什么不能返回false
而不是抛出异常?
A. 因为返回值很容易被忽略,你真的希望你的“写入”方法只是因为调用代码中发生错误(传递了错误的流对象或无法写入的对象等)而悄悄地跳过写入吗? 我不会这样做!
场景:方法返回一个对象的引用,有时没有对象
在这种情况下,合同规定null
是合法结果。 在我看来,应该避免使用null
,因为确保正确处理它很困难,但有时这是最好的方法。
在这种情况下,我会确保使用if
语句处理结果,以确保当返回null
引用时不会崩溃。
概括
如果您仔细观察上述两种情况,您会注意到一件事:
在这两种情况下,问题都在于期望什么,即合同是什么。
如果合同说“不是null
”,则抛出异常。 不要返回false
的旧式API方式,因为异常问题不应被悄悄忽略,并且用if
语句来确保每个方法调用成功并不是可读代码。
如果合同说“null
完全可能”,请使用if
语句处理它。
广告
为了更好地掌握null
问题,我还敦促您为您和您的团队获取ReSharper,但请注意,本答案适用于任何类型的异常和错误处理,同样的原则适用。
随着它,您可以嵌入到项目中的属性来标记这些情况,然后ReSharper会突出显示相关的代码。
public void Write([NotNull] Stream stream)
[CanBeNull]
public SomeObject GetSomeObject()
要了解ReSharper使用的合同属性的更多信息,请参见:
NullReferenceException
是我在调试时的噩梦。调用堆栈告诉你从哪里开始,但并不告诉你你的 10 个引用类型中哪些为空。希望我们有编译器支持的 "非空" 的 variableName!
语法。但总的来说,我会让异常抛出,并且只有在我能够处理潜在异常时才使用 try-catch
。然而,空引用可能受益于逻辑,而不是等待异常,因为你可以抛出自己的异常。 - Adam Houldsworth从性能角度来看,这取决于你在做什么。当没有异常抛出时,try/catch块对性能的影响很小(如果你真的需要那最后几个百分点的性能,你可能应该将代码重写为C++)。抛出异常对简单操作(如字符串操作)的性能影响很大;但是一旦你涉及文件/数据库操作,它们会变得非常慢,因此它成为一个微不足道的惩罚。在App域之间抛出异常将对几乎任何事情产生重大影响。
每秒操作性能:
Mode/operation Empty String File Database Complex
No exception 17,748,206 267,300 2,461 877 239
Catch without exception 15,415,757 261,456 2,476 871 236
Throw 103,456 68,952 2,236 864 236
Rethrow original 53,481 41,889 2,324 852 230
Throw across AppDomain 3,073 2,942 930 574 160
可以从文章 .NET中异常的性能影响 中获取附加测试结果和测试源代码。
使用try catch语句并不是一个好主意。因为当你使用try catch语句时,似乎如果出现错误,代码将不会终止应用程序。但是,如果你确定可能出现什么样的错误,你可以在那个点上捕获错误。这样就不会产生任何未知的错误。例如:
string name = null;
在这里,我将使用名称变量,并且我确信这将引发空引用错误。
try
{
Console.writeLine("Name ={0}",name);
}
catch (NullRefranceException nex)
{
//handle the error
}
catch(Exception ex)
{
// handle error to prevent application being crashed.
}
这不是一个好的做法,因为您可以处理此类错误并使代码更易读。例如。
if(name !=null)
Console.writeLine("Name ={0}",name);
我建议您在处理 NullReference
异常时使用 if语句
。对于其他异常,try-catch
应该足够了。
我之所以建议使用 if语句
处理 NullReference
异常是因为 C# 不会告诉您哪个变量为空。如果该行有多个对象可能为空,您将无法跟踪。如果您使用 if语句
,您可以更好地记录日志以帮助您获取足够的信息。
请参阅关于这个问题的讨论。如果您始终期望找到一个值,那么如果缺少该值,请抛出异常。异常意味着存在问题。
如果应用程序逻辑可以同时缺少或者存在值,那么请返回 null。
如果您想表明没有可用数据,返回 null 通常是最好的选择。
返回一个空对象意味着已经返回了数据,而返回 null 明确表示没有返回任何内容。
此外,如果您尝试访问对象中的成员,则返回 null 将导致空指针异常,这对于突出错误的代码非常有用 - 尝试访问不存在的成员是没有意义的。访问空对象的成员不会失败,这意味着错误可能会被忽略。
一些进一步的阅读材料:
在编程中,使用Try Catch比使用if else更好。
异常分为两种类型,即已处理和未处理异常。即使您想要处理某个函数的异常,也可以处理它...
处理异常始终允许您在Catch块中编写一些实现,例如警报消息、处理此类异常时的新功能等。