首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Linq-to-Objects和Linq-to-XML中的Let语义问题

Linq-to-Objects和Linq-to-XML中的Let语义问题
EN

Stack Overflow用户
提问于 2011-04-26 08:57:43
回答 2查看 422关注 0票数 0

请考虑以下示例,其中包含一个嵌套XElement的定义和一对Linq表达式。第一个表达式按照预期工作,通过选择通过获取机器人(用于底部)而生成的tmp,迭代地获取底层的第一个和最后一个XElements,这些tmp存储在匿名类型的新实例中,以重用名称“bot”。第二个表达式尝试做同样的事情,只是使用了一个"Let",但它根本不起作用。首先,编译器抱怨类型推断不起作用,然后,当我放入显式类型时,它进入IObservable,甚至更迷失。我希望这是完全直截了当的,但对失败感到非常惊讶和困惑。如果有人有时间看一看并给出建议,我将不胜感激。您可以将以下内容粘贴到LinqPad中,添加对System.Interactive的引用,然后查看失败的编译。

代码语言:javascript
复制
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")
    ;
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2011-04-27 02:25:00

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

代码语言:javascript
复制
root.Descendants("sub")
    .Select(sub => sub.Descendants("bot"))
    .Select(bots => new{fst = bots.First(), snd = bots.Last()})
    .Dump("bottoms2");
票数 1
EN

Stack Overflow用户

发布于 2011-04-27 02:14:57

好吧,我找到了,尽管我不完全理解答案。下面是两个产生所需结果的表达式,一个使用"Let“,另一个使用”Select“:

代码语言:javascript
复制
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,或者,更准确地说,

代码语言:javascript
复制
System.Linq.Enumerable+WhereSelectEnumerableIterator`2[System.Xml.Linq.XElement,System.Collections.Generic.IEnumerable`1[System.Xml.Linq.XElement]]

第一个"Select“.Select(sub =sub.Descendants(”=>“))位于两个表达式中的第二个表达式"Select”形式中,它生成一个匿名类型的可枚举对象,其中每个类型都包含XElements的一个可枚举的、命名为“bot”的对象:

代码语言:javascript
复制
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“表达式一样,只是它们没有内部转换。

代码语言:javascript
复制
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“中的”bot“的类型与第二个表达式中外部"Select”中的“bot”的类型不同。在"Let“中,”bot“的类型(大致)是IEnumerable<IEnumerable<XElement>> (它的全名是

代码语言:javascript
复制
System.Linq.Enumerable+WhereSelectEnumerableIterator`2[System.Xml.Linq.XElement,System.Collections.Generic.IEnumerable`1[System.Xml.Linq.XElement]]

我们可以通过选择“机器人”中的每个“机器人”是一个IEnumerable<XElement>来查看更详细的内容。

代码语言:javascript
复制
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>)

在外部的"Select“中,”bot“的类型是

代码语言:javascript
复制
System.Xml.Linq.XContainer+<GetDescendants>d__a

通过对上面的并行分析,我们看到“机器人”中的每个“机器人”都是某个东西的IEnumerable,并且某个东西是一个XElement。

代码语言:javascript
复制
    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”表单多一个级别的隐式打包,反之亦然,这取决于您的观点。

此外,很明显,“让”“运行”一次.Select(子=> sub.Descendants(“机器人”))的结果,而“选择”运行多次,每一个结果都运行一次以下是错误的,因为它忽略了“打包级别”。

代码语言:javascript
复制
root.Descendants("sub")
    .Select(sub => sub.Descendants("bot"))
    .Let(bots => new{fst = bots.First(), snd = bots.Last()})
    .Dump("bottoms2")
    ;

正如我所说的,我还没有完全理解这种现象的每一个细节。也许再举几个例子,再睡一晚,我就会开始对它有一个更精致的直觉。这是我完整的LinqPad脚本,如果你对这个细节感兴趣的话:

代码语言:javascript
复制
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")
    ;
}
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/5784846

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档