LINQ式を翻訳するときにC#コンパイラがSelectManyを選択する方法
-
05-07-2019 - |
質問
Enumerable.SelectManyには4つのオーバーロードされたシグネチャがあります。簡単にするために、 int 引数を持つ2つの署名を無視します。 SelectManyには2つの署名があります:
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#コンパイラは2番目の署名のみを選択するようです。最初の署名は使用されません。
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 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;
})
= i1 * i2return CS<*><*>.ToString();
})
上記の例には3つのタイプが含まれます。したがって、2番目のSelectMany署名を選択するのが妥当です。ただし、以下の例では、1つのタイプのみが関係し、2番目の署名が選択されます。
<*>次のように翻訳されます:
<*>したがって、LINQ式が最初のSelectMany署名に変換されるケースは見つかりません。そのような場合はありますか?
最初のSelectMany署名が使用されていない場合、それは関数型プログラミングのモナドのBINDであるという理由だけで存在しますか?
おそらく質問は次のとおりです。SelectManyの署名が2つあるのはなぜですか?
ありがとう。
解決
C#仕様によると、コンパイラはSelectManyの最初のバージョンへのオーバーロード呼び出しを生成しません。 SelectManyの最初のバージョンは、リストのリストを単一のフラットリストにフラット化するのに役立ちます。
public IEnumerable<string> Example(IEnumerable<IEnumerable<string>> enumerable) {
return enumerable.SelectMany(x => x);
}
クエリ式に強力な同等物はありません。
詳細については、C#言語仕様のセクション7.15.2を参照してください。
他のヒント
SelectManyの署名が2つあるのはなぜですか
つまり、コードで最初のコードを使用できます。
var orders = Customers.SelectMany(c => c.Orders)