2009-08-26 77 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; });