是否需要检查空值。

11

我知道在方法中应该始终检查传入的参数是否为 null。但是,如果我有一个 try/catch 代码块引用了一个局部变量,那么我真的需要在下面检查 null 吗?因为如果 refundResponse 变量为 null 并且下一行代码尝试使用它,try/catch 代码块会捕获到它:

    public string DoRefund(...)
    {
        try
        {
    ......
            string refundTransactionID = string.Empty;
    ......

            RefundTransactionResponseType refundResponse = transaction.DoRefund(...);

            if (refundResponse != null)
                refundTransactionID = refundResponse.RefundTransactionID;
    .....
        }
        catch (Exception ex)
        {
            LogError(ex);
            return ex.ToString();
        }
    }
记住,我特别指的是本地变量和在方法内部检查它们,而不是方法的传入参数。
我在这里问的只是在设置refundTransactionID之前是否需要检查null,还是可以直接设置而不进行if判断,假设编译器会处理并在为null时抛出异常,这个异常将被捕获并作为字符串返回给调用者。
还是应该像下面这样:
if (refundResponse == null)
                return null;

或者干脆完全去掉这个本地变量赋值的检查。由于在这种情况下我有一个try/catch,所以通过将异常作为字符串返回给调用者来自然地处理编译器捕获到的任何异常(不是我决定返回字符串,而是我的老板要求的...所以现在就先避免这场辩论):

 refundTransactionID = refundResponse.RefundTransactionID;

最终方法中更下面的代码取决于有效的refundTransactionID。


11
在一般情况下,捕获所有异常并将响应作为字符串返回给我似乎非常奇怪,可能不是很好的编程实践。有些情况下,您需要捕获所有错误,例如Web服务分派器,但是像这样的业务逻辑似乎不是这样的地方。 - Michael Ekstrand
这是一个网络服务方法。 - PositiveGuy
返回字符串取决于Web服务的使用方式。你怎么能说不要将错误作为字符串返回呢?那你会返回什么呢?我看除了错误消息,没有其他有用的东西可供返回了。 - PositiveGuy
10个回答

12

异常处理是用来处理特殊情况的。如果可以检查并处理可恢复错误,请尽量这样做!


是的,但如果refundResponse因某种原因为空,编译器已经会抛出空异常,如果我尝试使用它。那么为什么在这种情况下还要抛出异常,当编译器将无论如何抛出异常消息已经足够好了呢?这就是我正在讨论的问题...也许我错了,但如果它为空并且我没有进行检查,那么try/catch会捕获到它。 - PositiveGuy
我只是在设置transactionID之前检查null。这就是我特别询问的内容。对于局部变量,我真的需要这样做吗?还是让编译器在它到达尝试设置transactionID的行时自然地抛出异常? - PositiveGuy
什么被认为是异常情况,是 null 吗? - PositiveGuy
@coffeeaddict:有点像,但取决于调用者和应用程序类型(例如库、Web服务、本地类)。如果您认为它永远不会是“null”,那么检查就不值得了。另一方面,如果您有未知的调用者可能传递null,您可能希望以更好的方式指示出错了什么。这更多是一种观点,而不是“法律”。此外,null可能还有其他含义,而不仅仅是一个错误。 - leppie

8
我知道你应该总是检查方法的传入参数是否为null。
不,不一定。你应该指定你的方法的契约。指定如果参数为null,你会抛出NullPointer/NullReferenceException是完全可以接受(也很常见)的。这样你就不需要进行任何检查了。
当然,你也可以检查null,但只有在你可以实际有用地处理null(例如替换默认值)时才有意义。

9
然而,在.NET中,当一个不应该是null的参数接收到一个null值时,惯例是抛出ArgumentNullException - Pavel Minaev
3
通常最好在方法开头抛出一个ArgumentNullException,并提供有关为空的参数的信息。如果您忽略了错误的输入并让代码在稍后某个时刻抛出NullReferenceException,那么在此之前可能已经进行了多次内部调用。到那时,异常可能来自于某个未记录的内部函数,并且该参数名称在那里可能与外部可见名称不同,因此对调用方来说可能不会立即明显。 - Mark Byers
Pavel,这不是一个争论。它是一个方法内的局部变量。 - PositiveGuy
@Pavel,@Mark:感谢提供的信息和好观点。我来自Java编程背景,在Java中抛出NPE是很常见的。Java没有ArgumentNullException(只有IllegalArgumentException)。然而区别还是很有意思的。 - sleske
@coffeeaddict,我是在评论这个特定的答案(它谈论了合同和参数),而不是问题本身。 - Pavel Minaev

