Почему в Enumerable.Any() отдается приоритет свойству Count выше чем MoveNext()?

Рейтинг: 0Ответов: 1Опубликовано: 17.01.2023

Почему в теле метода Enumerable.Any<TSource> сначала пытаются достать свойство Count (внутри метода TryGetNonEnumeratedCount) и если не получается, то возвращается MoveNext() который как раз возвращает нужное нам bool значение. Почему бы сразу не сделать условный return MoveNext()? Какие преимущества у такого подхода? В IL никакой разницы между вызовом get у свойства и вызова метода нет, оба дёргаются за счет callvirt

Ответы

▲ 2Принят

Оптимизация потому что.

Первичная инициализация энумератора требует создания его экземпляра, а выдача Count не требует инициализации энумератора.

Код (GitHub):

public static bool Any<TSource>(this IEnumerable<TSource> source)
{
    return TryGetNonEnumeratedCount(source, out int count) ? count != 0 : WithEnumerator(source);

    static bool WithEnumerator(IEnumerable<TSource> source)
    {
        using IEnumerator<TSource> e = source.GetEnumerator(); // вот оно, создание экземпляра энумератора
        return e.MoveNext();
    }
}
public static bool TryGetNonEnumeratedCount<TSource>(this IEnumerable<TSource> source, out int count)
{
    if (source == null)
    {
        ThrowHelper.ThrowArgumentNullException(ExceptionArgument.source);
    }

    if (source is ICollection<TSource> collectionoft)
    {
        count = collectionoft.Count;
        return true;
    }

    if (source is IIListProvider<TSource> listProv)
    {
        int c = listProv.GetCount(onlyIfCheap: true);
        if (c >= 0)
        {
            count = c;
            return true;
        }
    }

    if (source is ICollection collection)
    {
        count = collection.Count;
        return true;
    }

    count = 0;
    return false;
}