我想从外部列表更新IEnumerable中的每个类属性。
class MyClass
{
public int Value { get; set; }
}
我尝试了以下
var numbers = new List<int>() { 1, 2, 3 };
var myclasses = Enumerable.Repeat(new MyClass(), numbers.Count);
myclasses.Zip(numbers, (a, b) => a.Value = b).ToList(); // Set each property [Not working as expected]
myclasses.ToList().ForEach(x => Console.WriteLine(x.Value)); // Print all properties
我希望之前的代码会在Value
中设置myclasses
属性,并且相应的1,2,3。但是这个代码在每个属性中设置3
并打印:
OUTPUT:
3
3
3
我不明白这种行为。有人可以解释为什么会发生这种情况,以及使用LINQ执行此操作的正确方法。
答案 0 :(得分:1)
Enumerable.Repeat(a, b)
生成的序列包含b
次a
。因此,您获得的序列包含相同实例的三倍(您只实例化一个 new MyClass()
)。
因此,在上次执行a.Value = b
时,您将单个Value
实例的MyClass
属性设置为3
,将x
设置为WriteLine
1}}始终是相同的实例,包含Value = 3
。
创建myclasses
的更好方法是
var myclasses = Enumerable.Range(1, numbers.Count).Select(i => new MyClass());
注意创建具有副作用的序列often a bad idea。您实际上不使用Zip
返回的序列,但仅使用此查询,因为它在lambda中的副作用。
我想在您的真实应用中,myclasses
的创建和Zip
的更新比您的问题更加分离。但如果没有,你可以将其全部减少到这个:
var myclasses = numbers.Select(i => new MyClass { Value = i });
<强>更新强>
你现在只获得0
作为输出的原因是延迟执行(再次:使用像linq这样的函数编程方法只是因为它的副作用很棘手)。
立即解决方案是将ToList
添加到创建中:
var myclasses = Enumerable.Range(1, numbers.Count).Select(i => new MyClass()).ToList();
如果没有它,只在这一行实例化类:
myclasses.Zip(numbers, (a, b) => a.Value = b).ToList();
创建的类<em>作为序列返回,然后由List
转入ToList()
。但是你没有使用那个返回值。
所以在下一行
myclasses.ToList().ForEach(x => Console.WriteLine(x.Value));
您创建了类的新实例。
您定义的 myclasses
只是查询,而不是该查询的回答。
所以我的最终提案是:
var myclasses = Enumerable.Range(1, numbers.Count).Select(i => new MyClass());
var updatedClasses = myclasses.Zip(numbers, (a, b) => {a.Value = b; return a;}).ToList();
updatedClasses.ForEach(x => Console.WriteLine(x.Value));
注意我更改了Zip
中的lambda以返回该类的实例,而不是int
。