如果未指定要删除的项目,服务应该抛出异常或返回


12

我有一段代码可以表示为:

public class ItemService {

    public void DeleteItems(IEnumerable<Item> items)
    {
        // Save us from possible NullReferenceException below.
        if(items == null)
            return;

        foreach(var item in items)
        {
            // For the purpose of this example, lets say I have to iterate over them.
            // Go to database and delete them.
        }
    }
}

现在我想知道这是正确的方法还是应该抛出异常。我可以避免出现异常,因为返回将与遍历一个空集合相同,这意味着无论如何都不会执行任何重要的代码,但是另一方面,我可能会将问题隐藏在代码中的某个地方,因为有人为什么要调用DeleteItemsnull参数吗?这可能表明代码中的其他地方存在问题。

这是我通常在服务中使用方法时遇到的一个问题,因为它们中的大多数都做某事并且不返回结果,因此,如果有人传递了无效信息,则该服务将无事可做,因此它将返回。


2
这只是我个人的观点,但是我始终发现,从某种意义上说,一种方法在执行意外的操作(异常)时应该抛出异常。在您的情况下,如果有人尝试删除null / 0项目,我将抛出InvalidOperationException。
Falgantil '18


1
@gnat我的问题特别是关于服务的,因为服务的使用方式和目的。这个“重复”仅讨论一般情况,而主要答案在我的确切方法中甚至与自己矛盾。
FCin

2
枚举是否可以为空而不是null?您会以不同的方式处理吗?
JAD

13
@Falgantil我可以看到null值得一个异常,但是我认为将异常抛出一个空列表是荒谬的。
凯文

Answers:


58

这是两个不同的问题。

你应该接受null吗?这取决于您关于null代码库的一般政策。我认为,禁止null在除明确记录的地方之外的任何地方都是一个很好的做法,但是最好还是遵循您的代码库已经存在的约定。

您应该接受空集合吗?我认为:是的,绝对如此。将所有调用者限制为非空集合比做正确的事情要付出更多的努力-即使这会让某些对零概念不满意的开发人员感到惊讶。


9
无论如何,如果您要抛出,那么AhaINoticedYouPassingInNullException当运行时已经为您提供了抛出的功能NullReferenceException而无需编写任何代码时,就没有那么多的抛出实用程序。有一些实用程序(例如,您的代码覆盖率工具会告诉您在前一种情况下是否有用于传递null的单元测试,而后一种情况则没有),但是每次对服务的调用都不应尝试/捕获任何异常,因为通常是程序员错误。
史蒂夫·杰索普

2
...如果您知道传入的信息在某些预期情况下为null,并且您积极希望所调用的函数为您进行验证,则您应该只应立即捕获由于粗略参数而引发的异常。就像Kilian在回答中所说的那样,您的一般政策可能是在未明确允许的地方禁止使用null。然后,你就不会赶上NullReferenceExceptionAhaINoticedYouPassingInNullException任何地方的高级别灾难恢复码除外。而且该异常处理程序可能应该创建一个错误报告:-)
史蒂夫·杰索普

2
@FCin:您的界面设计应与用户实际需要的服务相匹配。因此,如果每个调用者都打算对此进行尝试/捕获,则此方法或它旁边的某种便捷方法都应检查并忽略null,因为这是您的公众需求。但是,正如Kilian所说,通常将传播空值视为编程错误会更好,而不是尝试在每个调用站点继续进行。因此,如果调用此方法的服务是null-控制器是否也包含样板并在这种情况下继续运行,该怎么办?
史蒂夫·杰索普

2
...如果是这样,那么代码库似乎始终如一地将缺少的组件视为应在低级处理并继续的预期条件。您可以合理地问:“提供服务和枚举以使此操作能够继续进行是谁的责任?”。如果答案是“某人”,则您的功能正在检查前提条件,而前提条件失败的结果通常是在高层而不是每个调用中捕获到的异常。如果操作所需的内容确实是可选的,那么您的函数正在处理预期的用例
史蒂夫·杰索普

2
明确地抛出an ArgumentNullException优于允许内部代码抛出NullReferenceException-纯粹查看异常消息,很明显,这是输入错误,而不是抛出位置的方法主体中的逻辑错误,并且确保提前退出意味着您更有可能使其他数据保持有效状态,而不是稍后从意外的null进行部分突变。
米兰拉'18

9

空值

正如@KilianFoth已经说过的那样,请遵循您的一般政策。如果将其null视为空列表的“简写”,请这样做。

如果您对null价值观没有一致的政策,建议您采取以下措施:

null应该保留以表示无法用“正常”类型表示的情况,例如null用于表示“我不知道”。这是一个不错的选择,因为每个尝试不小心使用此值的人都会遇到异常,这是正确的选择。

