使用LINQ更新集合中的所有对象

lom*_*axx 460 .net c# linq foreach

有没有办法使用LINQ执行以下操作?

foreach (var c in collection)
{
    c.PropertyToSet = value;
}
Run Code Online (Sandbox Code Playgroud)

为了澄清,我想迭代集合中的每个对象,然后更新每个对象的属性.

我的用例是我在博客文章中有一堆评论,我想在博客文章中迭代每个评论,并将博客帖子上的日期时间设置为+10小时.我可以在SQL中完成它,但我想将它保留在业务层中.

Cam*_*and 789

虽然您可以使用ForEach扩展方法,但如果您只想使用框架,则可以使用

collection.Select(c => {c.PropertyToSet = value; return c;}).ToList();
Run Code Online (Sandbox Code Playgroud)

ToList是必要的,以评估立即选择由于懒惰的评估.

  • 恕我直言,这远比简单的foreach循环更具表现力.ToList()令人困惑,因为它不会用于强制评估的任何内容,否则将被延迟.该预测也令人困惑,因为它没有用于其预期目的; 相反,它用于迭代集合的元素并允许访问属性以便可以更新它.在我看来,唯一的问题是foreach循环是否可以使用Parallel.ForEach从并行性中受益,但这是一个不同的问题. (38认同)
  • **这个答案是最糟糕的做法。永远不要这样做。** (24认同)
  • 如果集合是一个`ObservableCollection`,那么更改项目而不是创建新列表可能很有用. (8认同)
  • @desaivv是的,这是一种语法滥用,所以Resharper警告你这件事. (7认同)
  • 我赞成这个,因为它是一个非常好的解决方案...我喜欢扩展方法的唯一原因是它使得更清楚地了解到底发生了什么...但是你的解决方案仍然很好 (6认同)
  • 有人可以解释为什么这是最高的投票答案?为什么这比使用ForEach更好? (6认同)
  • @KonradMorawski你正在对IQueryable进行选择而不是IEnumerable,所以很可能是LINQ to SQL.这仅适用于LINQ to对象. (5认同)
  • 请参阅所有其他注释,以了解为什么这是一个坏主意的原因。您绝对不要使用select来执行副作用。选择的目的是选择一个值,而不是为每个循环模拟一个。 (5认同)
  • @user960567:`foreach(var thing in stuff) thing.foo = bar;`很容易被认为是正确的,易于阅读,是一种最佳实践,并且最大限度地减少了无关分配的数量,但这些都不是真正的可怕的这个答案中描述的实践。 (5认同)
  • 我很震惊,这是第一答案,并且有很多票。此代码绝对是一场噩梦。当您只可以使用ForEach时为什么要使用select?甚至for循环的2行语法也更易于编写,并且更能体现代码的作用。为了上帝的爱,请不要在您的代码中这样做! (4认同)
  • @EricLippert 你能解释一下为什么这不是最佳实践吗? (4认同)
  • @ Pan.student地狱没有.说实话,我不确定我是否会使用这种技术. (3认同)
  • 此代码在对集合进行操作时会对其进行突变。“选择”实际上应该是不变的,并避免类似的副作用。ToList也导致分配另一个集合。从本质上讲,此代码乍一看并没有做什么。任何熟悉LINQ扫描的人都不会期望对该集合进行修改。 (3认同)
  • 考虑https://blogs.msdn.microsoft.com/ericlippert/2009/05/18/foreach-vs-foreach/ (3认同)
  • **如果@Cameron 至少能在答案的顶部放置一个巨大的横幅,说明这是一个多么糟糕的想法以及为什么永远不应该这样做**,那就太好了**。卡梅伦,你显然有能力进行教育,特别是现在你的答案是一个突出的谷歌目标,你也有责任。你在评论中的一个小脚注中说,你永远不会做你在这里有效提倡其他人做的事情; 你真的应该告诉人们为什么,因为评论流显然充满了不明白为什么这是一个坏主意的人,而且答案根本不包含劝阻 (3认同)
  • 我尝试了但是我继续遇到编译器错误:`带有语句体的lambda表达式无法转换为表达式树`.当我把它提取到`Func`时,再次没问题了.为什么? (2认同)
  • @ShiftN'Tab:Lambda表达式通常包含一个语句,例如`x => x.Id`.但是,它们可以有多个语句.在这种情况下,您需要在多个语句周围使用大括号.此外,如果需要从多语句lambda返回一个值,则使用`return`语句.上面的例子包括一个多语句lambda:`c => {c.PropertyToSet = value; return c;}`.它有2个语句,为了清楚起见,你通常会在不同的行上写:`c.PropertyToSet = value;`和`return c;`它非常像一个C#2.0匿名方法. (2认同)

