.NET 6: Улучшения в LINQ. Начало 1/2
Продолжаем изучать, что нас ждёт в релизе .NET 6. На этот раз про LINQ.
Индексные операции в IEnumerable<T>
Первоначально
IList<T> отличался от IEnumerable<T> наличием обращения по индексу. Идея заключалась в том, что только коллекции, поддерживающие быстрые операции с индексами ~O(1), могут реализовывать IList<T>. Теоретически индексные операции над IEnumerable<T> не поощрялись, поскольку считались медленными.С появлением LINQ многие из этих предположений позабылись. Методы расширения, такие как
Enumerable.Count() и Enumerable.ElementAt() доступны на любой коллекций, даже если в реальности они выполняются за O(N) время. Новые методы расширения для IEnumerable (ElementAt, ElementAtOrDefault и Take) продолжают этот тренд, позволяя использовать индексы и диапазоны:var elements = source.Take(range: 10..^10);Операции подсчёта в IEnumerable<T>
Когда на
IEnumerable<T> вызывается Count(), происходят две вещи. Сначала библиотека LINQ пытается привести коллекцию к интерфейсу, который предоставляет свойство Count. Если это невозможно, метод выполняет итерацию по всей коллекции, считая элементы.Для больших коллекций такой подсчет может быть очень дорогим, особенно для
IQueryable при запросе к базе данных. Поэтому разработчики попросили «безопасную» функцию подсчета. Эта функция проверяет наличие быстрого свойства Count и, если не может его найти, ничего не возвращает:public static bool TryGetNonEnumeratedCount(this IEnumerable<T> source, out int count);Длинное имя
TryGetNonEnumeratedCount как бы намекает, что вы делаете что-то не то. В идеале любой API, возвращающий список, должен возвращать либо коллекцию со строгим именем, либо интерфейс более высокого уровня, такой как IList<T> или IReadOnlyList<T>.Трёхсторонний Zip
Метод расширения
Zip объединяет две коллекции, перечисляя их одновременно. Например, если у вас есть список 1, 2, 3 и список A, B, C, то результирующая коллекция будет состоять из кортежей (1, A), (2, B), (3, C). Теперь предложено расширить метод для объединения трёх коллекций за раз.Разбиение на партии
Часто требуется разбить коллекцию на несколько партий. Например, может оказаться, что вставка 100 строк в базу за раз быстрее, чем вставка по одной или всех сразу. Хотя этот код нетрудно написать самому, он, как правило, подвержен ошибкам. Легко ошибиться в последней партии, если количество строк не делится равномерно на размер партии. Для этого добавлен метод расширения
Chunk для IEnumerable и IQueryable:public static IEnumerable<T[]> Chunk(this IEnumerable<T> source, int size);Проверки анализатора
public static IQueryable<T[]> Chunk(this IQueryable<T> source, int size);
Когда сам API не может предотвратить неправильное использование кода разработчиками, авторы библиотек все чаще обращаются к анализаторам. Некоторые из них встроены в компилятор C#, другие добавляются через библиотеки, такие как NetAnalyzers и Roslynator.
Первый из новых анализаторов касается метода расширения
OfType<T>. Он фильтрует входную коллекцию, возвращая только элементы указанного типа T. Если входной тип не может быть приведен к выходному типу, текущее поведение - просто вернуть пустую коллекцию. Предложено в этом случае выдавать предупреждение компилятора, чтобы избежать излишнего перебора коллекции.Предложение правильного использования AsParallel() относится к случаям, когда
AsParallel() следует за операцией перечисления коллекции. Хотя это неочевидно, AsParallel() должен использоваться перед любыми операциями, которые можно распараллелить, такими как отображение и фильтрация. В этом случае также будет выдано предупреждение компилятора.Окончание следует…
Источник: https://www.infoq.com/news/2021/04/Net6-Linq/