为了增加混淆,微软同时抛出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;
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);
}
如上评论所述,我建议将扩展方法实现为实例方法,并在
this
参数为
null
时抛出
NullReferenceException
。如果有人不当地调用我的扩展方法,他们可以这样做,但也必须预期不当行为(而不是
ArgumentNullException
的
NullReferenceException
)。但是,如果他们按照预期调用该方法,他们也应该获得预期的行为:一致的体验。
//Instance method
string foo = null;
foo.Trim();
和
//Extension method
string foo = null;
foo.Right(10);
它们看起来很相似,应该表现得相似,程序员甚至不需要知道它是实例方法还是扩展方法。
NullReferenceException
实际上表示空的引用。调整您的术语并指出一点。不过,最终问题在于您是否希望将扩展方法视为实例或静态。如果最佳实践是将它们视为实例(我认为不是),则抛出NullReferenceException
将是最直观的,“技术上”正确或不正确。 - snarf