2

在这种情况下,您应该检查是否为null。您的应用程序逻辑应该能够处理这些情况,而不需要使用异常。


好的,在这种情况下,我会在上游处理它。任何在try中发生的异常都将返回给调用者。在这种情况下,这是一个Web服务方法,我的老板因某种原因想要返回一个只包含“成功”或“失败”的字符串,但对我来说这是不好的做法。就像返回一个true/false一样,在进行交易时这是错误的。 - PositiveGuy

2

一种替代测试的方法是使用空对象模式。transaction::DoRefund()方法不返回Null或有效的交易,而是返回一个空对象:这个对象提供与RefundTransactionResponseType实例相同的接口,但其方法不执行任何操作。使用此方法,无需测试是否为Null。

应谨慎使用,因为这可能会轻易隐藏问题。


啊,我的一个朋友也建议过这个。不错。但如果你的老板说不行呢?你就不能使用那个模式了。 ;) - PositiveGuy
请注意,这个 Web 服务方法将被从除 .NET 以外的其他语言调用...不过这并不影响您对我的问题的回答。 - PositiveGuy

1

不,看起来你不应该在这里检查 null。而且我也不会为所有传入参数都检查 null(就像你的描述所建议的那样)。

另外,你返回一个字符串类型的 transactionID 或者异常信息的消息也很奇怪。调用此方法的人如何知道是否发生了异常?

如果你真的想记录异常,可以尝试这样:

    public string DoRefund(...) 
    { 
        try 
        {
            return transaction.DoRefund(...).RefundTransactionID; 
        } 
        catch (Exception ex) 
        { 
            LogError(ex); 
            throw ex;
        } 
    }

3
我认为这样可以清除这个堆栈跟踪。但是可能需要执行LogError(ex); throw;,这会将异常继续抛到堆栈上游。 - galford13x
我的老板是唯一的调用者,他要求我编写的所有 Web 服务方法都返回“成功”或“失败”。因此,在这种情况下,他可以检查是否“已批准”,如果未经批准,则会收到错误消息。 - PositiveGuy
好的,我不会放弃我的捕获...是的,你说得对,但如果(假设响应变量为null)我将捕获的异常作为字符串返回给调用者...再次强调,这是要求以字符串形式返回它。 - PositiveGuy

1

不,你不需要在那里检查 null。这又引出了另一个问题,你真的需要检查传入参数中的 null 吗?

记住:这是一种行为。你必须测试这种行为。


1

但如果你无法在那个点继续执行,就让异常传播。


1
你应该检查 null 而不是让异常处理来处理它。正如 leppie 所说,异常是用于异常情况而不是正常的控制流程。如果你知道可能会出现什么问题,那么你应该优雅地处理它们。
另一件需要记住的事情是异常的性能影响。当异常被抛出时,JVM 必须解开调用堆栈。在你的例子中,异常还被记录了下来。所有这些都需要时间,比简单的“if”检查要慢得多。

0
我建议先检查是否为空,然后进行柔性错误处理,而不是让它捕获并抛出错误消息。

在这种情况下,错误处理是在调用者端完成的。因为这是一个Web服务方法,将由我的老板用于他的管理系统,这就是我被告知要做的,返回一个字符串。是的,我认为这很愚蠢。 - PositiveGuy

0

这取决于(refundResponse == null)对您的程序意味着什么。如果它有一定的含义,那么报告更详细的错误信息是有意义的。如果它永远不应该发生,并且会表明DoRefund方法存在缺陷,那么我认为允许null在后面引发异常是可以的。在后一种情况下,只有在您怀疑该方法是否按照预期运行时才进行特定检查。


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