那么,让我们编写自己的比较器:
public class MyMessageComparer : IComparer<MessageType> {
protected IList<MessageType> orderedTypes {get; set;}
public MyMessageComparer() {
orderedTypes = new List<MessageType>() {
MessageType.Boo,
MessageType.Bar,
MessageType.Foo,
MessageType.Doo,
};
}
public int Compare(MessageType x, MessageType y) {
var xIndex = orderedTypes.IndexOf(x);
var yIndex = orderedTypes.IndexOf(y);
return xIndex.CompareTo(yIndex);
}
};
如何使用:
messages.OrderBy(m => m.MessageType, new MyMessageComparer())
有一个更简单的方法:只需创建orderTypes列表并使用OrderBy的另一个重载即可:
var orderedTypes = new List<MessageType>() {
MessageType.Boo,
MessageType.Bar,
MessageType.Foo,
MessageType.Doo,
};
messages.OrderBy(m => orderedTypes.IndexOf(m.MessageType)).ToList();
嗯...让我们尝试从编写自己的IComparer中获得优势。思路:像我们上一个示例那样编写,但在某些其他语义上。就像这样:
messages.OrderBy(
m => m.MessageType,
new EnumComparer<MessageType>() {
MessageType.Boo,
MessageType.Foo }
);
或者这个:
messages.OrderBy(m => m.MessageType, EnumComparer<MessageType>());
好的,我们需要什么。我们需要自己的比较器:
- 必须将枚举作为泛型类型进行接受(如何解决)
- 必须能够与集合初始化语法一起使用(如何做到)
- 当在我们的比较器中没有枚举值或某些枚举值不在我们的比较器中时,必须按默认顺序排序。
所以,这是代码:
public class EnumComparer<TEnum>: IComparer<TEnum>, IEnumerable<TEnum> where TEnum: struct, IConvertible {
protected static IList<TEnum> TypicalValues { get; set; }
protected IList<TEnum> _reorderedValues;
protected IList<TEnum> ReorderedValues {
get { return _reorderedValues.Any() ? _reorderedValues : TypicalValues; }
set { _reorderedValues = value; }
}
static EnumComparer() {
if (!typeof(TEnum).IsEnum)
{
throw new ArgumentException("T must be an enumerated type");
}
TypicalValues = new List<TEnum>();
foreach (TEnum value in Enum.GetValues(typeof(TEnum))) {
TypicalValues.Add(value);
};
}
public EnumComparer(IList<TEnum> reorderedValues = null) {
if (_reorderedValues == null ) {
_reorderedValues = new List<TEnum>();
return;
}
_reorderedValues = reorderedValues;
}
public void Add(TEnum value) {
if (_reorderedValues.Contains(value))
return;
_reorderedValues.Add(value);
}
public int Compare(TEnum x, TEnum y) {
var xIndex = ReorderedValues.IndexOf(x);
var yIndex = ReorderedValues.IndexOf(y);
if (xIndex == -1) {
if (yIndex == -1) {
xIndex = TypicalValues.IndexOf(x);
yIndex = TypicalValues.IndexOf(y);
return xIndex.CompareTo(yIndex);
}
return -1;
}
if (yIndex == -1) {
return -1;
}
return xIndex.CompareTo(yIndex);
}
public void Clear() {
_reorderedValues = new List<TEnum>();
}
private IEnumerable<TEnum> GetEnumerable() {
return Enumerable.Concat(
ReorderedValues,
TypicalValues.Where(v => !ReorderedValues.Contains(v))
);
}
public IEnumerator<TEnum> GetEnumerator() {
return GetEnumerable().GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator() {
return GetEnumerable().GetEnumerator();
}
}
好的,让我们让排序更快。 我们需要为我们的枚举类型覆盖默认的OrderBy方法:
public static class LinqEnumExtensions
{
public static IEnumerable<TSource> OrderBy<TSource, TEnum>(this IEnumerable<TSource> source, Func<TSource, TEnum> selector, EnumComparer<TEnum> enumComparer) where TEnum : struct, IConvertible
{
foreach (var enumValue in enumComparer)
{
foreach (var sourceElement in source.Where(item => selector(item).Equals(enumValue)))
{
yield return sourceElement;
}
}
}
}
是的,这很懒。你可以搜索一下yield的工作原理。好吧,让我们测试一下速度。简单的基准测试:http://pastebin.com/P8qaU20Y。n = 1000000时的结果;
Enumerable orderBy, elementAt: 00:00:04.5485845
Own orderBy, elementAt: 00:00:00.0040010
Enumerable orderBy, full sort: 00:00:04.6685977
Own orderBy, full sort: 00:00:00.4540575
我们发现,我们自己的orderBy比标准的orderBy更懒惰(是的,它不需要对所有内容进行排序)。即使是全排序,它也更快。
这段代码存在问题:它不支持ThenBy()
。如果您需要此功能,可以编写自己的LINQ扩展程序,该程序返回IOrderedEnumerable
。Jon Skeet撰写了一系列博客文章,深入介绍了LINQ to Objects,并提供了完整的替代实现。 IOrderedEnumerable
的基础知识在第26a部分和26b部分中有所涉及,更多细节和优化在26c部分和26d部分中。