简体   繁体   English

抛出 ArgumentNullException

[英]Throwing ArgumentNullException

Suppose I have a method that takes an object of some kind as an argument.假设我有一个方法将某种类型的 object 作为参数。 Now say that if this method is passed a null argument, it's a fatal error and an exception should be thrown.现在说如果这个方法传递了一个 null 参数,这是一个致命错误,应该抛出一个异常。 Is it worth it for me to code something like this (keeping in mind this is a trivial example):编写这样的代码对我来说是否值得(记住这是一个微不足道的例子):

void someMethod(SomeClass x)
{
    if (x == null){
        throw new ArgumentNullException("someMethod received a null argument!");
    }

    x.doSomething();
}

Or is it safe for me to just rely on it throwing NullException when it calls x.doSomething()?还是仅依靠它在调用 x.doSomething() 时抛出 NullException 对我来说是否安全?

Secondly, suppose that someMethod is a constructor and x won't be used until another method is called.其次,假设 someMethod 是一个构造函数,并且在调用另一个方法之前不会使用 x。 Should I throw the exception immediately or wait until x is needed and throw the exception then?我应该立即抛出异常还是等到需要 x 后再抛出异常?

I prefer the ArgumentNullException over the NullReferenceException that not checking the argument would provide.我更喜欢ArgumentNullException而不是NullReferenceException不检查参数会提供。 In general, my preference is to always check for nullity before trying to invoke a method on a potentially null object.通常,我的偏好是在尝试调用可能为空的对象上的方法之前始终检查空性。

If the method is a constructor, then it would depend on a couple of different factors: is there also a public setter for the property and how likely is it that the object will actually be used.如果该方法是一个构造函数,那么它将取决于几个不同的因素:是否还有该属性的公共设置器以及该对象实际被使用的可能性有多大。 If there is a public setter, then not providing a valid instance via the constructor would be reasonable and should not result in an exception.如果有公共 setter,那么不通过构造函数提供有效实例是合理的,不应导致异常。

If there is no public setter and it is possible to use the containing object without referencing the injected object, you may want to defer the checking/exception until its use is attempted.如果没有公共 setter 并且可以在不引用注入对象的情况下使用包含对象,则您可能希望推迟检查/异常,直到尝试使用它。 I would think that the general case, though, would be that injected object is essential to the functioning of the instance and thus an ArgumentNull exception is perfectly reasonable since the instance can't function without it.不过,我认为一般情况下,注入的对象对于实例的运行至关重要,因此 ArgumentNull 异常是完全合理的,因为没有它,实例就无法运行。

I always follow the practice of fail fast .我总是遵循快速失败的做法。 If your method is dependent on X and you understand X might be passed in null, null check it and raise the exception immediately instead of prolonging the point of failure.如果您的方法依赖于 X 并且您知道 X 可能以 null 形式传递,则 null 检查它并立即引发异常而不是延长故障点。

2016 update: 2016年更新:

Real world example.现实世界的例子。 I strongly recommend the usage of JetBrains Annotations .我强烈推荐使用JetBrains Annotations

[Pure]
public static object Call([NotNull] Type declaringType, 
                          [NotNull] string methodName, 
                          [CanBeNull] object instance)
{
    if (declaringType == null) throw new ArgumentNullException(nameof(declaringType));
    if (methodName == null) throw new ArgumentNullException(nameof(methodName));

Guard statements have been vastly improved with C# 6 providing the nameof operator. C# 6 提供了nameof运算符,Guard 语句得到了极大的改进。

I prefer the explicit exception, for these reasons:我更喜欢显式例外,原因如下:

  • If the method has more than one SomeClass argument it gives you the opportunity to say which one it is (everything else is available in the call stack).如果该方法有多个 SomeClass 参数,则它让您有机会说出它是哪一个(其他所有内容都在调用堆栈中可用)。
  • What if you do something that may have a side effect before referencing x?如果你在引用 x 之前做了一些可能有副作用的事情怎么办?

I agree with the idea of failing fast - however it is wise to know why failing fast is practical.我同意快速失败的想法 - 但是知道为什么快速失败是可行的是明智的。 Consider this example:考虑这个例子:

void someMethod(SomeClass x)
{       
    x.Property.doSomething();
}

If you rely on the NullReferenceException to tell you that something was wrong, how will you know what was null?如果您依靠NullReferenceException来告诉您出了什么问题,您怎么知道什么是 null? The stack trace will only give you a line number, not which reference was null.堆栈跟踪只会给你一个行号,而不是哪个引用为空。 In this example x or x.Property could both have been null and without failing fast with aggressive checking beforehand, you will not know which it is.在此示例中, xx.Property可能都为空,并且在事先进行积极检查的情况下不会快速失败,您将不知道它是哪个。

I'd prefer the parameter check with the explicit ArgumentNullException, too.我也更喜欢使用显式 ArgumentNullException 进行参数检查。

Looking at the metadata:查看元数据:

 //
    // Summary:
    //     Initializes a new instance of the System.ArgumentNullException class with
    //     the name of the parameter that causes this exception.
    //
    // Parameters:
    //   paramName:
    //     The name of the parameter that caused the exception.
    public ArgumentNullException(string paramName);

You can see, that the string should be the name of the parameter, that is null, and so give the developer a hint on what is going wrong.您可以看到,该字符串应该是参数的名称,即空值,因此可以提示开发人员出了什么问题。

No excuse not to make the check these days.这些天没有理由不进行检查。 C# has moved on and you can do this very neatly using a discard and a null coalescing operator: C# 已经向前发展,您可以使用丢弃和空合并运算符非常巧妙地做到这一点:

_ = declaringType ?? throw new ArgumentNullException(nameof(declaringType));
_ = methodname ?? throw new ArgumentNullException(nameof(methodName));

You should explicitly throw an ArgumentNullException if you are expecting the input to not be null.如果您希望输入不为空,则应明确抛出 ArgumentNullException。 You might want to write a class called Guard that provides helper methods for this.您可能想要编写一个名为 Guard 的类,它为此提供辅助方法。 So your code will be:所以你的代码将是:

void someMethod(SomeClass x, SomeClass y)
{
    Guard.NotNull(x,"x","someMethod received a null x argument!");
    Guard.NotNull(y,"y","someMethod received a null y argument!");


    x.doSomething();
    y.doSomething();
}

The NonNull method would do the nullity check and throw a NullArgumentException with the error message specified in the call. NonNull 方法将执行无效检查并抛出 NullArgumentException 和调用中指定的错误消息。

It is better to throw the ArgumentNullException sooner rather than later.最好尽早抛出 ArgumentNullException。 If you throw it, you can provide more helpful information on the problem than a NullReferenceException.如果你抛出它,你可以提供比 NullReferenceException 更有用的问题信息。

  1. Do it explicitly if you do not want a Null value.如果您不想要 Null 值,请明确执行此操作。 Otherwise, when someone else look at your code, they will think that passing a Null value is acceptable.否则,当其他人查看您的代码时,他们会认为传递 Null 值是可以接受的。

  2. Do it as early as you can.尽早做。 This way, you do not propagate the "wrong" behavior of having a Null when it's not supposed to.这样,您就不会在不应该传播 Null 时传播“错误”行为。

If you program defensively you should fail fast.如果你进行防御性编程,你应该很快就会失败。 So check your inputs and error out at the beginning of your code.因此,请在代码开头检查您的输入和错误。 You should be nice to your caller and give them the most descriptive error message you can.你应该对你的来电者友好,并尽可能给他们最描述性的错误信息。

I'll probably be downvoted for this, but I think completely different.我可能会因此被否决,但我认为完全不同。

What about following a good practice called "never pass null" and remove the ugly exception checking?遵循称为“从不传递空值”的良好实践并删除丑陋的异常检查怎么样?

If the parameter is an object, DO NOT PASS NULL.如果参数是一个对象,请勿传递 NULL。 Also, DO NOT RETURN NULL.另外,不要返回 NULL。 You can even use the Null object pattern to help with that.您甚至可以使用 Null 对象模式来帮助解决这个问题。

If it's optional, use default values (if your language supports them) or create an overload.如果它是可选的,请使用默认值(如果您的语言支持它们)或创建一个重载。

Much cleaner than ugly exceptions.比丑陋的例外要干净得多。

You can use syntax like the following to not just throw an ArgumentNullException but have that exception name the parameter as part of its error text as well.您可以使用如下语法不仅抛出ArgumentNullException异常,还可以将该异常命名为参数作为其错误文本的一部分。 Eg;例如;

void SomeMethod(SomeObject someObject)
{
    Throw.IfArgNull(() => someObject);
    //... do more stuff
}

The class used to raise the exception is;用于引发异常的类是;

public static class Throw
{
    public static void IfArgNull<T>(Expression<Func<T>> arg)
    {
        if (arg == null)
        {
            throw new ArgumentNullException(nameof(arg), "There is no expression with which to test the object's value.");
        }

        // get the variable name of the argument
        MemberExpression metaData = arg.Body as MemberExpression;
        if (metaData == null)
        {
            throw new ArgumentException("Unable to retrieve the name of the object being tested.", nameof(arg));
        }

        // can the data type be null at all
        string argName = metaData.Member.Name;
        Type type = typeof(T);
        if (type.IsValueType && Nullable.GetUnderlyingType(type) == null)
        {
            throw new ArgumentException("The expression does not specify a nullible type.", argName);
        }

        // get the value and check for null
        if (arg.Compile()() == null)
        {
            throw new ArgumentNullException(argName);
        }
    }
}

Since .NET 6 you can throw the argument null exception in one line for instance:由于 .NET 6 你可以在一行中抛出参数 null 异常,例如:

int? foo = null;
ArgumentNullException.ThrowIfNull(foo);

This will check if foo is null and throw an error.这将检查foo是否为 null 并抛出错误。 You can pass a second parameter to set the parameter name, but this is not recommended.您可以传递第二个参数来设置参数名称,但不建议这样做。

https://learn.microsoft.com/en-us/do.net/api/system.argumentnullexception.throwifnull?view.net-6.0 https://learn.microsoft.com/en-us/do.net/api/system.argumentnullexception.throwifnull?view.net-6.0

All code examples use error prone IF clause, where one use equal operator == .所有代码示例都使用容易出错的 IF 子句,其中一个使用相等运算符==

What happen, if type override == ?如果类型覆盖==会发生什么?

On C# 7 and greater, use constant pattern matching.在 C# 7 及更高版本上,使用常量模式匹配。

example:例子:

if (something is null) 
{
    throw new ArgumentNullException(nameof(something), "Can't be null.");
}

I strongly agree with @tvanfosson.我非常同意@tvanfosson。 Adding to his answer, with .net 6 it's very easy to throw the ArgumentNullException .添加到他的答案中,使用 .net 6 很容易抛出ArgumentNullException

ArgumentNullException.ThrowIfNull(object);

Here's the official documentation. 这是官方文档。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM