Первое, на что нужно обратить внимание, это то, что
someList.OrderBy(item => item.SomeProp).OrderBy(item => item.SomeOtherProp);
более или менее эквивалентно:
someList.OrderBy(item => item.SomeOtherProp);
, поскольку второе OrderBy
отменяет работу первого.Как правило, вы хотите:
someList.OrderBy(item => item.SomeProp).ThenBy(item => item.SomeOtherProp);
Обратите внимание, что эквивалент:
from item in someList orderby item.SomeProp, item.SomeOtherProp select item
Использует ThenBy
, как указано выше.
Теперь, с любым синтаксисом, с Linq-to-objects (но не база данных и другие поставщики запросов linq) OrderBy
работает через вызов IComparable<T>
, если доступен, и IComparable
в противном случае (за исключением исключения, к которому мы придем позже).Поскольку agreementsMatching
- это список в памяти, используется эта форма.Вот как OrderBy
может узнать, что означает "упорядочить" для данного типа.
Строки и все встроенные числовые типы (int
, double
и т. Д.), Реализующие IComparable<T>
так что все они могут использоваться без каких-либо действий с вашей стороны.
Предположительно, по крайней мере, одно из свойств, которые вы заказывали выше, не было одним из этих типов, а вашим собственным типом.Я не могу определить, какой из вашего кода, поэтому я собираюсь сделать следующее:
Я собираюсь предположить, что свойство Specification
вернуло объект Spec
, и что Spec
объекты должны быть упорядочены по их свойству Name
без учета регистра в соответствии с инвариантной культурой.Итак, я начинаю с:
class Spec
{
public property Name
{
get { /* code I don't care about here*/ }
set { /* code I don't care about here*/ }
}
/* more code I don't care about here*/
}
Я добавляю реализацию IComparable<Spec>
.В таких случаях также неплохо реализовать IComparable
для обратной совместимости, хотя это можно было бы пропустить.Оба определяют метод, который сравнивает экземпляр с другим объектом, возвращая число <0, если экземпляр «меньше» (идет первым по порядку), 0, если они эквивалентны по порядку, и> 0, если экземпляр «больше»:
class Spec : IComparable<Spec>, IComparable
{
public property Name
{
get { /* code I don't care about here*/ }
set { /* code I don't care about here*/ }
}
/* more code I don't care about here*/
public int CompareTo(Spec other)
{
if(other == null)
return 1;
//Often we make use of an already-existing comparison, though not always
return string.Compare(Name, other.Name, StringComparison.InvariantCultureIgnoreCase)
}
//For backwards compatibility:
public int CompareTo(object other)
{
if(other == null)
return 1;
Spec os = other as Spec;
if(os == null)
throw new ArgumentException("Comparison between Spec and " + other.GetType().FullName + " is not allowed");
return CompareTo(os);
}
}
Теперь OrderBy
может обрабатывать сравнения Spec
объектов, что он будет делать по имени (также мы можем использовать List<Spec>.Sort()
и целый ряд других вещей.
Последний вопрос: что произойдет, если нам нужно отсортировать по каким-то другим правилам или если нам нужно отсортировать тип, в котором у нас нет исходного кода, и он не реализует IComparable<T>
или IComparable
?
Здесь мы можем создать класс, который реализует IComparer<T>
, и это позволит обойти использование IComparable<T>
. Вот пример, демонстрирующий это:
public class OddBeforeEven : IComparer<int>
{
public int Compare(int x, int y)
{
int compareOddEven = y % 2 - x % 2;
if(compareOddEven != 0)
return compareOddEven;
//if both odd or both even, use default ordering:
return x.CompareTo(y);
}
}
/* ... */
var oddBeforeEven0To20 = Enumerable.Range(0, 21).OrderBy(x => x, new OddBeforeEven());
/*Enumerating oddBeforeEven0To20 will produce 1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20*/
Последнее замечание.
Вы уверены, что вам нужно ToList()
в вашем вопросе?
Новички в Linq часто звонят ToList()
, часто, чтобы держать вещи в структуре, которую они могут изобразить лучше, и частично, потому что многопримеров из учебников будет использовать его интенсивно.
Безусловно, бывают случаи, когда ToList()
- единственно разумная вещь, а также случаи, когда необходимо извлечь что-то из формы Linq без памяти (например, для базы данных или XMLDocument).в памяти.Однако:
Если что-то начинается в базе данных, то большую часть времени лучше хранить там как можно дольше.Существует множество исключений из этого, но обычно они стремятся сохранить вещи в базе данных и оптимизировать их, поместив в память в качестве оптимизации для этих нескольких исключений, вместо того, чтобы привыкать быстро вводить вещи в память, а затем оптимизировать их.% времени хранения в базе данных выполняется быстрее!
Если вам нужно переключиться на linq-to-objects, ToEnumerable()
сделает это без энергичного выполнения запроса, поэтому лучше, чем ToList()
большую часть времени.
Бывают случаи, когда ToList()
наиболее эффективен (особенно если вам нужно дважды попасть в один и тот же список), но вы должны вызывать его, когда видите очевидную потребность в нем.не по умолчанию.