在翻译LINQ表达式时,C#编译器如何选择SelectMany?

Enumerable.SelectMany有4个重载签名。 为简单起见,我们忽略了带有int参数的两个签名。 所以我们有4个SelectMany签名:

public static IEnumerable SelectMany( this IEnumerable source, Func<TSource, IEnumerable> selector ) public static IEnumerable SelectMany( this IEnumerable source, Func<TSource, IEnumerable> collectionSelector, Func resultSelector ) 

我的问题是:在将LINQ表达式转换为扩展方法调用时,C#编译器如何选择SelectMany?

基本上,如果LINQ表达式中有多个from ,则会有SelectMany。 但是,似乎C#编译器只选择第二个签名。 永远不会使用第一个签名。

  IEnumerable en1 = Enumerable.Range(1, 3); IEnumerable en2 = new double[] { 1.0, 3.14 }; IEnumerable en3 = from i1 in en1 from i2 in en2 select (i1 * i2).ToString(); foreach (var i in en3) { Console.WriteLine(i); } 

在Reflector的帮助下,我可以看到上面的LINQ表达式被翻译成了

 en1.SelectMany(delegate (int i1) { return en2; }, delegate (int i1, double i2) { double CS$0$0000 = i1 * i2return CS$0$0000.ToString(); }) 

以上示例涉及3种类型。 因此,选择第二个SelectMany签名是合理的。 但是,对于下面的示例,仅涉及一种类型,它仍然选择第二个签名。

  IEnumerable en4 = from i1 in en1 from i2 in Enumerable.Range(0, i1) select i2; 

它被翻译成:

 en1.SelectMany(delegate (int i1) { return Enumerable.Range(0, i1); }, delegate (int i1, int i2) { return i2; }) 

所以,我找不到将LINQ表达式转换为第一个SelectMany签名的情况。 有这种情况吗?

如果没有使用第一个SelectMany签名,那么它的存在只是因为它在函数式编程中是monad的BIND?

也许问题可能是:为什么我们有2个SelectMany签名?

谢谢。

根据C#Spec,编译器不会生成对SelectMany的第一个版本的重载调用。 SelectMany的第一个版本对于将列表列表展平为单个平面列表非常有用。

 public IEnumerable Example(IEnumerable> enumerable) { return enumerable.SelectMany(x => x); } 

它在查询表达式中没有强大的等价物。

有关详细信息,请参阅C#语言规范的第7.15.2节。

为什么我们有2个SelectMany签名?

所以我可以在我的代码中使用第一个。

 var orders = Customers.SelectMany(c => c.Orders)