Почему я вижу так много дублирования кода в Linq in Reflector?

Изменить: я ошибся в исходном вопросе. Речь должна идти о методах Last и LastOrDefault (или Single и SingleOrDefault, или First и FirstOrDefault - их много!).

На основе Этот вопрос, я открыл Reflector и посмотрел на код

Enumerable.Last<T>(this collection)

Затем я перешел к коду

Enumerable.LastOrDefault<T>(this collection)

и я видел точно такой же фрагмент кода (около 20 строк), отличающийся только одной последней строкой (первый метод возвращает значение по умолчанию (T), второй выдает исключение).

У меня вопрос, почему это так? Почему ребята из Microsoft разрешают дублировать нетривиальные фрагменты кода внутри .Net framework? Разве у них нет обзора кода?


person Konstantin Spirin    schedule 05.09.2009    source источник
comment
интересно, но не вопрос ....   -  person Mitch Wheat    schedule 05.09.2009
comment
Кроме того, Last (коллекция, предикат) имеет сложность O (N), но это, вероятно, тема для другого обсуждения.   -  person Konstantin Spirin    schedule 05.09.2009
comment
Я думаю, это реальный вопрос. Может быть, есть какая-то веская причина, почему это так?   -  person Alex Baranosky    schedule 05.09.2009
comment
Я считаю, что Enumerable.Last<t> должен быть в O (N), потому что IEnumerator<T> можно пройти только последовательно, начиная с первого элемента. Не существует метода O (1) для получения последнего элемента.   -  person bcat    schedule 05.09.2009
comment
Я вижу, что методы существенно различаются. О чем ты говоришь?   -  person mmx    schedule 05.09.2009
comment
@bcat: если IEnumerable ‹› является IList ‹›, можно напрямую получить последний элемент. Однако это возможно только при отсутствии предиката. Что интересно, именно это и делает фреймворк.   -  person jpbochi    schedule 05.09.2009
comment
IEnumerables не ограничиваются IList, например генератором. Last () - это не только O (n); это может быть неразрешимым.   -  person Joe Chung    schedule 05.09.2009
comment
@jpbochi: Правда? Круто, фреймворк умнее, чем я думал!   -  person bcat    schedule 05.09.2009
comment
Я имел ввиду Last и LastOrDefault. Спасибо, что обнаружили ошибку в моем вопросе - сейчас исправлено!   -  person Konstantin Spirin    schedule 06.09.2009
comment
Если статическим типом внутреннего выражения x является IEnumerable<T>, то x.Last() равно O (n). Если статическим типом внутреннего выражения является IList<T>, то x.Last() равно O (1).   -  person yfeldblum    schedule 06.09.2009
comment
Даже с предикатом можно получить O (1) (конечно, зависит от предиката и коллекции), выполняя итерацию от начала до конца и возвращая первый соответствующий элемент.   -  person Konstantin Spirin    schedule 06.09.2009
comment
@Konstantin - назад к началу и возвращение первого совпадающего элемента - O (n). В худшем случае вы просматриваете весь ввод.   -  person Kobi    schedule 06.09.2009
comment
@Kobi: Хотя технически все еще O (n), предложение Константина будет иметь лучшую производительность, чем текущая реализация фреймворка во всех ситуациях, кроме худшего. В худшем случае производительность будет такой же, как и в лучшем случае реализации фреймворка (т. Е. Повторение каждого элемента в коллекции).   -  person LukeH    schedule 07.09.2009


Ответы (1)


На самом деле это не совсем то же самое. Первый такой:

public static TSource Last<TSource>(this IEnumerable<TSource> source)
{
    if (source == null) throw Error.ArgumentNull("source");

    IList<TSource> list = source as IList<TSource>;
    if (list != null) {
        int count = list.Count;
        if (count > 0) return list[count - 1];
    } else {
        using (IEnumerator<TSource> enumerator = source.GetEnumerator()) {
            if (enumerator.MoveNext()) {
                TSource current;
                do { current = enumerator.Current;}
                while (enumerator.MoveNext());

                return current;
            }
        }
    }
    throw Error.NoElements();
}

А другой такой:

public static TSource Last<TSource>(this IEnumerable<TSource> source,
Func<TSource, bool> predicate)
{
    if (source == null) throw Error.ArgumentNull("source");
    if (predicate == null) throw Error.ArgumentNull("predicate");

    TSource last = default(TSource);
    bool foundOne = false;
    foreach (TSource value in source) {
        if (predicate(value)) {
            last = value;
            foundOne = true;
        }
    }
    if (!foundOne) throw Error.NoMatch();
    return last;
}

PS: Надеюсь, я сейчас не нарушаю авторские права. : S

person jpbochi    schedule 05.09.2009
comment
Я имел ввиду Last и LastOrDefault. Спасибо, что обнаружили ошибку в моем вопросе! - person Konstantin Spirin; 06.09.2009
comment
Учитывая обновление, я понимаю вашу точку зрения. Эти методы можно было легко переписать, чтобы избежать дублирования. Что касается причины, по которой они этого не сделали, я понятия не имею. : S - person jpbochi; 06.09.2009