【问题标题】:LINQ: Select an object and change some properties without creating a new objectLINQ:选择一个对象并更改某些属性而不创建新对象
【发布时间】:2010-10-22 21:49:33
【问题描述】:

我想更改 LINQ 查询结果对象的某些属性,而无需创建新对象并手动设置每个属性。这可能吗?

例子:

var list = from something in someList
           select x // but change one property

【问题讨论】:

  • 抱歉!这是正确的地址:robvolk.com/…
  • 虽然可以这样做,但正如答案所示,请注意这违反了 LINQ 的性质。 LINQ 方法不应该引起副作用,因此这样做不符合最小意外原则。与 LINQ 一样,您将获取对象然后对其进行修改。

标签: c# linq


【解决方案1】:

我不确定查询语法是什么。但这里是扩展的 LINQ 表达式示例。

var query = someList.Select(x => { x.SomeProp = "foo"; return x; })

这样做是使用匿名方法 vs 和表达式。这允许您在一个 lambda 中使用多个语句。所以你可以把设置属性和返回对象这两个操作组合成这个有点简洁的方法。

【讨论】:

  • @Rob,不容易。让它工作的语法是......充其量是不可读的。 var query = from it in list select ((Func)(() => { it.x = 42; return it; }))();
  • 我收到“无法将带有语句体的 lambda 表达式转换为表达式树”错误。它不适用于 LINQ to SQL,有什么建议吗?
  • @surya 这正是我在尝试使用 Linq to SQL 时得到的消息。在此之前添加ToList() 似乎可以解决问题。不知道为什么你会得到那个。如果是实体框架,它也不适用。
  • @surya 当您调用 ToList() 时,您实际上是将数据带回内存。所以它实际上不再是 Linq to SQL 了。
  • 根据 Eric Lippert 的说法,这个解决方案是“......低效、混乱、不可维护、不必要、浪费,取决于查询运算符的实现细节,不能推广到非 LINQ-to-objects 场景,以及滥用操作员的目的是回答问题,而不是执行副作用。”。见他的cmets到this anwer
【解决方案2】:

如果你只想更新所有元素的属性,那么

someList.All(x => { x.SomeProp = "foo"; return true; })

【讨论】:

  • 在 EF(实体框架)中:要替换 IEnumerable 的所有对象的属性,接受的答案对我有用。我的工作代码: var myList = _db.MyObjects.Where(o => o.MyProp == "bar").AsEnumerable().Select(x => { x.SomeProp = "foo"; return x; }) ;
【解决方案3】:

我更喜欢这个。它可以与其他 linq 命令结合使用。

from item in list
let xyz = item.PropertyToChange = calcValue()
select item

【讨论】:

  • 表达式树不能包含赋值
  • @Daniel 你在发帖前尝试过你的假设吗?
  • 是的;我实际上尝试了您的解决方案。以下是在 c# 中添加此功能的最新提案:github.com/dotnet/csharplang/discussions/4727
【解决方案4】:

不应该有任何 LINQ 魔法阻止您这样做。尽管会返回匿名类型,但不要使用投影。

User u = UserCollection.FirstOrDefault(u => u.Id == 1);
u.FirstName = "Bob"

这将修改真实的对象,以及:

