Редактировать (исправлено): В этом ответе рассматривается проблема выполнения запроса дважды, которая, как я считаю, является ключевой.Ниже показано, почему:
Умнее Any()
- это то, что могут делать только разработчики Linq, IMO ... Или это было бы грязное приключение с использованием отражения.
Использование класса, как показанониже вы можете кэшировать вывод исходного перечислимого и позволить его перечислить дважды:
public class CachedEnumerable<T>
{
public CachedEnumerable(IEnumerable<T> enumerable)
{
_source = enumerable.GetEnumerator();
}
public IEnumerable<T> Enumerate()
{
int itemIndex = 0;
while (true)
{
if (itemIndex < _cache.Count)
{
yield return _cache[itemIndex];
itemIndex++;
continue;
}
if (!_source.MoveNext())
yield break;
var current = _source.Current;
_cache.Add(current);
yield return current;
itemIndex++;
}
}
private List<T> _cache = new List<T>();
private IEnumerator<T> _source;
}
Таким образом, вы сохраняете ленивый аспект LINQ, сохраняете код читабельным и универсальным.Это будет медленнее, чем напрямую, используя IEnumerator<>
.Существует множество возможностей для расширения и оптимизации этого класса, таких как политика удаления старых элементов, избавления от сопрограммы и т. Д. Но я думаю, что это не относится к этому вопросу.
О, икласс не является потокобезопасным, как сейчас.Об этом не спрашивали, но я могу представить, как люди пытаются.Я думаю, что это можно было бы легко добавить, если бы перечисляемый источник не имел привязки к потоку ..
Почему это было бы оптимальным?
Давайте рассмотрим два варианта: перечисление можетСодержать элементы или нет.
- Если он содержит элементы, этот подход является оптимальным, поскольку запрос выполняется только один раз.
- Если он не содержит элементов, у вас возникнет соблазн исключить часть ваших запросов OrderBy и Select, поскольку они не добавляют значения.Но ... если после предложения
Where()
есть ноль элементов, то есть ноль элементов для сортировки, что будет стоить ноль времени (ну, почти).То же самое относится и к предложению Select()
.
Что, если это еще не достаточно быстро? В этом случае моей стратегией было бы обойти Linq.Теперь я действительно люблю linq, но его элегантность имеет свою цену.Таким образом, на каждые 100 раз использования Linq обычно требуется одно или два вычисления, которые важны для очень быстрого выполнения, которое я пишу со старым добрым значением для циклов и списков.Частью освоения технологии является признание того, где она не подходит.Linq не является исключением из этого правила.