更令人困惑的是,微软两者都做,投掷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;
}
这个ArgumentNullException
从 .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
如果this
-parameter 为,则抛出 a null
。如果有人不恰当地调用我的扩展方法,他们可以自由地这样做,但也必须期待不恰当的行为(aNullReferenceException
而不是 an ArgumentNullException
)。但是,如果他们按预期调用该方法,他们也应该得到预期的行为:完全一致的体验。
//Instance method
string foo = null;
foo.Trim();
和
//Extension method
string foo = null;
foo.Right(10);
它们看起来相似,行为也应该相似,程序员甚至不需要知道它是实例还是扩展方法。