LINQtoObject和LINQtoXML中的Let语义问题 请考虑下面的示例,包括嵌套XLead的定义和一对LINQ表达式。第一个表达式按预期工作,通过选择通过获取bots(用于底部)生成的tmp,在底部迭代获取第一个和最后一个元素,该tmp存储在匿名类型的新实例中,以重用名称“bots”。第二个表达式尝试做同样的事情,只使用“Let”,但它根本不起作用。首先,编译器抱怨类型推断不起作用,然后,当我放入显式类型时,它会进入IObservable,甚至丢失更多。我原以为这是完全直截了当的,并对失败感到十分惊讶和困惑。如果有人能抽出时间来看看并提出建议,我将不胜感激。您可以将以下内容粘贴到LinqPad中,添加对System.Interactive的引用,并查看失败的编译 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"); root.Descendants("sub") .Select(sub => new {bots = sub.Descendants("bot")}) .Select(tmp => new{fst = tmp.bots.First(), snd = tmp.bots.Last()}) .Dump("bottoms") ; root.Descendants("sub") .Select(sub => sub.Descendants("bot")) .Let(bots => new{fst = bots.First(), snd = bots.Last()}) .Dump("bottoms2") ;

LINQtoObject和LINQtoXML中的Let语义问题 请考虑下面的示例,包括嵌套XLead的定义和一对LINQ表达式。第一个表达式按预期工作,通过选择通过获取bots(用于底部)生成的tmp,在底部迭代获取第一个和最后一个元素,该tmp存储在匿名类型的新实例中,以重用名称“bots”。第二个表达式尝试做同样的事情,只使用“Let”,但它根本不起作用。首先,编译器抱怨类型推断不起作用,然后,当我放入显式类型时,它会进入IObservable,甚至丢失更多。我原以为这是完全直截了当的,并对失败感到十分惊讶和困惑。如果有人能抽出时间来看看并提出建议,我将不胜感激。您可以将以下内容粘贴到LinqPad中,添加对System.Interactive的引用,并查看失败的编译 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"); root.Descendants("sub") .Select(sub => new {bots = sub.Descendants("bot")}) .Select(tmp => new{fst = tmp.bots.First(), snd = tmp.bots.Last()}) .Dump("bottoms") ; root.Descendants("sub") .Select(sub => sub.Descendants("bot")) .Let(bots => new{fst = bots.First(), snd = bots.Last()}) .Dump("bottoms2") ;,linq,linq-to-xml,linq-to-objects,let,system.interactive,Linq,Linq To Xml,Linq To Objects,Let,System.interactive,好的,找到了,虽然我不完全明白答案。这里有两个表达式可以产生所需的结果,一个使用“Let”,另一个使用“Select”: 在两个表达式中的第一个表达式中,第一个“Select,”.Select(sub=>sub.substanders(“bot”)“Let”形式生成可枚举的元素枚举,或者更准确地说 System.Linq.Enumerable+WhereSelectEnumerableIterator`2[System.Xml.Linq.XElement,System.Collections.G

好的,找到了,虽然我不完全明白答案。这里有两个表达式可以产生所需的结果,一个使用“Let”,另一个使用“Select”:

在两个表达式中的第一个表达式中,第一个“Select,”.Select(sub=>sub.substanders(“bot”)“Let”形式生成可枚举的元素枚举,或者更准确地说

System.Linq.Enumerable+WhereSelectEnumerableIterator`2[System.Xml.Linq.XElement,System.Collections.Generic.IEnumerable`1[System.Xml.Linq.XElement]]
第一个“Select”.Select(sub=>new{bots=sub.substanders(“bot”))在两个表达式中的第二个“Select”表单中生成一个匿名类型的可枚举项,每个匿名类型都包含一个可枚举项,名为XElements的“bots”:

System.Linq.Enumerable+WhereSelectEnumerableIterator`2[System.Xml.Linq.XElement,<>f__AnonymousType0`1[System.Collections.Generic.IEnumerable`1[System....
第一个表达式中外部“Let”中的“bots”类型与第二个表达式中外部“Select”中的“bots”类型不同。在“Let”中,“bots”的类型(大致)为
IEnumerable
(其全名为

System.Linq.Enumerable+WhereSelectEnumerableIterator`2[System.Xml.Linq.XElement,System.Collections.Generic.IEnumerable`1[System.Xml.Linq.XElement]]
通过在内部选择“bots”中的每个“bot”都是一个
IEnumerable
,我们可以更详细地看到:

LET中的机器人类型

IEnumerable(2项)

typeof(IEnumerable)

typeof(IEnumerable)

在外部“选择”中,“机器人”的类型为

选择内部机器人的类型

IEnumerable(2项)

IEnumerable(2项)

typeof(XElement)

typeof(XElement)

IEnumerable(2项)

typeof(XElement)

typeof(XElement)

人们很容易认为它们在语义上是相同的,但事实并非如此。与“Let”形式相比,“Select”形式在类型级别上的隐式打包多了一个级别,反之亦然,这取决于您的观点

同样,很明显,“Let”在.Select的结果上运行一次(sub=>sub.substands(“bot”)),而“Select”在每个结果上运行多次 以下是错误的,因为它忽略了“包装级别”

正如我所说,我还没有完全理解这一现象的每一个细节。也许再举几个例子,再加上一夜的失眠,我将开始对这一现象形成更精确的直觉。以下是我的完整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")
    ;
}

let
只是一个简化转换的关键字,例如
tmp=>new{fst=tmp.bots.First(),snd=tmp.bots.Last()}
。与其使用
let
扩展方法,不如使用
Select
方法:

root.Descendants("sub")
    .Select(sub => sub.Descendants("bot"))
    .Select(bots => new{fst = bots.First(), snd = bots.Last()})
    .Dump("bottoms2");

在C#expression语言中,
let
不就是语法糖吗?我不知道你会在方法语法中使用任何
let
扩展方法。System.Interactive中肯定有一个let扩展方法。下面是一个正确编译的示例(但不是我想要的:(root.substands(“sub”)。let(bots=>bots.Select(bot=>bot)).Dump(“bottoms2”)这里是Let的另一个用法,它可以工作(但不能做我想要的:)root.substands(“sub”).Let(bots=>bots.Zip(bots,(a,b)=>new{a=a,b=b})).Dump(“bottoms2”)你把事情弄得太复杂了。看看我的回答。我可以发誓我试过了,但没有成功,但一定是在我在森林里迷路的时候。谢谢你的营救!
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")
    ;
System.Xml.Linq.XContainer+<GetDescendants>d__a
    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")
    ;
root.Descendants("sub")
    .Select(sub => sub.Descendants("bot"))
    .Let(bots => new{fst = bots.First(), snd = bots.Last()})
    .Dump("bottoms2")
    ;
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")
    ;
}
root.Descendants("sub")
    .Select(sub => sub.Descendants("bot"))
    .Select(bots => new{fst = bots.First(), snd = bots.Last()})
    .Dump("bottoms2");