【问题标题】:How C# Compiler choose SelectMany when translating LINQ expression?C#编译器在翻译LINQ表达式时如何选择SelectMany?
【发布时间】:2010-09-30 04:38:14
【问题描述】:

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
)

我的问题是:C# 编译器在将 LINQ 表达式转换为扩展方法调用时如何选择 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 签名?

谢谢。

【问题讨论】:

    标签: c# select expression linq


    【解决方案1】:

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

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

    它在查询表达式中没有强等效项。

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

    【讨论】:

    • 真的……一个 void 函数返回一些东西,但没有人在它上面?
    【解决方案2】:

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

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

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

    【讨论】:

    • 当然可以。这只是为了方便。
    • SelectMany 的第一个签名与一元“绑定”运算符同构,用 Haskell 编写 >>=。包含它的一个原因是为了巩固 LINQ 在完善的单子框架上的基础。该基础使 LINQ 多态地适用于各种事物,例如可组合状态传播、异常、延续、替代等:所有单子。见en.wikipedia.org/wiki/Monad_(functional_programming)
    猜你喜欢
    • 1970-01-01
    • 2013-06-05
    • 1970-01-01
    • 1970-01-01
    • 2021-12-28
    • 1970-01-01
    • 2022-12-03
    • 1970-01-01
    相关资源
    最近更新 更多