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

时间:2011-05-31 21:01:08

标签: c# null extension-methods

  

可能重复:
  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),但如果collectionnull,则for循环将默默无效(并且没有例外)即使action也是null ...),也会被抛出。

我决定为action添加空检查,因此我可以抛出ArgumentNullException而不是NullReferenceException。但是我想对collection做什么?

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

选项2:只是默默地让方法无效。

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

4 个答案:

答案 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)

首先:如果NullReferenceExceptioncollection,我确信会有null。所以你的部分问题根本没问题。关于其余部分:你通过检查null并抛出不同的例外来获得任何收益吗?在我看来:不!因此,我不会使用完全没有帮助的检查来混乱我的代码。

答案 3 :(得分:0)

如果集合参数为null,我会抛出NullReferenceException。这来自于考虑如果它是null它将如何表现,并且Each<T>碰巧只是一个常规方法 - 被抛出的NullReferenceException就是我期望发生的事情。 / p>

修改: 根据马丁的评论和对此的进一步研究,我收回了我所说的内容。似乎是NullReferenceException shouldn't be thrown in this caseMicrosoft recommends using an ArgumentNullException instead