我有一个消息列表.每条消息都有一个类型.
public enum MessageType
{
Foo = 0,
Bar = 1,
Boo = 2,
Doo = 3
}
Run Code Online (Sandbox Code Playgroud)
枚举名称是任意的,无法更改.
我需要返回列表排序为:Boo,Bar,Foo,Doo
我目前的解决方案是创建一个tempList,按我想要的顺序添加值,返回新列表.
List<Message> tempList = new List<Message>();
tempList.AddRange(messageList.Where(m => m.MessageType == MessageType.Boo));
tempList.AddRange(messageList.Where(m => m.MessageType == MessageType.Bar));
tempList.AddRange(messageList.Where(m => m.MessageType == MessageType.Foo));
tempList.AddRange(messageList.Where(m => m.MessageType == MessageType.Doo));
messageList = tempList;
Run Code Online (Sandbox Code Playgroud)
我怎么能用IComparer做到这一点?
voi*_*hos 39
使用的另一种方法IComparer
是构建排序字典.
var orderMap = new Dictionary<MessageType, int>() {
{ MessageType.Boo, 0 },
{ MessageType.Bar, 1 },
{ MessageType.Foo, 2 },
{ MessageType.Doo, 3 }
};
var orderedList = messageList.OrderBy(m => orderMap[m.MessageType]);
Run Code Online (Sandbox Code Playgroud)
Vik*_*ova 17
那么,让我们编写自己的比较器:
public class MyMessageComparer : IComparer<MessageType> {
protected IList<MessageType> orderedTypes {get; set;}
public MyMessageComparer() {
// you can reorder it's all as you want
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);
}
};
Run Code Online (Sandbox Code Playgroud)
如何使用:
messages.OrderBy(m => m.MessageType, new MyMessageComparer())
Run Code Online (Sandbox Code Playgroud)
有一种更简单的方法:只需创建ordereTypes列表并使用OrderBy的另一个重载:
var orderedTypes = new List<MessageType>() {
MessageType.Boo,
MessageType.Bar,
MessageType.Foo,
MessageType.Doo,
};
messages.OrderBy(m => orderedTypes.IndexOf(m.MessageType)).ToList();
Run Code Online (Sandbox Code Playgroud)
嗯..让我们尝试通过编写我们自己的IComparer来获得优势.想法:把它写成我们的最后一个例子,但是在其他一些语义中.像这样:
messages.OrderBy(
m => m.MessageType,
new EnumComparer<MessageType>() {
MessageType.Boo,
MessageType.Foo }
);
Run Code Online (Sandbox Code Playgroud)
或这个:
messages.OrderBy(m => m.MessageType, EnumComparer<MessageType>());
Run Code Online (Sandbox Code Playgroud)
好的,我们需要什么.我们自己的比较器:
所以,这是代码:
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);
// no such enums in our order list:
// so this enum values must be in the end
// and must be ordered between themselves by default
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();
}
}
Run Code Online (Sandbox Code Playgroud)
所以,好吧,让我们更快地进行排序.我们需要为我们的枚举覆盖默认的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;
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
是的,那很懒.你可以谷歌收益率如何工作.好吧,让我们测试速度吧.简单的基准: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
Run Code Online (Sandbox Code Playgroud)
我们看到,我们自己的订单通过更为懒惰的标准顺序(是的,它不需要排序所有内容).甚至对于fullsort也更快.
此代码中的问题:它不支持ThenBy()
.如果你需要这个,你可以编写自己的linq扩展返回IOrderedEnumerable
有一个由Jon Skeet撰写的博客文章系列,它深入LINQ to Objects,提供了一个完整的替代实现.部分26a和26bIOrderedEnumerable
涵盖了基础,26c和26d中有更多细节和优化.
如果您有固定数量的消息类型IComparer
,您也可以使用一种SelectMany
方法,而不是使用一种方法,该方法应该具有更好的大型消息列表性能.
var messageTypeOrder = new [] {
MessageType.Boo,
MessageType.Bar,
MessageType.Foo,
MessageType.Doo,
};
List<Message> tempList = messageTypeOrder
.SelectMany(type => messageList.Where(m => m.MessageType == type))
.ToList();
Run Code Online (Sandbox Code Playgroud)