什么时候返回IOrderedEnumerable?

时间:2011-12-15 10:26:40

标签: c# .net linq semantics iorderedenumerable

IOrderedEnumerable是否应该仅用作语义值的返回类型?

例如,在表示层中使用模型时,我们如何知道集合是否需要订购或已经订购?

如果存储库使用ORDER BY子句包装存储过程,那该怎么办?存储库应该返回IOrderedEnumerable吗?那将如何实现?

3 个答案:

答案 0 :(得分:18)

我认为这不是一个好主意:

  

IOrderedEnumerable是否应该仅用作语义值的返回类型?

     

例如,在表示层中使用模型时,我们如何知道集合是否需要订购或已经订购?

如果您不知道订购了哪个密钥,那么知道序列是否有序有什么意义呢? IOrderedEnumerable界面的要点是能够添加辅助排序标准,如果您不知道主要标准是什么,这没有多大意义。

  

如果存储库使用ORDER BY子句包装存储过程,那该怎么办?存储库是否应该返回IOrderedEnumerable?那将如何实现?

这没有意义。正如我已经说过的,IOrderedEnumerable用于添加辅助排序条件,但是当存储过程返回数据时,它已经被排序,并且添加辅助排序条件为时已晚。您所能做的就是完全重新排序,因此在结果上调用ThenBy将不会产生预期效果。

答案 1 :(得分:9)

正如托马斯指出的那样,知道一个物体是IOrderedEnumerable,只告诉我们它是以某种方式订购的,而不是以我们想要维护的方式订购。

还值得注意的是,返回类型将影响覆盖和编译能力,但不影响运行时检查:

private static IOrderedEnumerable<int> ReturnOrdered(){return new int[]{1,2,3}.OrderBy(x => x);}
private static IEnumerable<int> ReturnOrderUnknown(){return ReturnOrdered();}//same object, diff return type.
private static void UseEnumerable(IEnumerable<int> col){Console.WriteLine("Unordered");}
private static void UseEnumerable(IOrderedEnumerable<int> col){Console.WriteLine("Ordered");}
private static void ExamineEnumerable(IEnumerable<int> col)
{
  if(col is IOrderedEnumerable<int>)
    Console.WriteLine("Enumerable is ordered");
  else
    Console.WriteLine("Enumerable is unordered");
}
public static void Main(string[] args)
{
  //Demonstrate compile-time loses info from return types
  //if variable can take either:
  var orderUnknown = ReturnOrderUnknown();
  UseEnumerable(orderUnknown);//"Unordered";
  orderUnknown = ReturnOrdered();
  UseEnumerable(orderUnknown);//"Unordered"
  //Demonstate this wasn't a bug in the overload selection:
  UseEnumerable(ReturnOrdered());//"Ordered"'
  //Demonstrate run-time will see "deeper" than the return type anyway:
  ExamineEnumerable(ReturnOrderUnknown());//Enumerable is ordered.
}

因此,如果您遇到根据情况可能会将IEnumerable<T>IOrderedEnumerable<T>返回给调用者的情况,则该变量将被输入为IEnumerable<T>并且信息从返回类型丢失。同时,无论返回类型是什么,调用者都能够确定类型是否真的IOrderedEnumerable<T>

无论哪种方式,返回类型并不重要。

与返回类型的权衡是在调用者的实用程序与被调用者的灵活性之间。

考虑目前以return currentResults.ToList()结尾的方法。可能有以下返回类型:

  1. List<T>
  2. IList<T>
  3. ICollection<T>
  4. IEnumerable<T>
  5. IList
  6. ICollection
  7. IEnumerable
  8. object
  9. 让我们现在排除对象和非泛型类型不太可能有用(在它们有用的情况下,它们可能是很容易使用的决策)。这留下了:

    1. List<T>
    2. IList<T>
    3. ICollection<T>
    4. IEnumerable<T>
    5. 我们的列表越高,我们就越方便地使调用者使用该类型公开的功能,而不是下面的类型公开。在我们列表的下方,我们给被调用者更灵活地改变将来的实现。因此,理想情况下,我们希望在方法的目的上下文中尽可能高的列表(向调用者公开有用的功能,并减少创建新集合以提供我们已经提供的功能的情况)但不高(以便将来进行更改)。

      所以,回到我们有IOrderedEnumerable<TElement>的情况,我们可以IOrderedEnumerable<TElement>IEnumerable<T>(或IEnumerableobject返回)。

      问题是,这是一个IOrderedEnumerable本身与方法的目的相关的事实,还是仅仅是一个实现假象?

      如果我们有一个方法ReturnProducts碰巧按价格排序,作为执行删除同一产品两次以不同价格提供的情况的一部分,那么它应该返回IEnumerable<Product>,因为来电者不应该关心它的订购,当然也不应该依赖它。

      如果我们有一个方法ReturnProductsOrderedByPrice,其中排序是其目的的一部分,那么我们应该返回IOrderedEnumerable<Product>,因为这更接近于它的目的,并且可能合理地期望调用{{1它上面是{},CreateOrderedEnumerableThenBy(这是它真正提供的唯一内容),并且不会因后续的实施更改而中断。

      编辑:我错过了第二部分。

        

      在存储库包装存储过程的情况下怎么办?   一个ORDER BY子句。存储库是否应该返回IOrderedEnumerable?   那将如何实现?

      在可能的情况下(或许ThenByDescending),这是一个非常好的主意。但是,这并不简单。

      首先,您必须确保IOrderedQueryable<T>之后的任何内容都无法撤消订购,这可能并非易事。

      其次,您必须在调用ORDER BY时撤消此排序。

      例如,如果从使用CreateOrderedEnumerable<TKey>()的内容返回包含字段ABCD的元素,则返回实现ORDER BY A DESCENDING, B的名为MyOrderedEnumerable<El>的类型。然后,必须存储IOrderedEnumerable<El>A是已订购的字段的事实。致B(也是CreateOrderedEnumerable(e => e.D, Comparer<int>.Default, false)ThenBy调用的内容)的调用必须采用对ThenByDescendingA进行同等比较的元素组,数据库返回它们的相同规则(数据库和.NET之间的匹配很难匹配),只有在这些组中必须按照B进行排序。

      如果你可以这样做,它可能非常有用,如果所有调用使用的所有查询都存在cmp.Compare(e0.D, e1.D)子句,则返回类型为IOrderedEnumerable是完全合适的。 / p>

      否则,ORDER BY将是一个谎言 - 因为你无法履行它所提供的合同 - 而且它将毫无用处。

答案 2 :(得分:0)

IMO IOrderedEnumerableIOrderedCollection对于高级操作非常有用,该操作可以在列表和数组上工作,但不能在集合上工作,但是List不继承它,因此失去了这个目的。现在,它仅对您在问题第二部分显示的思维方式有用(按等排序)