Как работает метод расширения FirstOrDefault? - PullRequest
3 голосов
/ 13 сентября 2010

Мне было интересно, как работает метод расширения FirstOrDefault? Какой из следующих алгоритмов следует?

Использование:

var arr = new[] {1, 2, 3, 4, 5, 6, 7};
return arr.FirstOrDefault(x => x%2 == 0);

Алгоритм 1:

for(int i = 0; i < arr.Length; i++)
{
   if(arr[i] % 2 == 0)
     return arr[i];
}
return 0;

Алгоритм 2:

var list = new List<int>();
for(int i = 0; i < arr.Length; i++)
{
   if(arr[i] % 2 == 0)
     list.Add(arr[i]);
}
return list.Count == 0 ? 0 : list[0];

Достаточно ли умен алгоритм FirstOrDefault для выбора оптимального или он строго следует любому из этих алгоритмов?

Ответы [ 3 ]

8 голосов
/ 13 сентября 2010

Я посмотрел в Отражатель :

public static TSource FirstOrDefault<TSource>(this IEnumerable<TSource> source)
{
    if (source == null)
    {
        throw Error.ArgumentNull("source");
    }
    IList<TSource> list = source as IList<TSource>;
    if (list != null)
    {
        if (list.Count > 0)
        {
            return list[0];
        }
    }
    else
    {
        using (IEnumerator<TSource> enumerator = source.GetEnumerator())
        {
            if (enumerator.MoveNext())
            {
                return enumerator.Current;
            }
        }
    }
    return default(TSource);
}

Он пытается сделать это с помощью List, если коллекция может быть приведена как IList (и реализует свойство Count). В противном случае он использует перечислитель.

РЕДАКТИРОВАТЬ: другой метод с предикатом (о котором я сейчас вижу, вы говорите) не так оптимизирован и использует интерфейс IEnumerable для выполнения foreach, а не IList.

public static TSource FirstOrDefault<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
{
    if (source == null)
    {
        throw Error.ArgumentNull("source");
    }
    if (predicate == null)
    {
        throw Error.ArgumentNull("predicate");
    }
    foreach (TSource local in source)
    {
        if (predicate(local))
        {
            return local;
        }
    }
    return default(TSource);
}
1 голос
/ 13 сентября 2010

Кроме того, он использует перечислитель для чтения только самого первого значения. Когда нет первого значения, возвращается ноль (или, скорее, значение по умолчанию для текущего <T>).

0 голосов
/ 13 сентября 2010

First / FirstOrDefault с выбором первого элемента в последовательности, ничего умного.

...