2013-03-08 4 views
6

C#コードで動作が変わっているのを見て、私は説明することができません。私は理解の重要なビットを見逃している可能性がありますので、そこに誰かが私のために光を切り替えることを望む。あなたはそれをステップ実行するとC#でIEnumerable内でプロパティを設定できません。

IEnumberable<myObject> objects = GetObjectsFromApiCall(); 

    for (int i = 0; i < objects.Count(); i++) 
     { 
      if (String.IsNullOrEmpty(objects.ElementAt(i).SubObject.Title)) 
      { 
       SubObject sub = GetSubObjectFromDatabase((long)objects.ElementAt(i).SubObject.Id); 
       if (sub != null) 
       { 
        objects.ElementAt(i).SubObject.Title = sub.Title; 
       } 
      } 
     } 

、このコードについてのすべてが正常に動作しているよう:

は、このようなコードのブロックを手に入れました。 「オブジェクト」コレクションは、期待どおりに作成されます。 "sub"は収集されたものとしてフェッチされ、Populated Titleプロパティを含む期待されるプロパティのフルセットを持ちます。実行中にエラーはスローされません。

しかし、各オブジェクトに存在するSubObject.Titleプロパティ(標準のget; set;コードのみ)は頑固なままです。

私は迷っています。誰でも何が起こっているのかを説明します。

EDIT:forループとElementAtを使用しないことをお勧めした方は、foreachループを使い始めましたが、毎回新しいSubObjectを取得していたため問題の原因になっている可能性があると考えました。あなたのおかげで修正され、ForEachが復元されました。

乾杯、すべての マット・

+4

