【问题标题】:Change some value inside the List<T>更改 List<T> 中的一些值
【发布时间】:2012-10-10 19:22:45
【问题描述】:

我有一些列表(其中 T 是自定义类,并且类具有一些属性)。 我想知道如何使用 Lambda 表达式更改其中的一个或多个值,因此结果将与下面的 foreach 循环相同:

注意:列表中包含多个项目(多行)

        foreach (MyClass mc in list)  
        {
            if (mc.Name == "height")
                mc.Value = 30;
        }

这是 linq 查询(使用 Lambda 表达式),但它与上面的 foreach 循环不同,它只返回列表中的 1 项(一行)!

我想要的是,它返回所有项目(所有行)并且只更改适当的项目(在 WHERE 扩展方法中指定的项目。

list = list.Where(w => w.Name == "height").Select(s => { s.Value = 30; return s; }).ToList();

注意:这两个例子不一样!我再说一遍,linq 只返回 1 个项目(一行),这是我不想要的,我也需要列表中的所有项目(比如 foreach 循环,它只做更改,但它不会删除任何项目)。

【问题讨论】:

  • 为什么要使用 lambda 表达式? foreach 代码运行良好且简单。 LINQ 用于查询数据,而不是对其进行变异。
  • 根据定义,这里只返回匹配的记录。基本上:这不是你想要的 - 只需使用 foreach
  • 嗨,乔恩,我知道 foreach 循环如果击球手和更快,但我想学习,仅此而已。对于一些“小”代码就可以了。但重点主要在于学习。
  • 谢谢 Marc,很好很简单的解释。
  • @BluE:虽然这不是一种功能性方法。以您想要的形式创建一个 new 对象是函数式方法。

标签: c# linq lambda expression


【解决方案1】:

您可以使用ForEach,但您必须先将IEnumerable&lt;T&gt; 转换为List&lt;T&gt;

list.Where(w => w.Name == "height").ToList().ForEach(s => s.Value = 30);

【讨论】:

  • 这将创建一个新列表。它砍掉了 Name != "height" 的项目
  • 创建一个新列表 - 它只返回基于 WHERE 子句的项目。不是我想要的例子。
  • @MitjaBonca 我的错,我误解了你的问题。请参阅上面的编辑。
  • 这就是我一直在寻找的(如果它不是最有效的方式——我知道的话)。
  • 使用 ForEach 可能会导致排序不稳定
【解决方案2】:

我可能会选择这个(我知道它不是纯 linq),如果您想保留所有项目,请保留对原始列表的引用,并且您应该会在那里找到更新的值:

 foreach (var mc in list.Where(x => x.Name == "height"))  
     mc.Value = 30;

【讨论】:

    【解决方案3】:

    您可以使用带有语句 lambda 的投影,但原始的 foreach 循环更具可读性,并且正在就地编辑列表而不是创建新列表。

    var result = list.Select(i => 
       { 
          if (i.Name == "height") i.Value = 30;
          return i; 
       }).ToList();
    

    扩展方法

    public static IEnumerable<MyClass> SetHeights(
        this IEnumerable<MyClass> source, int value)
    {
        foreach (var item in source)
        {
           if (item.Name == "height")
           {
               item.Value = value;
           }
    
           yield return item;
        } 
    }
    
    var result = list.SetHeights(30).ToList();
    

    【讨论】:

    • 是否可以避免 if 阻塞?是否可以定义 Select 扩展方法?
    • 是的,您可以定义一个自定义扩展方法,但原始的 foreach 不那么晦涩,更易于维护,并且不会创建一个成本可能很高的新列表。
    • 我知道如何做一个自定义扩展方法,我只是想知道是否有可能以“短”的方式来做,关于代码?!就像@Robin 显示的示例一样,只是他的代码不起作用!
    • 不,满足您的要求并使用 LINQ 的最短示例是使用投影和语句 lambda,您可以将其包装在自定义扩展方法中,如图所示。
    • 不应该 SetHeights 返回 IEnumerator&lt;MyClass&gt; 而不是 void
    【解决方案4】:

    list.Find(x =&gt; x.Name == "height").Value = 20; 怎么样? 这工作正常。我知道这是一个旧帖子,但只是想知道为什么没有人建议这个?这段代码有缺点吗?

    【讨论】:

    • 如果list.Find(x =&gt; x.Name == "height") 不返回任何内容,则会出现错误[NullReferenceException: Object reference not set to an instance of an object.]
    • 在实际修改值之前,您始终可以进行空检查。
    【解决方案5】:

    我会这样做:说“列表”是&lt;List&lt;t&gt;&gt;,其中 t 是具有名称和值字段的类;但当然你可以使用任何其他类类型来做到这一点。

        list = list.Where(c=>c.Name == "height")
            .Select( new t(){Name = c.Name, Value = 30})
            .Union(list.Where(c=> c.Name != "height"))
            .ToList();
    

    这完美!这是一个没有任何循环逻辑的简单 linq 表达式。 您唯一应该注意的是,结果数据集中的行顺序与源数据集中的顺序不同。因此,如果排序对您很重要,只需在最终的 linq 查询中重现相同的 order by 子句。

    【讨论】:

      【解决方案6】:

      我知道这是一篇旧帖子,但我一直使用更新程序扩展方法:

            public static void Update<TSource>(this IEnumerable<TSource> outer, Action<TSource> updator)
              {
                  foreach (var item in outer)
                  {
                      updator(item);
                  }
              }
      
      list.Where(w => w.Name == "height").ToList().Update(u => u.height = 30);
      
      

      【讨论】:

      • 不回答问题。这会更改列表中的所有项目。
      【解决方案7】:

      你可以这样做:

      var newList = list.Where(w => w.Name == "height")
                    .Select(s => new {s.Name, s.Value= 30 }).ToList();
      

      但我宁愿选择使用foreach,因为LINQ 用于在您查询时 想要编辑数据。

      【讨论】:

      • 我之前也尝试过这个,但是我遇到了一个异常:“无效的匿名类型成员声明器。必须使用成员分配、简单名称或成员访问来声明匿名类型成员。”。这是针对“s.Value=30”的。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-02-16
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多