考虑在<5> 6级深度的对象的一个或多个属性上更改数据成员的要求。
有些子集需要迭代才能到达需要检查的属性。修改
我们在这里调用一种清除员工街道地址的方法。由于我们在循环中更改数据,因此当前实现需要for
循环来防止异常:
无法分配给“someVariable”,因为它是“foreach迭代变量”
以下是嵌套foreach
和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);
}
}
}
问题: 可以使用LINQ重新实现此算法吗?要求原始集合通过静态方法调用更改其数据。
更新:我在思考/倾向于jQuery / selector类型解决方案的方向。我没有以这种方式具体说出这个问题。我意识到我对这个想法的影响太大(没有副作用)。谢谢大家!如果有这样的方法来执行类似jQuery的选择器,请让我们看看它!
答案 0 :(得分: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);
答案 1 :(得分: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);
答案 2 :(得分:12)
LINQ不用于修改对象集。您不希望SELECT sql语句修改所选行的值,不是吗?它有助于记住LINQ代表什么 - L anguage IN tegrated Q uery。修改linq查询中的对象是恕我直言,反模式。
Stan R.的答案是使用 foreach 循环的更好的解决方案,我认为。
答案 3 :(得分: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);
}
关于代码的一些风格说明;这些是个人的,所以我可能不同意:
Emps
,emp
,addr
)addr
与Addresses
):选择一个并坚持下去type
字段设为字符串是代码气味。如果您可以将其设为enum
,那么您的代码可能会更好。答案 4 :(得分: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; });
答案 5 :(得分: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*/);
答案 6 :(得分:1)
你可以这样做,但你真的不想这样做。一些博主谈到了Linq的功能性,如果你看一下MS提供的所有Linq方法,你会发现它们不会产生副作用。它们产生返回值,但它们不会改变任何其他值。通过Linq ForEach方法搜索参数,您将对此概念有一个很好的解释。
考虑到这一点,你可能想要的是这样的:
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;
})
)
);
答案 7 :(得分:0)
要使用FOREACH循环更新LINQ结果,我首先创建本地'列表'变量,然后使用FOREACH循环执行更新。值以这种方式更新。在这里阅读更多内容: