好的,找到了,虽然我不完全理解答案。这里有两个产生所需结果的表达式,一个使用“Let”,另一个使用“Select”:
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”)),在两个表达式中的第一个,“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")),在两个表达式中的第二个,“Select”形式,产生一个可枚举的匿名类型,每个包含一个可枚举的、名为“bots”的 XElements:
System.Linq.Enumerable+WhereSelectEnumerableIterator`2[System.Xml.Linq.XElement,<>f__AnonymousType0`1[System.Collections.Generic.IEnumerable`1[System....
我们想将每个内部枚举转换成一个 {fst, snd} 对。首先注意以下两个表达式产生相同的结果,但在语义上并不相同,如下所示。这两个表达式之间的唯一区别是第一个表达式在第 3 行有“Let”,而第二个表达式在第 3 行有“Select”。它们就像“answer”表达式,只是它们没有内部转变。
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”类型与第二个表达式中外部“Select”中的“bots”类型不同。在“Let”中,“bots”的类型(大致)是IEnumerable<IEnumerable<XElement>>
(它的全称是
System.Linq.Enumerable+WhereSelectEnumerableIterator`2[System.Xml.Linq.XElement,System.Collections.Generic.IEnumerable`1[System.Xml.Linq.XElement]]
我们可以通过 Selecting over the insides 来更详细地看到“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”表单中的隐式包装多一级,反之亦然,具体取决于您的观点。
此外,显然,“Let”“运行”一次在 .Select(sub => sub.Descendants(“bot”)) 的结果上,而“Select”运行多次,每次运行一次以下结果是错误的,因为它忽略了“包装级别”。
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")
;
}