2015-12-14 11 views
9

私は、たくさんのオブジェクトを作成し、各オブジェクトのPriorityプロパティを設定するために繰り返します。IEnumerable - foreachループ内のオブジェクトを更新する

static void Main(string[] args) 
{ 
    foreach (var obj in ObjectCreator.CreateObjectsWithPriorities()) 
     Console.WriteLine(String.Format("Object #{0} has priority {1}", 
             obj.Id, obj.Priority)); 
} 

class ObjectCreator 
{ 
    public static IEnumerable<ObjectWithPriority> CreateObjectsWithPriorities() 
    { 
     var objs = new[] { 1, 2, 3 }.Select(i => new ObjectWithPriority() { Id = i }); 
     ApplyPriorities(objs); 
     return objs; 
    } 

    static void ApplyPriorities(IEnumerable<ObjectWithPriority> objs) 
    { 
     foreach (var obj in objs) 
     { 
      obj.Priority = obj.Id * 10; 
      Console.WriteLine(String.Format("Set priority of object #{0} to {1}", obj.Id, obj.Priority)); 
     } 
    } 
} 

class ObjectWithPriority 
{ 
    public int Id { get; set; } 
    public int Priority { get; set; } 
} 

私は修正の優先順位を持つオブジェクトを格納するための主な方法でIEnumerableを期待しています。

Set priority of object #1 to 10 
Set priority of object #2 to 20 
Set priority of object #3 to 30 
Object #1 has priority 0 
Object #2 has priority 0 
Object #3 has priority 0 

sucheの行動の理由は何であり、私が働いて、私の優先順位を取得するために、ここで何を変更する必要があります。しかし、それらのすべては、デフォルト値0ここで ログがあるがありますか?

+1

二つの重要な事実:(1)クエリ式の値が*問い合わせ*であり、(2)クエリが実行されます*クエリが実行されたとき*。これらのステートメントはトートロジーに見えるかもしれませんが、あなたのコードは、あなたがそれらを信じていないことを示しています。クエリの値はクエリの実行であり、それを2回実行することは一度だけ実行すると信じていました。 –

+0

@EricLippert、そうです、私はいつも、IEnumerableを、(名前が示すように)クエリの表現ではなく、_enumerate_を通じた一連の要素として考えました。また、シーケンスをループするだけで(要素を追加したり削除したりする必要がない)場合は、ToListを呼び出さずに、プログラム全体でIEnumerableを安全に使用することができます。多くの書籍や記事が提案するように、メソッド/クラス/プログラムの間でオブジェクトを渡すためには、「可能な限り一般的な型を使用する必要があります。」と私がここでやろうとしていたことです。今私はそれよりも複雑だと思う。 –

答えて

15

あなたがこれを行うときは:あなたは、単に遅延評価イテレータを作成している

var objs = new[] { 1, 2, 3 }.Select(i => new ObjectWithPriority() { Id = i }); 

、これはObjectWithPriortyプロジェクト格納する配列/リストを割り当てません。反復子を列挙するたびに、反復処理ごとに値を繰り返し反復し、ObjectWithPriorityを投影しますが、それらは破棄します。

あなたがやりたいことは、渡す前にクエリーを具体化することです。後で実際に割り当てられたリスト/配列を変更します。これはEnumerable.ToListまたはEnumerable.ToArrayを使用して達成することができます。

public static IEnumerable<ObjectWithPriority> CreateObjectsWithPriorities() 
{ 
    var objs = new[] { 1, 2, 3 }.Select(i => new ObjectWithPriority() { Id = i }) 
           .ToList(); 
    ApplyPriorities(objs); 
    return objs; 
} 

あなたが代わりに要求されるように遅延して数字を投影する固定サイズの配列を、割り当ての追加使用Enumerable.Range可能性:

回答に加えて、
var objs = Enumerable.Range(1, 3).Select(i => new ObjectWithPriority { Id = i }) 
           .ToList(); 
+0

説明をありがとう!この全体のIEnumerableの概念はちょっと混乱しています。だから私はいくつかの外部データ(すなわちファイルやDBテーブル)を読み込んでそれを私のドメインモデルオブジェクトに変換するメソッドを持っている場合、そのような問題を避けるために 'IEnumerable'の代わりに戻り値の型を' List'に変更するべきでしょうか?他のコードはこのメソッドから返されるデータを操作しますか? –

+0

データベースを照会しているときは、呼び出し元に返される 'IEnumerable 'に非常に注意する必要があります。たとえば、そのような列挙型を呼び出し側に渡し、LINQまたは 'foreach'文を使用して列挙型を複数回列挙すると、その問合せは複数回実行されます。おそらく、あなたは 'T []'や 'IList 'を返す方が良いでしょう。 –

0

Yuval Itzchakovの:

あなたができたあなたのオブジェクトのレイジーロード優先にしたい場合

ただ一つの目的のために、あなたのApplyPriorities()メソッドを定義し、選択-方法でそれを使用するか、または以下のコードに示すようなあなたのObjectWithPriorityクラスウィッヒは、優先度を算出するにデリゲートを追加します。

class ObjectWithPriority 
{ 
    public int Id { get; set; } 

    private int? priority; 
    public int Priority { 
     get 
     { 
      return (priority.HasValue ? priority.Value : (priority = PriorityProvider(this)).Value); 

     } 

     set { priority = value; } 
    } 

    Func<ObjectWithPriority, int> PriorityProvider { get; set; } 

    public ObjectWithPriority(Func<ObjectWithPriority, int> priorityProvider = null) 
    { 
     PriorityProvider = priorityProvider ?? (obj => 10 * obj.Id); 
    } 
} 
2

より良い何が起こっているのかを理解するためにあなたのプログラムの中で、あなたはこの表現クエリとしてではなく、オブジェクトのシーケンス/リスト/コレクションとして

var objs = new[] { 1, 2, 3 }.Select(i => new ObjectWithPriority() { Id = i }); 

を考える必要があります。

しかし、この特定のプログラムではクエリを必要としないことはあなたのコードから明らかです。有限個のオブジェクトを持ち、foreachを使用してループするたびに同じオブジェクトを返すコレクションが必要です。

IEnumerable<ObjectWithPriority>の代わりにICollection<ObjectWithPriority>を使用することをお勧めします。これは、プログラムのロジックをよりよく表現し、あなたが遭遇したような間違いや誤解を避けるのに役立ちます。

次のようにコードを変更することができます:あなたがLINQについての内部化しなければならない

public static ICollection<ObjectWithPriority> CreateObjectsWithPriorities() 
{ 
    IEnumerable<ObjectWithPriority> queryThatProducesObjectsWithPriorities = new[] { 1, 2, 3 }.Select(i => new ObjectWithPriority() { Id = i }); // just for clarification 
    ICollection<ObjectWithPriority> objectsWithPriorities = queryThatProducesObjectsWithPriorities.ToList(); 
    ApplyPriorities(objectsWithPriorities); 
    return objectsWithPriorities; 
} 

static void ApplyPriorities(ICollection<ObjectWithPriority> objs) 
{ 
    foreach (var obj in objs) 
    { 
     obj.Priority = obj.Id * 10; 
     Console.WriteLine(String.Format("Set priority of object #{0} to {1}", obj.Id, obj.Priority)); 
    } 
} 
関連する問題