简体   繁体   English

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

[英]Update all objects in a collection using LINQ

Is there a way to do the following using LINQ?有没有办法使用 LINQ 执行以下操作?

foreach (var c in collection)
{
    c.PropertyToSet = value;
}

To clarify, I want to iterate through each object in a collection and then update a property on each object.澄清一下,我想遍历集合中的每个 object,然后更新每个 object 的属性。

My use case is I have a bunch of comments on a blog post, and I want to iterate through each comment on a blog post and set the datetime on the blog post to be +10 hours.我的用例是我在博客文章上有一堆评论,我想遍历博客文章上的每条评论并将博客文章上的日期时间设置为 +10 小时。 I could do it in SQL, but I want to keep it in the business layer. SQL我可以,但是我想保留在业务层。

While you can use a ForEach extension method, if you want to use just the framework you can do虽然您可以使用ForEach扩展方法,但如果您只想使用框架,您可以这样做

collection.Select(c => {c.PropertyToSet = value; return c;}).ToList();

The ToList is needed in order to evaluate the select immediately due to lazy evaluation .由于惰性求值,为了立即求值选择,需要ToList

collection.ToList().ForEach(c => c.PropertyToSet = value);

我在做这个

Collection.All(c => { c.needsChange = value; return true; });

I actually found an extension method that will do what I want nicely我实际上找到了一个可以很好地完成我想要的扩展方法

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

Use:使用:

ListOfStuff.Where(w => w.Thing == value).ToList().ForEach(f => f.OtherThing = vauleForNewOtherThing);

I am not sure if this is overusing LINQ or not, but it has worked for me when wanting to update a specific items in the list for a specific condition.我不确定这是否过度使用 LINQ,但是当我想针对特定条件更新列表中的特定项目时,它对我有用。

Although you specifically asked for a LINQ solution and this question is quite old I post a non-LINQ-solution.尽管您特别要求提供 LINQ 解决方案并且这个问题已经很老了,但我还是发布了一个非 LINQ 解决方案。 This is because LINQ (= language integrated query ) is meant to be used for queries on collections.这是因为 LINQ(= 语言集成查询)旨在用于对集合的查询。 All LINQ-methods don't modify the underlying collection, they just return a new one (or more precise an iterator to a new collection).所有 LINQ 方法都不会修改底层集合,它们只是返回一个新集合(或者更准确地说是一个新集合的迭代器)。 Thus whatever you do eg with a Select doesn't effect the underlying collection, you simply get a new one.因此,无论您做什么,例如使用Select都不会影响基础集合,您只需获得一个新集合。

Of course you could do it with a ForEach (which isn't LINQ, by the way, but an extension on List<T> ).当然,您可以使用ForEach (顺便说一下,它不是 LINQ,而是List<T>上的扩展)。 But this literally uses foreach anyway, but with a lambda-expression.但这实际上使用了foreach ,但使用了 lambda 表达式。 Apart from this every LINQ method internally iterates your collection eg by using foreach or for , however it simply hides it from the client.除此之外,每个LINQ 方法都会在内部迭代您的集合,例如通过使用foreachfor ,但是它只是对客户端隐藏它。 I don't consider this any more readable nor maintainable (think of edit your code while debugging a method containing lambda-expressions).我不认为这更具可读性和可维护性(考虑在调试包含 lambda 表达式的方法时编辑代码)。

Having said this shouldn't use LINQ to modify items in your collection.话虽如此,这不应该使用 LINQ 来修改您的集合中的项目。 A better way is the solution you already provided in your question.更好的方法是您在问题中已经提供的解决方案。 With a classic loop you can easily iterate your collection and update its items.使用经典循环,您可以轻松迭代您的集合并更新其项目。 In fact all those solutions relying on List.ForEach are nothing different, but far harder to read from my perspective.事实上,所有依赖List.ForEach解决方案都没有什么不同,但从我的角度来看,阅读起来要困难得多。

So you shouldn't use LINQ in those cases where you want to update the elements of your collection.因此,在要更新集合元素的情况下,不应使用 LINQ。

There is no built-in extension method to do this.没有内置的扩展方法可以做到这一点。 Although defining one is fairly straight forward.虽然定义一个是相当简单的。 At the bottom of the post is a method I defined called Iterate.在帖子的底部是我定义的一个名为 Iterate 的方法。 It can be used like so它可以像这样使用

collection.Iterate(c => { c.PropertyToSet = value;} );

Iterate Source迭代源

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++;
    }
}

I've tried a few variations on this, and I keep going back to this guy's solution.我已经尝试了一些变化,我一直回到这个人的解决方案。

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

Again, this is somebody else's solution.同样,这是别人的解决方案。 But I've compiled the code into a small library, and use it fairly regularly.但是我已经将代码编译成一个小库,并且相当经常地使用它。

I'm going to paste his code here, for the off chance that his site(blog) ceases to exist at some point in the future.我将把他的代码贴在这里,以防他的网站(博客)在未来的某个时候不复存在。 (There's nothing worse than seeing a post that says "Here is the exact answer you need", Click, and Dead URL.) (没有什么比看到“这是您需要的确切答案”、点击和死 URL 的帖子更糟糕的了。)

    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; } );

