我有一个消息列表。 每条消息都有一个类型。
public enum MessageType
{
Foo = 0,
Bar = 1,
Boo = 2,
Doo = 3
}
枚举名称是任意的,无法更改。
我需要将列表返回为: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;
如何使用IComparer?
执行此操作答案 0 :(得分:38)
使用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]);
答案 1 :(得分: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);
}
};
使用方法:
messages.OrderBy(m => m.MessageType, new MyMessageComparer())
有一种更简单的方法:只需创建ordereTypes列表并使用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);
// 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();
}
}
所以,好吧,让我们更快地进行排序。我们需要为我们的枚举覆盖默认的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;
}
}
}
}
是的,那太懒了。你可以谷歌收益率如何工作。好吧,让我们测试速度吧。简单的基准: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
我们看到,我们自己的订单通过更为懒惰的标准顺序(是的,它不需要排序所有内容)。甚至对于fullsort也更快。
此代码中的问题:它不支持ThenBy()
。如果你需要这个,你可以编写自己的linq扩展,返回IOrderedEnumerable
有一个blog post series by Jon Skeet深入LINQ to Objects,提供了一个完整的替代实现。 IOrderedEnumerable
和part 26a涵盖{{1}}的基础,26b和26c中包含更多详情和优化。
答案 2 :(得分:8)
如果您拥有固定数量的邮件类型,则还可以使用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();
答案 3 :(得分:2)
您可以避免编写一个全新的类型来实现IComparable。改为使用Comparer类:
IComparer<Message> comparer = Comparer.Create<Message>((message) =>
{
// lambda that compares things
});
tempList.Sort(comparer);
答案 4 :(得分:1)
您可以使用 LINQ 从Enum
值动态构建映射字典,如下所示:
var mappingDIctionary = new List<string>((string[])Enum.GetNames(typeof(Hexside)))
.OrderBy(label => label )
.Select((i,n) => new {Index=i, Label=n}).ToList();
现在,添加到Enum n future的所有新值都将自动正确映射。
此外,如果有人决定对枚举进行重新编号,重构或重新排序,则会自动处理所有内容。
<强>更新强> 如下所述,没有要求按字母排序;而是一个半字母顺序,所以基本上是随机的。虽然不是这个特定问题的答案,但这种技术可能对未来的访问者有用,所以我会让它保持原状。
答案 5 :(得分:0)
无需映射。这应该给你基于枚举的列表和顺序。即使您更改了枚举订单或新商品,也无需修改任何内容......
var result = (from x in tempList
join y in Enum.GetValues(typeof(MessageType)).Cast<MessageType>()
on x equals y
orderby y
select y).ToList();
答案 6 :(得分:0)
如果您要使用Entity Framework(EF),那么您必须在OrderBy
中展开您的枚举:
messageList.OrderBy(m =>
m.MessageType == MessageType.Boo ? 0 :
m.MessageType == MessageType.Bar ? 1 :
m.MessageType == MessageType.Foo ? 2 :
m.MessageType == MessageType.Doo ? 3 : 4
);
这将创建一个带有CASE WHEN
的子选择,然后在该临时列上创建ORDER BY
。