设计选择:我是否希望扩展方法在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
,但是如果collection
为null
,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
元素的“懒惰”部分。
首先:如果collection
为null
,我很确定会有NullReferenceException
。 所以你的部分问题根本没问题。 关于其余的:你通过检查null
并抛出一个不同的exception来获得任何东西吗? 在我看来:不! 因此,我不会使用完全没有帮助的检查来混乱我的代码。
如果collection参数为null
我将抛出NullReferenceException
。 这来自于考虑如果它为null
它将如何表现,并且Each
碰巧只是一个常规方法 – 抛出NullReferenceException
就是我期望发生的事情。
编辑 :根据马丁的评论和对此的一些进一步研究,我收回我说的话。 在这种情况下似乎不应抛出 NullReferenceException
,因为Microsoft建议使用ArgumentNullException
。