【问题标题】:Strange behaviour in linq c# under delayed execution延迟执行下linq c#中的奇怪行为
【发布时间】:2011-09-03 16:34:13
【问题描述】:

嗨,我有以下代码会产生奇怪的行为。 由 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。!!

【问题讨论】:

    标签: c# linq deferred


    【解决方案1】:

    这很简单,它是well-documented。这是因为 LINQ 的deferred execution feature。这段代码

    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,然后遍历结果列表时,现在您只生成了一个对象序列,并且该对象序列在两次迭代中显然是相同的。

    【讨论】:

    • 谢谢你是对的。该属性确实得到更新。例如当插入 Console.WriteLine("in MyClassExtra list v1.val is {0}, prop1 is {1} ", fj.v1.val, fj.prop1);在第一个循环中。就在第二个 for 循环中,它正在创建对 MyClassExtrax 实例的新引用。
    猜你喜欢
    • 1970-01-01
    • 2011-09-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多