2009-08-26 74 views
12

考慮對更改的要求,該數據成員是深度爲5或6級的對象的一個​​或多個屬性。用LINQ替換嵌套的foreach;修改和更新深度內的屬性

有些子集需要迭代才能到達需要檢查&修改的屬性。

這裏我們調用一個清除員工街道地址的方法。由於我們在循環中不斷變化的數據,目前的實現需要一個for循環,防止異常:

不能分配給「someVariable」,因爲它是一個「的foreach迭代變量」

這裏的當前算法(混淆)嵌套foreachfor

foreach (var emp in company.internalData.Emps) 
{ 
    foreach (var addr in emp.privateData.Addresses) 
    { 
     int numberAddresses = addr.Items.Length; 

     for (int i = 0; i < numberAddresses; i++) 
     { 
      //transform this street address via a static method 
      if (addr.Items[i].Type =="StreetAddress") 
       addr.Items[i].Text = CleanStreetAddressLine(addr.Items[i].Text); 
     } 
    } 
} 

問: 可以此算法使用LINQ重新實現?要求是原始集合通過該靜態方法調用更改其數據。

更新:我在想/傾向於jQuery /選擇器類型的解決方案。我沒有那樣具體地說這個問題。我意識到我對這個想法過於深刻(沒有副作用)。謝謝大家!如果有這種方法來執行類似jQuery的選擇器,請讓我們看看它!

回答

11

LINQ不打算修改對象的集合。您不希望SELECT sql語句修改所選行的值,對嗎?它有助於記住LINQ代表什麼 - Language Integrated Natural 查詢。在linq查詢中修改對象,恕我直言,反模式。

Stan R.的回答是使用foreach循環的更好解決方案,我想。

+0

謝謝。你和Merhdad的答案在功能上是完全相同的,但我首先看到了你的答案:)我認爲重新輸入代碼是毫無意義的,但我認爲重要的是要提出副作用 – 2009-08-26 17:19:00

+0

LINQ不是SQL Server,儘管實現對於SQL而言,與.NET不同 - 您仍然可以使用這兩個集合(LINQ to SQL)和C#LINQ來更改屬性。 – ppumkin 2017-10-13 12:32:31

18
foreach(var item in company.internalData.Emps 
         .SelectMany(emp => emp.privateData.Addresses) 
         .SelectMany(addr => addr.Items) 
         .Where(addr => addr.Type == "StreetAddress")) 
    item.Text = CleanStreetAddressLine(item.Text); 
+0

將SelectMany改進嵌套的foreach性能? – ManirajSS 2015-01-17 11:20:49

+0

LINQ性能對於這個答案是多麼美好可以忽略不計。 (但沒有... LINQ是該死的很快) – ppumkin 2017-10-13 12:34:49

1

LINQ不提供副作用的選項。但是你可以做:

company.internalData.Emps.SelectMany(emp => emp.Addresses).SelectMany(addr => Addr.Items).ToList().ForEach(/*either make an anonymous method or refactor your side effect code out to a method on its own*/); 
12
var dirtyAddresses = company.internalData.Emps.SelectMany(x => x.privateData.Addresses) 
               .SelectMany(y => y.Items) 
               .Where(z => z.Type == "StreetAddress"); 

    foreach(var addr in dirtyAddresses) 
    addr.Text = CleanStreetAddressLine(addr.Text); 
0

你可以做到這一點,但你並不是真的想。幾位博主談到了Linq的功能性,如果你看看所有MS提供的Linq方法,你會發現它們不會產生副作用。它們會產生返回值,但它們不會改變其他任何東西。通過Linq ForEach方法搜索參數,您將很好地解釋這個概念。

考慮到這一點,你probaly想要的東西是這樣的:

var addressItems = company.internalData.Emps.SelectMany(
    emp => emp.privateData.Addresses.SelectMany(
      addr => addr.Items 
    ) 
); 
foreach (var item in addressItems) 
{ 
    ... 
} 

但是,如果你想你問什麼,那麼這就是你需要去的方向:

var addressItems = company.internalData.Emps.SelectMany(
    emp => emp.privateData.Addresses.SelectMany(
      addr => addr.Items.Select(item => 
      { 
       // Do the stuff 
       return item; 
      }) 
    ) 
); 
10

I don't like mixing "query comprehension" syntax and dotted-method-call syntax在相同的聲明。

我確實喜歡將查詢動作分開。它們在語義上是不同的,因此在代碼中分離它們通常是有意義的。

var addrItemQuery = from emp in company.internalData.Emps 
        from addr in emp.privateData.Addresses 
        from addrItem in addr.Items 
        where addrItem.Type == "StreetAddress" 
        select addrItem; 

foreach (var addrItem in addrItemQuery) 
{ 
    addrItem.Text = CleanStreetAddressLine(addrItem.Text); 
} 

有關代碼的幾個風格說明;這些都是個人的,所以我可能會不同意:

  • 一般情況下,我儘量避免縮寫(Empsempaddr
  • 名稱不一致是更加令人困惑(addrAddresses):選擇一個與堅持它
  • 「數字」這個詞是含糊不清的。它可以是一個身份(「犯人號碼378請前進」)或計數(「該場的羊數爲12」)。由於我們在代碼中大量使用這兩個概念,因此清楚這一點很有價值。我經常使用第一個「索引」和第二個「計數」。
  • 擁有type字段的字符串是一種代碼異味。如果你能使它成爲一個enum你的代碼可能會更好。
+0

感謝您的評論傑伊。該代碼沒有從實際項目中複製粘貼,並且被混淆以供公共消費。 – 2009-08-26 17:58:32

+0

我第二個這個。從更新中分離查詢使得更清潔和更易維護的解決方案。這比這裏列出的其他「一體式」lambda表達式解決方案要好得多。 – SeeMoreGain 2014-02-03 05:15:41

2

骯髒的單線。

company.internalData.Emps.SelectMany(x => x.privateData.Addresses) 
    .SelectMany(x => x.Items) 
    .Where(x => x.Type == "StreetAddress") 
    .Select(x => { x.Text = CleanStreetAddressLine(x.Text); return x; });