将 Foreach 转换为 linq 表达式
Posted
技术标签:
【中文标题】将 Foreach 转换为 linq 表达式【英文标题】:Convert Foreach into linq Expression 【发布时间】:2022-01-01 04:03:00 【问题描述】:我正在尝试从Device
表中获取针对当前客户的设备列表。我做到了,但使用了 foreach
循环,但我想使用 linq 来进行操作。
这是我的班级结构
public class Device
public string type get; set;
public int id get; set;
public List<Role> roles get; set;
public class Role
public int roleId get; set;
public string roleName get; set;
public int customerId get; set;
这是我的代码
var devList = list.Where(x => x.type == "device1").ToList();
foreach (var item in devList)
if (item.roles != null)
var kss = item.roles.Where(z => z.customerId == kunId).FirstOrDefault();
if (kss != null)
m.devicesList.Add(item);
我需要针对当前客户获取设备,因此我必须将我当前的用户 ID 与 customerId
进行比较。
如何将其转换为 linq-to-sql?
【问题讨论】:
【参考方案1】:有两种方法可以解决这个问题。
首先,您可以将所有内容放入复杂的Where
操作中:
var devices = list.Where(d => d.type == "device1" && d.roles is object && d.roles.Any(r => r.customerId == kunId) );
其次,您可以将其分散到多个单独的操作中,但每个操作都更容易理解:
var devices = list.Where(d => d.type == "device1").
Where(d => d.roles is object).
Where(d => d.roles.Any(r => r.customerId == kunId) );
任一选项的性能将相似。额外的Where()
调用会产生开销,但并没有你想象的那么多...... JITter 可以用这种代码做一些惊人的事情,看到这两个结果我不会感到惊讶完全相同的 IL。
还要注意在这两种情况下都没有ToList()
调用。通过更长时间地使用IEnumerable
,您通常可以大大提高性能和内存使用率。
此时,我不清楚您是否只是附加到现有的 m.devicesList
或这些值是否可以替换该集合中当前可能存在的任何内容。
假设m.devicesList
被声明为List<Device>
(或类似),前一个选项将如下所示:
m.devicesList.AddRange(devices);
同样,没有必要曾经致电ToList()
。
后者看起来像这样:
m.devicesList = devices.ToList();
我们还可以将这两个步骤合并到一个语句中。以下是四种可能的组合之一:
m.devicesList.AddRange(list.Where(d => d.type == "device1").
Where(d => d.roles is object).
Where(d => d.roles.Any(r => r.customerId == kunId) )
);
【讨论】:
删除了我的评论和我的回答;如果检查添加项目,则错过了内部。你的好多了。 非常感谢您提供这个具有正确解释的惊人解决方案。 @joel-coehoorn @Joel Coehoorn 这完全不相关,但你的头像很好。【参考方案2】:所以你有一个对象kunId
,它与Role.CustomerId
的类型相同。您在对象devList
中还有一个Items
序列。最后,你还有一个属性m.DevicesList
,它实现了ICollection<Item>
(=你有一个方法Add(Item)
)
您当前的方法检查您的项目序列中的每个Item
。如果属性Roles
为空,则忽略该项目。另一方面,如果您有一些角色,您将获得第一个或默认角色,其属性CustomerId
的值等于 kunId。如果有这样的非默认角色,那么您将项目添加到m.DevicesList
。
我将重新表述这个要求。
当且仅当 Item
具有属性 Roles
的非空值,并且这些角色中的至少一个角色具有等于 kunId
的属性 CustomerId 的值,然后您将项目添加到序列中m.DevicesList
中的项目数
var itemsToAddToMDevicesList = devList.Where(item =>
item.Roles != null &&
item.Roles.Where(role => role.CustomerId == kunId).Any();
换句话说:您要添加到m.DeviceList
的Items
的序列是devList
中所有Items
的序列,其中属性Roles
具有非空值,并且至少具有属性 Roles
中的一个 Role
,其属性 CustomerId 的值等于 kunId
。
【讨论】:
【参考方案3】:始终尝试首先用简单的自然语言“实现”您的要求。
您需要什么设备?那些:
-
类型为“device1”
具有分配给给定客户的角色
分别构建每个部分,添加必要的防护,例如roles != null
你会得到这样的结果:
var devicesFound = list.Where(d =>
d.type == "device1" &&
d.roles != null &&
d.roles.Any(r => r.customerId == kunId));
m.devicesList.AddRange(devicesFound);
【讨论】:
【参考方案4】:您应该能够删除分配给 devList 的 linq 表达式值末尾的 .ToList()。
此外,如果您的目标是优化您正在执行的操作,您可能会考虑维护当前代码,而不是将其转换为 linq 表达式。
不过,如果您需要转换 foreach 循环,请查看 official microsoft documentation,您应该能够在智能感知的帮助下自动完成。
【讨论】:
请添加一些关于您提到的性能方面的解释。 @clyToList()
通常会浪费 CPU 和内存。最好编写代码以坚持使用更简单的 IEnumerable,并在最后一刻保存任何 ToList() 调用。此外,用于 linq 操作的状态机有时会产生内存分配开销。如果您有良好的非 linq 代码,它有时可以胜过等效的 linq,尤其是在云 Web 或虚拟 Web 服务器等内存受限的环境中。
@joel-coehoorn 所以我使用的 foreach 代码比 linq 查询更好?性能也更好?仅仅因为行数我想使用 linq 而不是 foreach。
@JoelCoehoorn 避免ToList()
对我来说也是微不足道的。关于 LINQ 与非 LINQ 代码的陈述是我感兴趣的。开发团队承诺自 LINQ 首次发布以来,它的性能会得到如此多的改进,这就是为什么你在这里所说的关于状态机和其他内容的内容很有趣。强调“拥有好的非LINQ代码”你当然是对的。
@JoelCoehoorn,感谢您的解释。在内存管理需要循环的环境中,尤其是在微服务架构中,尽可能高效是关键。以上是关于将 Foreach 转换为 linq 表达式的主要内容,如果未能解决你的问题,请参考以下文章
LINQ to nHibernate - 将 SQL“NOT IN”表达式转换为 LINQ
如何将带有内连接的 sql 查询转换为 linq lambda 表达式?