什么时候返回IOrderedEnumerable?
IOrderedEnumerable
应该IOrderedEnumerable
作语义值的返回类型吗?
例如,在表示层中使用模型时,我们如何知道集合是否需要订购或已经订购?
如果存储库使用ORDER BY
子句包装存储过程,那该怎么办? 存储库是否应该返回IOrderedEnumerable
? 那将如何实现?
我认为这不是一个好主意:
IOrderedEnumerable应该仅用作语义值的返回类型吗?
例如,在表示层中使用模型时,我们如何知道集合是否需要订购或已经订购?
如果您不知道订购了哪个密钥,那么知道序列是否有序有什么意义呢? IOrderedEnumerable
接口的IOrderedEnumerable
是能够添加辅助排序标准,如果您不知道主要标准是什么,这没有多大意义。
如果存储库使用ORDER BY子句包装存储过程,那该怎么办? 存储库是否应该返回IOrderedEnumerable? 那将如何实现?
这没有意义。 正如我已经说过的, IOrderedEnumerable
用于添加二级排序条件,但是当存储过程返回数据时,它已经被排序,并且添加二级排序条件为时已晚。 你所能做的就是完全重新排序,所以在结果上调用ThenBy
将没有预期的效果。
正如托马斯指出的那样,知道一个对象是一个IOrderedEnumerable
只告诉我们它是以某种方式订购的,而不是它是以我们想要维护的方式订购的。
值得注意的是,返回类型将影响覆盖和编译能力,但不会影响运行时检查:
private static IOrderedEnumerable ReturnOrdered(){return new int[]{1,2,3}.OrderBy(x => x);} private static IEnumerable ReturnOrderUnknown(){return ReturnOrdered();}//same object, diff return type. private static void UseEnumerable(IEnumerable col){Console.WriteLine("Unordered");} private static void UseEnumerable(IOrderedEnumerable col){Console.WriteLine("Ordered");} private static void ExamineEnumerable(IEnumerable col) { if(col is IOrderedEnumerable ) 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
或IOrderedEnumerable
根据情况返回给调用者的情况,则该变量将被输入为IEnumerable
并且返回类型中的信息将丢失。 同时,无论返回类型是什么,调用者都能够确定该类型是否真的是IOrderedEnumerable
。
无论哪种方式,返回类型并不重要。
与返回类型的权衡是在调用者的实用程序与被调用者的灵活性之间。
考虑一个当前以return currentResults.ToList()
结尾的方法。 以下返回类型是可能的:
-
List
-
IList
-
ICollection
-
IEnumerable
-
IList
-
ICollection
-
IEnumerable
-
object
让我们现在排除对象和非generics类型不太可能有用(在它们有用的情况下,它们可能是很容易使用的决策)。 这留下:
-
List
-
IList
-
ICollection
-
IEnumerable
我们列表中的列表越高,我们就越方便地使调用者使用该类型公开的function,而不是下面的类型公开。 我们的列表越低,我们给被调用者更灵活地改变将来的实现。 因此,理想情况下,我们希望在方法的目的上下文中尽可能高的列表(向调用者公开有用的function,并减少创建新集合以提供我们已经提供的function的情况)但不高于(以允许将来的变化)。
所以,回到我们的情况,我们有一个IOrderedEnumerable
,我们可以作为IOrderedEnumerable
或IEnumerable
(或IEnumerable
或object
)返回。
问题是,这是一个IOrderedEnumerable
固有地与方法的目的相关,还是仅仅是一个实现假象?
如果我们有一个方法ReturnProducts
碰巧按价格订购,作为删除同一产品两次提供不同价格的情况的实现的一部分,那么它应该返回IEnumerable
,因为调用者不应该关心它的订购,当然不应该依赖它。
如果我们有一个方法ReturnProductsOrderedByPrice
,其中排序是其目的的一部分,那么我们应该返回IOrderedEnumerable
,因为这更接近于它的目的,并且可能合理地期望调用CreateOrderedEnumerable
, ThenBy
或ThenByDescending
就可以了(唯一的东西)这确实提供了)并且没有通过随后的实施更改而打破这一点。
编辑:我错过了第二部分。
如果存储库使用ORDER BY子句包装存储过程,那该怎么办? 存储库是否应该返回IOrderedEnumerable? 那将如何实现?
在可能的情况下(或者IOrderedQueryable
)这是一个非常好的主意。 但是,这并不简单。
首先,您必须确保ORDER BY
之后的任何内容都无法撤消排序,这可能并非无足轻重。
其次,您必须在调用CreateOrderedEnumerable
不撤消此排序。
例如,如果从使用ORDER BY A DESCENDING, B
返回具有字段A
, B
, C
和D
ORDER BY A DESCENDING, B
将返回实现IOrderedEnumerable
名为MyOrderedEnumerable
的类型。 然后,必须存储A
和B
是订购的字段的事实。 对CreateOrderedEnumerable(e => eD, Comparer
的调用CreateOrderedEnumerable(e => eD, Comparer
(这也是ThenBy
和ThenByDescending
调用的)必须采用对A
和B
进行相同比较的元素组,它们的规则相同由数据库返回(数据库和.NET之间的匹配排序可能很难),只有在这些组中必须根据cmp.Compare(e0.D, e1.D)
。
如果你能做到这一点,它可能非常有用,如果所有调用使用的所有查询都存在ORDER BY
子句,那么返回类型是IOrderedEnumerable
是完全合适的。
否则, IOrderedEnumerable
将是一个谎言 – 因为你无法履行它提供的合同 – 而且它将是无用的。