あなたは:[IEnumerable内のアイテムプロパティを更新しますが、プロパティが設定されたままになりませんか?](http://stackoverflow.com/a/9104212/93732) –

+0

このコードは非常に遅くなる可能性があります面白くない。 – ChaosPandion

+0

実際の*コードのように見えるものではなく、あなたが持っている*実際のコードをコピー/ペーストできますか? – ken2k

答えて

4

私はそれをこのように修正します:

var objects = GetObjectsFromApiCall().ToList(); 

次に、あなたがあるとして(それが動作する)ループを維持する、またはそれをforeachのを使用してビットおよびいくつかのLINQを最適化する他の回答によって示唆されているように、それはありませんでした実際問題ではありません。問題は、IEnumeratorの要素を変更しようとしたことです。<> @AhmetKakıcıが指しているthis questionで説明されています。

+1

-1これは実際には間違っています。 'IEnumerable'が返す要素を変更する際には問題ありません。実際には、 'ToList()'の後に 'foreach'を使用しているときは' List 'が' IEnumerable 'を実装しているので、正確に' IEnumerable'を使用しています。 1つの問題は、DBクエリの*遅延実行*になる可能性がありますが、IEnumerableインターフェースの単純な存在のためではありません... – ken2k

+0

あなたは正しいですが、私はあまりにも速い読み込み...問題はIEnumerableではありませんそれが実装されている方法です。そのため、ToList()を使用するのが理にかなっています。説明をありがとう。 – Larry

+0

@ ken2k修正。 IEnumerableによって返される項目を変更する例については私の答えを見てください。 –

1

まず、あなたはまた、あなたがあなたの方法は、その後たびにダイナミックIEnumerableを返す場合に注意すべきである

foreach (var o in objects) 
{ 
    if (string.IsNullOrEmpty(o.SubObject.Title)) 
    { 
     o.SubObject.Title = ...; 
    } 
} 

を使用し、コードのこの種のElementAt()を使用しないでくださいobjects.Something()にコールすると、APIが再度呼び出され、新しいコピーが取得されます。この場合、.ToList()メソッドを使用して列挙型をリストにコピーする必要があります。このように動的な列挙子を作成することによって -

もないリストにコピーを置く方法があります:ない(以前のものは助けにはならなかった場合)が正しく設定されている値については

objects = objects.Select(o => 
{ 
    if (string.IsNullOrEmpty(o.SubObject.Title)) 
    { 
     o.SubObject.Title = ...; 
    } 
    return o; 
}); 

- Titleプロパティのセッターにthrow new Exception(value)を追加してください。正しい値で呼び出されているかどうかを確認してください。

+0

"まず、この種のコードにElementAt()を使用しないでください。どうして? –

+0

.NETは、 'Enumerator.MoveNext()'を使用して値を取得するたびに列挙します。 'list [i]'アプローチよりも遅いです。 –

2

この

List<myObject> objects = GetObjectsFromApiCall().ToList(); 

foreach(var obj in objects.Where(o => string.IsNullOrEmpty(objects.SubObject.Title)).ToList()) 
{ 
    var subObject = GetSubObjectFromDatabase(obj.SubObject.Id); 
    if(subObject == null) continue; 

    obj.SubObject.Title = subObject.Title; 
} 
1

IゲストGetObjectsFromApiCallは、次のようになり機能してみてください。

public IEnumberable<myObject> GetObjectsFromApiCall(){ 
    for(var i = 0; i < 10; i++) 
    { 
     yield return new myObject(); 
    } 
} 

私が正しい場合は、すべての時間はあなたがオブジェクトを取得するためにobjects.ElementAt(I)関数を呼び出しを"yield return new myObject()"で新しいオブジェクトを取得します。

+0

これは良い理論です。私はそのような例を掲示することを考えていました。 –

+0

ああ、 "objects.ElementAt(i).SubObject.Title = sub.Title;"を変更する必要があります"var obj = objects.ElementAt(i).SubObject; obj.Title = sub.Title;" – fengyj

+0

あなたのコメントについて:それはどう変わるでしょうか? –

1

しかし、Titleのプロパティが変更されているかどうかを確認するにはどうすればよいですか? GetObjectsFromApiCall()に再度電話しますか?またはforeachを介して同じobjectsインスタンスを再度実行しますか?

IEnumerableインスタンスは、「列挙」されるたびに新しいオブジェクトを作成し、生成することがあります。ここでは簡単な例を示しますたとえば、定義:

class SomeObject 
{ 
    public string Title { get; set; } 
} 

をその後、我々は、「ソース」の2種類、最初の配列、及び、このように定義されたイテレータブロックを検討します。そして、このようにそれをテスト

static IEnumerable<SomeObject> GetSomeSequence() 
    { 
     yield return new SomeObject { Title = "Alpha", }; 
     yield return new SomeObject { Title = "Beta", }; 
     yield return new SomeObject { Title = "Gamma", }; 
    } 

を:

static void Main() 
    { 
     IEnumerable<SomeObject> thingsToModify; 

     // set source to an array 
     thingsToModify = new[] { new SomeObject { Title = "Alpha", }, new SomeObject { Title = "Beta", }, new SomeObject { Title = "Gamma", }, }; 

     foreach (var t in thingsToModify) 
      Console.WriteLine(t.Title); 

     foreach (var t in thingsToModify) 
      t.Title = "Changed!"; 

     foreach (var t in thingsToModify) 
      Console.WriteLine(t.Title); // OK, modified 


     // set source to something which yields new object each time a new GetEnumerator() call is made 
     thingsToModify = GetSomeSequence(); 

     foreach (var t in thingsToModify) 
      Console.WriteLine(t.Title); 

     foreach (var t in thingsToModify) 
      t.Title = "Changed!";   // no-one keeps these modified objects 

     foreach (var t in thingsToModify) 
      Console.WriteLine(t.Title); // new objects, titles not modified 

    } 

結論:反復処理中のソースに属する可変オブジェクトの状態を完全に変更することは可能です。しかし、いくつかのタイプのIEnumerableソースは、呼び出されるたびにデータの新しいコピーを生成し、コピーを変更することは無意味です。

関連する問題