用LINQ替换嵌套的foreach; 在内部修改和更新属性

p.c*_*ell 13 c# linq algorithm linq-to-objects

考虑在5或6级深度的对象的一个​​或多个属性上更改数据成员的要求.

有些子集需要迭代才能到达需要检查和修改的属性.

在这里,我们将调用一种方法来清除Employee的街道地址.由于我们在循环中更改数据,因此当前实现需要一个for循环来防止异常:

无法分配给"someVariable",因为它是'foreach迭代变量'

这是使用嵌套foreach和a 的当前算法(混淆)for.

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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

问题: 可以使用LINQ重新实现此算法吗?要求原始集合通过静态方法调用更改其数据.

更新:我正在思考/倾向于jQuery/selector类型解决方案的方向.我没有以这种方式具体说出这个问题.我意识到我对这个想法的影响太大(没有副作用).谢谢大家!如果有这样的方法来执行类似jQuery的选择器,请让我们看看它!

Meh*_*ari 19

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);
Run Code Online (Sandbox Code Playgroud)


Sta*_* R. 13

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);
Run Code Online (Sandbox Code Playgroud)


Jos*_*h E 12

LINQ不用于修改对象集.您不希望SELECT sql语句修改所选行的值,不是吗?它有助于记住LINQ代表什么 - 语言集成自然查询.修改linq查询中的对象是恕我直言,反模式.

我认为Stan R.的回答是使用foreach循环的更好的解决方案.


Jay*_*uzi 10

我不喜欢在同一语句中混合使用"查询理解"语法和点名方法调用语法.

我喜欢将查询动作分开的想法.这些在语义上是不同的,因此在代码中将它们分开通常是有意义的.

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);
}
Run Code Online (Sandbox Code Playgroud)

关于代码的一些风格说明; 这些是个人的,所以我可能不同意:

  • 在一般情况下,我尽量避免缩写(Emps,emp,addr)
  • 不一致的名字更令人困惑(addr对比Addresses):选择一个并坚持下去
  • "数字"这个词是暧昧的.它可以是一个身份("囚犯号码378请前进.")或一个计数("该领域的绵羊数量是12").由于我们在代码中经常使用这两个概念,因此明确这一点很有价值.我经常使用"index"作为第一个,"count"作为第二个.
  • type字段设为字符串是代码气味.如果你可以做到这一点,enum你的代码可能会更好.