有没有用LINQ实现以下操作的方法?
foreach (var c in collection)
{
c.PropertyToSet = value;
}
澄清一下,我想遍历集合中的每个对象,然后更新每个对象的一个属性。
我的用例是我在博客文章上有一堆评论,我想遍历博客文章上的每个评论,并将博客文章的日期时间设置为 +10小时。我可以在 SQL 中做到这一点,但我想把它留在业务层中。
有没有用LINQ实现以下操作的方法?
foreach (var c in collection)
{
c.PropertyToSet = value;
}
澄清一下,我想遍历集合中的每个对象,然后更新每个对象的一个属性。
我的用例是我在博客文章上有一堆评论,我想遍历博客文章上的每个评论,并将博客文章的日期时间设置为 +10小时。我可以在 SQL 中做到这一点,但我想把它留在业务层中。
虽然您可以使用 ForEach
扩展方法,但如果您想仅使用框架,则可以这样做
collection.Select(c => {c.PropertyToSet = value; return c;}).ToList();
ToList
是必需的,因为由于 惰性求值,需要立即评估选择。
collection.ToList().ForEach(c => c.PropertyToSet = value);
我正在做这件事
Collection.All(c => { c.needsChange = value; return true; });
我实际上发现了一个很好地实现我想要的功能的扩展方法。
public static IEnumerable<T> ForEach<T>(
this IEnumerable<T> source,
Action<T> act)
{
foreach (T element in source) act(element);
return source;
}
使用:
ListOfStuff.Where(w => w.Thing == value).ToList().ForEach(f => f.OtherThing = vauleForNewOtherThing);
我不确定这是否过度使用了LINQ,但当我想要更新列表中特定条件下的特定项目时,它对我很有效。
Although you specifically asked for a LINQ solution and this question is quite old I post a non-LINQ-solution. This is because LINQ (=
language integrated query) is meant to be used for queries on collections. All LINQ-methods don’t modify the underlying collection, they just return a new one (or more precise an iterator to a new collection). Thus whatever you do e.g. with a Select
doesn’t effect the underlying collection, you simply get a new one.
当然,您可以使用ForEach(这不是LINQ,而是 List&lt; T&gt; code>上的扩展),但这 实际上 em> 仍然使用
foreach code>,但使用lambda表达式。 除此之外,每个 em> LINQ方法都是通过使用
foreach code>或
for code>对集合进行内部迭代的,但它只是从客户端隐藏了它。我认为这没有任何可读性或可维护性(在包含lambda表达式的方法调试期间编辑您的代码)。
说到这一点,不应该使用LINQ来修改您的集合中的项目。更好的方法是您在问题中已经提供的解决方案。通过经典循环,您可以轻松迭代您的集合并更新其项目。事实上,所有那些依赖于List.ForEach
的解决方案都没有什么不同,但从我的角度来看要难得多。
所以在您想要更新集合元素的情况下,不应使用LINQ。
:没有内置的扩展方法来完成这个任务。虽然定义一个扩展方法相当简单。在这篇文章的底部,我定义了一个名为 Iterate 的方法。它可以像这样使用。
collection.Iterate(c => { c.PropertyToSet = value;} );
迭代源代码
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++;
}
}
我尝试了几种方案,但还是回到了这个人的解决方案。
将这个翻译成中文: http://www.hookedonlinq.com/UpdateOperator.ashx http://www.hookedonlinq.com/UpdateOperator.ashx
再次说明,这是另外一个人的解决方案。但是我已经将代码编译成一个小型库,并经常使用它。
我要在这里粘贴他的代码,以防他的网站(博客)在将来的某个时候停止运行。(最糟糕的是看到一篇文章说“这就是你需要的确切答案”,然后点击链接后出现“404错误”页面)。
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; } );
不,LINQ不支持批量更新的方式。唯一更加简短的方式是使用一个ForEach扩展方法-为什么IEnumerable接口上没有一个ForEach扩展方法?
有些人认为这是一种评论,但对我而言,它是一个回答,因为正确的做法是避免做错误的事情。因此,这个问题的答案就在问题本身。
不要使用LINQ 修改数据。使用循环。
你可以使用LINQ将你的集合转换为数组,然后调用Array.ForEach():
Array.ForEach(MyCollection.ToArray(), item=>item.DoSomeStuff());
很明显,这对结构体集合或内置类型如整数或字符串不起作用。
我写了一些扩展方法来帮助我解决这个问题。
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;
}
}
}
我是这样使用的:
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
作为参考,参数检查:
/// <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");
}
}
}
}
这是我使用的扩展方法...
/// <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;
}
你可以使用Magiq,这是一个专为LINQ设计的批量操作框架。(链接)
我认为你想要更改查询内部的值,因此你可以编写一个函数来实现。
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; }
}
但我不确定这是否是您想要的。
我的两分钱:-
collection.Count(v => (v.PropertyToUpdate = newValue) == null);
引用Adi Lester的回答(https://stackoverflow.com/a/5755487/8917485)
我相当喜欢这个答案,但是它有一个漏洞。它只是在创建的新列表中更改值。它必须更改为两行以读取实际更改的列表。
var aList = collection.ToList();
aList.ForEach(c => c.PropertyToSet = value);