2011-04-26 123 views
0

請考慮以下示例,它由嵌套XElement和一對Linq表達式的定義組成。第一個表達式按預期工作,通過選擇一個通過獲取機器人(用於底部)的tmp來選擇最底層的第一個和最後一個XElement,存儲在一個匿名類型的新實例中,以重用名稱「機器人「。第二個表達式試圖做同樣的事情,只是使用「Let」,但它根本不起作用。首先,編譯器抱怨類型推斷不起作用,然後,當我放入明確的類型時,它會進入IObservable並且更加丟失。我認爲這是非常直接的,對於失敗感到非常驚訝和失望。如果有人有時間看看並提出建議,我將不勝感激。您可以將以下內容粘貼到LinqPad中,添加對System.Interactive的引用,並查看失敗的編譯。在Linq-to-Objects和Linq-to-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"); 

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") 
    ; 
+0

是不是'let'在C#表達式語言只是語法糖嗎?我沒有意識到任何'Let'擴展方法,您可以使用方法語法。 – 2011-04-26 10:42:21

+0

System.Interactive中絕對有一個Let擴展方法。下面是正確編譯(但不會做我想做的:(root.Descendants( 「子」) \t \t。讓(機器人=>機器人 \t \t \t。選擇(BOT => BOT)) 樣本\t \t使用.dump( 「塔底」) – 2011-04-26 14:45:50

+0

這裏還有一個使用放手的那個作品(但不會做我想做的:) \t root.Descendants( 「子」) \t \t。讓(機器人=> bots.Zip( (a,b)=> new {a = a,b = b})) \t \t .Dump(「bottoms2」) – 2011-04-26 15:41:53

回答

1

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"); 
+0

我可以發誓我試過風趣成功,但是當我迷失在森林裏的時候一定是成功的。感謝救援! – 2011-04-26 19:08:10

0

好的,發現它,雖然我不完全明白答案。下面是產生期望結果的兩個表達式,一個使用「讓」,另一個使用「選擇:」

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") 
    ; 

第一「選擇」。選擇(分=> sub.Descendants(「機器人」) ),在第一兩個表達式的,「讓」的形式,產生XElements的可枚舉的可枚舉,或者更精確地說,

System.Linq.Enumerable+WhereSelectEnumerableIterator`2[System.Xml.Linq.XElement,System.Collections.Generic.IEnumerable`1[System.Xml.Linq.XElement]] 

第一「選擇」。選擇(分=>新的{ bots = sub.Descendants(「bot」)),在這兩個表達式中的第二個表達式中,「選擇」表單生成一個匿名類型的枚舉,其中每個匿名類型都包含一個可枚舉的,名爲「機器人」的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」。它們就像「答案」表達式,除了它們沒有內部轉換。

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") 
    ; 

中的類型「機器人」的外「讓」中的第一個表達式從「機器人」的類型的不同之處外的第二表達式「選擇」。在「機器人」「讓」的類型爲(大約)IEnumerable<IEnumerable<XElement>>(它的全稱是

System.Linq.Enumerable+WhereSelectEnumerableIterator`2[System.Xml.Linq.XElement,System.Collections.Generic.IEnumerable`1[System.Xml.Linq.XElement]] 

我們可以通過選擇在每個「殭屍」中的「機器人」是內部詳細看一個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 

由並行分析上述,我們看到,每個「機器人」,在「機器人」是一個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)

人們很容易想到這些語義上是相同的,但他們沒有。在「選擇」表單中,類型級別的隱式包裝比「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") 
    ; 
} 
+0

你有過分複雜的事情,請參閱我的回覆 – StriplingWarrior 2011-04-26 18:25:53