C# 将foreach转换为Linq

C# 将foreach转换为Linq,c#,linq,C#,Linq,当前代码: 对于MapEntryTable中的每个元素,检查属性IsDisplaydColumn和IsReturnColumn,如果它们为true,则将元素添加到另一组列表中,其运行时间将为O(n),许多元素的两个属性都为false,因此不会添加到循环中的任何列表中 foreach (var mapEntry in MapEntryTable) { if (mapEntry.IsDisplayedColumn) Type1.DisplayColumnId.Add(mapEn

当前代码:

对于
MapEntryTable
中的每个元素,检查属性
IsDisplaydColumn
IsReturnColumn
,如果它们为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,那么where是否为我提供了快速查找具有给定条件的元素的好处


Type1
是一种自定义类型,具有一组
List
结构、
DisplayColumnId
ReturnColumnId

我想说,使用
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循环,它的表现大大优于这两种方法。for(int i;i 不过,使用LINQforeach循环确实有一个很大的缺点。你不能打破这个循环。如果您需要这样做,您必须维护一个类似“breakLoop=false”的布尔值,将其设置为true,并在breakLoop为true时让每个递归退出。。。在那里表现很差。其次,不能使用continue,而是使用return

我从不使用Linq的foreach循环

如果你和linq打交道,例如

List<Thing> things = .....;
var oldThings = things.Where(p.DateTime.Year < DateTime.Now.Year);
即使我能像你一样写那本书

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
   });
things.ForEach(thing=>{
//做点什么
//不能打破
//无法继续

return;//在C#中,要在一次调用中实现您想要的功能,您必须编写自己的分区方法。如果您愿意使用F#,您可以使用
List。分区
ForEach
不是LINQ方法。它是
List
的方法。它不仅不是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);

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

例如,覆盖
列表1
(如果它实际上是一个
列表
):

或附加:

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

如果要稍微缩短代码,请去掉“==true”比较。x=>x.IsDisplayedColumn将返回true如果IsDisplayedColumn为true,则无需将其与实际值进行比较。您可能还调用了ToList()不必要的。@Andrew B第一部分是正确的,但第二部分是强制性的,Foreach是一个列表扩展方法,而不是IEnumerablew什么类型是list1和list2?我从未见过具有DisplayColumnId属性的列表……我肯定在这里遗漏了什么。无论如何,如果没有足够的信息来回答:您可能不需要
ToList()。Foreach()
当您可以使用
sequence.ToList()或
list.AddRange(sequence)将序列分配或附加到列表中时
@MrinalKamboj那么你的名字都很容易让人误解。当变量不是列表时,你不应该有名为
list1
list2
的变量,而且名字
DisplayColumnId
听起来根本不像是一个集合,它的名字意味着它是一个单数标识符。你为什么需要foreach?你可以o类似于Andrew建议的内容:
list1.DisplayColumnId=MapEntryTable.Where(x=>x.isDisplaydColumn)。选择(mapEntry=>mapEntry.OutputColumnId);
list2.ReturnColumnId=MapEntryTable.Where(x=>x.IsReturnColumn)。选择(mapEntry=>mapEntry.OutputColumnId)
@Carlos假设
list2.ReturnColumnId
的类型是
IEnumerable
,并且那里的所有内容都应该替换为该内容。如果不是变量的类型,并且/或者该集合中的某些项也应该保留在那里,那么这将不起作用。啊,好的,这是有意义的。干杯!:)@非常感谢您的详细说明,我将坚持使用foreach,但是linq中的Where扩展方法会快速搜索元素,或者通过完整列表进行枚举,因此不会产生任何过滤的主要好处,因此我的
for (int i = 0; i < things.Count; ++i) {
    var thing = things[i];
    //do something
}
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);
list1 = MapEntryTable.Where(x => x.IsDisplayedColumn == true)
.Select(mapEntry => mapEntry.OutputColumnId).ToList();
list1.AddRange(MapEntryTable.Where(x => x.IsDisplayedColumn == true)
.Select(mapEntry => mapEntry.OutputColumnId));