Ε Г*_*И О 321

collection.ToList().ForEach(c => c.PropertyToSet = value);
Run Code Online (Sandbox Code Playgroud)

  • @SanthoshKumar:使用`collection.ToList().ForEach(c => {c.Property1ToSet = value1; c.Property2ToSet = value2;});` (33认同)
  • 这比Cameron MacFarland更新列表的答案更有优势,而不是创建新列表. (11认同)
  • 哇,这个答案真的没用.创建一个新集合只是为了能够使用循环 (7认同)
  • @Allie:您的批评是正确的,但这只是一个问题,如果集合中的元素是值类型(即结构)。如果“c”是引用类型,则以这种方式设置“PropertyToSet”将按预期运行。请参阅 https://pastebin.com/rtG2i1KX (4认同)
  • 我该怎么做才能更新多个房产? (3认同)
  • 这个答案完全不正确:它实际上并没有改变原始集合,它只是创建了一个新集合,您仍然需要将其分配到某个地方 (3认同)
  • 我使用VB.NET,这对我不起作用:( (2认同)

Rah*_*hul 67

我这样做

Collection.All(c => { c.needsChange = value; return true; });
Run Code Online (Sandbox Code Playgroud)

  • 这种方法肯定有效,但它违反了`All()`扩展方法的意图,当其他人读取代码时会导致混淆. (28认同)
  • 绝对更喜欢这个不必要地调用ToList(),即使它对使用All()的内容有点误导. (2认同)

lom*_*axx 27

我实际上找到了一个扩展方法,可以很好地完成我想要的操作

public static IEnumerable<T> ForEach<T>(
    this IEnumerable<T> source,
    Action<T> act)
{
    foreach (T element in source) act(element);
    return source;
}
Run Code Online (Sandbox Code Playgroud)

  • 很好:) Lomaxx,也许添加一个例子,所以窥视可以在'动作'中看到它(繁荣的tish!). (4认同)
  • 如果您确实想避免`foreach`循环(无论出于何种原因),这是唯一有用的方法。 (2认同)

Hen*_*ish 13

使用:

ListOfStuff.Where(w => w.Thing == value).ToList().ForEach(f => f.OtherThing = vauleForNewOtherThing);
Run Code Online (Sandbox Code Playgroud)

我不确定这是否过度使用LINQ,但是当想要更新列表中特定条件的特定项时,它对我有用.


Him*_*ere 13

尽管您特别要求提供 LINQ 解决方案并且这个问题已经很老了,但我还是发布了一个非 LINQ 解决方案。这是因为 LINQ(= 语言集成查询)旨在用于对集合的查询。所有 LINQ 方法都不会修改底层集合,它们只是返回一个新集合(或者更准确地说是一个新集合的迭代器)。因此,无论您做什么,例如使用 aSelect都不会影响基础集合,您只需获得一个新集合。

当然,您可以使用 a ForEach(顺便说一下,这不是 LINQ,而是 上的扩展List<T>)。但这字面上foreach无论如何都可以使用,但是带有 lambda 表达式。除此之外,每个LINQ 方法都会在内部迭代您的集合,例如使用foreachfor,但是它只是对客户端隐藏它。我不认为这更具可读性和可维护性(考虑在调试包含 lambda 表达式的方法时编辑代码)。

话虽如此,这不应该使用 LINQ 来修改您的集合中的项目。更好的方法是您在问题中已经提供的解决方案。使用经典循环,您可以轻松迭代您的集合并更新其项目。事实上,所有依赖的解决方案List.ForEach都没有什么不同,但从我的角度来看更难阅读。