No, LINQ doesn't support a manner of mass updating.不,LINQ 不支持大规模更新的方式。 The only shorter way would be to use a ForEach extension method - Why there is no ForEach extension method on IEnumerable?唯一更短的方法是使用ForEach扩展方法 - 为什么 IEnumerable 上没有 ForEach 扩展方法?

I wrote some extension methods to help me out with that.我写了一些扩展方法来帮助我解决这个问题。

namespace System.Linq
{
    /// <summary>
    /// Class to hold extension methods to Linq.
    /// </summary>
    public static class LinqExtensions
    {
        /// <summary>
        /// Changes all elements of IEnumerable by the change function
        /// </summary>
        /// <param name="enumerable">The enumerable where you want to change stuff</param>
        /// <param name="change">The way you want to change the stuff</param>
        /// <returns>An IEnumerable with all changes applied</returns>
        public static IEnumerable<T> Change<T>(this IEnumerable<T> enumerable, Func<T, T> change  )
        {
            ArgumentCheck.IsNullorWhiteSpace(enumerable, "enumerable");
            ArgumentCheck.IsNullorWhiteSpace(change, "change");

            foreach (var item in enumerable)
            {
                yield return change(item);
            }
        }

        /// <summary>
        /// Changes all elements of IEnumerable by the change function, that fullfill the where function
        /// </summary>
        /// <param name="enumerable">The enumerable where you want to change stuff</param>
        /// <param name="change">The way you want to change the stuff</param>
        /// <param name="where">The function to check where changes should be made</param>
        /// <returns>
        /// An IEnumerable with all changes applied
        /// </returns>
        public static IEnumerable<T> ChangeWhere<T>(this IEnumerable<T> enumerable, 
                                                    Func<T, T> change,
                                                    Func<T, bool> @where)
        {
            ArgumentCheck.IsNullorWhiteSpace(enumerable, "enumerable");
            ArgumentCheck.IsNullorWhiteSpace(change, "change");
            ArgumentCheck.IsNullorWhiteSpace(@where, "where");

            foreach (var item in enumerable)
            {
                if (@where(item))
                {
                    yield return change(item);
                }
                else
                {
                    yield return item;
                }
            }
        }

        /// <summary>
        /// Changes all elements of IEnumerable by the change function that do not fullfill the except function
        /// </summary>
        /// <param name="enumerable">The enumerable where you want to change stuff</param>
        /// <param name="change">The way you want to change the stuff</param>
        /// <param name="where">The function to check where changes should not be made</param>
        /// <returns>
        /// An IEnumerable with all changes applied
        /// </returns>
        public static IEnumerable<T> ChangeExcept<T>(this IEnumerable<T> enumerable,
                                                     Func<T, T> change,
                                                     Func<T, bool> @where)
        {
            ArgumentCheck.IsNullorWhiteSpace(enumerable, "enumerable");
            ArgumentCheck.IsNullorWhiteSpace(change, "change");
            ArgumentCheck.IsNullorWhiteSpace(@where, "where");

            foreach (var item in enumerable)
            {
                if (!@where(item))
                {
                    yield return change(item);
                }
                else
                {
                    yield return item;
                }
            }
        }

        /// <summary>
        /// Update all elements of IEnumerable by the update function (only works with reference types)
        /// </summary>
        /// <param name="enumerable">The enumerable where you want to change stuff</param>
        /// <param name="update">The way you want to change the stuff</param>
        /// <returns>
        /// The same enumerable you passed in
        /// </returns>
        public static IEnumerable<T> Update<T>(this IEnumerable<T> enumerable,
                                               Action<T> update) where T : class
        {
            ArgumentCheck.IsNullorWhiteSpace(enumerable, "enumerable");
            ArgumentCheck.IsNullorWhiteSpace(update, "update");
            foreach (var item in enumerable)
            {
                update(item);
            }
            return enumerable;
        }

        /// <summary>
        /// Update all elements of IEnumerable by the update function (only works with reference types)
        /// where the where function returns true
        /// </summary>
        /// <param name="enumerable">The enumerable where you want to change stuff</param>
        /// <param name="update">The way you want to change the stuff</param>
        /// <param name="where">The function to check where updates should be made</param>
        /// <returns>
        /// The same enumerable you passed in
        /// </returns>
        public static IEnumerable<T> UpdateWhere<T>(this IEnumerable<T> enumerable,
                                               Action<T> update, Func<T, bool> where) where T : class
        {
            ArgumentCheck.IsNullorWhiteSpace(enumerable, "enumerable");
            ArgumentCheck.IsNullorWhiteSpace(update, "update");
            foreach (var item in enumerable)
            {
                if (where(item))
                {
                    update(item);
                }
            }
            return enumerable;
        }

