2013-08-26 65 views
2

假設我們有一個來源的IEnumerable序列:調試時檢查IEnumerable「堆棧」?

IEnumerable<Tuple<int, int>> source = new [] { 
    new Tuple<int, int>(1, 2), 
    new Tuple<int, int>(2, 3), 
    new Tuple<int, int>(3, 2), 
    new Tuple<int, int>(5, 2), 
    new Tuple<int, int>(2, 0), 
}; 

我們要應用一些過濾器和一些轉換:

IEnumerable<int> result1 = source.Where(t => (t.Item1 + t.Item2) % 2 == 0) 
           .Select(t => t.Item2) 
           .Select(i => 1/i); 
IEnumerable<int> result2 = from t in source 
          where (t.Item1 + t.Item2) % 2 == 0 
          let i = t.Item2 
          select 1/i; 

這兩個查詢是等效的,都將在最後一個項目拋出DivideByZeroException

但是,當枚舉第二個查詢時,VS調試器將讓我檢查整個查詢,因此非常方便地確定問題的根源。

VS Debugger is useful

然而,當第一個查詢列舉沒有相應的幫助。檢查到LINQ執行國債收益率沒有有用的數據,可能是由於二進制進行優化:

LINQ is too optimized

有沒有辦法來有效地檢查枚舉值了的IEnumerable的「棧」時不使用查詢語法?查詢語法不是一種選擇,因爲共享代碼是不可能的(即,轉換不是微不足道的,並且不止一次使用)。

回答

1

但你可以調試第一個。只要在任何一個lambdas上插入一個斷點,你就可以自由地檢查參數的值或其他範圍內的其他值。

enter image description here

當調試然後就可以檢查的值(在第一Where內斷裂的情況下)tt.Item1

enter image description here

至於你可以檢查01的原因在第二個查詢中執行最後的select時,但不是您的第一個查詢,這是因爲您尚未創建等效查詢。你編寫的第二個查詢,當編譯器寫出來時,而不是會生成類似於你的第一個查詢。它會巧妙地創造出一些東西,但仍然顯着不同。它會創建這樣的事情:

IEnumerable<int> result1 = source.Where(t => (t.Item1 + t.Item2) % 2 == 0) 
         .Select(t => new 
         { 
          t, 
          i = t.Item2, 
         }) 
         .Select(result => 1/result.i); 

一個let調用不只是選擇的是價值,因爲你寫的第一個查詢一樣。它選擇一個新的匿名類型,從let子句中取出值以及先前的值,然後修改後續查詢以提取相應的變量。這就是爲什麼「以前的」變量(即t仍然在查詢結束的範圍內(在編譯時和運行時;這本身應該是一個很大的暗示)。 select,你可以通過調試器看到result.t的值。

+0

是的,但是如果序列很大,那麼我們會得到很多不相關的命中。我們不能(總是)在斷點上設置適當的條件,因爲我們還不知道問題是什麼!問題是「我怎麼變成了零?」,並且在Where子句中確定相應的中斷條件並不總是微不足道的。 – felipe

+0

@felipe請參閱編輯。 – Servy

+0

標記爲答案,似乎沒有創建中間類型的替代方法。謝謝! – felipe