2

アルバハリ兄弟[ページNo.273]によると、次のIEnumerable理由で使用されます。

列挙子を返す単一のメソッドを定義することによりIEnumerable

->反復ロジックは次のようになりますframed off to another class理解済み

-> Moreover it means that several consumers can enumerate the collection at once without interfering with each otherわからない

2点目がわかりません!

を使用するIEnumerable代わりに、複数のコンシューマーがコレクションを一度に列挙できるようにするにはどうすればよいですか?IEnumerator

4

4 に答える 4

4

IEnumerableGetEnumerator()を返す単一のメソッド、を実装しますIEnumerator。メソッドが呼び出されるたびにIEnumerator、独自の状態を持つnewが返されます。このようにして、あるスレッドが別のスレッドの現在のポインターを変更する危険性なしに、複数のスレッドが同じコレクションを反復処理できます。

コレクションがを実装している場合IEnumerator、それは一度に1つのスレッドによってのみ効果的に反復できます。次のコードを検討してください。

public class EnumeratorList : IEnumerator
{
    private object[] _list = new object[10];
    private int _currentIndex = -1;

    public object Current { get { return _list[_currentIndex] } };

    public bool MoveNext()
    {
        return ++_currentIndex < 10;
    }

    public void Reset()
    {
        _currentIndex = -1;
    }
}

その実装では、2つのスレッドが同時にEnumeratorListをループしようとすると、インターリーブされた結果が得られ、リスト全体が表示されません。

これをリファクタリングするとIEnumerable、複数のスレッドがそのような問題なしに同じリストにアクセスできます。

public class EnumerableList : IEnumerable
{
    private object[] _list = new object[10];

    public IEnumerator GetEnumerator()
    {
        return new ListEnumerator(this);
    }

    private object this[int i]
    {
        return _list[i];
    }

    private class ListEnumerator : IEnumerator
    {
        private EnumeratorList _list;
        private int _currentIndex = -1;

        public ListEnumerator(EnumeratorList list)
        {
            _list = list;
        }

        public object Current { get { return _list[_currentIndex] } };

        public bool MoveNext()
        {
            return ++_currentIndex < 10;
        }

        public void Reset()
        {
            _currentIndex = -1;
        }
    }
}

もちろん、これは単純で不自然な例ですが、これがより明確になることを願っています。

于 2012-07-25T18:09:58.710 に答える
1

MSDN記事IEnumerableは、適切な使用法の良い例を提供します。

質問に直接答えるために、正しく実装さIEnumeratorれている場合、コレクションを通過するために使用されるオブジェクトは、呼び出し元ごとに一意です。これは、複数のコンシューマーforeachにコレクションを呼び出させることができ、それぞれがコレクションへの独自のインデックスを持つ独自の列挙子を持つことを意味します。

これは、コレクションへの変更からの基本的な保護のみを提供することに注意してください。そのためには、適切なlock()ブロックを使用する必要があります(ここを参照)。

于 2012-07-25T18:05:12.747 に答える
1

コードを考えてみましょう:

    class Program
{
    static void Main(string[] args)
    {
        var test = new EnumTest();
        test.ConsumeEnumerable2Times();
        Console.ReadKey();
    }
}

public class EnumTest
{
    public IEnumerable<int>  CountTo10()
    {
        for (var i = 0; i <= 10; i++)
            yield return i;
    }

    public void ConsumeEnumerable2Times()
    {
        var enumerable = CountTo10();

        foreach (var n in enumerable)
        {
            foreach (int i in enumerable)
            {
                Console.WriteLine("Outer: {0}, Inner: {1}", n, i);
            }
        }
    }
}

このコードは出力を生成します:

Outer: 0, Inner: 1
Outer: 0, Inner: 2
...
Outer: 1, Inner: 0
Outer: 1, Inner: 1
...
Outer: 10, Inner: 10

IEnumerableを使用すると、同じコレクションを何度も列挙できます。IEnumerableは、実際には、列挙の要求ごとにIEnumeratorの新しいインスタンスを返します。

上記の例では、メソッドEnumTest()が1回呼び出されましたが、返されたIEnumerableが2回使用されました。毎回独立して10まで数えました。

これが、「複数の消費者が互いに干渉することなく、コレクションを一度に列挙できる」理由です。同じIEnumerableオブジェクトを2つのメソッドに渡すことができ、それらはコレクションを個別に列挙します。IEnumeratorを使用すると、それを実現することはできません。

私の英語について申し訳ありません。

于 2012-07-25T18:07:05.980 に答える
1

実装する型にIEnumeratorは、反復できるようなメソッドとプロパティ、つまり、、、が必要MoveNext()です。このオブジェクトを同時に反復しようとする複数のスレッドがある場合はどうなりますか?どちらも同じプロパティを変更する同じ関数を呼び出すため、お互いにステップします。Reset()CurrentMoveNext()Current

実装するタイプはIEnumerable、のインスタンスを提供できる必要がありますIEnumerator。では、複数のスレッドがオブジェクトを反復処理するとどうなりますか?各スレッドには、IEnumeratorオブジェクトの個別のインスタンスがあります。返されるIEnumeratorは、コレクションと同じタイプのオブジェクトではありません。それらはまったく異なるものです。ただし、次のアイテムを取得してコレクションの現在のアイテムを表示する方法を知っており、これらの各オブジェクトには、列挙の現在の状態に関する独自の内部データがあります。したがって、それらは互いに踏むことはなく、別々のスレッドからコレクションを安全に反復します。

場合によっては、コレクションタイプはそれ自体にIEnumeratorを実装し(それはそれ自体の列挙子です)、次にそれ自体を返すだけでIEnumerableを実装します。この場合、複数のスレッドはまだ列挙に同じオブジェクトを使用しているため、何も得られません。これは逆です。代わりに、正しい手順は、最初にコレクションに個別の(ネストできる)列挙子タイプを実装することです。次に、そのタイプの新しいインスタンスを返すことでIEnumerableを実装し、プライベートインスタンスを保持することでIEnumeratorを実装します。

于 2012-07-25T18:18:19.500 に答える