是否可以在不使用OrderBy的情况下将IEnumerable转换为IOrderedEnumerable?

假设有一种扩展方法可以根据SortMethod枚举指定的几种排序类型(即按各种属性排序)来订购IQueryable。

 public static IOrderedEnumerable OrderByX(this IQueryable values, SortMethod? sortMethod) { IOrderedEnumerable queryRes = null; switch (sortMethod) { case SortMethod.Method1: queryRes = values.OrderBy(a => a.Property1); break; case SortMethod.Method2: queryRes = values.OrderBy(a => a.Property2); break; case null: queryRes = values.OrderBy(a => a.DefaultProperty); break; default: queryRes = values.OrderBy(a => a.DefaultProperty); break; } return queryRes; } 

sortMethodnull的情况下(即指定我不关心值的顺序),是否有一种方法来代替通过某个默认属性进行排序,而只是将IEnumerator值传递给“订购“无需执行实际排序?

我希望能够调用此扩展,然后可能执行一些额外的ThenBy排序。

您需要为默认情况做的只是:

 queryRes = values.OrderBy(a => 1); 

这实际上是一种noop排序。 因为OrderBy执行稳定的排序,所以在所选对象相等的情况下将保持原始顺序。 请注意,由于这是一个IQueryable而不是IEnumerable因此查询提供程序可能无法执行稳定的排序。 在这种情况下,您需要知道维护订单是否重要,或者只是说“我不关心结果的顺序是什么,只要我可以在结果上调用ThenBy ”。

另一个允许您避免实际排序的选项是创建自己的IOrderedEnumerable实现:

 public class NoopOrder : IOrderedEnumerable { private IQueryable source; public NoopOrder(IQueryable source) { this.source = source; } public IOrderedEnumerable CreateOrderedEnumerable(Func keySelector, IComparer comparer, bool descending) { if (descending) { return source.OrderByDescending(keySelector, comparer); } else { return source.OrderBy(keySelector, comparer); } } public IEnumerator GetEnumerator() { return source.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return source.GetEnumerator(); } } 

有了它你的查询可以是:

 queryRes = new NoopOrder(values); 

请注意,上述类的结果是,如果调用ThenByThenBy将实际上是顶级排序。 它实际上将后续的ThenBy转换为OrderBy调用。 (这不应该是令人惊讶的;然后它将调用CreateOrderedEnumerable方法,并且在那里这个代码调用OrderBy ,基本上将ThenBy转换为OrderBy 。从概念排序的角度来看,这是一种说法“所有的在这种情况下,这个序列中的项目是相同的,但是如果你指定相同的对象应该被其他东西打破,那么就这样做。

另一种思考“无操作排序”的方法是它根据输入序列的索引对项目进行排序。 这意味着项目并非全部“相等”,这意味着订单输入序列是输出序列的最终顺序,并且由于输入序列中的每个项目总是大于之前的项目,因此添加额外的“决胜局” “比较将无所作为,使任何后续的ThenBy调用毫无意义。 如果需要这种行为,它比前一个更容易实现:

 public class NoopOrder : IOrderedEnumerable { private IQueryable source; public NoopOrder(IQueryable source) { this.source = source; } public IOrderedEnumerable CreateOrderedEnumerable(Func keySelector, IComparer comparer, bool descending) { return new NoopOrder(source); } public IEnumerator GetEnumerator() { return source.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return source.GetEnumerator(); } } 

如果总是返回相同的索引值,您将获得保留原始列表顺序的IOrderedEnumerable:

 case null: queryRes = values.OrderBy(a => 1); break; 

顺便说一下,我认为这不是一件正确的事情。 您将获得一个被命令订购的集合,但实际上并非如此。

底线,IOrderedEnumerable仅用于为OrderBy()/ ThenBy()方法提供语法结构,从而阻止您尝试使用ThenBy()启动排序子句。 处理。 它不是一个“标记”,它将集合标识为有序,除非它实际上是由OrderBy()订购的。 因此,答案是如果排序方法为null应该表明可枚举是某种“默认顺序”,则应指定默认顺序(如当前实现所做的那样)。 如果没有指定一个SortingMethod,你推断它是“无序排序”并且不关心实际的顺序,那么说明可枚举的顺序是不诚实的。

尝试简单地使用界面标记集合时所固有的“问题”是,该过程不仅仅是简单排序。 通过执行一个排序方法链,例如myCollection.OrderBy().ThenBy().ThenByDescending() ,你实际上并没有对每次调用对集合进行排序; 反正还没有。 您正在定义名为OrderedEnumerable的“迭代器”类的行为,该类将使用您在链中定义的投影和比较来在您需要实际排序元素时执行排序。

Servy的回答,说明OrderBy(x => 1)是一个noop, 应该优化SQL提供者忽略了这个针对Enumerable的调用仍将做很多工作的现实,并且大多数SQL提供者都在事实上优化这种呼叫; 在大多数Linq提供程序中,OrderBy(x => 1)将生成一个带有“ORDER BY 1”子句的查询,该子句不仅强制SQL提供程序执行自己的排序,它实际上会导致更改顺序,因为在T-SQL中,至少“ORDER BY 1”表示按选择列表的第一列排序。