foreach (User u in UserCollection.Where(u => u.Id > 10)
{
    u.Property = SomeValue;
}

【讨论】:

  • 我喜欢这个,我的问题是在第一个示例中,如果在列表中找不到某些 u > 10 怎么办?我添加了一个空检查,似乎可以工作。 LHS 我也将你命名为 v. +1。非常简洁。
【解决方案5】:

使用标准查询运算符是不可能的 - 它是语言集成查询,而不是语言集成更新。但是你可以在扩展方法中隐藏你的更新。

public static class UpdateExtension
{
    public static IEnumerable<Car> ChangeColorTo(
       this IEnumerable<Car> cars, Color color)
    {
       foreach (Car car in cars)
       {
          car.Color = color;
          yield return car;
       }
    }
}

现在您可以按如下方式使用它。

cars.Where(car => car.Color == Color.Blue).ChangeColorTo(Color.Red);

【讨论】:

  • 并不是要更新基础集合。我们希望更新结果集合属性。
  • 好吧,LINQ 取代了代表结构化 Query 语言的 SQL,但它仍然向 UPDATEDELETEINSERT 公开了能力。所以我不会争辩说语义是阻止这个功能的东西。
【解决方案6】:

如果您想使用 Where 子句更新项目,则使用 .Where(...) 会截断您的结果:

list = list.Where(n => n.Id == ID).Select(n => { n.Property = ""; return n; }).ToList();

您可以像这样对列表中的特定项目进行更新:

list = list.Select(n => { if (n.Id == ID) { n.Property = ""; } return n; }).ToList();

即使您没有进行任何更改,也始终返回项目。这样它就会保留在列表中。

【讨论】:

    【解决方案7】:

    我们经常遇到这种情况,我们希望在列表中包含索引值以及第一个和最后一个指标而不创建新对象。这使您可以知道项目在列表、枚举等中的位置,而无需修改现有类,然后知道您是在列表中的第一项还是最后一项。

    foreach (Item item in this.Items
        .Select((x, i) => {
        x.ListIndex = i;
        x.IsFirst = i == 0;
        x.IsLast = i == this.Items.Count-1;
        return x;
    }))
    

    您可以使用以下方法简单地扩展任何类:

    public abstract class IteratorExtender {
        public int ListIndex { get; set; }
        public bool IsFirst { get; set; } 
        public bool IsLast { get; set; } 
    }
    
    public class Item : IteratorExtender {}
    

    【讨论】:

      【解决方案8】:

      【讨论】:

        【解决方案9】:

        由于我没有在这里找到我认为最好的解决方案的答案,所以我的方式是:

        使用“选择”来修改数据是可能的,但只是一个技巧。无论如何,“选择”不是为此而设计的。它只是在与“ToList”一起使用时执行修改,因为 Linq 在需要数据之前不会执行。 无论如何,最好的解决方案是使用“foreach”。在下面的代码中可以看到:

            class Person
            {
                public int Age;
            }
        
            class Program
            {
                private static void Main(string[] args)
                {
                    var persons = new List<Person>(new[] {new Person {Age = 20}, new Person {Age = 22}});
                    PrintPersons(persons);
        
                    //this doesn't work:
                    persons.Select(p =>
                    {
                        p.Age++;
                        return p;
                    });
                    PrintPersons(persons);
        
                    //with "ToList" it works
                    persons.Select(p =>
                    {
                        p.Age++;
                        return p;
                    }).ToList();
                    PrintPersons(persons);
        
                    //This is the best solution
                    persons.ForEach(p =>
                    {
                        p.Age++;
                    });
                    PrintPersons(persons);
                    Console.ReadLine();
                }
        
                private static void PrintPersons(List<Person> persons)
                {
                    Console.WriteLine("================");
                    foreach (var person in persons)
                    {
                        Console.WriteLine("Age: {0}", person.Age);
                    ;
                    }
                }
            }
        

        在“foreach”之前,还可以做一个linq选择...

        【讨论】:

          【解决方案10】:
          var item = (from something in someList
                 select x).firstordefault();
          

          将获得item,然后您可以通过item.prop1=5; 更改特定属性。

          或者您是否想从数据库中获取项目列表并让它将返回列表中每个项目的属性prop1 更改为指定值? 如果是这样,您可以这样做(我在 VB 中这样做,因为我更了解它):

          dim list = from something in someList select x
          for each item in list
              item.prop1=5
          next
          

          list 将包含您的更改返回的所有项目)

          【讨论】:

          • 虽然这可行,但我认为 OP 是在询问如何编写 LINQ 语句而无需编写 for each 循环。
          【解决方案11】:

          我遇到了确切的要求(OP 的问题),但我没有看到使用表达式在上面的任何地方回答。

          假设xMyType类型,你可以调用一个返回MyType的方法,它接受x。在该方法中,您可以进行所有您想要的修改。代码将如下所示。

          var list = from something in someList
                     select GetModifiedObject(x); // but change one property
          
          private MyType GetModifiedObject(MyType x)
          {
             x.Property1 = "Changed Value";
             return x;
          }
          

          【讨论】:

          • 嗯,基本上这与接受的答案和其他答案相同。
          【解决方案12】:

          这是最简单的解决方案: list.Where(t =&gt; t.Id == 1).ToList().ForEach(t =&gt; t.Property = "something");

          尝试和工作,从这里引用: https://visualstudiomagazine.com/articles/2019/07/01/updating-linq.aspx

          【讨论】:

            【解决方案13】:
            User u = UserCollection.Single(u => u.Id == 1);
            u.FirstName = "Bob"
            

            【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2010-10-29
            • 1970-01-01
            • 2011-01-23
            • 2012-05-07
            • 1970-01-01
            • 1970-01-01
            • 2021-06-15
            • 1970-01-01
            相关资源
            最近更新 更多