.net 扩展方法的ArgumentNullException或NullReferenceException? 当NULL实例调用扩展方法(扩展方法不允许它)时,您认为什么是最佳抛出类型?由于扩展方法只不过是静态方法,您可以认为它应该是ArgumentNullException,但另一方面,它们的使用方式与实例方法类似,因此使用NullReferenceException可能更自然。让我们以下面的例子为例: public static string ToInvariantString(this IFormattable value, string format) { return value.ToString(format, CultureInfo.InvariantCulture); }

.net 扩展方法的ArgumentNullException或NullReferenceException? 当NULL实例调用扩展方法(扩展方法不允许它)时,您认为什么是最佳抛出类型?由于扩展方法只不过是静态方法,您可以认为它应该是ArgumentNullException,但另一方面,它们的使用方式与实例方法类似,因此使用NullReferenceException可能更自然。让我们以下面的例子为例: public static string ToInvariantString(this IFormattable value, string format) { return value.ToString(format, CultureInfo.InvariantCulture); },.net,exception,extension-methods,.net,Exception,Extension Methods,这样,如果value参数为null,将引发NullReferenceException 另一个例子是: public static string ToInvariantString(this IFormattable value, string format) { if (value == null) throw new ArgumentNullException("value"); return value.ToString(format, CultureInfo.Invari

这样,如果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的空间,因为在大多数使用该方法的地方,它看起来更自然。

从用户的角度看,该方法看起来和行为都像一个实例方法,因此,如果我是他们,我希望看到一个NullReferenceException


也就是说,我建议在代码中显式地抛出一个或另一个,而不是像第一个示例中那样“碰巧”抛出一个。不需要像调用实例方法那样调用扩展方法。您可以像调用普通方法一样调用它们。在这种情况下,NullReferenceException将完全不正确

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


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

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

出于以下几个原因,在这里抛出NullReferenceException是个坏主意


  • 实际上并没有出现空引用,所以看到空引用是违反直觉的
  • 引发NullReferenceException并导致NullReferenceException发生会产生明显不同的异常(查看差异的一种方法是错误代码)。CLR引发的许多异常都是如此
看(我在这个主题上写的帖子)

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

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

更让人困惑的是,Microsoft会同时抛出
ArgumentNullException
NullReferenceException

这个抛出隐式
NullReferenceException
的示例来自Roslyn(src\Workspaces\CSharp\Portable\Extensions\StringExtensions.cs):

此扩展方法从.NET Framework(System.Core/System/Linq/Enumerable.cs)抛出
ArgumentNullException


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

是的,我曾想过将NullReferenceException显式地抛出给,但我的感觉是,它实际上应该留给编译器,但你很可能是对的。从用户的角度来看,扩展方法既可以通过扩展调用,也可以作为静态方法调用。我同意mqp,也不同意JaredPar:从用户的角度来看,扩展方法永远不应该通过静态接口调用,它的行为应该与实例方法完全相同。因此,我建议抛出一个不带任何参数名的NullReferenceException(您自己)。在扩展方法中不允许(第一个参数的)其他错误处理-尽管这很酷,并且经常出现,例如在对未初始化的字符串调用.Left(..)扩展方法时返回空字符串,但是完全出乎意料,不透明。评论很棒,正如我在编辑文章时提到的,这就是我没有显式抛出NullReferenceException的原因,我仍然让CLR抛出它。“实际上没有发生null引用”。是的,的确如此;您传递了一个空引用,该方法试图取消引用。@piedar该方法试图取消引用,而不是运行时@OhadSchneider实际上也没有。一个
NullReferenceException
实际上表示一个空的解引用。调整你的术语并指出一点。不过,归根结底,这取决于您是想将扩展方法视为实例还是静态方法。如果最佳实践是将它们视为实例(当然不是这样),抛出
NullReferenceException
将是最直观的,“技术上”正确与否。好主意,我自己有过,没有做过,不知道为什么!;-)这是我经常使用的一种有用的技巧。然而,我们应该始终记住,仅仅因为FCL使用特定的方法来实现某些东西,并不意味着该方法是最优的或正确的。我建议将FCL方法看作是如何实现某些目标的建议,但给定方法的优点应该是:
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;
    }
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);


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