ChatGPT解决这个技术问题 Extra ChatGPT

LINQ:选择一个对象并更改某些属性而不创建新对象

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

例子:

var list = from something in someList
           select x // but change one property
对于那个很抱歉!这是正确的地址:robvolk.com/…
虽然可以这样做,但正如答案所示,请注意这违反了 LINQ 的性质。 LINQ 方法不应该引起副作用,因此这样做不符合最小意外原则。与 LINQ 一样,您将获取对象然后修改它们。

J
JaredPar

我不确定查询语法是什么。但这里是扩展的 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 场景,以及滥用预期的运算符回答问题,而不是执行副作用。”。查看他对 this anwer 的评论
M
Mahmoud Gamal

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

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; }) ;
J
Jan Zahradník

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

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

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

不应该有任何 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。非常简洁。
D
Daniel Brückner

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

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 公开了能力。所以我不会争辩说语义是阻止这个功能的东西。
P
Pierre

如果您想使用 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();

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


I
Informitics

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

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 {}

V
Victor Wilson
S
Stefan R.

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

使用“选择”来修改数据是可能的,但只是一个技巧。无论如何,“选择”不是为此而设计的。它只是在与“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 选择...


d
d219
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 循环。
a
alexDuty

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

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


J
James Poulose

我遇到了确切的要求(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;
}

好吧,基本上这与接受的答案和其他答案相同。
k
kazem
User u = UserCollection.Single(u => u.Id == 1);
u.FirstName = "Bob"

无需复制 existing answers on this thread。如果您对该答案有评论,可以将其放在那里。