Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How C# Compiler choose SelectMany when translating LINQ expression?

There are 4 overloaded signatures for Enumerable.SelectMany. To make it simple, we ignore the two signatures with int argument. So we have 2 signatures for 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
)

My question is: how C# compiler choose SelectMany when translating LINQ expression to extension method invocation?

Basically, if there are multiple from in LINQ expression, there will be SelectMany. But, it seems that C# compiler only choose the second signature. The first signature is never used.

        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);
        }

With the help of Reflector, I can see that above LINQ expression is translated into

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();
    })

Above example involves 3 types. So, it is reasonable to select the second SelectMany signature. However, for below example, only one type is involved, it still selects the second signature.

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

It is translated into:

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

So, I cannot find a case that LINQ expression is translated into the first SelectMany signature. Is there such case?

If the first SelectMany signature is not used, then it exists just because it is BIND of monad in functional programming?

Perhaps the question can be: why do we have 2 signatures of SelectMany?

Thanks.

like image 949
Morgan Cheng Avatar asked Jan 08 '09 01:01

Morgan Cheng


2 Answers

According to the C# Spec, the compiler will not generate an overload call to the first version of SelectMany. The first version of SelectMany is useful for flattening a List of Lists into a single flat list.

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

It doesn't have a strong equivalent in a query expression.

See Section 7.15.2 of the C# Language Spec for more information.

like image 67
JaredPar Avatar answered Nov 15 '22 18:11

JaredPar


why do we have 2 signatures of SelectMany?

So I can use the first one in my code.

var orders = Customers.SelectMany(c => c.Orders)
like image 44
Amy B Avatar answered Nov 15 '22 19:11

Amy B