2010-11-12 77 views
5

我試圖在我鏈接linq方法時,特別是在多次鏈接相同方法時,繞過C#編譯器執行的操作。瞭解C#編譯器如何處理鏈接linq方法

簡單示例:假設我試圖根據兩個條件篩選一個整數序列。

最明顯的事情是這樣的:

IEnumerable<int> Method1(IEnumerable<int> input) 
{ 
    return input.Where(i => i % 3 == 0 && i % 5 == 0); 
} 

但我們可以也鏈條,其中的方法,在每一個條件:

IEnumerable<int> Method2(IEnumerable<int> input) 
{ 
    return input.Where(i => i % 3 == 0).Where(i => i % 5 == 0); 
} 

我有一個在Reflector中查看IL;這是兩種方法明顯不同,但進一步分析它超出了我的知識,此刻:)

我想了解一下:
一)編譯器在每個實例做什麼不同,爲什麼。
B)是否有任何性能問題(不是要微觀優化;!只是好奇)

回答

9

答案(一)是短暫的,但我會去到下面詳細:

編譯器實際上並沒有做的鏈接 - 它發生在運行時,通過正常對象的組織!這裏的魔術遠不及乍一看 - Jon Skeet recently completed the "Where clause" step在他的博客系列中,重新實現LINQ to Objects。我建議閱讀。

在很短的方面,會發生什麼情況是這樣的:每次調用Where擴展方法,它返回一個新WhereEnumerable對象,有兩件事情 - 到以前IEnumerable(你叫Where的一個)的引用,你提供的lambda。

當你開始遍歷這個WhereEnumerable(例如,在foreach在後面的代碼下),在內部它只是開始迭代的IEnumerable有參考。

「這foreach剛纔問我在我的序列中的下一個元素,所以我轉身問你爲你的序列下一個元素」。

直到我們碰到原點,它實際上是某種數組或真實元素的存儲。因爲每個Enumerable都會說「OK,這是我的元素」,將它傳遞迴鏈,它也會應用自己的自定義邏輯。對於Where,它應用lambda來查看元素是否通過條件。如果是這樣,它允許它繼續到下一個呼叫者。如果失敗,則停止在該點處,返回到其引用的Enumerable,並請求下一個元素。

這種情況一直持續發生,直到所有人的MoveNext返回false,這意味着枚舉已完成且沒有更多元素。

要回答(B),有總是有差別,但在這裏它太瑣碎打擾。不要擔心:)

+0

好的答案。我們需要更多像Stackoverflow這樣的材料。 – 2010-11-12 03:53:07

1
  1. 第一個將使用一個迭代器,第二個將使用兩個。也就是說,第一個建立一個階段的管道,第二個將涉及兩個階段。

  2. 兩個迭代器有一個輕微的性能劣勢。