6

こんにちは、奇妙な動作を生成する次のコードがあります。linq to Objects によって生成された IEnumerable に含まれるオブジェクトのインスタンスのプロパティは、後続の foreach ステートメントで更新されません。foreach ステートメントは、IEnumerable を列挙する必要があります。代わりに、解決策は前に列挙することです。

私は解決策を見つけましたが、同様の例を扱っている本や記事のどこにもこれが文書化されているのを見たことがありません。おそらく、linq の複雑な知識を持つ誰かがそれを説明できるでしょう。

エラーの正確な原因を特定するのに 1 日かかりました。大規模なアプリケーションでデバッグするのは簡単ではありません。次に、以下に示す、はるかに単純な環境でそれを再現しました。

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 に更新されません。

4

1 に答える 1

7

これは非常に単純で、十分に文書化されています。これは、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); 
}

オブジェクトを生成するルールを 2 回実行しています。つまり、オブジェクトの 2 つの異なるシーケンスが生成され、それらはシーケンス間で共有されません。そのため、値が更新されません。シーケンスの 2 つの繰り返しの参照は同じではありません。

ただし、 を呼び出しToListて結果のリストをたどると、オブジェクトのシーケンスは 1 つしか生成されず、そのオブジェクトのシーケンスは 2 回の繰り返しで明らかに同じです。

于 2011-09-03T16:39:30.497 に答える