可能重复:
C#: Best practice for validating “this” argument in extension methods
我对设计选择感到矛盾,并希望听到SO社区的意见。我在这里提出的例子只是一个可能的情况,必须进行这种设计选择 - 实际上,可能会有更多的情况。对这个具体案例和更一般的方法都欢迎回答,并且对如何在特定案例中做出决定的指导方针也表示赞赏。
基本上,我想知道如何考虑这个问题:如果将空引用作为this
实例传递,编写一个本质上不会失败的扩展方法时,应该执行空检查关于论证与否?
示例:的
我正在IEnumerable<T>
上编写一个扩展方法,它将遍历集合并执行一些Action<T>
- 基本上,这就是它所做的:
public static void Each<T>(this IEnumerable<T> collection, Action<T> action)
{
foreach (var t in collection)
{
action.Invoke(t);
}
}
我无法决定的是,如果将null
传递给任一参数,此扩展方法应该做什么。如果我不添加任何空检查,我将在NullReferenceException
上获得action.Invoke(T)
,但如果collection
为null
,则for循环将默默无效(并且没有例外)即使action
也是null
...),也会被抛出。
我决定为action
添加空检查,因此我可以抛出ArgumentNullException
而不是NullReferenceException
。但是我想对collection
做什么?
选项1:添加空检查,然后抛出ArgumentNullException
。
选项2:只是默默地让方法无效。
在将来我想使用该方法的哪个位置会更有用?为什么呢?
答案 0 :(得分:12)
如果在LINQ中调用的集合为空,则Microsoft抛出ArgumentNullException。这实际上更多的是风格问题,但与扩展方法的行为方式一致。
@ m0sa是正确的,但你会得到你的foreach的空引用,但我会说检查顶部并抛出ArgumentNullException。这样你就可以和LINQ一样。
例如,如果您在反编译器中查看Any(),您会看到:
public static bool Any<TSource>(this IEnumerable<TSource> source)
{
if (source == null)
{
throw Error.ArgumentNull("source");
}
using (IEnumerator<TSource> enumerator = source.GetEnumerator())
{
if (enumerator.MoveNext())
{
return true;
}
}
return false;
}
答案 1 :(得分:2)
您绝对应该null
进行检查并抛出ArgumentNullException
以避免在代码中难以理解NullReferenceExceptions
。
一般情况下,我会避免将null
视为“空”IEnumerable<T>
。只需使用Enumerable.Empty<T>()
来避免null
集合的特殊情况。
如果你决定null
检查你的扩展方法,你应该考虑“热切地”这样做。如果在扩展方法中使用yield return
,则在迭代开始之前,实际上不会对其进行评估。您将扩展方法分为两部分。支持参数的“急切”部分和yield return
元素的“懒惰”。
答案 2 :(得分:0)
首先:如果NullReferenceException
为collection
,我确信会有null
。所以你的部分问题根本没问题。关于其余部分:你通过检查null
并抛出不同的例外来获得任何收益吗?在我看来:不!因此,我不会使用完全没有帮助的检查来混乱我的代码。
答案 3 :(得分:0)
如果集合参数为null
,我会抛出NullReferenceException
。这来自于考虑如果它是null
它将如何表现,并且Each<T>
碰巧只是一个常规方法 - 被抛出的NullReferenceException
就是我期望发生的事情。 / p>
修改强>:
根据马丁的评论和对此的进一步研究,我收回了我所说的内容。似乎是NullReferenceException
shouldn't be thrown in this case,Microsoft recommends using an ArgumentNullException
instead。