延迟执行下 linq c# 中的奇怪行为

发布于 2024-12-02 23:02:37 字数 1424 浏览 1 评论 0原文

你好,我有以下代码,它会产生奇怪的行为。 linq to Objects 生成的 IEnumerable 中包含的对象实例的属性不会在后续 foreach 语句中更新。 foreach 语句应枚举 IEnumerable。相反,解决方案是先枚举它。

尽管我找到了解决方案,但我还没有在书籍或文章中看到任何处理类似示例的文档。也许具有 linq 复杂知识的人可以解释它。

我花了一天时间才查明错误的确切原因,并且在大型应用程序中调试起来并不容易。然后我在一个更简单的环境中重现了它,如下所示。

public class MyClass
{
    public int val ;
 }

public class MyClassExtrax
{
    public MyClass v1 { get; set; }
    public int prop1 { get; set; }
}

void  Main() 
{
 List <MyClass> list1 = new List<MyClass>(); 
 MyClass obj1 = new MyClass(); obj1.val = 10; 
 list1.Add(obj1); 
 MyClass obj2 = new MyClass(); 
 obj2.val = 10; 
 list1.Add(obj2);

IEnumerable<MyClassExtrax> query1 =
     from v in list1
     where v.val >= 0
     select new MyClassExtrax{ v1=v ,  prop1=0 } ;

 //query1=query1.ToList(); solves the problem..but why is this needed..?
 foreach (MyClassExtrax fj in query1)
  {
    fj.v1.val = 40;
    fj.prop1 = 40;   //property does not get updated..
  }


 foreach (MyClass obj in list1)
  {
    Console.WriteLine("in list 1 value is {0} : ", obj.val);
  }


 foreach (MyClassExtrax obj in query1)
  {
   Console.WriteLine("in MyClassExtra list v1.val is {0}, prop1 is {1}  ", obj.v1.val,  obj.prop1); 
  }

 }

输出: 在列表 1 中值为 40 :

在列表 1 中值为 40 :

在 MyClassExtra 列表 v1.val 中为 40,prop1 为 0

在 MyClassExtra 列表 v1.val 中为 40,prop1 为 0

如您所见,prop1 没有更新为 40。 !!

Hi, I have the following code which produces a strange behaviour.
A property of an instance of objects contained in an IEnumerable produced by linq to Objects, does not get updated in subsequent foreach statements. The foreach statement should enuemerate the IEnumerable. Instead the solution is to enumerate it before.

Although I found the solution I have not seen this documented anywhere either in books or articles, dealing with similar examples. Perhaps somebody with intricate knowledge of linq can explain it.

It took me a day to pinpoint the exact cause of the error, and is not easy to debug in a large application. I then reproduced it in a much simpler environment, presented below.

public class MyClass
{
    public int val ;
 }

public class MyClassExtrax
{
    public MyClass v1 { get; set; }
    public int prop1 { get; set; }
}

void  Main() 
{
 List <MyClass> list1 = new List<MyClass>(); 
 MyClass obj1 = new MyClass(); obj1.val = 10; 
 list1.Add(obj1); 
 MyClass obj2 = new MyClass(); 
 obj2.val = 10; 
 list1.Add(obj2);

IEnumerable<MyClassExtrax> query1 =
     from v in list1
     where v.val >= 0
     select new MyClassExtrax{ v1=v ,  prop1=0 } ;

 //query1=query1.ToList(); solves the problem..but why is this needed..?
 foreach (MyClassExtrax fj in query1)
  {
    fj.v1.val = 40;
    fj.prop1 = 40;   //property does not get updated..
  }


 foreach (MyClass obj in list1)
  {
    Console.WriteLine("in list 1 value is {0} : ", obj.val);
  }


 foreach (MyClassExtrax obj in query1)
  {
   Console.WriteLine("in MyClassExtra list v1.val is {0}, prop1 is {1}  ", obj.v1.val,  obj.prop1); 
  }

 }

output:
in list 1 value is 40 :

in list 1 value is 40 :

in MyClassExtra list v1.val is 40, prop1 is 0

in MyClassExtra list v1.val is 40, prop1 is 0

As you can see prop1 does not get updated to 40.!!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

做个少女永远怀春 2024-12-09 23:02:37

这非常简单,有详细记录。这是因为延迟执行功能 的 LINQ。这段代码

IEnumerable<MyClassExtrax> query1 =
 from v in list1
 where v.val >= 0
 select new MyClassExtrax{ v1=v ,  prop1=0 } ;

实际上并不创建对象。它只是创建一个对象,当迭代时实际创建该对象。也就是说,您可以将 query1 视为知道如何在请求时吐出对象的规则。因此,当您执行以下操作时:

foreach (MyClassExtrax fj in query1)
{
    fj.v1.val = 40;
    fj.prop1 = 40;   //property does not get updated..
}

然后执行以下操作:

foreach (MyClassExtrax obj in query1)
{
   Console.WriteLine("in MyClassExtra list v1.val is {0}, prop1 is {1}  ", obj.v1.val, obj.prop1); 
}

您正在执行两次生成对象的规则。也就是说,产生了两个不同的对象序列,并且它们不在序列之间共享。这就是为什么您看不到值更新的原因;序列的两次迭代中的引用不相同。

但是,当您调用 ToList,然后遍历结果列表时,现在您只生成了一个对象序列,并且该对象序列在两次迭代中显然是相同的。

This is quite simple, it's well-documented. This is because of the deferred execution feature of LINQ. This code

IEnumerable<MyClassExtrax> query1 =
 from v in list1
 where v.val >= 0
 select new MyClassExtrax{ v1=v ,  prop1=0 } ;

doesn't actually create the objects. It just creates an object that when iterated over actually creates the object. That is, you can think of query1 as the rule that knows how to spit out the objects when they are requested. So when you do this:

foreach (MyClassExtrax fj in query1)
{
    fj.v1.val = 40;
    fj.prop1 = 40;   //property does not get updated..
}

and then this:

foreach (MyClassExtrax obj in query1)
{
   Console.WriteLine("in MyClassExtra list v1.val is {0}, prop1 is {1}  ", obj.v1.val, obj.prop1); 
}

you are executing the rule for generating the objects twice. That is, two distinct sequences of objects are produced, and they are not shared between the sequences. That is why you don't see the values updated; the references across the two iterations of the sequence are not the same.

However, when you call ToList, and then walk the resulting list, now you have only produced one sequence of objects, and that sequence of objects is obviously the same across the two iterations.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文