使用null作为一个空列表的简写没有资格这样,因为已经有一个完美的表现,是与零个元素的列表。从技术上来说,这是一个错误的选择,因为它会迫使您处理列表的代码的每个部分都要检查有效的简写形式null

空清单

对于一种DeleteItems()方法,有效地传递一个空列表意味着什么也不做。我允许它作为参数,而不是引发异常,只是快速返回。

当然,DeleteItems()在这种情况下,调用方可以先检查零个元素,然后跳过该调用。如果我们在谈论Web API,出于效率方面的考虑,调用者实际上应该这样做,以避免不必要的流量和往返延迟。但是我不认为您的API应该执行该操作。


1

引发异常并处理调用代码中的null。

作为设计规则,请尝试避免将null作为参数值。通常,它将减少NullPointerExceptions,因为null确实是一个异常。

除此之外,请查看其余的代码。如果这是项目中的常见模式,请保持一致。


我正在“塑造”我的服务的结构,因此可能需要进行一些重构才能产生无效的输入,但不是很多。但是我同意您的观点,即当我开始抛出而不是隐藏它们时,空值将成为一个例外。
FCin

0

一般而言,抛出异常应保留用于例外情况,即,如果代码在当前上下文中没有采取合理的措施。

您可以将该思考过程应用于这种情况。鉴于这是带有公共方法的公共类,因此您具有公共API,并且从理论上讲,无法控制传递给它的内容。

您的代码没有关于调用它的上下文(因为它不应该这样做),并且您无法合理地使用null值。这肯定是一个候选人ArgumentNullException


“如果代码在当前上下文中没有采取合理的行动方针”。但是我的想法是,它有合理的行动方针,那就是要回报虚无。它的作用与传递空集合时完全相同,因此,如果我允许空集合,那么为什么我不允许null
FCin

1
@FCin由于集合为空,您知道它为空。有了null你有没有收藏。如果应该/期望调用代码为该方法提供一个集合,则说明存在问题,因此应抛出异常。将空值与空集合相同的唯一原因是,如果您可以合理地期望调用代码产生空集合。正如其他答案所指出的,在大多数情况下,某种事物null是一种异常。如果将它们视为空集合,则可能会忽略代码中其他地方的问题。
JAD

@FCin:同样,如果您null要表示为空,则此方法特定于上下文。在同一代码库中的其他位置,您可能具有IEnumerableIContainer参数,该参数会进行某种过滤,null可能意味着“无过滤器”(允许所有操作),而空过滤器则意味着什么都不做。在另一个地方,null可能会明确表示“我不知道”。因此,鉴于在所有地方null并不总是意味着完全相同的意思,因此,最好不要为此发明任何含义,除非该含义是必要的或至少对某人有用。
史蒂夫·杰索普

0

这个问题似乎与例外无关,而与null成为有效论点有关。

因此,首先,您必须确定null该方法的参数是否为允许的值。如果是,那么您不需要例外。如果不是,那么您确实需要一个例外。

nullGoogle 是否对此表示欢迎,这是否值得商,还是有争议的。意思是,您不会得到一个明确的答案,这在一定程度上取决于您工作所在的地方的观点和传统。

争论的另一个领域是一个库函数是否应该真正严格的这样的事情,或宽大越好,只要它是不是要“修复”错误的参数。将此与网络协议,邮件传输等(它们是接口协定,就像编程方法一样)进行比较。在那里,通常的策略是,发送方应尽可能严格地遵守协议,而接收方应尽其所能来处理即将发生的一切。

因此,您必须决定:实施相当大的策略(例如null处理)真的是一种库方法的工作吗?特别是如果您的图书馆也被其他人使用(他们可能有不同的政策)。

我可能会错在允许null值,定义和记录其语义(例如,null = empty array在这种情况下)方面,并且不会抛出异常,除非您的其他类似代码(“库”样式代码)中有99%这样做了'回合。


1
“执行诸如空处理之类的相当大的策略真的是库方法的工作吗?” -就是断言和调试模式,如果您要这样做的话,它们可以帮助您实施策略,而不是实际执行策略。因此,如果您要null接受接受内容时应保持开放的原则,那么登录或什至抛出都将对那些意图严格限制所传入内容但又弄乱了代码和他们的单元测试仍然可以通过null
史蒂夫·杰索普

@SteveJessop,我真的不知道您是否反对我的回答的精神,或者您是否继续遵循我的观点。就是说,我不建议简单地吞咽null,而是在方法的契约中公开声明它是可以接受的(或不,取决于OP提出的解决方案),然后是是否引发异常的问题。 (或不)解决自己。
AnoE
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.