因此,在要更新集合元素的情况下,不应使用 LINQ 。

  • 题外话:我同意,并且有太多 LINQ 被滥用的例子,人们请求“高性能 LINQ 链”的例子,做可以用单个循环完成的事情等。我很感激不使用 LINQ对我来说太根深蒂固了,通常不使用它。我看到人们使用 LINQ 链来执行单个操作,但几乎没有意识到每次使用 LINQ 命令时,您都在“幕后”创建另一个 `for` 循环。我觉得创建不那么冗长的方式来完成简单任务是一种语法糖,而不是替代标准编码。 (4认同)

Jar*_*Par 6

没有内置的扩展方法来执行此操作.虽然定义一个是相当直接的.在帖子的底部是我定义的一个名为Iterate的方法.它可以像这样使用

collection.Iterate(c => { c.PropertyToSet = value;} );
Run Code Online (Sandbox Code Playgroud)

迭代源

public static void Iterate<T>(this IEnumerable<T> enumerable, Action<T> callback)
{
    if (enumerable == null)
    {
        throw new ArgumentNullException("enumerable");
    }

    IterateHelper(enumerable, (x, i) => callback(x));
}

public static void Iterate<T>(this IEnumerable<T> enumerable, Action<T,int> callback)
{
    if (enumerable == null)
    {
        throw new ArgumentNullException("enumerable");
    }

    IterateHelper(enumerable, callback);
}

private static void IterateHelper<T>(this IEnumerable<T> enumerable, Action<T,int> callback)
{
    int count = 0;
    foreach (var cur in enumerable)
    {
        callback(cur, count);
        count++;
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 这非常接近我想要的,但是有点..涉及。我发布的博客文章具有类似的实现方式,但代码行较少。 (2认同)
  • @ Cameron,IterateHelper有两个用途。1)单一实现和2)允许在调用时与使用时引发ArgumentNullException。C#迭代器被延迟执行,使助手能够防止在迭代过程中引发异常的奇怪行为。 (2认同)
  • @JaredPar:除非您没有使用迭代器。没有收益声明。 (2认同)

gra*_*der 6

我已经尝试了一些变化,我会继续回到这个人的解决方案.

http://www.hookedonlinq.com/UpdateOperator.ashx

再次,这是别人的解决方案.但我已经将代码编译成一个小型库,并且经常使用它.

我将在这里粘贴他的代码,因为他的网站(博客)将来某个时候不再存在.(没有什么比看到帖子上写着"这是你需要的确切答案",点击和死网更糟糕了.)

    public static class UpdateExtensions {

    public delegate void Func<TArg0>(TArg0 element);

    /// <summary>
    /// Executes an Update statement block on all elements in an IEnumerable<T> sequence.
    /// </summary>
    /// <typeparam name="TSource">The source element type.</typeparam>
    /// <param name="source">The source sequence.</param>
    /// <param name="update">The update statement to execute for each element.</param>
    /// <returns>The numer of records affected.</returns>
    public static int Update<TSource>(this IEnumerable<TSource> source, Func<TSource> update)
    {
        if (source == null) throw new ArgumentNullException("source");
        if (update == null) throw new ArgumentNullException("update");
        if (typeof(TSource).IsValueType)
            throw new NotSupportedException("value type elements are not supported by update.");

        int count = 0;
        foreach (TSource element in source)
        {
            update(element);
            count++;
        }
        return count;
    }
}



int count = drawingObjects
        .Where(d => d.IsSelected && d.Color == Colors.Blue)
        .Update(e => { e.Color = Color.Red; e.Selected = false; } );
Run Code Online (Sandbox Code Playgroud)


Lea*_*dro 6

有些人认为这是一个评论,但对我来说这是一个答案,因为做错事的正确方法是不做。所以,这个问题的答案就在问题本身。

不要使用 LINQ 修改数据。使用循环。


Aar*_*ell 5

不,LINQ 不支持大规模更新的方式。唯一更短的方法是使用ForEach扩展方法 -为什么 IEnumerable 上没有 ForEach 扩展方法?