3

一連のメソッド()全体で一連のアイテム()を処理する必要があるC#クラスがあるIEnumerable<T>ため、メソッド内で単純に処理することはできませんforeach。私はこれを呼び出し.GetEnumerator()て渡します。これIEnumerator<T>は非常にうまく機能し、単一のシーケンスをループするときに必要な柔軟性を提供します。

ここで、他の人がこのプロセスにロジックを追加できるようにします。これを行う最も自然な方法は、を受け入れるメソッドとのインターフェースを提供することIEnumerator<T>です。簡単、完了、そしてそれは機能します。

しかし、これがアンチパターンであることが心配です。彼らは、IEnumerator<T>がすでに.MoveNext()電話をかけていることを知っている必要があるので、簡単にアクセスできます.CurrentIEnumerator<T>加えて、実装されるインターフェースで使用する前例は見当たりません。

  1. 私が考慮していない落とし穴は何ですか?
  2. それ自体を公開せずに、これと同じ効率的なメカニズム(つまり、複数のコピーを作成/破棄したくない)を可能にする別のパターンはありIEnumerator<T>ますか?

更新:以下のコメントで述べたように:私が欲しいのはある種の一般的なものStream<T>です。次のアイテムを効果的に見て(IEnumerator.Current-> .Peek())、それを消費できるようにする必要があります(IEnumerator<T>.MoveNext()-> .Pop())。

IEnumerator<T>ビルのインターフェースにぴったり合うので使用しました。私は一般的なBCLタイプを適切なときに使用することを好みますが、これを悪用しているように見えました。

それで質問3)このニーズに合うクラスはありますか?IEnumerator<T>または、内部で遅延実行する独自のストリームを作成する必要がありますか?その後、完全にカプセル化されます。既存のコレクションの多くは内部ストレージを備えているため使用したくないのですが、ストレージはIEnumerable<T>iteslfにしたいのです。


OK、コンセンサスは、IEnumerator<T>多くの場合ValueType、の状態を事前に知らないだけでなく、IEnumerator<T>それを渡すことは一般的に悪い考えであるということのように聞こえます。

私が聞いた中で最も良い提案は、渡される独自のクラスを作成することです。他に何か提案はありますか?

4

4 に答える 4

6

あなたは絶対に回ってはいけませんIEnumerator<T>。他のものとは別に、場合によっては非常に奇妙な影響を与える可能性があります。たとえば、ここで何が起こると思いますか?

using System;
using System.Collections.Generic;

class Test
{
    static void ShowCurrentAndNext(IEnumerator<int> iterator)        
    {
        Console.WriteLine("ShowCurrentAndNext");
        Console.WriteLine(iterator.Current);
        iterator.MoveNext(); // Let's assume it returns true
        Console.WriteLine(iterator.Current);
    }

    static void Main()
    {
        List<int> list = new List<int> { 1, 2, 3, 4, 5 };
        using (var iterator = list.GetEnumerator())
        {
            iterator.MoveNext(); // Get things going
            ShowCurrentAndNext(iterator);
            ShowCurrentAndNext(iterator);
            ShowCurrentAndNext(iterator);
        }
    }
}

試すべきいくつかの変更:

using (List<int>.Enumerator iterator = list.GetEnumerator())

using (IEnumerator<int> iterator = list.GetEnumerator())

それぞれの場合の結果を予測してみてください:)

確かに、これは特に邪悪な例ですが、可変状態の通過に関連するいくつかのコーナーケースを示しています。現在の値だけで適切な他のメソッドを呼び出す「中央」メソッドですべての反復を実行することを強くお勧めします。

于 2010-07-06T18:26:23.277 に答える
2

処理するアイテムの通知をリスナーにプッシュできるように、イベントを使用することでメリットが得られるように思えます。通常の.NETイベントは、サブスクライブされた順序で処理されるため、順序付けが必要な場合は、より明示的なアプローチを選択できます。

また、リアクティブフレームワークもご覧ください。

于 2010-07-06T18:18:02.730 に答える
2

列挙子自体を渡すことは強くお勧めします。現在の値が必要なこと以外に、これにはどのような理由がありますか?

明らかな何かが欠けている場合を除いて、ユーティリティ関数に、列挙している型をパラメーターとして取得しforeach、実際の列挙を処理する単一の外部ループを作成することをお勧めします。

おそらく、これまでにこの設計上の決定を行った理由について、いくつかの追加情報を提供できます。

于 2010-07-06T18:13:12.783 に答える
1

これを正しく理解していれば、シーケンスですべてMoveNextを呼び出すことができるメソッドがいくつかあり、これらのメソッドを相互に連携させたいので、を渡しますIEnumerator<T>。あなたが言ったように、ここには確かにいくつかの緊密な結合があります。なぜなら、列挙子は各メソッドの入り口で特定の状態にあると期待しているからです。ここで実際に求めているのは、コレクション(一種)とイテレータ(現在の場所の概念を持つ)の両方であるStreamクラスのようなもののようです。私はあなたのイテレーションとあなたが必要とする他の状態をあなた自身のクラスにラップし、そのクラスのメンバーとしてさまざまなメソッドを持っています

于 2010-07-06T18:23:41.437 に答える