设计选择:我是否希望扩展方法在null上抛出exception?

可能重复:
C#:在扩展方法中validation“this”参数的最佳实践

我对设计选择感到矛盾,并希望听到SO社区的意见。 我在这里提出的例子只是一个可能的情况,必须进行这种设计选择 – 实际上,可能会有更多的情况。 对这个具体案例和更一般的方法都欢迎回答,并且对于如何在特定案例中做出决定的指导方针也表示赞赏。

基本上,我想知道如何考虑这个问题: 当编写一个本质上没有失败的扩展方法时,如果将null引用作为this实例传递,是否应该对参数执行null检查?

例:

我正在IEnumerable上编写一个扩展方法,它将遍历集合并执行一些Action – 基本上,这就是它的作用:

 public static void Each(this IEnumerable collection, Action action) { foreach (var t in collection) { action.Invoke(t); } } 

我无法决定的是,如果将null传递给任一参数,这个扩展方法应该做什么。 如果我不添加任何空检查,我将在action.Invoke(T)上获得NullReferenceException ,但是如果collectionnull ,for循环将默默地执行任何操作(即使action也为null ,也不会抛出任何exception。 )。

我决定添加一个null检查action ,所以我可以抛出ArgumentNullException而不是NullReferenceException 。 但是我想对这个collection做些什么呢?

选项1:添加空检查,并抛出ArgumentNullException

选项2:只是默默地让方法什么都不做。

哪个在将来我想使用该方法会更有用? 为什么?

如果在LINQ中调用的集合为空,则Microsoft抛出ArgumentNullException。 这实际上更多的是风格问题,但与扩展方法的行为方式一致。

@ m0sa是正确的,但你会得到你的foreach的空引用,但我会说检查顶部并抛出ArgumentNullException。 那样你就可以和LINQ一样。

例如,如果您在反编译器中查看Any(),您会看到:

 public static bool Any(this IEnumerable source) { if (source == null) { throw Error.ArgumentNull("source"); } using (IEnumerator enumerator = source.GetEnumerator()) { if (enumerator.MoveNext()) { return true; } } return false; } 

你绝对应该进行null检查并抛出ArgumentNullException以避免难以理解代码中的NullReferenceExceptions

一般来说,我会避免将null视为en“empty” IEnumerable 。 只需使用Enumerable.Empty()来避免空集合的特殊情况。

如果您决定在扩展方法中进行null检查,则应该考虑“热切”。 如果在扩展方法中使用yield return ,则在迭代开始之前,实际上不会对其进行评估。 您将扩展方法分为两部分。 检查参数的“急切”部分和yield return元素的“懒惰”部分。

首先:如果collectionnull ,我很确定会有NullReferenceException 。 所以你的部分问题根本没问题。 关于其余的:你通过检查null并抛出一个不同的exception来获得任何东西吗? 在我看来:不! 因此,我不会使用完全没有帮助的检查来混乱我的代码。

如果collection参数为null我将抛出NullReferenceException 。 这来自于考虑如果它为null它将如何表现,并且Each碰巧只是一个常规方法 – 抛出NullReferenceException就是我期望发生的事情。

编辑 :根据马丁的评论和对此的一些进一步研究,我收回我说的话。 在这种情况下似乎不应抛出 NullReferenceException ,因为Microsoft建议使用ArgumentNullException