わかりました、答えは完全にはわかりませんが、見つけました。1 つは "Let" を使用し、もう 1 つは "Select:" を使用して、目的の結果を生成する 2 つの式を次に示します。
root.Descendants("sub")
.Select(sub => sub.Descendants("bot"))
.Let(bots => bots.Select(bot => new{fst = bot.First(), snd = bot.Last()}))
.Dump("bottoms2")
;
root.Descendants("sub")
.Select(sub => new {bots = sub.Descendants("bot")})
.Select(tmp => new{fst = tmp.bots.First(), snd = tmp.bots.Last()})
.Dump("bottoms")
;
最初の "Select" .Select(sub => sub.Descendants("bot")) は、2 つの式の最初の "Let" 形式で、XElements の列挙型の列挙型を生成します。より正確には、
System.Linq.Enumerable+WhereSelectEnumerableIterator`2[System.Xml.Linq.XElement,System.Collections.Generic.IEnumerable`1[System.Xml.Linq.XElement]]
最初の "Select" .Select(sub => new {bots = sub.Descendants("bot")) は、2 つの式のうちの 2 番目の "Select" フォームで、列挙可能な匿名型を生成します。 XElements の「ボット」という名前の列挙可能なものが含まれています。
System.Linq.Enumerable+WhereSelectEnumerableIterator`2[System.Xml.Linq.XElement,<>f__AnonymousType0`1[System.Collections.Generic.IEnumerable`1[System....
内部列挙型のそれぞれを {fst, snd} ペアに変換します。以下に示すように、次の 2 つの式は同じ結果を生成しますが、意味的には同一ではないことに注意してください。これら 2 つの式の唯一の違いは、最初の式の 3 行目に「Let」があり、2 番目の式の 3 行目に「Select」があることです。変換。
root.Descendants("sub")
.Select(sub => sub.Descendants("bot"))
.Let(bots => bots.Select(bot => bot))
.Dump("bottoms3")
;
root.Descendants("sub")
.Select(sub => sub.Descendants("bot"))
.Select(bots => bots.Select(bot => bot))
.Dump("bottoms4")
;
最初の式の外側の「Let」の「bots」のタイプは、2 番目の式の外側の「Select」の「bots」のタイプとは異なります。「レッツ」における「ボット」の種類は(ざっくり)IEnumerable<IEnumerable<XElement>>
(フルネームは
System.Linq.Enumerable+WhereSelectEnumerableIterator`2[System.Xml.Linq.XElement,System.Collections.Generic.IEnumerable`1[System.Xml.Linq.XElement]]
「bots」の各「bot」が次の内部を選択することで、より詳細に確認できますIEnumerable<XElement>
。
root.Descendants("sub")
.Select(sub => sub.Descendants("bot"))
.Let(bots =>
{
bots.GetType().Dump("bots in Let");
return bots.Select(bot => bot.GetType());
})
.Dump("Types of bots inside the LET")
;
Types of bots inside the LET
IEnumerable<Type> (2 items)
typeof (IEnumerable<XElement>)
typeof (IEnumerable<XElement>)
外側の「選択」では、「ボット」の種類は
System.Xml.Linq.XContainer+<GetDescendants>d__a
上記と並行して分析すると、「bots」内の各「bot」は何かの IEnumerable であり、何かは XElement であることがわかります。
root.Descendants("sub")
.Select(sub => sub.Descendants("bot"))
.Let(bots =>
{
bots.GetType().Dump("bots in Let");
return bots.Select(bot => bot.GetType());
})
.Dump("Types of bots inside the LET")
;
Types of bots inside the SELECT
IEnumerable<IEnumerable<Type>> (2 items)
IEnumerable<Type> (2 items)
typeof (XElement)
typeof (XElement)
IEnumerable<Type> (2 items)
typeof (XElement)
typeof (XElement)
これらは意味的に同じであると考えがちですが、そうではありません。「Select」フォームでは、「Let」フォームよりもタイプ レベルで 1 レベル以上の暗黙的なパッケージングが行われます。視点によっては、その逆もあります。
また、明らかに、「Let」は .Select(sub => sub.Descendants("bot")) の結果に対して 1 回「実行」しますが、「Select」は複数回実行され、各結果に対して 1 回実行されます。その「パッケージのレベル」は無視されます。
root.Descendants("sub")
.Select(sub => sub.Descendants("bot"))
.Let(bots => new{fst = bots.First(), snd = bots.Last()})
.Dump("bottoms2")
;
私が言ったように、私はまだこの現象のすべての詳細を完全に理解していません. おそらく、さらにいくつかの例と、それについての別の夜の睡眠不足により、私はそれについてより洗練された直感を開発し始めるでしょう. この微妙な部分を試してみたいと思っている場合に備えて、LinqPad の完全なスクリプトを次に示します。
void Main()
{
Console.WriteLine ("Here is a sample data set, as XML:");
var root = new XElement("root",
new XElement("sub",
new XElement("bot", new XAttribute("foo", 1)),
new XElement("bot", new XAttribute("foo", 2))),
new XElement("sub",
new XElement("bot", new XAttribute("foo", 3)),
new XElement("bot", new XAttribute("foo", 4))));
root.Dump("root");
Console.WriteLine ("The following two expressions produce the same results:");
root.Descendants("sub")
.Select(sub => sub.Descendants("bot"))
.Let(bots => bots.Select(bot => new{fst = bot.First(), snd = bot.Last()}))
.Dump("LET form: bottoms1")
;
root.Descendants("sub")
.Select(sub => new {bots = sub.Descendants("bot")})
.Select(tmp => new{fst = tmp.bots.First(), snd = tmp.bots.Last()})
.Dump("SELECT form: bottoms2")
;
Console.WriteLine ("Analysis of LET form");
root.Descendants("sub")
.Select(sub => sub.Descendants("bot"))
.Dump("Top-Level Select in the \"Let\" form:")
;
root.Descendants("sub")
.Select(sub => sub.Descendants("bot"))
.GetType()
.Dump("Type of the top-Level Select in the \"Let\" form:")
;
root.Descendants("sub")
.Select(sub => sub.Descendants("bot"))
.Let(bots => bots.Select(bot => bot))
.Dump("Let(bots => bots.Select(bot => bot))")
;
root.Descendants("sub")
.Select(sub => sub.Descendants("bot"))
.Let(bots =>
{
bots.GetType().Dump("bots in Let");
return bots.Select(bot => bot.GetType());
})
.Dump("Types of bots inside the LET")
;
Console.WriteLine ("Analysis of SELECT form");
root.Descendants("sub")
.Select(sub => new {bots = sub.Descendants("bot")})
.Dump("Top-level Select in the \"Select\" form:")
;
root.Descendants("sub")
.Select(sub => new {bots = sub.Descendants("bot")})
.GetType()
.Dump("Type of the top-level Select in the \"Select\" form:")
;
root.Descendants("sub")
.Select(sub => sub.Descendants("bot"))
.Select(bots => bots.Select(bot => bot))
.Dump("bots => bots.Select(bot => bot)")
;
root.Descendants("sub")
.Select(sub => sub.Descendants("bot"))
.Select(bots =>
{
bots.GetType().Dump("bots in Select");
return bots.Select(bot => bot.GetType());
})
.Dump("Types of bots inside the SELECT")
;
}