我有一个有序的枚举数组IorderedEnumerable<T>[] foo
,我希望将它展平,以便foo
的有序枚举按照它们存储在数组中的顺序连接在一起。
例如{{1,2,3},{4,5},{6}} =&gt; {1,2,3,4,5,6}
我可以通过IOrderedEnumerable<T> bar = foo.SelectMany(x => x);
执行此操作,还是LINQ无法保证在展平时如何处理订单?
答案 0 :(得分:8)
所有LINQ to Objects方法(显然,OrderBy()
和ToDictionary()
除外)都将保留源排序。
答案 1 :(得分:6)
列表(由.net中的IEnumerable<T>
表示)以及两个操作组成一个monad,必须遵守monad laws。这两个操作在不同的语言中被赋予不同的名称,维基百科文章使用Haskell,称之为return
和>>=
(称为&#39; bind&#39;)。 C#调用>>=
SelectMany
并且没有return
的内置函数。这些名称并不重要,重要的是类型。专门针对IEnumerable<T>
的是:
Return :: T -> IEnumerable<T>
SelectMany :: IEnumerable<T> -> Func<T, IEnumerable<U>> -> IEnumerable<U>
Return
只返回包含给定元素的1元素序列,例如
public static IEnumerable<T> Return<T>(T item)
{
return new[] { item };
}
SelectMany
已实施为Enumerable.SelectMany
:
public static IEnumerable<U> SelectMany<T, U>(IEnumerable<T> seq, Func<T, IEnumerable<U>> f) { ... }
SelectMany
采用输入序列和函数,为输入序列的每个项生成另一个序列,并将得到的序列序列展平为一个。
重申C#中的前两个monad法则:
左侧身份
Func<T, IEnumerable<U>> f = ...
Return(x).SelectMany(f) == f(x)
正确的身份
IEnumerable<T> seq = ...
seq.SelectMany(Return) == seq
根据正确的身份法,SelectMany
必须根据输入元素的顺序展平Func<T, IEnumerable<U>>
生成的每个序列。
假设它以相反的顺序压扁它们,例如
new[] { 1, 2 }.SelectMany(i => new[] { i, -i }) == new[] { 2, -2, 1, -1 }
然后
var s = new[] { 1, 2 }
s.SelectMany(Return) == new[] { 2, 1 } != s
不符合所要求的权利身份法。