C#:List 中的更新项值
我正在寻找一种方法来更新列表中的元素,而不是自己枚举它。
我得到了包含名为Projects的List的Class MyProjects。
我想找到MyProjects.Projects-Class,其中Class1(Name)的成员属性等于Value“Overhead”。
什么有效:
foreach (Project prj in MyProjects.Projects) { if (prj.Name == "Overhead") prj.IsActive = true; };
但是,我尝试使用Linq来做同样的事情,但是把它写成一行却失败了。 这有可能吗? 我不喜欢以上述方式进行迭代的原因是我已经迭代了这个代码块中的整个列表并认为可能有更美妙的方式:)
你不应该试图把所有东西都归结为一行 – 尽可能简短。 在这种情况下,您可以使用:
foreach (var project in MyProjects.Projects.Where(p => p.Name == "Overhead")) { project.IsActive = true; }
那是使用LINQ 查询部分,这是LINQ的Q代表的。 我强烈建议你不要像Mayank的回答那样改变LINQ调用中的项目。 它容易出错(原始答案不起作用)并违背了LINQ的精神。
这是可读的,IMO。 它与原始代码完全相同,请注意 – 如果每个项目都可能是您要更新的项目,则无法避免迭代列表中每个项目的内容。
编辑:只是为了笑,如果你真的,真的想用非常简单的代码做,你可以使用:
// DON'T USE THIS! MyProjects.Project.Count(p => p.Name == "Overhead" && (p.IsActive = true));
这里我们使用&&
短路的事实来避免评估赋值( p.IsActive = true
),除非条件匹配。 我们将一个bool
值bool
一个属性是很方便的,因为这意味着我们不需要做任何其他事情来使它成为&&
运算符的有效第二个操作数。 我们使用Count()
来完全评估结果而不创建任何其他列表等 – 我们使用带有谓词的版本来避免甚至需要Where
调用,这是以前的版本所做的。 ( LastOrDefault
也会起作用。)但这一切都是一种可怕的滥用,并且永远不会出现在任何真正的代码中。
我想出了一种方法,可以将它降低到一行,而不会滥用LINQ,因为我只是将它用于查询部分(filter),并使用自定义扩展方法来执行属性设置操作。 你仍然要枚举项目(你必须),但你可以在扩展方法中隐藏它。 我怀疑你是否真的不关心你是否枚举了这个项目,你只是不喜欢foreach
循环在主代码中占用的可见空间量。
使用此扩展方法:
public static IEnumerable SetProperty (this IEnumerable list, Action action) { foreach (var item in list) { action.Invoke(item); } return list; }
这允许您将其降低到一条可读行。
Projects.Where(p => p.Name == "Overhead").SetProperty(p => p.IsActive = true);
完整的测试程序:
using System; using System.Collections.Generic; using System.Linq; namespace ConsoleApplication2 { class Program { static void Main(string[] args) { var Projects = new List() { new Project() { Name="Overhead", IsActive=false }, new Project() { Name="Nadfadfs", IsActive=false }, new Project() { Name="Overhead", IsActive=false }, new Project() { Name="dasfasdf", IsActive=false } }; PrintProjectList(Projects); Console.WriteLine("--Setting property--"); Projects.Where(p => p.Name == "Overhead").SetProperty(p => p.IsActive = true); PrintProjectList(Projects); Console.WriteLine("Press any key to exit."); Console.ReadKey(); } static void PrintProjectList(IEnumerable projects) { foreach(var p in projects) { Console.WriteLine($"Name: {p.Name} IsActive: {p.IsActive}"); } } } class Project { public string Name { get; set; } public bool IsActive { get; set; } } public static class Extensions { public static IEnumerable SetProperty (this IEnumerable list, Action action) { foreach (var item in list) { action.Invoke(item); } return list; } } }
输出:
名称:Overhead IsActive:False
姓名:Nadfadfs IsActive:False
名称:Overhead IsActive:False
姓名:dasfasdf IsActive:False
– 设置属性 –
名称:Overhead IsActive:True
名称:Overhead IsActive:False
名称:Overhead IsActive:True
名称:Overhead IsActive:False
事实certificate,我的SetProperty
函数非常类似于已经内置到框架中的ForEach 。 主要的区别是我的可以在任何IEnumerable
。 由于Eric Lippert在他的博客上指出的原因,这种语法被一些人所喜爱,并被其他人所厌恶(感谢Jon Skeet指出这一点)。 另外,请参阅Microsoft团队的此讨论 。 我会留给你自己得出结论。
另外,调用它SetProperty
有点不准确,因为你可以对集合中的项目执行任何操作。 您可以将其称为ForEach
,但这与框架发生冲突。 我称之为不正面,但也许是PerformAction
。
以下Linq声明应该工作
MyProjects.Projects.Where(p => p.Name == "Overhead") .Select(x => {x.IsActive = true; return x;}) .ToList();
根据@JonSkeet,@ TimSchmelter和@LeandroSoares的评论,上面的代码真的很糟糕。 这是一些原因
- 调用
.ToList()
会导致整个惰性集合被执行并加载到内存中。 - 上面的代码不是很易读并且难以维护。
- 上面的代码滥用了API,因为它强制API执行它不是为其设计的东西。