1

投稿を読むとき、いくつかのポイントが例なしで与えられました:

IEnumerable / IEnumerable を実装するには、列挙子を提供する必要があります。

クラスが別のコレクションを「ラップ」している場合は、ラップされたコレクションの列挙子を返すことによって。

•yield return を使用したイテレータ経由。

•独自の IEnumerator/IEnumerator 実装をインスタンス化することによって

(私の赤ちゃんの心はそれを次のように解釈します)

(ポイント1)

    If the class is "wrapping" another collection, by returning the
wrapped collection's enumerator.

ということでしょうか..

class  StringCollections
{

 //A class is wrapping another collection
  string[]  names=new string {“Jon Skeet”,”Hamish Smith”,
                  ”Marc Gravell”,”Jrista”,”Joren”};

//by returning the wrapped collection’s enumerator

 public IEnumerator GetEnumerator( )
  {
      // What should I return here ?
       //like the following ?
        yield return names[0];
        yield return names[1];
        yield return names[2];
      ....

       (or)
        foreach(string str in names)
        {
            yield return str;
         }                  

  }

}

(ポイント2)

•Via an iterator using yield return.(This point was well explained 
 by Marc Gravell)

ポイント3

By instantiating your own IEnumerator/IEnumerator<T> implementation*

ここでポイント 3 は何を表しているのですか? それはつまり、私はカスタム列挙子を構築することができます..(右?)私の質問は、ビルド前の列挙子/列挙子で十分な場合です(初心者として盲目的にこれを確認するべきではありません)。例は私の疑問を明確にします。

この長々とした話と親切な回答を読んでくれてありがとう。

4

4 に答える 4

3

(ポイント 1) GetEnumerator の呼び出しを他のコレクション列挙子にチェーンできます。

public class PrimeNumbers : IEnumerable
{
    public IEnumerator GetEnumerator()
    {
        var primes = new List<int> { 2, 3, 5, 7, 11 };
        return primes.GetEnumerator();
    }
}

(ポイント2)コードの例に似ています

public IEnumerator GetEnumerator()
{
    var primes = new List<int> { 2, 3, 5, 7, 11 };
    foreach (var number in primes)
    {
        yield return number;
    }
}

または、論理で列挙子を配置します。

public class PrimeNumbers : IEnumerable
{
    public IEnumerator GetEnumerator()
    {
        for(int i=2; ;i++)
        {
            if(IsPrime(i))
            {
                yield return i;
            }
        }
    }
}

(ポイント 3)独自の Enumerator を実装したい場合はあまりありませんが、たとえば、素数などの無限の値のセットを探すことができます。

public class PrimeNumbers : IEnumerable
{
    public IEnumerator GetEnumerator()
    {
        return new MyEnumerator();
    }
}

public class MyEnumerator : IEnumerator
{
    private int lastPrimeNumber = 1;

    public bool MoveNext()
    {
        lastPrimeNumber = /* some logic that find the next prime */;
        return true; // There is always next prime
    }

    public void Reset()
    {
        lastPrimeNumber = 1;
    }

    public object Current
    {
        get { return lastPrimeNumber; }
    }
}

使用例は次のとおりです。

public void PrintAllPrimes()
{
    var numbers = new PrimeNumbers();

    // This will never end but it'll print all primes until my PC crash
    foreach (var number in numbers)
    {
        Console.WriteLine(number);
    }
}

私が考えることができる長所と短所:

  • ポイント 1 : アイテムを列挙する最も簡単な方法ですが、事前にすべてのアイテムを知っている必要があります
  • ポイント 2 : 列挙に何らかのロジックがある場合は読み取り可能であり、遅延も発生するため、実際に要求されるまで項目を計算する必要はありません
  • ポイント 3 : 再利​​用するのが最も簡単ですが、読みにくくなります (MoveNext でアイテムを計算しますが、実際には Current プロパティから返されます)。
于 2009-10-20T21:15:35.063 に答える
2

以下に例を示します: (注: これは簡略化されたものであり、スレッドセーフな例ではありません)

public class PersonCollection : IEnumerable
{
    private ArrayList alPers = new ArrayList();
    public IEnumerator GetEnumerator() { return new myTypeEnumerator(this); }
    public class myTypeEnumerator : IEnumerator
    {
        int nIndex;
        PersonCollection pers;
        private int count { get { return pers.alPers.Count; } }
        public myTypeEnumerator(PersonCollection myTypes) 
         { pers = myTypes; nIndex = -1; }
        public bool MoveNext() { return nIndex <= count && ++nIndex < count; }
        // MovePrev() not strictly required
        public bool MovePrev() { return (nIndex > -1 && --nIndex > 0 ); }
        public object Current { get { return (pers[nIndex]); } }
        public void Reset() { nIndex = -1; }
    }
}

EDIT:-1未満のインデックス値を取るMove Previousに関連して、以下の@Jorenによって提起された問題を修正します。foreach 実装の一部としてフレームワークによって呼び出された場合、MoveNext() はこの修正を必要としません。その場合、MoveNext() が false を返すと、列挙子インスタンスが終了するためです。ただし、クライアントが手動で MoveNext() を呼び出すと、列挙子が終了しないため、修正も必要です。

また、注: この内部の詳細をどのように実装するかはあなた次第であり、内部で状態を管理する方法に依存します。たとえば、参照を保持する iunternal の「バケット」が ArrayList ではなく、リンクされたリストである場合、MoveNext() は、Current フィールドを古い Current の nextItem プロパティを指すように変更するだけで実装できます...

于 2009-10-20T21:07:28.620 に答える
1

あなたが 1 に対して言ったことは、実際には 2 にも当てはまります。

1の場合、それはもっと似ているでしょう

public IEnumerator GetEnumerator() {
    return names.GetEnumerator();
}

ポイント 3 については、GetEnumerator に実際の作業を行わせることを意味します。つまり、定義したコレクションの Enumerator を手動で実装します。

于 2009-10-20T21:09:58.460 に答える
0

「ラップされたコレクションの列挙子を返すことにより、クラスが別のコレクションを「ラップ」している場合」とは、次のことを意味します。

class StringCollections{
    //A class is wrapping another collection
    string[] names=new string {“Jon Skeet”,”Hamish Smith”,
                                ”Marc Gravell”,”Jrista”,”Joren”};
    public IEnumerator GetEnumerator() { return names.GetEnumerator(); }
}
于 2009-10-20T21:25:34.527 に答える