我可以将foreach和LINQ查询合并吗?

8

我有一段 C# 代码,看起来像这样:

foreach (var entry in this.ChangeTracker.Entries()
                     .Where(e => e.Entity is IAuditableTable &&
                                 e.State == EntityState.Added))
{
    IAuditableTable e = (IAuditableTable)entry.Entity;
    e.ModifiedDate = DateTime.Now;
}

这似乎是foreach和LINQ的结合体。请问是否有可能将其转化为一个仅包含LINQ语句的代码块,而不需要使用foreach循环?

1
我建议在没有副作用的部分使用LINQ,在有副作用的部分使用foreach,例如对e进行变异的情况。 - CodesInChaos
1
您可以这样做,请不要那样做。使用查询提问。使用foreach执行操作。不要使用查询来执行操作。 - Eric Lippert
4个回答

34

我建议不要这样做。尽可能保持所有内容的可读性:

var auditableTables = this.ChangeTracker.Entries()
                                        .Where(e => e.State == EntityState.Added)
                                        .Select(e => e.Entity)
                                        .OfType<IAuditableTable>();

foreach (var table in auditableTables)
{
    table.ModifiedDate = DateTime.Now;
}

我的编程经验法则是:如果你无法像读一句话一样阅读代码,那么它需要修复。


非常正确 :) 而其他答案似乎只是为了回答问题,这个答案确实提出了一个整洁的处理方式。谢谢。 - Tolga Evcimen

7
你最好的选择是:
foreach (var entry in from x in this.ChangeTracker.Entries()
                      where x.Entity is IAuditableTable &&
                            x.State == EntityState.Added
                      select (IAuditableTable)x.Entity)
{
    entry.ModifiedDate = DateTime.Now;
}

更新操作没有查询语法。


4

Here you go:

this.ChangeTracker.Entries()
    .Where(e => e.Entity is IAuditableTable && e.State == EntityState.Added)
    .ToList()
    .ForEach(entry => {
        IAuditableTable e = (IAuditableTable) entry.Entity;
        e.ModifiedDate = DateTime.Now;
    });

5
请稍等,这个问题不是关于效率,而是关于可行性。答案是肯定的,微软发明了ForEach()并提供了在LINQ风格中使用foreach的功能。您可能不喜欢它,但它确实是可行的。 - Borys Generalov
1
@BorysGeneralov 1. ForEach的行为与foreach完全不同。ForEach在.NET 4中使用for循环,在.NET 4.5中使用while循环。在.NET 4.0中,这会导致未定义的行为。幸运的是,在.NET 4.5中有一个版本检查(抛出InvalidOperationException)。基本上,说foreach(var e:enumerable){}enumerable.ToList().ForEach(e=>{})相同是完全错误的。2. 对于Metro应用程序,这将无法编译,因为ForEach已经不存在了。 - Leri
2
效率始终是重要的,特别是在集合操作中。 - Leri
@DavidBainbridge,这是真的(我给了他一个+1),但在SO上回答“问题”而不是“提问”(它们不一定是相同的东西!)是可以接受的,据我所知。 - Brian S
3
@DavidBainbridge 虽然如此,SO也关注“好”的代码。我们不希望一些笨蛋简单地将这段糟糕的代码复制粘贴到他们的应用程序中。这个答案应该说,“是的,这是可能的:<code> 然而,这会增加额外的开销,因为...” - Cole Tobin
显示剩余5条评论

3
这应该可以解决问题。
this.ChangeTracker.Entries()
    .Where(e => e.Entity is IAuditableTable &&
                e.State == EntityState.Added)
    .ToList().ForEach(entry => {
        IAuditableTable e = (IAuditableTable)entry.Entity;
        e.ModifiedDate = DateTime.Now;
    });

4
虽然这种方法可以运行,但它需要很高的处理器性能。事实上,你会两次枚举可枚举对象,一次使用 ToList(),一次使用 ForEach() 调用。 - Matan Shahar
2
@ISun 这里需要注意双重枚举,但它回答了问题,我认为它不应该被点踩。 - asawyer
感谢@asawyer的支持,正如你所说,这是OP问题的确切答案。并不声称完美无缺。如果你看一下我对被接受的答案的评论,你会发现我认为那个答案是最好的。 - Tolga Evcimen
你可以为IEnumarable编写一个ForEach扩展,它的行为与列表ForEach相同,并在扩展中使用普通的foreach循环。 - Letseatlunch

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接