有没有办法使用LINQ执行以下操作?
foreach (var c in collection)
{
c.PropertyToSet = value;
}
为了澄清,我想遍历集合中的每个对象,然后更新每个对象的属性。
我的用例是,我在博客文章上有很多评论,我想遍历博客文章上的每个评论,并将博客文章上的日期时间设置为+10小时。我可以用SQL做到这一点,但我想将其保留在业务层中。
有没有办法使用LINQ执行以下操作?
foreach (var c in collection)
{
c.PropertyToSet = value;
}
为了澄清,我想遍历集合中的每个对象,然后更新每个对象的属性。
我的用例是,我在博客文章上有很多评论,我想遍历博客文章上的每个评论,并将博客文章上的日期时间设置为+10小时。我可以用SQL做到这一点,但我想将其保留在业务层中。
Answers:
虽然可以使用ForEach
扩展方法,但是如果只想使用框架,则可以执行
collection.Select(c => {c.PropertyToSet = value; return c;}).ToList();
该ToList
是必要的,以评估立即到期选择偷懒的评价。
ObservableCollection
说收集是一种话语,那么更改项目而不是创建新列表可能会很有用。
collection.ToList().ForEach(c => c.PropertyToSet = value);
collection.ToList().ForEach(c => { c.Property1ToSet = value1; c.Property2ToSet = value2; });
collection.ToList().ForEach(c => { collection[collection.IndexOf(c)] = new <struct type>() { <propertyToSet> = value, <propertyToRetain> = c.Property2Retain }; });
我正在做这个
Collection.All(c => { c.needsChange = value; return true; });
All()
扩展方法的意图,导致在其他人读取代码时可能造成混乱。
List<>
,则该ForEach()
方法将是一种不太复杂的方法。前ForEach(c => { c.needsChange = value; })
我实际上找到了一种扩展方法,可以很好地满足我的需求
public static IEnumerable<T> ForEach<T>(
this IEnumerable<T> source,
Action<T> act)
{
foreach (T element in source) act(element);
return source;
}
foreach
-loop(无论出于何种原因),这是唯一有用的方法。
foreach
因为代码本身包含foreach
循环
没有内置的扩展方法可以执行此操作。尽管定义一个很简单。我在帖子的底部定义了一种称为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++;
}
}
尽管您专门要求使用LINQ解决方案,并且这个问题已经很老了,但我还是发布了非LINQ解决方案。这是因为LINQ(=语言集成查询)旨在用于集合查询。所有LINQ方法都不修改基础集合,它们只是返回一个新的集合(或更精确地说,是一个迭代器到一个新的集合)。因此,无论您做什么,例如Select
都不会影响基础集合),您只会得到一个新的集合。
当然,您可以使用来实现ForEach
(顺便说一句,它不是LINQ,而是扩展名List<T>
)。但这实际上使用foreach
了,但带有lambda表达式。除此之外,每个 LINQ方法都会在内部对您的集合进行迭代,例如通过使用foreach
或for
,但是它只是对客户端隐藏了它。我不认为这更具可读性和可维护性(想在调试包含lambda-expressions的方法时编辑代码)。
话虽如此,这不应该使用LINQ来修改集合中的项目。更好的方法是您已经在问题中提供的解决方案。使用经典循环,您可以轻松地迭代集合并更新其项目。实际上,所有这些解决方案所依赖的List.ForEach
都没有什么不同,但从我的角度来看却很难阅读。
因此,在要更新集合元素的情况下,不应使用LINQ 。
for
在“后台” 创建另一个循环。我觉得创建简单的任务的冗长方式,而不是代替标准编码是一种语法糖。
我对此进行了一些尝试,然后继续讨论这个人的解决方案。
http://www.hookedonlinq.com/UpdateOperator.ashx
同样,这是别人的解决方案。但是我已经将代码编译到一个小的库中,并相当定期地使用它。
我将在此处粘贴他的代码,以免他的网站(blog)在将来的某个时候不再存在。(没有什么比看到显示“这里是您需要的确切答案”,点击和无效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; } );
Action<TSource>
代替创建额外的委托。但是,在撰写本文时可能还不可用。
where T: struct
在编译时捕获约束。
不,LINQ不支持大规模更新方式。唯一较短的方法是使用ForEach
扩展方法- 为什么IEnumerable上没有ForEach扩展方法?
我写了一些扩展方法来帮助我。
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");
}
}
}
}
您可以使用LINQ将您的集合转换为数组,然后调用Array.ForEach():
Array.ForEach(MyCollection.ToArray(), item=>item.DoSomeStuff());
显然,这不适用于结构或内置类型(如整数或字符串)的集合。
这是我使用的扩展方法...
/// <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;
}
List<T>.ForEach
也起到了作用,但仅仅是为了所有人IEnumerable
。
我假设您想更改查询中的值,以便可以为其编写函数
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; }
}
但是,如果这就是您的意思,请不要犹豫。
假设我们有如下数据,
var items = new List<string>({"123", "456", "789"});
// Like 123 value get updated to 123ABC ..
如果要修改列表并将列表的现有值替换为修改后的值,则首先创建一个新的空列表,然后通过在每个列表项上调用Modify方法来遍历数据列表,
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;