抛出ArgumentNullException
假设我有一个方法将某种对象作为参数。 现在说如果这个方法传递一个空参数,这是一个致命的错误,应该抛出一个exception。 是否值得我这样编码(请记住这是一个微不足道的例子):
void someMethod(SomeClass x) { if (x == null){ throw new ArgumentNullException("someMethod received a null argument!"); } x.doSomething(); }
或者是我安全的只是依靠它抛出NullException当它调用x.doSomething()?
其次,假设someMethod是一个构造函数,在另一个方法被调用之前x不会被使用。 我应该立即抛出exception还是等到x需要抛出exception呢?
我更喜欢NullReferenceException
的ArgumentNullException
,不检查参数将提供。 一般来说,我的首选是在试图调用一个潜在的空对象的方法之前总是检查无效。
如果这个方法是一个构造函数,那么它将取决于几个不同的因素:是否还有一个属性的公共setter,以及该对象实际上可能被使用的可能性。 如果有一个公共setter,那么不通过构造函数提供一个有效的实例将是合理的,不应该导致一个例外。
如果没有公共setter,并且可以在不引用注入对象的情况下使用包含对象,则可能希望将检查/exception推迟到尝试使用。 我认为一般的情况是,注入的对象对于实例的运行是必不可less的,因此一个ArgumentNullexception是完全合理的,因为实例不能没有它。
我总是遵循快速失败的做法。 如果你的方法依赖于X,并且你知道X可能在null中传递,那么检查它并立即抛出exception,而不是延长失败点。
2016更新:
真实世界的例子。 我强烈build议使用Jetbrains注释 。
[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));
C#6提供了操作符的nameof
Guard语句大大改进了。
我更喜欢明确的例外,因为这些原因:
- 如果该方法具有多个SomeClass参数,则可让您有机会说出它是哪一个(调用堆栈中的其他所有内容都可用)。
- 如果你在引用x之前做了一些可能有副作用的事情呢?
我同意快速失败的想法,不过明智地知道为什么快速失败是实际的。 考虑这个例子:
void someMethod(SomeClass x) { x.Property.doSomething(); }
如果你依赖NullReferenceException
来告诉你某些事情是错的,你怎么知道什么是空的? 堆栈跟踪只会给你一个行号,而不是哪个引用是空的。 在这个例子中, x
或者x.Property
可能都是空的,并且事先没有通过积极的检查来快速失败,你将不知道它是哪一个。
我更喜欢显式的ArgumentNullException的参数检查。
查看元数据:
// // 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);
你可以看到,这个string应该是参数的名字,也就是null,所以给开发者一个提示错误的提示。
尽早抛出ArgumentNullException更好。 如果抛出它,你可以提供比NullReferenceException更有用的信息。
如果您希望input不为空,则应显式抛出ArgumentNullException。 您可能想要编写一个名为Guard的类,为此提供辅助方法。 所以你的代码将是:
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(); }
NonNull方法将执行无效性检查,并在调用中指定的错误消息中抛出NullArgumentException。
1-如果你不想要空值,明确地做。 否则,当别人看你的代码时,他们会认为传递一个空值是可以接受的。
2-可以更快地做到这一点。 这样你就不会在没有假设的情况下传播一个Null的“错误”行为。
如果你在防守方面进行编程,你应该快速失败。 因此,请检查您的input,并在代码开始时输出错误。 你应该对你的调用者很好,给他们最可能的描述性错误信息。
我可能会因此而低估,但我认为完全不同。
那么遵循一个称为“永不传递null”的良好做法并除去丑陋的exception检查呢?
如果该参数是一个对象,请不要通过NULL。 另外,不要返回NULL。 你甚至可以使用Null对象模式来帮助。
如果是可选的,则使用默认值(如果您的语言支持它们)或创build超载。
比丑恶的例外更清洁。
您可以使用类似下面的语法来引发一个ArgumentNullException
exception,但也会将该exception作为其错误文本的一部分命名。 例如;
void SomeMethod(SomeObject someObject) { Throw.IfArgNull(() => someObject); //... do more stuff }
用来引发例外的类是;
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); } } }