Проверьте, имеет ли IEnumerable меньше определенного количества элементов без какой-либо ненужной оценки?

Иногда я ожидаю определенный диапазон элементов и должен выполнить некоторую проверку, чтобы убедиться, что я в этом диапазоне. Самый очевидный способ сделать это - просто сравнить количество элементов в коллекции с диапазоном.

public static bool IsWithinRange<T>(this IEnumerable<T> enumerable, int max)
{
    return enumerable.Count() <= max;
}

Хотя, я понимаю, что метод linq Count() будет оценивать все перечисляемые перед возвратом результата. В идеале я бы только оценил минимальное количество предметов, чтобы получить результат.

Каким будет лучший способ обеспечить, чтобы перечислимое число было меньше определенного количества элементов без какой-либо ненужной оценки?

Ответ 1

Не используйте Count(), как вы уже знаете, вся коллекция должна быть пройдена в целом.

Вместо этого вы можете сделать это:

public static bool IsWithinRange<T>(this IEnumerable<T> enumerable, int max)
{
    return !enumerable.Skip(max).Any();
}

Обратите внимание, что вам все равно придется перечислить первые max элементы в коллекции, что неизбежно, если вы не попытаетесь сделать некоторые предположения относительно базовой коллекции.


Чтобы действительно оптимизировать это, вы можете проверить, является ли базовый тип ICollection<> или ICollection для доступа к свойству Count. Таким образом, вам не нужно перечислять элементы вообще. В противном случае резерв при перечислении элементов.

public static bool IsWithinRange<T>(this IEnumerable<T> enumerable, int max)
{
    var asCollection = enumerable as System.Collections.ICollection;
    if (asCollection != null) return asCollection.Count <= max;
    var asGenericCollection = enumerable as ICollection<T>;
    if (asGenericCollection != null) return asGenericCollection.Count <= max;
    return !enumerable.Skip(max).Any();
}

Конечно, это не совсем бесплатно, поскольку вы выполняете дополнительные проверки, но это может быть связано с необходимостью перечислить коллекцию, если это вообще возможно, особенно если max велико.

Ответ 2

Существует несколько способов сделать это. Возможно, самое простое:

public static bool IsWithinRange<T>(this IEnumerable<T> enumerable, int max)
{
    return enumerable.Take(max+1).Count() <= max;
}

Ответ 3

Вы можете объединить Count() с Take().

return enumerable.Take(max + 1).Count() <= max;