更新Enumerable.Zip中的类属性linq c#

时间:2016-03-04 11:31:58

标签: c# linq ienumerable

我想从外部列表更新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执行此操作的正确方法。

1 个答案:

答案 0 :(得分:1)

Enumerable.Repeat(a, b)生成的序列包含ba。因此,您获得的序列包含相同实例的三倍(您只实例化一个 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