Здравствуйте, Ikemefula, Вы писали:
I>Здравствуйте, Serginio1, Вы писали:
I>>>И что? Сколько стоит использование IEnumerable ?
S>> То же что и yield, а именно MoveNext и Current
I>Именно.
S>> Я подправил потому, что изначальный смыл в том, что вычисления идут с права налево и нет лишних циклов если бы вычисляли слева направо c созданием новых коллекций
I>Ты путаешь linq и ленивую обработку коллекций. Linq это про соответствующие расширения, которые можно навесить на любой интерфейс, а не только IEnumerable. Более того — совсем необязательно это будет коллекция.
I>RX, XML, WMI, IQueryable и тд и тд и тд.
I>Т.е. yield это частный случай для IEnumerable. То есть, частный случай частного случая
Да но именно yield и создает IEnumerable ленивым. Посмотрим
https://github.com/dotnet/runtime/blob/main/src/libraries/System.Linq/src/System/Linq/Where.cs
Или
https://referencesource.microsoft.com/#system.core/system/linq/Enumerable.cs
private static IEnumerable<TSource> WhereIterator<TSource>(IEnumerable<TSource> source, Func<TSource, int, bool> predicate)
{
int index = -1;
foreach (TSource element in source)
{
checked
{
index++;
}
if (predicate(element, index))
{
yield return element;
}
}
}
S>> Где ты нашел про то, что yield ничего не стоит?
I>Ты так пишешь.
I>>>Не к энумератору, а к значению. Вместо x[i] у тебя будет обращение к moveNext, switch присваивание Current, чтение Current.
S>> Ну я тебе подам на вход IEnumerable не основанном на List или array, как ты с ним будешь работать.
S>>Это же Linq!!!
I>Не подашь. У меня параметры это массивы в явном виде шоб люди навроде тебя не подпихивали хрень и не заявляли "Ваша обработка коллекций тормозит после того, как я сто фильтров объединил"
То есть вычисления слева направо. И если мне нужно вернуть из метода Ienumrable и к нему применить свои действия и вернуть другой енумератор это все будут новые списки.
И вот в конце наконец вызвать FirstOrDefault вот она вся прелесть вычислений справо на лево в отличие от твой позиции с вычислением слева направо
S>> главное, что yield обеспечивает вычисление справа на лево!!!
I>yield это просто последовательность, а не "слева направо". А вот как будут реальные элементы данных выбираться — как угодно.
Нет именно слева направо. Потому что мы вызываем MoveNext у последнего элемента в цепочке, а он уже зовет у своего родителя.
S>>то тебе не нужно перебирать всю коллекцию (возможно и не один раз) и вызвать все лямбды, new в цепочке расширений если вычисления идут слева направо.
На этом основан Linq. И часто новички пишут вместо yield
var result= new List<TSource>();
foreach (TSource element in source)
{
if (predicate(element, index))
{
// yield return element;
result.Add(element);
}
}
return result;
При этом тратя лишнюю память и вычислений predicate если уже в вычислениях отпала необходимость.
I>Это тот самый частный случай.
Это один из массовых случаев Where
Ну и можно посмотреть тот же Select
private static IEnumerable<TResult> SelectIterator<TSource, TResult>(IEnumerable<TSource> source, Func<TSource, int, TResult> selector)
{
int index = -1;
foreach (TSource element in source)
{
checked
{
index++;
}
yield return selector(element, index);
}
}
Да для первоначальных коллекций там оптимизировано в том, реализован MoveNext по аналогии с тем, что трансформиует yield
public static IEnumerable<TSource> Where<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate) {
if (source == null) throw Error.ArgumentNull("source");
if (predicate == null) throw Error.ArgumentNull("predicate");
if (source is Iterator<TSource>) return ((Iterator<TSource>)source).Where(predicate);
if (source is TSource[]) return new WhereArrayIterator<TSource>((TSource[])source, predicate);
if (source is List<TSource>) return new WhereListIterator<TSource>((List<TSource>)source, predicate);
return new WhereEnumerableIterator<TSource>(source, predicate);
}
public override bool MoveNext() {
switch (state) {
case 1:
enumerator = source.GetEnumerator();
state = 2;
goto case 2;
case 2:
while (enumerator.MoveNext()) {
TSource item = enumerator.Current;
if (predicate(item)) {
current = item;
return true;
}
}
Dispose();
break;
}
return false;
}
Кстати еще раз посмотрел исходники они заккоментировали
//public static IEnumerable<TSource> Where<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate) {
// if (source == null) throw Error.ArgumentNull("source");
// if (predicate == null) throw Error.ArgumentNull("predicate");
// return WhereIterator<TSource>(source, predicate);
//}
//static IEnumerable<TSource> WhereIterator<TSource>(IEnumerable<TSource> source, Func<TSource, bool> predicate) {
// foreach (TSource element in source) {
// if (predicate(element)) yield return element;
// }
//}
И заменили их классами наследующие abstract class Iterator<TSource> : IEnumerable<TSource>, IEnumerator<TSource> которые по сути генерит yield при компиляции. Но в остальном методы используют yield.
На yield построен Linq
Непонятно?