C#:更新List <of t =“”>中的项目值

时间:2015-10-07 13:30:41

标签: c# linq 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来做同样的事情,但是把它写成一行却失败了。这甚至可能吗?我不喜欢以上述方式迭代的原因是我已经迭代了这个代码块中的整个列表并认为可能有更美妙的方式:)

3 个答案:

答案 0 :(得分:13)

你不应该试图将所有内容都归结为一行 - 尽可能简短。在这种情况下,您可以使用:

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值很方便,因为这意味着我们不需要做任何其他事情来使它成为&&运算符的有效第二个操作数。我们使用Count()来完全评估结果而不创建任何其他列表等 - 我们使用带有谓词的版本来避免甚至需要Where调用,这与之前的版本相同。 (LastOrDefault也会起作用。)但这完全是一种可怕的滥用行为,不应该出现在任何真实的代码中。

答案 1 :(得分:1)

我想出了一种方法可以将它降低到一行,而不会滥用LINQ,因为我只是将它用于查询部分(过滤器),并使用自定义扩展方法来执行属性设置操作。你仍然要枚举项目(你必须)但你可以在扩展方法中隐藏它。我怀疑你是否真的在乎你是否枚举了这个项目,你只是不喜欢foreach循环占用主代码的可见空间量。

使用此扩展方法:

public static IEnumerable<T> SetProperty<T>(this IEnumerable<T> list, Action<T> 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<Project>() {
                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<Project> 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<T> SetProperty<T>(this IEnumerable<T> list, Action<T> 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

事实证明,我的SetProperty函数与已构建到框架中的ForEach非常相似。主要区别在于我可以在任何IEnumerable<T>上运作。由于Eric Lippert在his blog上指出的原因,这种语法被一些人所喜爱并被其他人所厌恶(感谢Jon Skeet指出这一点)。另请参阅Microsoft团队的this discussion。我会留给你自己得出结论。

在旁注中,将其称为SetProperty有点不准确,因为您可以对集合中的项目执行任何操作。您可以将其称为ForEach,但这与框架发生冲突。我称之为不正面,但也许PerformAction

答案 2 :(得分:-3)

以下Linq声明应该可以正常工作

MyProjects.Projects.Where(p => p.Name == "Overhead")
.Select(x => {x.IsActive = true; return x;})
.ToList();

根据@JonSkeet,@ TimSchmelter和@LeandroSoares的评论,上面的代码真的很糟糕。以下是一些原因

  1. 调用.ToList()会导致整个惰性集合被执行并加载到内存中。
  2. 上面的代码不易读,难以维护。
  3. 上面的代码滥用了API,因为它强制API执行不是为其设计的内容。