IEnumerable пуст? - PullRequest
       16

IEnumerable пуст?

44 голосов
/ 23 сентября 2010

Я знаю, что это, вероятно, не имеет значения / влияет на производительность по большей части, но я ненавижу идею получить IEnumerable и делать .Count(). Есть ли IsEmpty или NotEmpty или какая-то функция? (аналогично stl empty ())

Ответы [ 9 ]

74 голосов
/ 23 сентября 2010

Вы хотите IEnumerable.Any () метод расширения (.Net Framework 3.5 и выше). Это позволяет избежать подсчета элементов.

15 голосов
/ 29 августа 2012

, если он не универсальный, чем что-то подобное

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

, если он универсальный, используйте расширение Enumerable, как уже было сказано

8 голосов
/ 23 сентября 2010

Без необходимости LINQ, вы можете сделать следующее:

bool IsEmpty(IEnumerable en)
{
    foreach(var c in en) { return false; }
    return true;
}
3 голосов
/ 23 сентября 2010

Вы можете использовать методы расширения, такие как 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>, а не ленивый IEnumerable<T>, по причинам, которые KeithS приводит в своем комментарии.

  • @ Bear Monkey отмечает, что создание экземпляра List может оказаться дорогостоящим в приведенном выше примере, если база данных возвращает большое количество записей. Это также верно, и в некоторых (возможно, редких) случаях может быть целесообразно игнорировать совет @ KeithS и возвращать перечисление с ленивой оценкой, если потребитель делает что-то не слишком трудоемкое (например, генерирует некоторые агрегированные значения).

2 голосов
/ 23 сентября 2010

Имейте в виду, что IEnumerable - это просто интерфейс.Реализация этого может сильно отличаться от класса к классу (рассмотрим пример Джо).Метод расширения IEnumerable.Any () должен быть универсальным и может не соответствовать вашему желанию (с точки зрения производительности).Yossarian предлагает средство, которое должно работать для многих классов, но если базовая реализация не использует «yield», вы все равно можете заплатить цену.

Обычно, если вы придерживаетесь коллекций или массивов, заключенных в интерфейс IEnumerableтогда Кристобалито и Йоссариан, вероятно, имеют лучшие ответы.Я предполагаю, что встроенный метод .Any () ext делает то, что рекомендует Йоссариан.

1 голос
/ 23 сентября 2010

Вкл. IEnumerable или IEnumerable<T>, №

Но это действительно не имеет особого смысла. Если коллекция пуста, и вы пытаетесь перебрать ее, используя IEnumerable, вызов IEnumerator.MoveNext() просто вернет false без затрат производительности.

0 голосов
/ 09 октября 2014

Используйте Enumerable.Empty (), который вместо IEnumerable.Any () не позволит получить нулевой список.

0 голосов
/ 03 июня 2011

Вы также можете написать свои собственные перегрузки метода расширения 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;
    }
0 голосов
/ 23 сентября 2010

Я так не думаю, для этого Count.Кроме того, что будет быстрее:

  1. Доступ к свойству и получение сохраненного Integer
  2. Доступ к свойству и получение сохраненного Boolean
...