Самое компактное законченное решение будет выглядеть примерно так:
CultureInfo sloveneCultureInfo = new CultureInfo("sl-SI");
_items = _items.OrderBy(fn => DateTime.ParseExact("01 " + fn, @"dd MMMM yyyy\.\c\s\v", sloveneCultureInfo)).ToList();
Вышесказанное присваивает этот список себе. Если вы собираетесь это сделать, то вы можете получить прирост производительности при относительно небольшом увеличении сложности кода путем сортировки на месте:
CultureInfo sloveneCultureInfo = new CultureInfo("sl-SI");
_items.Sort((x, y) => DateTime.ParseExact("01 " + x, @"dd MMMM yyyy\.\c\s\v",sloveneCultureInfo).CompareTo(DateTime.ParseExact("01 " + y, @"dd MMMM yyyy\.\c\s\v", sloveneCultureInfo)));
(Если вы работаете в системе, работающей в словенском языке, вы можете использовать CurrentCulture
вместо созданного sloveneCultureInfo
выше).
Дальнейшие улучшения могут быть сделаны с помощью класса сравнения, а не лямбды, и само сравнение может быть более эффективным, чем приведенное выше, но я не стал бы беспокоиться об этом, если бы сортировка не оказалась узким местом.
Редактировать: разбить только на то, что работает здесь.
Существует два удобных способа сортировки списка (строго один способ сортировки списка и другой способ сортировки любого IEnumerable или IQueryable).
OrderBy
принимает параметр, который вычисляет ключ сортировки, и возвращает IOrderedEnumerable<T>
(или IOrderedQueryable<T>
с этого момента. Я собираюсь игнорировать тот факт, что это можно сделать и на IQueryable
, так как принцип тот же). Этот класс в большинстве случаев действует как IEnumerable<T>
, отсортированный по ключу, с той лишь разницей, что если вы выполните последующую ThenBy
для него, он будет упорядочен по первому ключу и, следовательно, позволит вам есть многоэтапные заказы.
Итак, в коде:
var x = _items.OrderBy(SomeMethod);
SomeMethod
вернет значения, которые могут быть отсортированы, и на основании этого x будет дано IOrderedEnumerable<T>
, отсортированных соответственно. Например, если SomeMethod
взял строку и вернул длину строки, то мы могли бы использовать ее для сортировки списка строк по длине.
Если бы мы собирались выполнить итерации по элементам _, то это наша работа. Если бы мы хотели получить новый список, то мы могли бы вызвать Tolist()
для результатов, и это вернул бы такой список.
Другой подход, который работает только со списками, заключается в вызове Sort()
. Существует форма без параметров, которая просто сортируется в соответствии со сравнением по умолчанию рассматриваемого типа (если он есть), форма, которая принимает объект IComparer<T>
(в большинстве случаев более сложный, чем необходимый, хотя иногда очень полезный) и форма, которая принимает либо делегат, либо лямбду (строго, это делегат, но мы можем использовать лямбду для создания этого делегата).
Этот делегат должен получить два значения типа элементов списка и вернуть отрицательное число, если первое должно быть отсортировано раньше, чем последнее, ноль, если они эквивалентны, и положительное число в противном случае.
Sort()
меняет список, в котором он сделан. Это дает выигрыш в эффективности, но явно губительно, если вам нужно придерживаться исходного порядка сортировки.
Хорошо, пока у нас есть два способа сортировки списка. Им обоим нужен способ превратить ваши имена файлов во что-то, что можно отсортировать.
Поскольку имена файлов относятся к датам, а даты уже сортируются, наиболее разумным является получение этих дат.
Мы не можем создать дату непосредственно из имени файла, потому что Avgust 2010 - это не дата, а значение месяца-года, и в BCL нет класса месяца-года ( могут быть в других библиотеках, но давайте не будем позолотить лилии).
Однако у каждого месяца есть первый день, и поэтому мы можем создать действительную дату из «01», объединенную с именем файла. Итак, наш первый шаг - сказать, что мы будем действовать на "01 " + fn
, где fn
- имя файла.
Это дает нам строки в форме, например, "01 Avgust 2010.csv"
. Изучая, как работает разбор дат, мы знаем, что мы можем использовать dd
для двузначной даты, MMMM
для полного названия месяца на соответствующем языке (в данном случае словенском) и yyyy
для полного года. Нам просто нужно добавить \.\c\s\v
, чтобы означать, что за ним последует ".csv", который мы не анализируем, и мы настроены. Следовательно, мы можем превратить имя файла в первое число месяца с DateTime.ParseExact("01 " + fn, @"dd MMMM yyyy\.\c\s\v", new Culture("sl-SI"))
, где fn - имя файла. Чтобы сделать это лямбда-выражение, мы используем fn => DateTime.ParseExact("01 " + fn, @"dd MMMM yyyy\.\c\s\v", new Culture("sl-SI"))
.
Первый подход завершен, мы называем _items.OrderBy(fn => DateTime.ParseExact("01 " + fn, @"dd MMMM yyyy\.\c\s\v", new Culture("sl-SI")))
и зависим от OrderBy
, зная, как сортировать DateTime
значения.
Для второго подхода нам нужно взять два значения и сравнить их самостоятельно. Мы можем сделать это, вызвав CompareTo()
на DateTime
s, возвращаемых ParseExact
.
Наконец, мы перемещаем конструкцию CultureInfo
, чтобы инициализировать переменную с именем sloveneCultureInfo
, чтобы избежать расточительных вызовов нескольких созданий по существу одного и того же объекта.