インターフェイスでIENumerator を使用するためのパターン
-
02-10-2019 - |
質問
一連のアイテムを処理する必要があるC#クラスがあります(IEnumerable<T>
)多くの方法を越えて、私は単にできません foreach
メソッド内。電話する .GetEnumerator()
そしてこれを渡します IEnumerator<T>
周りでうまく機能し、単一のシーケンスをループする際に必要な柔軟性を与えてくれます。
今、私は他の人がこのプロセスにロジックを追加できるようにしたいと思います。これを行う最も自然な方法は、彼らに、 IEnumerator<T>
. 。簡単で、完了し、機能します。
しかし、私はこれがアンチパターンであることを心配しています。彼らはそれを知っている必要があります IEnumerator<T>
すでに持っています .MoveNext()
呼び出されて、彼らが単純にアクセスできるように .Current
. 。さらに、使用する前例はありません IEnumerator<T>
実装されるインターフェイス内。
- 私はどのような落とし穴を考えていませんか?
- これを許可する別のパターンはありますか 効率的 露出せずにメカニズム(つまり、複数のコピーが作成/破壊されたくない)
IEnumerator<T>
自体?
アップデート: 以下のコメントで述べたように:私が欲しいのはある種のジェネリックです Stream<T>
. 。次のアイテムを効果的に見ることができる必要があります(IEnumerator.Current
-> .Peek()
)そしてそれを消費する(IEnumerator<T>.MoveNext()
-> .Pop()
).
使った IEnumerator<T>
それが賢明な法案のインターフェースに適合するからです。私はそれらが適合するときに一般的なBCLタイプを使用することを好みますが、私はこれを乱用しているように見えました。
質問3)このニーズに合ったクラスはありますか?または、ゆっくりと実行する独自のストリームを作成する必要があります IEnumerator<T>
初めの?その後、完全にカプセル化されます。既存のコレクションの多くは内部ストレージを持っているので、多くのものを使用したくないのに対し、ストレージを持ちたいと思います。 IEnumerable<T>
iteslf。
わかりました、コンセンサスは IEnumerator<T>
しばしばです ValueType
アプリオリを知らないだけでなく、 IEnumerator<T>
, 、一般的にそれを渡すのは悪い考えであること。
私が聞いた最良の提案は、自分のクラスを作成することです。他に提案はありますか?
解決
これを正しく理解していれば、すべてがシーケンスでmovenextを呼び出すことができる多くの方法があり、これらの方法が各他の方法と協力したいので、あなたは IEnumerator<T>
. 。あなたが述べたように、ここには間違いなくいくつかの狭い結合があります。なぜなら、列挙者は各方法の入り口の特定の状態にあると予想されるからです。ここで本当に後を追っているのは、コレクション(一種)とイテレーター(現在の場所の概念がある)の両方であるストリームクラスのようなものであるように聞こえます。私はあなたの反復とあなたがあなた自身のクラスで必要とする他の状態を包み、そのクラスのメンバーとしてさまざまな方法を持っています
他のヒント
あなたがすべき 絶対に 渡さないでください 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())
それぞれの場合の結果を予測してみてください:)
確かにそれは特に邪悪な例ですが、可変状態を通過することに関連するいくつかのコーナーケースを示しています。現在の値だけで適切な他の方法を呼び出す「中央」メソッドですべての反復を実行することを強くお勧めします。
列挙者自体を渡すことを強くお勧めします。現在の値を必要とすることを除けば、これにはどのような理由がありますか?
明白なものが欠けていない限り、ユーティリティ関数をパラメーターとして列挙しているタイプを単純に取得することをお勧めします。 foreach
実際の列挙を処理するループ。
おそらく、これまでこの設計上の決定を下した理由について、いくつかの追加情報を提供できるでしょう。
イベントを使用して、リスナーに加工するアイテムの通知をプッシュできるように、私には聞こえます。通常の.NETイベントは、サブスクライブされている順序で処理されるため、注文が必要な場合は、より明確なアプローチをとることができます。
また、リアクティブなフレームワークを見たいと思うかもしれません。