扩展方法中的 ArgumentNullException 或 NullReferenceException?

发布于 2024-10-20 03:10:23 字数 967 浏览 5 评论 0原文

在空实例上调用扩展方法时(扩展方法不允许),您认为抛出的最佳异常类型是什么?由于扩展方法只不过是静态方法,您可能会认为它应该是 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,因为在大多数要使用该方法的地方它似乎更自然。

What would you consider to be the best exception type to throw when an extension method is called on a null instance (where the extension method does not allow it)? Since extension methods are nothing but static methods you could argue that it should be ArgumentNullException, but on the other hand they're used like instance methods so it might be more natural to use the NullReferenceException. Let's take the following example:

public static string ToInvariantString(this IFormattable value, string format)
{
    return value.ToString(format, CultureInfo.InvariantCulture);
}

This way a NullReferenceException will be thrown if the value parameter is null.

The other example would be:

public static string ToInvariantString(this IFormattable value, string format)
{
    if (value == null) throw new ArgumentNullException("value");
    return value.ToString(format, CultureInfo.InvariantCulture);
}

EDIT:
In some of the answers you have pointed out that an extension methods can be called like a static method and in those cases a null reference exception would be wrong, which is a great point, and actually one of my concerns, not sure why I forgot to mention that in the question in the first place.

Someone also pointed out that it's wrong to throw a NullReferenceException, and yes, it is. That's why I don't throw it, I just let it happen (let the CLR throw it) by not guarding the method.

I think I favor the ArgumentNullException (that's what I've use so far) but I still think there is at least room to argue for an against the NullReferenceException since it seems more natural in most places where the method is going to be used.

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

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

发布评论

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

评论(6

嗼ふ静 2024-10-27 03:10:23

一般来说,包括例外情况,您应该将扩展方法视为普通的静态方法。在这种情况下,您应该抛出 ArgumentNullException。

出于多种原因,在这里抛出 NullReferenceException 是一个坏主意

  • 空引用实际上并未发生,因此看到一个 NullReferenceException 是违反直觉的
  • 抛出 NullReferenceException 并导致 NullReferenceException 发生会产生明显不同的异常(查看差异的一种方法是错误代码)。 CLR 引发的许多异常都是如此。

请参阅何时可以捕获 StackOverflowException (我就这个主题发表过一篇文章)。

  • 调用扩展方法就像调用常规方法一样是完全合法的。在这种情况下,我当然不会排除 NullReferenceException,而是排除 ArgumentNullException。

In general, exceptions included, you should treat an extension method as if it were a normal static method. In this case you should throw an ArgumentNullException.

Throwing a NullReferenceException here is a bad idea for a few reasons

  • A null reference did not actually occur so seeing one is counterintuitive
  • Throwing a NullReferenceException and causing a NullReferenceException to occur produce discernably different exceptions (One way to see the difference is the error code). This is true of many exceptions that are thrown by the CLR.

See When can you catch a StackOverflowException (a post I did on this subject).

  • It's perfectly legal to call an extension method just as if it were a regular method. In that case I would certainly not except a NullReferenceException, but instead an ArgumentNullException.
作业与我同在 2024-10-27 03:10:23

除了所有其他答案(都很好)之外,我认为为了一致性,值得看看微软所做的事情......并且据我所知,Enumerable 中的扩展方法都抛出 ArgumentNullException 。

Aside from all the other answers (which are good) I think it's worth looking at what Microsoft does for the sake of consistency... and the extension methods in Enumerable all throw ArgumentNullException as far as I can see.

怂人 2024-10-27 03:10:23

由于扩展方法可以在 C# 2.0 中使用,并且可以像静态方法一样调用它们(您不必将它们用作扩展方法),因此您应该使用 ArgumentNullException。

仅仅因为它们看起来类似于类型上的方法,并不意味着它们就是这样,或者总是像这样被调用。

Since extension methods can be used in C# 2.0, and they can be called just like static methods (you do not HAVE to use them as extension methods), you should use ArgumentNullException.

Just because they look like methods on the type doesn't mean that they are, or are always called like one.

慵挽 2024-10-27 03:10:23

从用户的角度来看,该方法的外观和行为都类似于实例方法,因此如果我是他们,我会期望看到 NullReferenceException。

也就是说,我建议在代码中显式地抛出一个或另一个,而不是像第一个示例中那样“碰巧”抛出一个。

From the user's standpoint, the method looks and acts like an instance method, so if I were them, I would expect to see a NullReferenceException.

That said, I would suggest throwing either one or the other explicitly in the code, instead of just "happening" to throw one as in your first example.

三月梨花 2024-10-27 03:10:23

参数空异常。不需要像调用实例方法一样调用扩展方法。您可以像调用普通方法一样调用它们。在这种情况下,NullReferenceException 将是完全错误的。

ArgumentNullException. There is no requirement to call extension methods as though they were instance methods. You can call them as if they were normal methods. NullReferenceException would be completely incorrect in that case.

空心空情空意 2024-10-27 03:10:23

更让人困惑的是,微软同时抛出了 ArgumentNullExceptionsNullReferenceExceptions

这个抛出隐式 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):

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 >此-参数为null。如果有人不恰当地调用我的扩展方法,他们可以随意这样做,但也必须预料到不恰当的行为(NullReferenceException 而不是 ArgumentNullException)。但是,如果他们按预期调用该方法,他们也应该获得预期的行为:完全一致的体验。

//Instance method
string foo = null;
foo.Trim();

它们

//Extension method
string foo = null;
foo.Right(10); 

看起来很相似,行为也应该相似,程序员甚至不需要知道它是实例还是扩展方法。

To add to the confusion, Microsoft does both, throwing ArgumentNullExceptions as well as NullReferenceExceptions.

This example that throws an implicite NullReferenceException is from 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;
    }

This extension method that throws an ArgumentNullException from the .NET Framework (System.Core/System/Linq/Enumerable.cs):

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);


   }

As mentioned in a comment above, I would recommend to implement extension methods exactly as instance methods and therefore throw a NullReferenceException if the this-parameter is null. If somebody calls my extension methods inappropriately they are free to do so but also have to expect inappropriate behavior (a NullReferenceException instead of an ArgumentNullException). But if they call the method as it is intended to, they should also get the expected behavior: a through out consistent experience.

//Instance method
string foo = null;
foo.Trim();

and

//Extension method
string foo = null;
foo.Right(10); 

They look alike and they should behave alike and the programmer should never even need to know whether it is an instance or an extension method.

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