IEnumerable是空的?

时间:2010-09-23 15:16:09

标签: .net linq ienumerable

我知道在大多数情况下它可能无关紧要/影响性能,但我讨厌获得IEnumerable和做.Count()的想法。有IsEmptyNotEmpty或某些功能吗? (类似于stl empty())

9 个答案:

答案 0 :(得分:74)

您需要IEnumerable.Any()扩展方法(.Net Framework 3.5及更高版本)。它避免了对元素的计数。

答案 1 :(得分:15)

如果它不是通用的,那么

enumeration.Cast<object>().Any();

如果它是通用的,请按照已经说过的那样使用Enumerable的扩展

答案 2 :(得分:8)

无需任何LINQ,您可以执行以下操作:

bool IsEmpty(IEnumerable en)
{
    foreach(var c in en) { return false; }
    return true;
}

答案 3 :(得分:3)

您可以使用Any()或Count()等扩展方法。 Count()比Any()更昂贵,因为它必须执行整个枚举,正如其他人所指出的那样。

但是在惰性评估的情况下(例如使用产量的方法),要么成本高昂。例如,使用以下IEnumerable实现,每次调用Any或Count都会产生新的数据包往返的成本:

IEnumerable<MyObject> GetMyObjects(...)
{
    using(IDbConnection connection = ...)
    {
         using(IDataReader reader = ...)
         {
             while(reader.Read())
             {
                 yield return GetMyObjectFromReader(reader);
             }
         }
    }
}

我认为道德是:

  • 如果您只有IEnumerable<T>,并且您想做的不仅仅是枚举它(例如使用Count或Any),那么请考虑先将其转换为List(扩展方法ToList)。这样你就可以保证只枚举一次。

  • 如果您正在设计一个返回集合的API,请考虑返回ICollection<T>(甚至IList<T>)而不是IEnumerable<T>,因为很多人似乎都建议这样做。通过这样做,您可以加强合同,以保证不进行惰性评估(因此无需进行多重评估)。

请注意我说你应该考虑返回一个集合,而不是总是返回一个集合。一如既往权衡取舍,可以从下面的评论中看出。

  • @KeithS认为你永远不应该屈服于DataReader,虽然我永远不会说永远,但我会说一般来说,数据访问层应该返回ICollection<T>而不是懒惰的建议由于KeithS在评论中给出的原因,对IEnumerable<T>进行了评估。

  • @Bear Monkey指出,如果数据库返回大量记录,在上面的示例中实例化List可能会很昂贵。这也是正确的,在一些(可能是罕见的)情况下,忽略@ KeithS的建议并返回惰性评估的枚举可能是适当的,前提是消费者正在做一些不太耗时的事情(例如生成一些聚合值)。

答案 4 :(得分:2)

请记住,IEnumerable只是一个界面。它背后的实现可能因类而异(考虑Joe的例子)。扩展方法IEnumerable.Any()必须是通用方法,可能不是您想要的(性能明智)。 Yossarian建议一种适用于许多类的方法,但如果底层实现不使用'yield',你仍然可以付出代价。

通常,如果您坚持使用IEnumerable接口中包含的集合或数组,那么Cristobalito和Yossarian可能会得到最好的答案。我的猜测是内置的.Any()ext方法做了约塞连推荐的方法。

答案 5 :(得分:1)

IEnumerableIEnumerable<T>上,否。

但它确实没有多大意义。如果某个集合为空,并且您尝试使用IEnumerable对其进行迭代,则对IEnumerator.MoveNext()的调用只会返回false,而无需支付任何性能费用。

答案 6 :(得分:0)

我不这么认为,这就是Count的用途。此外,什么会更快:

  1. 访问媒体资源并检索已存储的Integer
  2. 访问媒体资源并检索已存储的Boolean

答案 7 :(得分:0)

您也可以编写自己的Count扩展方法重载,如下所示:

    /// <summary>
    /// Count is at least the minimum specified.
    /// </summary>
    /// <typeparam name="TSource"></typeparam>
    /// <param name="source"></param>
    /// <param name="min"></param>
    /// <returns></returns>
    public static bool Count<TSource>(this IEnumerable<TSource> source, int min)
    {
        if (source == null)
        {
            throw new ArgumentNullException("source");
        }
        return source.Count(min, int.MaxValue);
    }

    /// <summary>
    /// Count is between the given min and max values
    /// </summary>
    /// <typeparam name="TSource"></typeparam>
    /// <param name="source"></param>
    /// <param name="min"></param>
    /// <param name="max"></param>
    /// <returns></returns>
    public static bool Count<TSource>(this IEnumerable<TSource> source, int min, int max)
    {
        if (source == null)
        {
            throw new ArgumentNullException("source");
        }
        if (min <= 0)
        {
            throw new ArgumentOutOfRangeException("min", "min must be a non-zero positive number");
        }
        if (max <= 0)
        {
            throw new ArgumentOutOfRangeException("max", "max must be a non-zero positive number");
        }
        if (min >= max)
            throw new ArgumentOutOfRangeException("min and max", "min must be lest than max");

        var isCollection = source as ICollection<TSource>;

        if (isCollection != null)
            return isCollection.Count >= min && isCollection.Count <= max;

        var count = 0;
        using (var enumerator = source.GetEnumerator())
        {
            while (enumerator.MoveNext())
            {
                count++;
                if (count >= min && count <= max)
                    return true;
            }
        }
        return false;
    }

答案 8 :(得分:-1)

使用Enumerable.Empty()而不是IEnumerable.Any()将阻止最终有空列表。