关于c#:LINQ:选择一个对象并在不创建新对象的情况下更改某些属性

LINQ: Select an object and change some properties without creating a new object

使用LINQ,如果我想执行一些查询并从查询中返回对象,但只更改该对象中的一些属性,那么在不创建新对象和手动设置每个属性的情况下,我该如何做呢?这有可能吗?

例子:

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


我不知道查询语法是什么。但下面是扩展的LINQ表达式示例。

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

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


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

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


我喜欢这个。它可以与其他LINQ命令组合使用。

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


不应该有任何Linq魔法阻止你这么做。不要使用投影,尽管它会返回匿名类型。

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

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

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


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

1
2
3
4
5
6
7
8
9
10
11
12
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;
       }
    }
}

现在您可以使用它,如下所示。

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


如果要使用Where子句更新项,则如果执行以下操作,则使用.where(…)将截断结果:

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

您可以更新列表中的特定项目,如下所示:

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

即使不做任何更改,也始终返回项目。这样它就会被保存在列表中。


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

1
2
3
4
5
6
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;
}))

您可以使用以下命令扩展任何类:

1
2
3
4
5
6
7
public abstract class IteratorExtender {
    public int ListIndex { get; set; }
    public bool IsFirst { get; set; }
    public bool IsLast { get; set; }
}

public class Item : IteratorExtender {}

因为我在这里没有找到我认为最好的解决方案的答案,我的方法是:

使用"选择"来修改数据是可能的,但这只是一个技巧。不管怎样,"选择"并不是为它而做的。它只在与"tolist"一起使用时执行修改,因为linq在需要数据之前不会执行。不管怎样,最好的解决方案是使用"foreach"。在以下代码中,您可以看到:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
    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选择…


1
2
var item = (from something in someList
       select x).firstordefault();

会得到item,然后你可以做item.prop1=5;来更改特定的属性。

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

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

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


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