        /// <summary>
        /// Update all elements of IEnumerable by the update function (only works with reference types)
        /// Except the elements from the where function
        /// </summary>
        /// <param name="enumerable">The enumerable where you want to change stuff</param>
        /// <param name="update">The way you want to change the stuff</param>
        /// <param name="where">The function to check where changes should not be made</param>
        /// <returns>
        /// The same enumerable you passed in
        /// </returns>
        public static IEnumerable<T> UpdateExcept<T>(this IEnumerable<T> enumerable,
                                               Action<T> update, Func<T, bool> where) where T : class
        {
            ArgumentCheck.IsNullorWhiteSpace(enumerable, "enumerable");
            ArgumentCheck.IsNullorWhiteSpace(update, "update");

            foreach (var item in enumerable)
            {
                if (!where(item))
                {
                    update(item);
                }
            }
            return enumerable;
        }
    }
}

I am using it like this:我是这样使用它的:

        List<int> exampleList = new List<int>()
            {
                1, 2 , 3
            };

        //2 , 3 , 4
        var updated1 = exampleList.Change(x => x + 1);

        //10, 2, 3
        var updated2 = exampleList
            .ChangeWhere(   changeItem => changeItem * 10,          // change you want to make
                            conditionItem => conditionItem < 2);    // where you want to make the change

        //1, 0, 0
        var updated3 = exampleList
            .ChangeExcept(changeItem => 0,                          //Change elements to 0
                          conditionItem => conditionItem == 1);     //everywhere but where element is 1

For reference the argument check:作为参考参数检查:

/// <summary>
/// Class for doing argument checks
/// </summary>
public static class ArgumentCheck
{


    /// <summary>
    /// Checks if a value is string or any other object if it is string
    /// it checks for nullorwhitespace otherwhise it checks for null only
    /// </summary>
    /// <typeparam name="T">Type of the item you want to check</typeparam>
    /// <param name="item">The item you want to check</param>
    /// <param name="nameOfTheArgument">Name of the argument</param>
    public static void IsNullorWhiteSpace<T>(T item, string nameOfTheArgument = "")
    {

        Type type = typeof(T);
        if (type == typeof(string) ||
            type == typeof(String))
        {
            if (string.IsNullOrWhiteSpace(item as string))
            {
                throw new ArgumentException(nameOfTheArgument + " is null or Whitespace");
            }
        }
        else
        {
            if (item == null)
            {
                throw new ArgumentException(nameOfTheArgument + " is null");
            }
        }

    }
}

我的 2 便士:-

 collection.Count(v => (v.PropertyToUpdate = newValue) == null);

You can use LINQ to convert your collection to an array and then invoke Array.ForEach():您可以使用 LINQ 将您的集合转换为数组,然后调用 Array.ForEach():

Array.ForEach(MyCollection.ToArray(), item=>item.DoSomeStuff());

Obviously this will not work with collections of structs or inbuilt types like integers or strings.显然,这不适用于结构集合或内置类型(如整数或字符串)。

您可以使用Magiq ,一个用于 LINQ 的批处理操作框架。

Here is the extension method I use...这是我使用的扩展方法...

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

        var count = 0;
        foreach (var element in source)
        {
            action(element);
            count++;
        }
        return count;
    }

I assume you want to change values inside a query so you could write a function for it我假设您想更改查询中的值,以便您可以为它编写一个函数

void DoStuff()
{
    Func<string, Foo, bool> test = (y, x) => { x.Bar = y; return true; };
    List<Foo> mylist = new List<Foo>();
    var v = from x in mylist
            where test("value", x)
            select x;
}

class Foo
{
    string Bar { get; set; }
}

But not shure if this is what you mean.但不确定这是否是您的意思。

Some people consider this is a comment, but for me is an answer, because the right way to do something wrong is not do it.有些人认为这是一个评论,但对我来说是一个答案,因为做错事的正确方法是不去做。 So, the answer for this question is in the question itself.所以,这个问题的答案就在问题本身。

DO NOT USE LINQ to modify data.不要使用 LINQ 修改数据。 Use a loop.使用循环。

Quoting Adi Lester's answer ( https://stackoverflow.com/a/5755487/8917485 )引用 Adi Lester 的回答 ( https://stackoverflow.com/a/5755487/8917485 )

I quite like this answer, but this answer has a bug.我很喜欢这个答案,但是这个答案有一个错误。 It just changes values in a new created list.它只是更改新创建列表中的值。 It must be changed to two lines to read the real changed list.它必须改为两行才能读取真正的更改列表。

var aList = collection.ToList();
aList.ForEach(c => c.PropertyToSet = value);

Suppose we have data like below,假设我们有如下数据,

var items = new List<string>({"123", "456", "789"});
// Like 123 value get updated to 123ABC ..

and if we want to modify the list and replace the existing values of the list to modified values, then first create a new empty list, then loop through data list by invoking modifying method on each list item,如果我们想修改列表并将列表的现有值替换为修改后的值,那么首先创建一个新的空列表,然后通过在每个列表项上调用修改方法来循环遍历数据列表,

var modifiedItemsList = new List<string>();

items.ForEach(i => {
  var modifiedValue = ModifyingMethod(i);
  modifiedItemsList.Add(items.AsEnumerable().Where(w => w == i).Select(x => modifiedValue).ToList().FirstOrDefault()?.ToString()) 
});
// assign back the modified list
items = modifiedItemsList;

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM