C# 中的空参数检查
在 C# 中,是否有任何好的理由(除了更好的错误消息之外)向每个 null 不是有效值的函数添加参数 null 检查?显然,使用 s 的代码无论如何都会抛出异常。这样的检查会使代码变慢并且更难维护。
void f(SomeType s)
{
if (s == null)
{
throw new ArgumentNullException("s cannot be null.");
}
// Use s
}
In C#, are there any good reasons (other than a better error message) for adding parameter null checks to every function where null is not a valid value? Obviously, the code that uses s will throw an exception anyway. And such checks make code slower and harder to maintain.
void f(SomeType s)
{
if (s == null)
{
throw new ArgumentNullException("s cannot be null.");
}
// Use s
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(10)
是的,有充分的理由:
现在对于您的反对意见:
对于你的断言:
真的吗?考虑一下:
它使用了
s
,但它不会抛出异常。如果s
为 null 无效,并且表明出现了问题,则异常是此处最合适的行为。现在,将这些参数验证检查放在哪里是另一回事了。您可能决定信任您自己的类中的所有代码,因此不必担心私有方法。您可能决定信任程序集的其余部分,因此不必担心内部方法。您几乎肯定应该验证公共方法的参数。
附注:
ArgumentNullException
的单参数构造函数重载应该只是参数名称,因此您的测试应该是:或者您可以创建一个扩展方法,允许更简洁:
在我的版本中(通用)扩展方法,如果它不为空,我让它返回原始值,允许您编写如下内容:
您还可以有一个不采用参数名称的重载,如果您对此不太在意的话。
更新 .NET 6
有一个 新方法.NET API 中的 简化了
null
检查语法。Yes, there are good reasons:
NullReferenceException
Now as for your objections:
And for your assertion:
Really? Consider:
That uses
s
, but it doesn't throw an exception. If it's invalid fors
to be null, and that indicates that something's wrong, an exception is the most appropriate behaviour here.Now where you put those argument validation checks is a different matter. You may decide to trust all the code within your own class, so not bother on private methods. You may decide to trust the rest of your assembly, so not bother on internal methods. You should almost certainly validate the arguments for public methods.
A side note: the single-parameter constructor overload of
ArgumentNullException
should just be the parameter name, so your test should be:Alternatively you can create an extension method, allowing the somewhat terser:
In my version of the (generic) extension method, I make it return the original value if it's non null, allowing you to write things like:
You can also have an overload which doesn't take the parameter name, if you're not too bothered about that.
Update .NET 6
There is a new method in .NET API which simplifies
null
check syntax.我同意乔恩的观点,但我想补充一件事。
我对何时添加显式空检查的态度基于以下前提:
throw
语句是语句。if
的结果是一个语句。if (x == null) throwwhat;
中的throw
如果没有可能的方法如果要执行该语句,则无法对其进行测试,应将其替换为 Debug.Assert(x != null);。
如果有可能执行该语句,则编写该语句,然后编写一个执行该语句的单元测试。
公共类型的公共方法以这种方式检查其参数是特别重要的;你不知道你的用户会做什么疯狂的事情。给他们“嘿,你这个笨蛋,你做错了!”尽快排除。
相比之下,私有类型的私有方法更有可能出现在您控制参数的情况下,并且可以强有力地保证参数永远不为空;使用断言来记录该不变量。
I agree with Jon, but I would add one thing to that.
My attitude about when to add explicit null checks is based on these premises:
throw
statements are statements.if
is a statement.throw
inif (x == null) throw whatever;
If there is no possible way for that statement to be executed then it cannot be tested and should be replaced with
Debug.Assert(x != null);
.If there is a possible way for that statement to be executed then write the statement, and then write a unit test that exercises it.
It is particularly important that public methods of public types check their arguments in this way; you have no idea what crazy thing your users are going to do. Give them the "hey you bonehead, you're doing it wrong!" exception as soon as possible.
Private methods of private types, by contrast, are much more likely to be in the situation where you control the arguments and can have a strong guarantee that the argument is never null; use an assertion to document that invariant.
我已经使用它一年了:
它是一个单行代码,丢弃(
_
)意味着没有不必要的分配。I've been using this for a year now:
It's a oneliner, and the discard (
_
) means there's no unnecessary allocation.如果没有显式的
if
检查,如果您不拥有该代码,则很难弄清楚null
是什么。如果您在没有源代码的情况下从库深处收到
NullReferenceException
,您可能很难弄清楚自己做错了什么。这些
if
检查不会使您的代码明显变慢。请注意
ArgumentNullException< 的参数/code> 构造函数
是参数名称,而不是消息。
你的代码应该是
我写的一个代码片段,以使其更容易:
Without an explicit
if
check, it can be very difficult to figure out what wasnull
if you don't own the code.If you get a
NullReferenceException
from deep inside a library without source code, you're likely to have a lot of trouble figuring out what you did wrong.These
if
checks will not make your code noticeably slower.Note that the parameter to the
ArgumentNullException
constructor is a parameter name, not a message.Your code should be
I wrote a code snippet to make this easier:
如果您需要更好的方法,您可能需要查看代码契约确保您没有获取任何空对象作为参数。
You might want to take a look at Code Contracts if you need a nicer way to make sure you do not get any null objects as a parameter.
在 C# 11 预览版中,它非常酷!!
您只需在参数名称末尾添加两个感叹号
!!
即可。这将进行空参数检查。这相当于:
可以找到一篇关于它的很好的解释文章
In C# 11 preview it is super cool!!
You just need to add two exclamation marks
!!
at the end of the parameter name. This will do the null parameter checking.This is equivalent to:
A good explanation article about it can be found here.
当您遇到异常时,它可以节省一些调试。
ArgumentNullException 明确指出“s”为空。
如果您没有进行该检查并让代码崩溃,那么您会从该方法中的某些未识别的行中得到 NullReferenceException。在发布版本中,您不会获得行号!
It saves some debugging, when you hit that exception.
The ArgumentNullException states explicitly that it was "s" that was null.
If you don't have that check and let the code blow up, you get a NullReferenceException from some unidentified line in that method. In a release build you don't get line numbers!
主要好处是您从一开始就明确了方法的要求。这使得处理该代码的其他开发人员清楚地知道,调用者向您的方法发送空值确实是一个错误。
该检查还将在执行任何其他代码之前停止该方法的执行。这意味着您不必担心方法所做的修改未完成。
The main benefit is that you're being explicit with the requirements of your method right from the start. This makes it clear to other developers working on the code that it is truly an error for a caller to send a null value to your method.
The check will also halt the execution of the method before any other code executes. That means you won't have to worry about modifications being made by the method that are left unfinished.
原始代码:
重写为:
使用
nameof
重写的原因是它可以更轻松地重构。如果变量的名称s
发生变化,那么调试消息也会更新,而如果您只是硬编码变量的名称,那么随着时间的推移更新,它最终会过时。这是业界采用的良好做法。Original code:
Rewrite it as:
The reason to rewrite using
nameof
is that it allows for easier refactoring. If the name of your variables
ever changes, then the debugging messages will be updated as well, whereas if you just hardcode the name of the variable, then it will eventually be outdated when updates are made over time. It's a good practice used in the industry.因此,对于您的示例:
或:
或:
或三元:
So for your example:
Or:
Or:
Or ternary: