是否应该IOrderedEnumerable
纯粹用作语义值的返回类型?
例如,在表示层消费模型时,我们如何知道集合是需要排序的还是已经排序的?
ORDER BY
在存储库使用子句包装存储过程的情况下呢?存储库应该返回IOrderedEnumerable
吗?那将如何实现?
是否应该IOrderedEnumerable
纯粹用作语义值的返回类型?
例如,在表示层消费模型时,我们如何知道集合是需要排序的还是已经排序的?
ORDER BY
在存储库使用子句包装存储过程的情况下呢?存储库应该返回IOrderedEnumerable
吗?那将如何实现?
我认为这不是一个好主意:
是否应该将 IOrderedEnumerable 用作纯粹用于语义值的返回类型?
例如,在表示层消费模型时,我们如何知道集合是需要排序的还是已经排序的?
如果你不知道它是按哪个键排序的,那么知道一个序列是有序的有什么意义呢?界面的重点IOrderedEnumerable
是能够添加次要排序标准,如果您不知道主要标准是什么,这没有多大意义。
如果存储库使用 ORDER BY 子句包装存储过程,该怎么办。存储库是否应该返回 IOrderedEnumerable?那将如何实现?
这没有意义。正如我已经说过的,IOrderedEnumerable
用于添加辅助排序条件,但是当存储过程返回数据时,它已经排序,添加辅助排序条件已经来不及了。您所能做的就是完全重新排序,因此调用ThenBy
结果不会产生预期的效果。
正如 Thomas 所指出的,知道一个对象是 anIOrderedEnumerable
只告诉我们它是以某种方式排序的,而不是它是以我们想要维护的方式排序的。
还值得注意的是,返回类型会影响覆盖和编译能力,但不会影响运行时检查:
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()
。以下返回类型是可能的:
List<T>
IList<T>
ICollection<T>
IEnumerable<T>
IList
ICollection
IEnumerable
object
让我们现在排除对象和非泛型类型,因为它们不太可能有用(在它们有用的情况下,它们可能是使用的明智决定)。这留下:
List<T>
IList<T>
ICollection<T>
IEnumerable<T>
我们去的列表越高,我们就越方便调用者使用该类型公开的功能,即下面的类型未公开的功能。我们去的列表越低,我们给被调用者在未来更改实现的灵活性就越大。因此,理想情况下,我们希望根据方法的目的(向调用者公开有用的功能,并减少创建新集合以提供我们已经提供的功能的情况)的情况下尽可能高的列表,但不要更高(以允许将来进行更改)。
所以,回到我们的例子,我们有一个IOrderedEnumerable<TElement>
可以返回为 anIOrderedEnumerable<TElement>
或 an IEnumerable<T>
(or IEnumerable
or object
) 的例子。
问题是,这是IOrderedEnumerable
与方法的目的内在相关的事实,还是仅仅是实现的产物?
如果我们有一个方法ReturnProducts
碰巧按价格订购,作为删除相同产品以不同价格提供两次的情况的一部分,那么它应该返回IEnumerable<Product>
,因为调用者不应该关心它是否被订购,当然应该'不依赖它。
如果我们有一个方法ReturnProductsOrderedByPrice
,其中排序是其目的的一部分,那么我们应该 return IOrderedEnumerable<Product>
,因为这与其目的更密切相关,并且可以合理地期望调用CreateOrderedEnumerable
,ThenBy
或者ThenByDescending
它(它真正提供的唯一东西)并且没有这个被随后的实施更改破坏。
编辑:我错过了第二部分。
如果存储库使用 ORDER BY 子句包装存储过程,该怎么办。存储库是否应该返回 IOrderedEnumerable?那将如何实现?
IOrderedQueryable<T>
如果可能(或者也许),这是一个很好的主意。然而,这并不简单。
首先,您必须确保在 之后没有任何东西ORDER BY
可以撤消排序,这可能不是微不足道的。
其次,您不必在调用CreateOrderedEnumerable<TKey>()
.
例如,如果带有 fields A
、B
和C
的元素D
从使用的东西返回,ORDER BY A DESCENDING, B
从而导致返回名为MyOrderedEnumerable<El>
该 implements的类型IOrderedEnumerable<El>
。然后,必须存储A
和是被排序的字段这一事实。B
调用CreateOrderedEnumerable(e => e.D, Comparer<int>.Default, false)
(也是调用什么ThenBy
和ThenByDescending
调用)必须采用一组元素,这些元素对A
和进行相同比较B
,按照数据库返回的相同规则(数据库和 .NET 之间的匹配排序规则可能很困难),并且只有在这些组中,它必须按照cmp.Compare(e0.D, e1.D)
.
如果你能做到这一点,它可能会非常有用,并且返回类型是IOrderedEnumerable
ifORDER BY
子句将出现在所有调用使用的所有查询中是完全合适的。
否则,IOrderedEnumerable
这将是一个谎言——因为你无法履行它提供的合同——而且它不会毫无用处。
IMOIOrderedEnumerable
和 anIOrderedCollection
对于高级操作可能非常有用,即在列表和数组上工作而不是在集合上工作,但是不知何故 List 没有继承它,所以它失去了那个目的。它现在仅对您在问题的第二部分中显示的那种想法有用(按等排序)