我有一个List / IEnumerable对象,我想对其中一些进行计算。
e.g。 myList.Where(F => f.Calculate == TRUE).Calculate();
根据Where子句更新myList,以便执行所需的calulcation并根据需要更新整个列表。
该列表包含“行”,其中金额在Month1,Month2,Month3 ... Month12,Year1,Year2,Year3-5或“Long Term”中 大多数行是固定的,并且总是属于这些月中的一个,但是一些“行”根据其“成熟日期”进行了计算。
哦,只是让事情复杂化!列表(目前)是来自几个linq查询的匿名类型。如果需要,我可以把它变成一个具体的课程,但如果我能避免的话,我宁愿不这样做。
所以,我想调用一种仅适用于计算行的方法,并将正确的数量放入正确的“月份”。 我并不担心计算逻辑,而是如何将它变成一个易于阅读的方法来更新列表,而不是理想情况下返回一个新列表。
[是否有可能写一个lambda扩展方法来进行计算和where - 或者这种矫枉过正是因为Where()已经存在?]
答案 0 :(得分:5)
就个人而言,如果你想要更新列表 ,我只会使用一个简单的循环。遵循和维护将更加简单:
for (int i=0;i<list.Count;++i)
{
if (list[i].ShouldCalculate)
list[i] = list[i].Calculate();
}
这至少会更加明显,它会更新。 LINQ期望执行查询,而不是改变数据。
如果你真的想为此使用LINQ,你可以 - 但如果你想要List<T>
作为你的结果,它仍然需要一份副本:
myList = myList.Select(f => f.ShouldCalculate ? f.Calculate() : f).ToList();
这会根据需要调用您的Calculate()
方法,并在不需要时复制原始文件。但它确实需要一个副本来创建一个新的List<T>
,正如您所提到的那样(在评论中)。
但是,我个人的偏好仍然是在这种情况下使用循环。我发现意图更加明确 - 另外,你避免了不必要的复制操作。
编辑#2:
鉴于此评论:
哦,只是让事情复杂化!列表(目前)是来自几个linq查询的匿名类型
如果您真的想使用LINQ样式语法,我建议您不要在原始查询上调用ToList()
。如果您使用原始IEnumerable<T>
形式保留它们,则可以轻松地执行上面的第二个选项,但是在原始查询中:
var myList = query.Select(f => f.ShouldCalculate ? f.Calculate() : f).ToList();
这样做的好处是只能构建一次列表并阻止复制,因为在此操作之前不会对原始序列进行评估。
答案 1 :(得分:2)
LINQ主要是针对无副作用的查询,而匿名类型本身是不可变的(尽管它们当然可以维护对可变类型的引用)。
鉴于你想要改变列表,LINQ不太适合。
根据Reed的建议,我会使用直接for
循环。但是,如果要在不同点执行不同的计算,可以将其封装起来:
public static void Recalculate<T>(IList<T> list,
Func<T, bool> shouldCalculate,
Func<T, T> calculation)
{
for (int i = 0; i < list.Count; i++)
{
if (shouldCalculate(items[i]))
{
items[i] = calculation(items[i]);
}
}
}
如果真的希望以流畅的方式使用它,你可以让它返回列表 - 但我个人会反对,因为它会看起来就像LINQ一样没有副作用。
和里德一样,我也更愿意通过创建新序列来实现这一目标...
答案 2 :(得分:0)
Select
不会复制或克隆它传递给传递的委托的对象,对该对象的任何状态更改都将通过容器中的引用进行反映(除非它是值类型)。
因此更新参考类型不是问题。
要替换对象(或使用值类型 1 时),这更复杂,并且没有内置的LINQ解决方案。 for循环最清晰(与其他答案一样)。
1 当然,请记住mutable value types are evil。