2013-11-22 59 views
1

LINQ是否執行寬度優先或深度優先?是LINQ廣度優先還是深度優先?

例如,考慮下面的代碼片斷:

var stories = syndicationFeed.Items 
    .Select (item => ConstructStory (item)) 
    .Where (story => story != null) 
    .OrderByDescending (story => story.WhenPublished) 
    .ToList(); 

是對其中應用子句之前在它整體執行的選擇條款(如同樣爲訂單子句)?或者在試圖計算下一個對象之前計算最終列表中的第一個對象?

+3

這取決於你使用LinqToObjects(使用'IEnumerable's)或LinqToEntites(使用'IQueryable's) –

+0

我特別指LinqToObjects(使用IEnumerable) – prmph

回答

2

「之前整體上在它執行SELECT子句where子句應用」

如果您正在使用LinqToEntites(IQueyable對象),它是由一個可與數據源的驅動程序工作確定訂單。

如果您正在使用LinqToObjects工作是管道的地方就可以了,所以它第一個前每一個對象上執行Select進程在它的過濾檢查,期間過濾器檢查選擇評估。你的代碼大致相當於。

List<Story> YourCode(Feed syndicationFeed) 
{ 
    IEnumerable<Story> storiesAsIEnumrable = syndicationFeed.EquivlantFunctionUpToToList() 
    var stories = storiesAsIEnumrable.ToList(storiesAsIEnumrable); 

    return stories; 
} 

IEnumerable<Story> EquivlantFunctionUpToToList(Feed feed) 
{ 
    List<Story> storyBufferForSorting = new List<Story>(); 
    foreach(item in feed.Items) 
    { 
     var story = ConstructStory(item); 
     if(story != null) 
     { 
      storyBufferForSorting.Add(story); 
     } 
    } 
    storyBufferForSorting.OrderBy((x,y) => y.WhenPublished.CompareTo(x.WhenPublushed)); 
    return storyBufferForSorting; 
} 

//Taken right from the .NET framework source code via ILSpy 
public static List<TSource> ToList<TSource>(this IEnumerable<TSource> source) 
{ 
    if (source == null) 
    { 
     throw Error.ArgumentNull("source"); 
    } 
    return new List<TSource>(source); 
} 

所以,是的,該項目將充分排序之前列舉的(它必須能找出哪些項目是「第一」),以及一個完整的枚舉再次發生創造了ToList()名單,但過濾器在第一次枚舉過程中發生,而不是作爲額外的過程。

+0

感謝您的洞察 – prmph

0

如果syndicationFeed.ItemsIQueryable,則查詢將通過組合各種方法來構建,直到實際請求數據(在這種情況下,通過調用ToList())。

但是,這實際上取決於您正在查詢什麼類型的數據以及您想要對其執行哪些操作。

0

我想沒有明確的答案(假設你談論「Linq to Entities」)。我相信這取決於在「SQL查詢」中翻譯「Linq to Entities」查詢的「Linq to Entities」驅動程序。然後它取決於SQL Server(或任何其他數據庫服務器)處理特定查詢的方式。

如果你真的關心性能,我會建議配置特定的查詢。使用SQL服務器,您可以查看執行計劃。也許這會回答你的問題。檢查SQL server profilerSQL Server execution planning

希望我幫了忙!

0

你說你指的是LinqToObjects(使用Enumarble(of T)上的擴展方法)。

大多數LinqToObjects方法在實現中使用yield關鍵字,而Select是其中之一。 這意味着它「選擇」所有的項目,然後進行到下一個動作,它一個接一個地做。

0

Linq(對象)方法可以是流式傳輸非流式傳輸。有一個完整的概述here

這大部分可以通過常識來理解。 A Select可以將其數據流式傳輸到隨後的LINQ方法,因爲它可以「激發並忘記」單個項目。另一方面,在它可以做任何事情之前,顯然需要知道所有元素。所以Select是流媒體,OrderBy不是。同樣,Where也是流媒體,因爲它同樣只需要處理一個元素。

您可以通過將跟蹤語句放入用作LINQ方法參數的代表來檢查此問題。我做了一個簡單的測試,用一些對象碰巧找到了某個地方。在Linqpad中:

Crops.Select (c => { ("s-" + c.CrpId).Dump(); return c.CrpId;}) 
    .Where(i => { ("w1-" + i).Dump(); return i > 120; }) 
    .Where(i => { ("w2-" + i).Dump(); return i > 150; }) 
    .OrderBy (i => { ("o-" + i).Dump(); return i; }) 

正如你所看到的,lambda表達式被方法體取代,它基本上沒有任何區別。輸出是:

s-21 
w1-21 
s-93 
w1-93 
... 
s-122 
w1-122 
w2-122 
s-123 
w1-123 
w2-123 
... 
s-158 
w1-158 
w2-158 
o-155 
o-157 
o-158 

所以你看到WhereSelect s的流,以及OrderBy等待,直到所有的項目都可用。這也會導致第二個Select(這裏有點人爲設計,確定)僅在必要時執行。