Я знаю, что это, вероятно, не имеет значения/влияет на производительность по большей части, но я ненавижу идею получения IEnumerable
и выполнения .Count()
. Есть ли IsEmpty
или NotEmpty
или некоторая функция? (аналогично stl empty())
IEnumerable пуст?
Ответ 1
Вы хотите метод расширения IEnumerable.Any() (.Net Framework 3.5 и выше). Это позволяет избежать подсчета элементов.
Ответ 2
если он не является общим, чем smth, подобным этому
enumeration.Cast<object>().Any();
если он является общим, используйте расширение Enumerable, как уже было сказано
Ответ 3
Без LINQ, вы можете сделать следующее:
bool IsEmpty(IEnumerable en)
{
foreach(var c in en) { return false; }
return true;
}
Ответ 4
Вы можете использовать методы расширения, такие как 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), рассмотрите сначала преобразование его в список (метод расширения ToList). Таким образом, вы гарантируете, что будете перечислять только один раз. -
Если вы разрабатываете API, который возвращает коллекцию, подумайте о возврате
ICollection<T>
(или дажеIList<T>
), а неIEnumerable<T>
, как многие рекомендуют. Поступая таким образом, вы укрепляете свой контракт, чтобы гарантировать отсутствие ленивой оценки (и, следовательно, не многократную оценку).
Обратите внимание, что я говорю, что вам следует рассмотреть вопрос о возврате коллекции, а не всегда возвращать коллекцию. Как всегда есть компромиссы, как видно из комментариев ниже.
-
@KeithS думает, что вы никогда не должны уступать DataReader, и, хотя я никогда не говорю никогда, я бы сказал, что в целом разумный совет заключается в том, что уровень доступа к данным должен возвращать
ICollection<T>
, а не lazy-оцениваемыйIEnumerable<T>
, по причинам, которые KeithS дает в своем комментарии. -
@Bear Monkey отмечает, что создание экземпляра списка может быть дорогостоящим в приведенном выше примере, если база данных возвращает большое количество записей. Это тоже верно, и в некоторых (возможно, редких) случаях может быть уместно игнорировать совет @KeithS и возвращать lazy-rated перечисление, если потребитель делает что-то, что не слишком трудоемко (например, генерирование некоторых совокупных значений).
Ответ 5
Имейте в виду, что IEnumerable - это просто интерфейс. Реализация за ним может сильно отличаться от класса к классу (рассмотрим пример Джо). Метод расширения IEnumerable.Any() должен быть общим подходом и может быть не тем, что вы хотите (с точки зрения производительности). Йоссариан предлагает средства, которые должны работать для многих классов, но если базовая реализация не использует "доходность", вы все равно можете заплатить цену.
Как правило, если вы придерживаетесь коллекций или массивов, завернутых в интерфейс IEnumerable, то, вероятно, лучшие ответы будут у Кристобалли и Йоссариана. Я предполагаю, что встроенный метод .Any() ext делает то, что рекомендует Йоссариан.
Ответ 6
Вкл. IEnumerable
или IEnumerable<T>
, нет.
Но это действительно не имеет большого смысла. Если коллекция пуста, и вы пытаетесь ее перебрать с помощью IEnumerable
, вызов IEnumerator.MoveNext()
будет просто возвращать значение false без каких-либо затрат.
Ответ 7
Я так не думаю, что для Count
. Кроме того, что будет быстрее:
- Доступ к собственности и извлечение сохраненного
Integer
- Доступ к собственности и извлечение сохраненного
Boolean
Ответ 8
Вы также можете написать свои собственные методы перерасчета расширений так:
/// <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;
}
Ответ 9
Использовать Enumerable.Empty(), который вместо IEnumerable.Any() не позволит завершить нулевой список.