2012-02-10 8 views
8

私は以前にいくつかのコードをリファクタリングしていましたが、イテレータブロックの実装を知りました。クライアントがあるデータのためにextrernal APIを呼び出すシステムの統合レイヤーでは、APIから返されたデータを取得し、ロジックレイヤーで使用されるビジネスエンティティのコレクションに変換する一連のトランスレータを持っています。一般的な翻訳者クラスは次のようになります。イテレータブロックの正しい使い方

// translate a collection of entities coming back from an extrernal source into business entities 
public static IEnumerable<MyBusinessEnt> Translate(IEnumerable<My3rdPartyEnt> ents) { 

    // for each 3rd party ent, create business ent and return collection 
    return from ent in ents 
      select new MyBusinessEnt { 
       Id = ent.Id, 
       Code = ent.Code 
      }; 
} 

今日、私は次のコードを発見しました。ここでも、トランスレータクラスです。パラメータのコレクションをメソッドの戻り値の型に変換するのが目的です。しかし、この時には、反復子ブロックです:

// same implementation of a translator but as an iterator block 
public static IEnumerable<MyBusinessEnt> Translate(IEnumerable<My3rdPartyEnt> ents) { 
    foreach(var ent in ents) 
    { 
     yield return new MyBusinessEnt { 
      Id = ent.Id, 
      Code = ent.Code 
     }; 
    } 
} 

私の質問は:これは反復子ブロックの有効利用のですか?このようにして翻訳者クラスを作成することの利点はわかりません。これは予期しない動作を引き起こす可能性がありますか?

+0

私には完全に妥当と思われます.2つのエンティティ間でコンパイルセーフな変換を行います。何が問題なの? –

答えて

13

あなたの2つのサンプルは、まったく同じものです。クエリバージョンはSelectへの呼び出しに書き換えられ、Selectは2番目の例のように書かれます。ソースコレクション内の各要素を繰り返し処理し、変換された要素を返します。もちろん、あなただけSelectを使用することができますので、このように、独自の反復子ブロックを記述することはもはや必要があるが

これは、反復子ブロックの完全に有効な使用ではありません。

+0

鮮やかです - 明確化のおかげで。 –

3

はい、有効です。 foreachにはデバッグ可能という利点があるので、私はそのデザインを好む傾向があります。

+0

したがって、イテレータブロックは、後でシステム内のこのビジネスエンティティのコレクションに対してforeachをデバッグしようとする人に混乱を招く可能性がありますか? –

+1

私は必ずしも混乱がないとは思わない。 'foreach'ブロックは単に翻訳されている広告申込情報のターゲットを簡単にします。これにより、翻訳が些細なものになっても、より簡単なデバッグが可能になります。両方とも完全に実装されています。 – eouw0o83hf

3

最初の例はイテレータではありません。これはIEnumerable<MyBusinessEnt>を作成して返します。

2番目はイテレーターであり、私はそれに何か間違いは見られません。呼び出し元がそのメソッドの戻り値を反復処理するたびに、yieldは新しい要素を返します。

-1

メジャー差分は、各コードが実行されているときにになると、になります。 最初のものは、戻り値が反復され、2番目のものがすぐに実行されるまで、遅延されます。つまり、ループのが繰り返し実行されています。クラスがIEnumerable<T>を公開し、この場合遅延しているという事実は別のものです。

これは単なるSelectよりも利点がありません。条件付きの関与があるときyieldの本当の力は、次のとおりです。

foreach(var ent in ents) 
{ 
    if(someCondition) 
    yield return new MyBusinessEnt { 
     Id = ent.Id, 
     Code = ent.Code 
    }; 
} 
+6

ちょっと待ってください - 単純な* Where *の後に* Select *がありますが、あなたのバージョンは何の利点もありません。そして、はい、クリス・シャウトのコメントは正しいです。両方とも実行を延期する。 –

+0

@EricLippertはい、ループを使用していて、選択していないためです。しかし、私が言ったように、私はこの場合プレーンなLinqを使うだけです。 – Aliostad

+0

これは本当ですか?私は両者が効果的に需要に応じて価値を創造したと考えました。つまり、第2のものは即座に実行されません。 – Chris

3

はい、正常に動作し、その結果は非常に似ています。

両方とも、結果を返すことができるオブジェクトを作成します。どちらも、結果が完了するまで(または短縮されるまで)そのまま残るように列挙可能なソースに依存します。どちらも遅延実行を使用します。つまり、オブジェクトは結果を反復処理するときに1つずつ作成されます。

ライブラリメソッドを使用して列挙子を生成する式を最初に返し、2番目にカスタム列挙子を作成するという点で違いがあります。

+0

OKですが、この例でカスタムイテレータを作成する必要はありません。 @Aliostadが指摘しているように、イテレータブロックは条件付きで最もよく使用されます。だからおそらく第2の方法は少し読みにくく\ debuggableなので、おそらく最初のアプローチはこのシナリオに適しているでしょう。 –

+0

また、相違点は、最初の列挙型( 'select this from that')の実行が遅れている間に、カスタム列挙子がすぐに実行されることです。 – Aliostad

+0

@Aliostad - あまり理解していないので、最初の例はまだ列挙されていないコレクションを返します。 2つ目は、呼び出されるたびに返品コレクションを列挙していますか?あれは正しいですか? –

関連する問題