SortedList и Linq

Я запутался после прочтения документации о том, чего ожидать при использовании Linq с SortedList.

https://msdn.microsoft.com/en-us/library/ms132319(v=vs.110).aspx

Я предполагаю, что перечисление гарантированно будет отсортировано, а также извлечено по индексу, но как насчет значений и ключей? Все эти случаи безопасны?

        var list = new SortedList<DateTime, object>();

        //add entries here ...

        var firstValue1 = list.Values[0];
        var firstValue2 = list.First().Value;
        var firstValue3 = list.Values.First();

        var firstKey1 = list.Keys[list.Count-1];
        var firstKey2 = list.First().Key;
        var firstKey3 = list.Keys.First();

        var sortedList = list.Where(x => x.Key > DateTime.Now)
            .Select(x => x.Value);

person Stig    schedule 09.05.2017    source источник
comment
Смысл запросов Linq состоит в том, чтобы на самом деле запрашивать что-то, поэтому простое выполнение .First().Value для меня так же хорошо, как случайность.   -  person EpicKip    schedule 09.05.2017
comment
@EpicKip: я не понимаю. Что делает это случайным, а не получением первого элемента в отсортированном списке?   -  person Chris    schedule 09.05.2017
comment
@Chris, читающий .First(), никогда не говорит вам, что вы получаете. Я бы предпочел всегда говорить, что я ищу, и использовать .First() только в том случае, если мне просто нужен элемент для тестирования или что-то в этом роде.   -  person EpicKip    schedule 09.05.2017
comment
@EpicKip точно говорит вам, что вы получаете. Первый элемент перечисления. Есть много сценариев, когда это именно то, что вам нужно.   -  person Daveoc64    schedule 09.05.2017
comment
@EpicKip: Если вы ничего не знаете о базовом элементе, возможно, вы правы, но в данном случае это отсортированный список. Вы получаете очень конкретную вещь. Самый ранний элемент, самый последний элемент, элемент с наибольшим количеством очков... Какой бы ни была ваша сортировка, определяется, какой элемент будет первым. Вы тоже считаете бессмысленным писать .OrderBy(x=>x.Thing).First()?   -  person Chris    schedule 09.05.2017
comment
@ Daveoc64 Daveoc64 Вам это может понадобиться время от времени, но это не значит, что вы всегда должны использовать отсортированный список и .First. Чаще всего в производстве люди склонны просто сортировать вручную, чтобы знать, что они получают.   -  person EpicKip    schedule 09.05.2017
comment
@Chris Нет, тогда я действительно знаю, прочитав это, что я сортирую. Читая запрос linq   -  person EpicKip    schedule 09.05.2017
comment
@EpicKip Если вам не нравится использовать SortedList, скажите это. Говорить, что использование First дает вам случайный элемент, когда это объективно не делает это, просто неверно. Сказать, что вам не нравится дизайн SortedList (и упорядоченных коллекций в целом, судя по его звучанию) и то, что он делает с вашим кодом, - это вопрос мнения, которого вы более чем можете придерживаться.   -  person Servy    schedule 09.05.2017
comment
@EpicKip: Вы также должны точно знать, по чему сортируется ваш отсортированный список ... Фреймворк не просто выбирает случайный элемент для сортировки вашего списка, вы говорите ему именно то, что хотите ... Я подозреваю, что вы это знаете хотя и это просто придется списать на разницу во мнениях.   -  person Chris    schedule 09.05.2017
comment
@Chris Я неправильно сформулировал свой первый комментарий, а не то, что хотел сказать (полностью). Я имею в виду, что если я читаю someList.OrderyBy(x => x.Something).First(), я могу сказать в этой строке, по какой сортировке идет, если я вижу someList.First(), я не могу   -  person EpicKip    schedule 09.05.2017
comment
@EpicKip: А, в этом больше смысла. Я по-прежнему не обязательно согласен с вами, но я гораздо больше понимаю вашу точку зрения. ;-)   -  person Chris    schedule 09.05.2017
comment
@Chris Это нормально, конечно, я подумал, что был слишком поспешным со своими комментариями и, возможно, не сказал то, что хотел сказать, спасибо за то, что имел дело с такой горячей головой, как я, и оставайся на 100% вежливым;)   -  person EpicKip    schedule 09.05.2017
comment
Это было что-то близкое. Было несколько правок, чтобы сделать мои ответы немного более цивилизованными перед публикацией. ;-)   -  person Chris    schedule 09.05.2017


Ответы (3)


Читайте документацию...

Из документации по свойству Values :

«Порядок значений в IList<T> такой же, как и в SortedList<TKey, TValue>».

Из документации по свойству Keys :

«Порядок клавиш в IList<T> такой же, как и в SortedList<TKey, TValue>».

person Chris    schedule 09.05.2017
comment
Спасибо, мои сомнения развеялись. :) - person Stig; 09.05.2017

Вы можете проверить исходный код здесь:

https://referencesource.microsoft.com/#System/compmod/system/collections/generic/sortedlist.cs,de670561692e4a20

По-видимому, свойство Keys — это просто оболочка для экземпляра этого класса:

https://referencesource.microsoft.com/#System/compmod/system/collections/generic/sortedlist.cs,374aa21b960ae2e2

Если вы посмотрите на метод GetEnumerator(), вы увидите, что он создает SortedListKeyEnumerator. Вот исходный код для этого:

https://referencesource.microsoft.com/#System/compmod/system/collections/generic/sortedlist.cs,a4492235f85c77d8

Насколько я могу судить, MoveNext() этого просто перебирает ключи содержащегося SortedList.

Точно так же можно узнать, как работает Values.

person Akos Nagy    schedule 09.05.2017
comment
Исходный код может измениться, документированные гарантии — нет. :) - person Stig; 09.05.2017
comment
Гарантии @Stig Documentation тоже меняются, только реже. C# и .NET действительно раньше вносили критические изменения, хотя и скупо. - person Servy; 09.05.2017

Если вы посмотрите на исходный код Enumerable.cs, вы увидите, что перегрузка без предиката просто пытается обработать источник как IList, и если это не сработает, она возвращает первый элемент с помощью перечислителя. И индекс, и перечислитель должны обрабатываться внутри класса SortedList, чтобы вы получили соответствующий (отсортированный) результат:

public static TSource First<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> e = source.GetEnumerator()) {
                    if (e.MoveNext()) return e.Current;
                }
            }
            throw Error.NoElements();
        }

Перегрузка с предикатом работает немного иначе, поскольку она выполняет предикат для каждого элемента с помощью перечислителя, ища первое совпадение:

    public static TSource First<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 element in source) {
            if (predicate(element)) return element;
        }
        throw Error.NoMatch();
    }

В любом случае вы должны получить тот же (отсортированный) результат.

person JuanR    schedule 09.05.2017