OrderBy和List与IOrderedEnumerable有何区别?

35

我在使用以下代码时遇到了一个意外的问题。

List<string> items = new List<string>();
items = items.OrderBy(item => item);

这段代码会产生以下错误:

无法隐式转换类型 'System.Linq.IOrderedEnumerable' 为 'System.Collections.Generic.List', 存在一个显式转换 (是否缺失强制类型转换?)

看起来,我可以将变量 items 更改为 IEnumerable<string> 类型,这样就消除了错误。但我需要能够添加元素到列表中,而 IEnumerable 并不支持该操作。

有人能帮我理解这个错误以及最简单的修复方法吗?直接对结果进行强制类型转换安全吗?


你首先创建了一个空列表,然后尝试对该空列表进行排序? - user743382
29
当然不是。我在这里做的是尝试创建一个简单的代码片段,以尽可能少的代码展示问题。 - Jonathan Wood
2
但它并没有展示问题,因为您已经删除了很多内容,无法清楚地知道您是否已经有了需要排序的列表,还是您正在尝试对可枚举对象进行排序并将其转换为列表。 - user743382
6个回答

54
为什么不直接使用Sort()实例方法对列表进行原地排序,然后如果需要的话,随时可以添加项目:
List<string> items = GetSomeItems();
items.Sort();

或者,使用一个有序集合,比如二叉搜索树。 SortedSet<T> 根据您的需求可能适合。

其他人建议的解决方案:

items = items.OrderBy(item => item).ToList(); 

...创建另一个列表,其中包含按新顺序排列的原始项目。只有在需要保留原始排序以供其他用途时才有用;与在原地对列表进行排序相比,这更浪费内存。

就理解错误而言,很简单:List<T>不是IOrderedEnumerable<T>的子类型,因此两者之间没有隐式引用转换。编译器建议的显式转换将满足编译器,但在运行时会失败,因为OrderBy<T>返回的对象不继承自List<T>

编辑

以下是List<T>.Sort(Comparison<T>)的示例,假设类型MyType具有某种类型T的Key属性,其中T:IComparable<T>

List<MyType> items = GetSomeItems();
items.Sort((a, b) => a.Key.CompareTo(b.Key));

1
谢谢,那似乎是最完整的答案。我喜欢使用Sort()的想法,但这不太方便,因为我的真实数据是一个类,并且我想要更复杂的排序规则。不过,我猜我总可以创建一个自定义的比较器。 - Jonathan Wood
@JonathanWood 如果类的排序不依赖于上下文,你可以在类上实现 IComparable<T> 接口,然后 List.Sort<T>() 将使用该实现。 - phoog
1
您还可以传递一个 Comparison<T> 或 lambda (a,b)=>return int 作为排序函数(内联或通过传递方法名称),这有时更加方便。 (参见:http://msdn.microsoft.com/en-us/library/tfakywbh.aspx) - jessehouwing
在.NET 4中,还有一个接受Comparison<T>委托的Sort重载方法,因此您仍然可以使用Lambda表达式。 - phoog
1
来自 MSDN 的提示:List<T>.Sort - 此实现执行不稳定排序;也就是说,如果两个元素相等,则它们的顺序可能不会被保留。相比之下,稳定排序会保留相等元素的顺序。 Enumerable.OrderBy<TSource, TKey> - 此方法执行稳定排序;也就是说,如果两个元素的键相等,则元素的顺序将被保留。相比之下,不稳定排序不会保留具有相同键的元素的顺序。 - Peter
我卡住了,因为没有意识到没有添加 System.Linq(命名空间),所以仍然在使用“using System.Xml.Linq;”这个命名空间,但缺少“OrderBy”,而 toList() 就是解决我的问题的部分。 - visual

20

你需要将 IEnumerable 转换为 List。尝试使用以下方法:

items = items.OrderBy(item => item).ToList();

谢谢。有人知道 ToList() 有多高效吗?例如,它是否需要分配一个新的集合,然后复制列表中的每个项。 - Jonathan Wood
1
@JonathanWood 可能吧。你可以使用 Reflector 来找出。如果你不需要保留原始列表,我会选择 Sort() 选项。 - kitti
4
@JonathanWood 是的,它会分配一个新的列表。 - phoog
但是为什么我需要进行转换呢?请详细解释。 - Unbreakable
1
@Unbreakable,因为.OrderBy不返回可以隐式转换为List<string>的内容,而items是声明为List<string>的。 - Shiv

7

你需要使用LINQ的ToList()方法。

items = items.OrderBy(item => item).ToList();

您不能直接从IEnumerable<>转换为List<>。

6

试试这个

items = items.OrderBy(item => item).ToList();

4

如果要对字符串列表进行排序,首先不需要使用Linq - 只需使用 Sort()

List<string> items = new List<string>();
//add items here
items.Sort();

2

OrderBy()是IEnumerable的扩展方法,而不是List的。

当编译器遇到OrderBy()扩展方法时,它将范围变量强制转换为IOrderedEnumerable,然后使用IComparer等通过CreateOrderedEnumerable方法执行所需的排序。一旦排序完成,编译器通常将该变量作为IEnumerable输出。

建议:在LinQ子句中使用var关键字来对“items”进行类型定义。

当然,使用Sort()和ToList()方法提供的选项也可以工作,但使用它们涉及贪婪操作符,并且您失去了惰性加载的优势。

这里有一个很好的比较:C# Sort and OrderBy comparison,介绍了运行Sort()和OrderBy()之间的区别。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接