使用Try Catch还是If语句?

22
如果您认为存在空指针异常的可能性,应该使用if语句确保变量不为空,还是只捕获异常?
我认为两者并无区别,因为您可以在if语句或catch块中处理空指针的逻辑。所以哪种做法更好呢?

7
请访问 https://dev59.com/qUnSa4cB1Zd3GeqPKxI9,该文章探讨了在.NET中使用 try-catch 作为流程控制的技巧。 - argaz
8
异常通常是指不应经常发生的情况。如果在您的流程中存在可能为空的情况,您不应该让它触发异常。 - Karthik T
2
捕获异常比使用 if..else 慢得多。 - Dmitry Bychenko
1
主要问题在于当抛出异常时,它会(尝试)获取堆栈跟踪,这将耗费大量时间。 - Alvin Wong
请返回翻译后的文本:相关内容:http://blogs.msdn.com/b/ericlippert/archive/2008/09/10/vexing-exceptions.aspx - jk.
显示剩余2条评论
10个回答

36

我会说始终使用逻辑来捕获异常,而不是使用 try/catch。

当您进行验证但出现某些奇怪的情况导致错误时,应使用 try/catch 来处理错误以更妥善地处理它。


7
补充一下,我认为关键在于名称,你捕获的异常就是一个异常,它打破了你所预料或无法处理的代码流程。如果逻辑可以处理它,那么它就不是异常。 - CeejeeB

20

这里没有单一的答案,它取决于

让我们举几个场景,这样你就能明白我的意思了。

场景:接受不了 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使用的合同属性的更多信息,请参见:


很棒的解释,因为场景让人更易理解。另外谢谢R#的提示,对我来说是新知识。 - Koen
请查看ReSharper中可用的合同注释的更多信息,此处 - 介绍Resharper属性合同此处 - ReSharper 7中合同属性的添加 - Lasse V. Karlsen

4
好的。异常通常只是例外。当发生一些意外情况并且不应该成为正常程序流程的一部分时,就会抛出它们。
在这里,发生了这种情况。您期望指定参数,但实际上没有。这是不可预期的,因此您应该抛出自己的异常来告知用户。如果您想得到额外的积分,还可以解释为什么必须指定参数(如果不明显的话)。
我已经写了一系列关于异常的文章:http://blog.gauffin.org/2013/04/what-is-exceptions/

1
这与logixologist上面的帖子相矛盾。 - David Klempfner
logixologist自相矛盾。他先说他总是使用逻辑,然后又说可以使用try/catch。如果您阅读我的文章系列,您会发现try/catch很少能用来拯救情况。 - jgauffin
使用逻辑测试空引用的好处在于你可以抛出一个有意义的异常。NullReferenceException 是我在调试时的噩梦。调用堆栈告诉你从哪里开始,但并不告诉你你的 10 个引用类型中哪些为空。希望我们有编译器支持的 "非空" 的 variableName! 语法。但总的来说,我会让异常抛出,并且只有在我能够处理潜在异常时才使用 try-catch。然而,空引用可能受益于逻辑,而不是等待异常,因为你可以抛出自己的异常。 - Adam Houldsworth

3

从性能角度来看,这取决于你在做什么。当没有异常抛出时,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中异常的性能影响 中获取附加测试结果和测试源代码。


0

使用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);

0

我建议您在处理 NullReference 异常时使用 if语句。对于其他异常,try-catch 应该足够了。

我之所以建议使用 if语句 处理 NullReference 异常是因为 C# 不会告诉您哪个变量为空。如果该行有多个对象可能为空,您将无法跟踪。如果您使用 if语句,您可以更好地记录日志以帮助您获取足够的信息。


0
主要的问题是是否有方法返回 Null 值是一个好主意,个人认为这没有问题,但一旦您尝试访问从该方法返回的对象的修饰符并且忘记检查它是否已分配,则会出现问题。
Ken 就此提出了 好的回答

如果您始终期望找到一个值,那么如果缺少该值,请抛出异常。异常意味着存在问题。

如果应用程序逻辑可以同时缺少或者存在值,那么请返回 null。

请参阅关于这个问题的讨论

如果您想表明没有可用数据,返回 null 通常是最好的选择。

返回一个空对象意味着已经返回了数据,而返回 null 明确表示没有返回任何内容。

此外,如果您尝试访问对象中的成员,则返回 null 将导致空指针异常,这对于突出错误的代码非常有用 - 尝试访问不存在的成员是没有意义的。访问空对象的成员不会失败,这意味着错误可能会被忽略。

一些进一步的阅读材料:


0
在我的经验中,使用if更好,但前提是你确实预期一个空引用指针。没有任何代码或上下文的情况下,很难说哪个选项比另一个更好。
还有一个优化问题 - 在try-catch块中的代码不会被优化

0
一般而言,try-catch块非常好用,因为只要出现异常,它们就会中断(转到catch语句)。if-else块则需要你预测错误何时发生。
此外,catch块不会在出错时阻止代码停止。

-2

在编程中,使用Try Catch比使用if else更好。

异常分为两种类型,即已处理未处理异常。即使您想要处理某个函数的异常,也可以处理它...

处理异常始终允许您在Catch块中编写一些实现,例如警报消息、处理此类异常时的新功能等。


3
我不同意。在我的观点中,使用异常来捕获空值、获取数组的非法索引、除以零和其他相当常见的情况并不是一个好主意。 - Dariusz

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