9

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

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

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

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

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

        IEnumerable<int> en1 = Enumerable.Range(1, 3);
        IEnumerable<double> en2 = new double[] { 1.0, 3.14 };

        IEnumerable<string> 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<int, double, string>(delegate (int i1) {
        return en2;
    }, delegate (int i1, double i2) {
        double CS$0$0000 = i1 * i2return CS$0$0000.ToString();
    })

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

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

它被翻译成:

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

因此,我找不到将 LINQ 表达式转换为第一个 SelectMany 签名的情况。有没有这样的情况?

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

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

谢谢。

4

2 回答 2

5

根据 C# 规范,编译器不会对 SelectMany 的第一个版本生成重载调用。SelectMany 的第一个版本对于将列表列表扁平化为单个扁平列表很有用。

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

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

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

于 2009-01-08T02:56:04.750 回答
5

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

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

var orders = Customers.SelectMany(c => c.Orders)
于 2009-01-09T15:29:31.413 回答