将foreach转换为Linq

现行代码:

对于MapEntryTable中的每个元素,检查属性IsDisplayedColumnIsReturnColumn ,如果它们为true,则将该元素添加到另一组列表中,其运行时间为O(n) ,将有许多元素都具有false属性,因此不会被添加到循环中的任何列表中。

  foreach (var mapEntry in MapEntryTable) { if (mapEntry.IsDisplayedColumn) Type1.DisplayColumnId.Add(mapEntry.OutputColumnId); if (mapEntry.IsReturnColumn) Type1.ReturnColumnId.Add(mapEntry.OutputColumnId); } 

以下是Linq版本的相同:

 MapEntryTable.Where(x => x.IsDisplayedColumn == true).ToList().ForEach(mapEntry => Type1.DisplayColumnId.Add(mapEntry.OutputColumnId)); MapEntryTable.Where(x => x.IsReturnColumn == true).ToList().ForEach(mapEntry => Type1.ReturnColumnId.Add(mapEntry.OutputColumnId)); 

我正在将所有这些foreach代码转换为linq,因为我正在学习它,但我的问题是:

  • 在这种情况下,我是否可以获得Linq转换的任何优势,还是不利?

  • 有没有更好的方法来使用Linq做同样的事情

更新:

考虑这样的情况:列表中的1000个元素中的80个元素都具有false属性,那么在哪里为我提供了快速查找具有给定条件的元素的好处。

Type1是一个自定义类型,包含List结构, DisplayColumnIdReturnColumnId

ForEach不是LINQ方法。 这是List的一种方法。 它不仅不是LINQ的一部分,而且非常违背LINQ的价值和模式。 Eric Lippet 在一篇博客文章中解释了这一点,该文章是他在C#编译团队中的主要开发人员时编写的。

你的“LINQ”方法也是:

  • 完全不必要地复制要添加到列表中的所有项目,这既浪费时间和内存,又与LINQ执行查询时延迟执行的目标冲突。
  • 除了Where运算符之外, 实际上不是查询 。 您正在处理查询中的项目,而不是执行查询。 LINQ是一种查询工具,而不是用于操作数据集的工具。
  • 您正在迭代源序列两次。 这可能是也可能不是问题,取决于源序列实际是什么以及迭代它的成本是多少。

使用LINQ的解决方案就像它设计的那样,就像使用它一样:

 foreach (var mapEntry in MapEntryTable.Where(entry => mapEntry.IsDisplayedColumn)) list1.DisplayColumnId.Add(mapEntry.OutputColumnId); foreach (var mapEntry in MapEntryTable.Where(entry => mapEntry.IsReturnColumn)) list2.ReturnColumnId.Add(mapEntry.OutputColumnId); 

我会说使用foreach循环的原始方式,因为你只是在列表中迭代1次。

你的linq应该看起来更像这样:

 list1.DisplayColumnId.AddRange(MapEntryTable.Where(x => x.IsDisplayedColumn).Select(mapEntry => mapEntry.OutputColumnId)); list2.ReturnColumnId.AddRange(MapEntryTable.Where(x => x.IsReturnColumn).Select(mapEntry => mapEntry.OutputColumnId)); 

foreach与Linq ForEach的表现几乎完全相同,彼此在纳秒之内。 假设在测试时两个版本的循环中都有相同的内部逻辑。

然而,for循环的表现优于LARGE。 for(int i; i

使用Linq ForEach循环确实有一个很大的缺点。 你无法摆脱循环。 如果你需要这样做,你必须维护一个类似“breakLoop = false”的布尔值,将其设置为true,并且如果breakLoop为true,则每次递归退出…在那里执行不好。 其次你不能使用continue,而是使用“return”。

我从不使用Linq的foreach循环。

如果你正在处理linq,例如

 List things = .....; var oldThings = things.Where(p.DateTime.Year < DateTime.Now.Year); 

内部将与linq联系并仅返回年份少于当年的项目。 凉..

但如果我这样做:

 List things = new List(); foreach(XElement node in Results) { things.Add(new Thing(node)); } 

我不需要为每个循环使用linq。 即使我做了......

 foreach(var node in thingNodes.Where(p => p.NodeType == "Thing") { if (node.Ignore) { continue; } thing.Add(node); } 

即使我能写出那样清洁的东西

 foreach(var node in thingNodes.Where(p => p.NodeType == "Thing" && !node.Ignore) { thing.Add(node); } 

没有理由我能想到这样做..>

  things.ForEach(thing => { //do something //can't break //can't continue return; //<- continue }); 

如果我想要最快的循环,

 for (int i = 0; i < things.Count; ++i) { var thing = things[i]; //do something } 

会更快。

您的LINQ不太正确,因为您将Where的结果转换为List,然后使用ForEach对这些结果进行伪迭代以添加到另一个列表。 使用ToListAddRange将序列转换或添加到列表。

例如,覆盖list1 (如果它实际上是List ):

 list1 = MapEntryTable.Where(x => x.IsDisplayedColumn == true) .Select(mapEntry => mapEntry.OutputColumnId).ToList(); 

或附加:

 list1.AddRange(MapEntryTable.Where(x => x.IsDisplayedColumn == true) .Select(mapEntry => mapEntry.OutputColumnId)); 

在C#中,要在一次调用中执行您想要的function,您必须编写自己的分区方法。 如果您愿意使用F#,则可以使用List.Partition<'T>

https://msdn.microsoft.com/en-us/library/ee353782.aspx