在null实例(扩展方法不允许)的情况下调用扩展方法时,您认为最好的异常类型是什么?由于扩展方法只不过是静态方法,你可以说它应该是ArgumentNullException,但另一方面它们像实例方法一样使用,因此使用NullReferenceException可能更自然。我们来看下面的例子:
public static string ToInvariantString(this IFormattable value, string format)
{
return value.ToString(format, CultureInfo.InvariantCulture);
}
这样,如果value参数为null,则抛出NullReferenceException。
另一个例子是:
public static string ToInvariantString(this IFormattable value, string format)
{
if (value == null) throw new ArgumentNullException("value");
return value.ToString(format, CultureInfo.InvariantCulture);
}
修改 在一些答案中,您已经指出扩展方法可以像静态方法一样调用,在这种情况下,空引用异常会出错,这是一个很好的观点,实际上是我的一个问题,不知道为什么我忘了首先在问题中提到这一点。
有人还指出抛出NullReferenceException是错误的,是的,确实如此。这就是为什么我不扔它,我只是让它发生(让CLR抛出它)不守护方法。
我认为我赞成ArgumentNullException(这是我到目前为止所使用的)但我仍然认为至少有空间来反对NullReferenceException,因为在大多数方法中它似乎更自然使用
答案 0 :(得分:36)
通常,包括异常,您应该将扩展方法视为普通的静态方法。在这种情况下,您应该抛出ArgumentNullException。
由于一些原因,抛出NullReferenceException是个坏主意
请参阅When can you catch a StackOverflowException(我就此主题发表的一篇文章)。
答案 1 :(得分:25)
除了所有其他答案(这些都是好的)之外,我认为值得看一下Microsoft为了一致性所做的事情......并且Enumerable中的扩展方法都会抛出ArgumentNullException,就我所见。
答案 2 :(得分:7)
由于扩展方法可以在C#2.0中使用,并且可以像静态方法一样调用它们(你不必将它们用作扩展方法),你应该使用ArgumentNullException。
仅仅因为他们看起来类似于该类型的方法并不意味着他们是,或者总是被称为一个。
答案 3 :(得分:2)
ArgumentNullException。 no 要求调用扩展方法,就好像它们是实例方法一样。您可以将它们称为正常方法。在这种情况下,NullReferenceException完全不正确。
答案 4 :(得分:1)
从用户的角度来看,该方法看起来像一个实例方法,所以如果我是他们,我希望看到一个NullReferenceException。
那就是说,我建议在代码中明确地抛出一个或另一个,而不是像第一个例子那样“发生”抛出一个。
答案 5 :(得分:0)
为了增加混乱,Microsoft会同时抛出ArgumentNullExceptions
和NullReferenceExceptions
。
此示例引发隐式NullReferenceException
来自Roslyn(src \ Workspaces \ CSharp \ Portable \ Extensions \ StringExtensions.cs):
internal static class StringExtensions
{
public static string EscapeIdentifier(
this string identifier,
bool isQueryContext = false)
{
var nullIndex = identifier.IndexOf('\0');
if (nullIndex >= 0)
{
identifier = identifier.Substring(0, nullIndex);
}
var needsEscaping = SyntaxFacts.GetKeywordKind(identifier) != SyntaxKind.None;
// Check if we need to escape this contextual keyword
needsEscaping = needsEscaping || (isQueryContext && SyntaxFacts.IsQueryContextualKeyword(SyntaxFacts.GetContextualKeywordKind(identifier)));
return needsEscaping ? "@" + identifier : identifier;
}
此扩展方法从.NET Framework(System.Core / System / Linq / Enumerable.cs)中抛出ArgumentNullException
:
public static IEnumerable<TResult> Select<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult> selector) {
if (source == null) throw Error.ArgumentNull("source");
if (selector == null) throw Error.ArgumentNull("selector");
if (source is Iterator<TSource>) return ((Iterator<TSource>)source).Select(selector);
if (source is TSource[]) return new WhereSelectArrayIterator<TSource, TResult>((TSource[])source, null, selector);
if (source is List<TSource>) return new WhereSelectListIterator<TSource, TResult>((List<TSource>)source, null, selector);
return new WhereSelectEnumerableIterator<TSource, TResult>(source, null, selector);
}
如上面的评论中所述,我建议完全像实例方法一样实现扩展方法,因此如果NullReferenceException
参数为this
,则抛出null
。如果有人不恰当地调用我的扩展方法,他们可以自由地这样做,但也必须期待不适当的行为(NullReferenceException
而不是ArgumentNullException
)。但是,如果他们按预期方式调用该方法,则他们还应该获得预期的行为:始终如一的体验。
//Instance method
string foo = null;
foo.Trim();
和
//Extension method
string foo = null;
foo.Right(10);
它们看起来相似,它们的行为也应该相似,程序员甚至不需要知道它是实例还是扩展方法。