我该如何解释 if (xyz == null) 检查不是“保护性”的?

发布于 2024-08-10 07:23:06 字数 406 浏览 13 评论 0原文

我有一些开发人员经常在他们的代码中添加 If null 检查

Run(Order order)
{
  if (order == null) return;
}

因为他们认为如果有人传入 null 参数,他们会保护自己的类。我试图告诉他们逻辑中的缺陷,因为如果有人在这种情况下传入 null,则很可能是消费者代码的问题,并且该类不会抛出异常并快速失败,而是优雅地处理以下不良行为消费者并继续畅饮。

另一个建议是使用快速失败并抛出异常的前提条件或保护类。除了忽略消费者可能还有其他问题这一事实之外,我可以帮助掩盖它。

我怎样才能让人们认识到你们的班级不应该如此宽容这一事实。如果有人没有提供良好的数据,就应该告诉他们。

有什么好的文章或建议可以帮助我理解这一点吗?

i have a few developers who constantly put If null checks

For example:

Run(Order order)
{
  if (order == null) return;
}

in their code as they think they are protecting their class if someone passes in a parameter that is null. I am trying to tell them the flaw in their logic because if someone is passing in null in this case, its most likely an issue with the consumer code and instead of this class throwing an exception and failing fast, it gracefully handles the bad behavior of the consumer and keep chugging away.

another suggestion is to have precondition or guard class that fail fast and throw exceptions. any thing but ignoring that fact that the consumer probably has some other issue and i am help mask it.

how do i get folks to appreciate the fact that your class shouldn't be so forgiving. if someone doesn't pass in good data, they should be told.

any good articles or suggestions to help me get this point across?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

随遇而安 2024-08-17 07:23:06

如果您的类不能接受 null 参数,那么最好的做法是:

if (arg == null)
    throw new ArgumentNullException();

这比在堆栈更深处获取 NullPointerException 更好。在最坏的情况下,您会将 null 缓存在某处,直到很久以后才真正触发异常,然后看看调试问题会有多少乐趣

正如其他人所说,有时合同说 null 是可以的。在这种情况下,在代码的某些部分周围有一个保护子句是正确的——尽管即使如此,我还是想说最好的设计是添加一个不带可选空参数的重载。

If your class cannot accept null arguments, then the best thing to do is this:

if (arg == null)
    throw new ArgumentNullException();

This is vastly preferable to getting a NullPointerException deeper down the stack. In the worst case scenario, you'll cache that null somewhere and won't actually trigger the exception until much later, and see how much fun you'll have debugging the problem then.

And as others have stated, sometimes the contract says that null is okay. In that case, having a guard clause around some parts of the code is correct--although even then I'd say that the best design would be to add an overload without the optionally-null arguments.

晨曦÷微暖 2024-08-17 07:23:06

这确实取决于具体情况。正如您似乎指出的那样,很少建议提供诸如“不要在代码中进行空检查”之类的一般建议。类的契约应该定义什么是合法的,什么是不合法的。但如果合约明确规定传入 null 是不可接受的,那么异常确实是一个合适的响应。

It really depends on the precise situation. It's rarely advisable to give general suggestions like "don't put null checks in your code", as you seem to be indicating. The contract of the class should define what's legit and what isn't. But if the contract makes it clear that passing in null is not acceptable, then an exception is indeed an appropriate response.

饮惑 2024-08-17 07:23:06

正如其他人所说,尽早失败比在生产中遇到神秘问题要好得多,因为该功能没有按照预期执行任何操作。如果函数返回空参数,如您的示例所示)。

即使函数不返回而只是抛出 NullReferenceException,当您知道参数为 null 时,也可以更轻松地解决错误。如果函数抛出 NullReferenceException,您不知道什么是 null 或是谁的错误。

我想补充一点,ArgumentNullException 采用参数是有原因的。

更好

if(myArg == null) throw new ArgumentNullException("myArg");

编写不带 paramName 的代码比抛出 ArgumentNullException

。这样,如果带有五个参数的函数出现异常,您将知道哪个参数导致了问题。如果您无法附加调试器,这一点尤其重要。 (例如,在生产 Web 服务器或最终用户计算机上)

如果您要编写许多函数,这可能会产生大量开销,特别是因为没有针对字符串的 IntelliSense。我编写了一个代码片段来生成这些检查:

<?xml version="1.0" encoding="utf-8" ?>
<CodeSnippets  xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
    <CodeSnippet Format="1.0.0">
        <Header>
            <Title>Check for null arguments</Title>
            <Shortcut>tna</Shortcut>
            <Description>Code snippet for throw new ArgumentNullException</Description>
            <Author>SLaks</Author>
            <SnippetTypes>
                <SnippetType>Expansion</SnippetType>
                <SnippetType>SurroundsWith</SnippetType>
            </SnippetTypes>
        </Header>
        <Snippet>
            <Declarations>
                <Literal>
                    <ID>Parameter</ID>
                    <ToolTip>Paremeter to check for null</ToolTip>
                    <Default>value</Default>
                </Literal>
            </Declarations>
            <Code Language="csharp"><![CDATA[if ($Parameter$ == null) throw new ArgumentNullException("$Parameter$");
        $end$]]>
            </Code>
        </Snippet>
    </CodeSnippet>
</CodeSnippets>

As everyone else has said, it is vastly preferable to fail early than to get mysterious problems in production because the function didn't do anything when it was expected to. if the function returns for null arguments, as in your example).

Even if the function doesn't return and just throws a NullReferenceException, it's eaiser to solve a bug when you know that an argument was null. If a function throws a NullReferenceException, you have no idea what was null or whose fault it was.

I'd like to add that ArgumentNullException takes a parameter for a reason.

It is better to write

if(myArg == null) throw new ArgumentNullException("myArg");

than to throw an ArgumentNullException without a paramName.

This way, if you have an exception from a function that takes five parameters, you'll know which of the parameters caused the problem. This is especially important if you cannot attach a debugger. (For example, on a production web server or an end-user machine)

If you're writing many functions, this can be a lot of overhead, especially since there's no IntelliSense for the strings. I wrote a code snippet to generate these checks:

<?xml version="1.0" encoding="utf-8" ?>
<CodeSnippets  xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
    <CodeSnippet Format="1.0.0">
        <Header>
            <Title>Check for null arguments</Title>
            <Shortcut>tna</Shortcut>
            <Description>Code snippet for throw new ArgumentNullException</Description>
            <Author>SLaks</Author>
            <SnippetTypes>
                <SnippetType>Expansion</SnippetType>
                <SnippetType>SurroundsWith</SnippetType>
            </SnippetTypes>
        </Header>
        <Snippet>
            <Declarations>
                <Literal>
                    <ID>Parameter</ID>
                    <ToolTip>Paremeter to check for null</ToolTip>
                    <Default>value</Default>
                </Literal>
            </Declarations>
            <Code Language="csharp"><![CDATA[if ($Parameter$ == null) throw new ArgumentNullException("$Parameter$");
        $end$]]>
            </Code>
        </Snippet>
    </CodeSnippet>
</CodeSnippets>
泅渡 2024-08-17 07:23:06

.net 4.0 中的代码契约有望使这种行为更加一致。
任何谈论代码契约的文章都将有助于理解这个想法,并且将来这种语法将提供方法。

http:// blogs.msdn.com/bclteam/archive/2008/11/11/introduction-to-code-contracts-melitta-andersen.aspx

Code contracts in .net 4.0 will hopefully make this behavior much more consistent.
Any articles which talk about code contracts will help get the idea across, and in the future, this kind of syntax will provide the method.

http://blogs.msdn.com/bclteam/archive/2008/11/11/introduction-to-code-contracts-melitta-andersen.aspx

北音执念 2024-08-17 07:23:06

我还没看完,但是 eiffel.com 有 2 个演示文稿(幻灯片+音频)关于按合同设计的主题。这些人发明了这个概念,所以如果有人能解释的话那就是他们了:-)

I haven't watched through it, but eiffel.com has 2 presentations (slides+audio) on the topic of design by contract. These guys have invented the concept, so if anyone can explain it it's them :-)

流星番茄 2024-08-17 07:23:06

有时你无法告诉人们为什么这样的做法是错误的——他们必须自己找出答案。但是您可以通过提出一些单元测试来帮助他们实现这一目标,这些单元测试会因这个问题而导致一些严重的失败,并让他们调试错误。

Sometimes you can't tell people why a practice like this is wrong - they have to figure it out for themselves. But you could help them get there by coming up with some unit test that causes some nasty failure due to this problem, and make them debug the error.

情丝乱 2024-08-17 07:23:06

如果方法的约定指定其参数不应为空,那么正确的做法是使用断言使其显式化,如下所示:

Debug.Assert( item != null, "Null items are not supported );

当使用调试配置构建可执行文件时,这将快速失败,但会出现使用发布配置构建时性能为零。

If the method's contract specifies that its arguments should not be null, then the right thing to do is to make it explicit, by using an Assert, like this:

Debug.Assert( item != null, "Null items are not supported );

This will fail fast when the executable is built using a debug configuration, but will present zero performance degradation when built using a release configuration.

在巴黎塔顶看东京樱花 2024-08-17 07:23:06

这似乎是一个关于如何最好地编写易于管理的代码的问题。我的新信念是,您必须假设您的代码的所有使用者都一无所知。由于假设我或具有深厚知识的人会使用我的代码,我给自己带来了麻烦。我要添加的抛出异常的唯一内容是创建自定义异常以及在内部异常中留下面包屑。我坚信应该给开发人员一个机会来解决这个问题,特别是当问题是由数据引起的时。我花了大部分时间寻找破坏我的代码的数据,如果你能留下提示,你将在一年内节省数周的时间。

This seems to be a question about how best write code that is manageable. It is my new belief that you must assume ignorance of all consumers of your code. I have gotten myself into trouble by assuming I or someone with deep knowledge would be consuming my code. The only thing I would add to throwing an exception is creating custom exceptions as well as leaving breadcrumbs in the inner exception. I believe strongly in giving your developers a chance to run down the issue especially if it is due to data. I spend most of my time looking for the data that breaks my code and if you can leave hints you will save weeks in a year.

累赘 2024-08-17 07:23:06

好吧,首先,你的观点绝对是错误的。你正在接受一个非常严重的逻辑谬误。您希望您的代码是正确的,因为代码假设周围发生的一切都是正确的。就好像正确性是某种神奇的仙尘,你只需要把它喷得到处都是。

所有的错误一旦暴露出来都是愚蠢的或看起来是愚蠢的。但这样的检查会引诱他们暴露自己。在那之前,错误是看不见的。对于足够大且复杂的项目,您不知道谁会发现错误或在什么条件下会发现它们。为弹性而设计的代码通常在各处都有这样的检查,并且还检查必须包含错误值的每个函数的返回值。因此,您最终会编码“我不能这样做,因为我依赖的子功能无法正常工作”的语义,而该语义实际上已正确处理。这样做的巨大价值在于,您通常可以非常轻松地实现变通方法或自我意识调试工具。 为什么你想要做这样的事情是因为最困难的错误通常依赖于这两个属性来正确调试。

向您的开发人员学习一些经验教训。他们把这样的检查放在那里,因为他们不知道为什么有时会从函数中得到奇怪的结果。你称他们为天真或过于谨慎,因为你拥有他们所不具备的一项狭隘知识。但是当你调试一些令人讨厌的东西时,你会想知道为什么你的代码中没有这样的检查,并且你最终会因为无法首先发现错误而显得天真。

简而言之:没有任何代码可以通过假设周围环境的稳健性而变得稳健。

Well first of all, you are unequivocally incorrect. You are embracing a very serious logical fallacy. You want your code to be correct by virtue of the code assuming that everything happening around it is correct. As if correctness was some kind of magical pixie dust that you just need to spray everywhere.

All bugs are or seem stupid once they are exposed. But its checks like this that tease them out to expose themselves. Until then, bugs are invisible. And for large and complex enough projects, you don't know who will find the bug or under what conditions they will be found. Code which is architected for resilience typically has checks like this all over the place and also check the return values for every function which have to include error values. So you end up encoding a "I can't do that because the sub-functions I rely on are not working" semantic that is actually handled properly. The great value of this is that you can usually implement work arounds or self-aware debug instrumentation quite easily. Why you want to do things like this is because the most difficult bugs usually rely on both of those properties to debug properly.

Learn some lessons from your developers. They put checks like that in there, because they don't know why sometimes they get strange results from functions. You call them naive or overly cautious because of one narrow piece of knowledge that you have that they don't. But when you are debugging something nasty you are going to wonder why you don't have such checks in your code, and you are going to end up looking just as naive for not being able to spot the bug in first place.

In short: No code is made robust by assuming robustness about the environment around them.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文