【问题标题】:When to return IOrderedEnumerable?何时返回 IOrderedEnumerable?
【发布时间】:2012-01-21 01:24:09
【问题描述】:

是否应该将IOrderedEnumerable 用作纯粹用于语义值的返回类型?

例如,在表示层消费一个模型时,我们如何知道集合是需要排序的还是已经排序的?

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

【问题讨论】:

标签: c# .net linq semantics iorderedenumerable


【解决方案1】:

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

是否应该将 IOrderedEnumerable 用作纯粹用于语义值的返回类型?

例如,在表示层消费一个模型时,我们如何知道该集合是需要排序的还是已经排序的?

如果您不知道它是按哪个键排序的,那么知道一个序列是有序的有什么意义? IOrderedEnumerable 接口的重点是能够添加辅助排序条件,如果您不知道主要条件是什么,这没有多大意义。

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

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

【讨论】:

  • 添加辅助排序条件不一定为时已晚。以非常一般的方式这样做非常困难 - 也许到了不可能的地步,但是知道它总是会在字段A 上订购的方法有可能根据该知识提供CreateOrderedEnumerable() ( ThenBy 调用)。不过,在更一般的情况下要复杂得多,而且实际上可能不是很有用。
【解决方案2】:

正如 Thomas 所指出的,知道一个对象是 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&lt;T&gt;IOrderedEnumerable&lt;T&gt; 返回给调用者,则该变量将被键入为IEnumerable&lt;T&gt;,并且返回类型中的信息将丢失。同时,无论返回什么类型,调用者都可以判断该类型是否真的是IOrderedEnumerable&lt;T&gt;

不管怎样,返回类型并不重要。

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

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

  1. List&lt;T&gt;
  2. IList&lt;T&gt;
  3. ICollection&lt;T&gt;
  4. IEnumerable&lt;T&gt;
  5. IList
  6. ICollection
  7. IEnumerable
  8. object

让我们现在排除对象和非泛型类型,因为它们不太可能有用(在它们有用的情况下,它们可能是不费吹灰之力的决定)。这会留下:

  1. List&lt;T&gt;
  2. IList&lt;T&gt;
  3. ICollection&lt;T&gt;
  4. IEnumerable&lt;T&gt;

我们去的列表越高,我们就越方便调用者使用该类型公开的功能,即下面的类型未公开的功能。我们去的列表越低,我们给被调用者更多的灵活性来改变未来的实现。因此,理想情况下,我们希望根据方法的目的(向调用者公开有用的功能,并减少创建新集合以提供我们已经提供的功能的情况)的情况下尽可能高的列表,但不要更高(以允许将来进行更改)。

所以,回到我们有一个IOrderedEnumerable&lt;TElement&gt; 的情况,我们可以将其返回为IOrderedEnumerable&lt;TElement&gt;IEnumerable&lt;T&gt;(或IEnumerableobject)。

问题是,这是一个 IOrderedEnumerable 与方法的目的固有相关的事实,还是仅仅是一个实现工件?

如果我们有一个方法 ReturnProducts 碰巧按价格订购,作为删除以不同价格提供两次相同产品的案例的一部分,那么它应该返回 IEnumerable&lt;Product&gt;,因为调用者不应该关心它是有序的,当然不应该依赖它。

如果我们有一个方法ReturnProductsOrderedByPrice,其中排序是其目的的一部分,那么我们应该返回IOrderedEnumerable&lt;Product&gt;,因为这与其目的更密切相关,并且可以合理地期望调用CreateOrderedEnumerableThenByThenByDescending (这是它真正提供的唯一东西),并且不会被后续的实现更改破坏。

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

如果存储库将存储过程包装为 ORDER BY 子句。存储库是否应该返回 IOrderedEnumerable? 那将如何实现?

如果可能的话,这是个好主意(或者可能是IOrderedQueryable&lt;T&gt;)。然而,这并不简单。

首先,您必须确保ORDER BY 之后的任何内容都无法撤消排序,这可能不是小事。

其次,您必须在调用 CreateOrderedEnumerable&lt;TKey&gt;() 时不要撤消此排序。

例如,如果从使用 ORDER BY A DESCENDING, B 的对象返回具有字段 ABCD 的元素,则会返回一个名为 MyOrderedEnumerable&lt;El&gt; 的类型,该类型实现了 @987654362 @。然后,必须存储AB 是排序的字段这一事实。对CreateOrderedEnumerable(e =&gt; e.D, Comparer&lt;int&gt;.Default, false) 的调用(这也是ThenByThenByDescending 的调用)必须采用与AB 进行同等比较的元素组,其规则与数据库返回的规则相同(数据库和 .NET 之间的匹配排序规则可能很难),并且只有在这些组中,它才必须根据 cmp.Compare(e0.D, e1.D) 进行排序。

如果你能做到这一点,它可能会非常有用,如果 ORDER BY 子句出现在所有调用使用的所有查询中,则返回类型为 IOrderedEnumerable 是完全合适的。

否则,IOrderedEnumerable 将是一个谎言——因为你无法履行它提供的合同——而且它不会毫无用处。

【讨论】:

    【解决方案3】:

    IMO IOrderedEnumerableIOrderedCollection 对于高级操作可能非常有用,即在列表和数组上工作但在集合上工作,但不知何故 List 没有继承它,所以它失去了那个目的。它现在仅对您在问题的第二部分中显示的那种想法有用(按等排序)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-10-24
      • 1970-01-01
      • 2012-10-22
      • 1970-01-01
      • 2010-12-19
      • 1970-01-01
      • 1970-01-01
      • 2012-03-06
      相关资源
      最近更新 更多