IEnumerable <T>を実装する方法


124

次のように、非ジェネリックIEnumerableを実装する方法を知っています。

using System;
using System.Collections;

namespace ConsoleApplication33
{
    class Program
    {
        static void Main(string[] args)
        {
            MyObjects myObjects = new MyObjects();
            myObjects[0] = new MyObject() { Foo = "Hello", Bar = 1 };
            myObjects[1] = new MyObject() { Foo = "World", Bar = 2 };

            foreach (MyObject x in myObjects)
            {
                Console.WriteLine(x.Foo);
                Console.WriteLine(x.Bar);
            }

            Console.ReadLine();
        }
    }

    class MyObject
    {
        public string Foo { get; set; }
        public int Bar { get; set; }
    }

    class MyObjects : IEnumerable
    {
        ArrayList mylist = new ArrayList();

        public MyObject this[int index]
        {
            get { return (MyObject)mylist[index]; }
            set { mylist.Insert(index, value); }
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return mylist.GetEnumerator();
        }
    }
}

ただし、IEnumerableには汎用バージョンがあることにも気づきましたが、IEnumerable<T>それを実装する方法がわかりません。

using System.Collections.Generic;usingディレクティブに追加し、次に変更した場合:

class MyObjects : IEnumerable

に:

class MyObjects : IEnumerable<MyObject>

次に、右クリックしIEnumerable<MyObject>て[ ] を選択するとImplement Interface => Implement Interface、Visual Studioによって次のコードブロックが追加されます。

IEnumerator<MyObject> IEnumerable<MyObject>.GetEnumerator()
{
    throw new NotImplementedException();
}

GetEnumerator();メソッドから非ジェネリックIEnumerableオブジェクトを返すことは今回は機能しないので、ここに何を入れますか?CLIは、非汎用の実装を無視し、foreachループ中に配列を列挙しようとすると、汎用バージョンに直接向かいます。

回答:


149

List<MyObject>代わりになどの一般的なコレクションを使用することを選択した場合はArrayListList<MyObject>が使用できる一般的な列挙子と非一般的な列挙子の両方を提供することがわかります。

using System.Collections;

class MyObjects : IEnumerable<MyObject>
{
    List<MyObject> mylist = new List<MyObject>();

    public MyObject this[int index]  
    {  
        get { return mylist[index]; }  
        set { mylist.Insert(index, value); }  
    } 

    public IEnumerator<MyObject> GetEnumerator()
    {
        return mylist.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return this.GetEnumerator();
    }
}

1
非ジェネリック実装では、返すことthis.GetEnumerator()と単に返すことの間に違いはありGetEnumerator()ますか?
Tanner Swett、

1
@TannerSwett違いはありません。
モンロートーマス

から継承Collection<MyObject>することもでき、カスタムコードを記述する必要はありません。
John Alexiou、2018年

@ ja72すでに別の基本クラスから継承していて、Collection <MyObject>から継承できない場合はどうなりますか?
モンロートーマス

1
@MonroeThomas-次に、回答に示されているようにラップList<T>して実装する必要がありIEnumerable<T>ます。
John Alexiou

69

あなたはおそらくあなたが明示的に実装したくないでしょうIEnumerable<T>(これはあなたが示したものです)。

いつものパターンは使用することがあるIEnumerable<T>のをGetEnumeratorの明示的な実装ではIEnumerable

class FooCollection : IEnumerable<Foo>, IEnumerable
{
    SomeCollection<Foo> foos;

    // Explicit for IEnumerable because weakly typed collections are Bad
    System.Collections.IEnumerator IEnumerable.GetEnumerator()
    {
        // uses the strongly typed IEnumerable<T> implementation
        return this.GetEnumerator();
    }

    // Normal implementation for IEnumerable<T>
    IEnumerator<Foo> GetEnumerator()
    {
        foreach (Foo foo in this.foos)
        {
            yield return foo;
            //nb: if SomeCollection is not strongly-typed use a cast:
            // yield return (Foo)foo;
            // Or better yet, switch to an internal collection which is
            // strongly-typed. Such as List<T> or T[], your choice.
        }

        // or, as pointed out: return this.foos.GetEnumerator();
    }
}

2
これは、SomeCollection <T>がまだIEnumerable <T>を実装していない場合、または列挙シーケンスで返される前に各要素に対して変換またはその他の処理を行う必要がある場合に適したソリューションです。これらの条件のいずれにも該当しない場合は、this.foos.GetEnumerator();を返すだけの方がおそらく効率的です。
モンロートーマス

@MonroeThomas:同意。OPが使用しているコレクションはわかりません。
user7116

8
いい視点ね。しかし、実装IEnumerableは冗長です(IEnumerable<T>すでに継承しています)。
rsenna 2014

@rsenna本当のこと、しかしIListのようにも.NETインタフェースは冗長インターフェイスを実装覚えておいて、それは読みやすくするためだ-公開インタフェースのIList:ICollectionを、IEnumerableを
デビッドKlempfner

@Backwards_Dave不要なノイズを追加するため、実際には(まだ)読みにくくなると思いますが、私はあなたの言ったことを理解しており、それは妥当な意見だと思います。
rsenna 2018

24

なぜ手動で行うのですか?yield returnイテレータを処理するプロセス全体を自動化します。(コンパイラーが生成したコードも含めて、ブログにも書いています)。

あなたが本当にそれを自分でやりたいのなら、あなたもジェネリック列挙子を返さなければなりません。ArrayList汎用ではないため、これ以上使用できなくなります。List<MyObject>代わりにそれを変更してください。もちろんMyObject、コレクションには型(または派生型)のオブジェクトのみがあると想定しています。


2
+1、推奨されるパターンは、ジェネリックインターフェイスを実装し、非ジェネリックインターフェイスを明示的に実装することです。イールドリターンは、ジェネリックインターフェイスに対する最も自然なソリューションです。
user7116

5
OPが列挙子で何らかの処理を行わない限り、yield returnを使用すると、別の状態マシンのオーバーヘッドが追加されるだけです。OPは、基になるジェネリックコレクションによって提供される列挙子を返すだけです。
モンロートーマス

@MonroeThomas:その通りです。について書く前に、質問を注意深く読みませんでしたyield return。カスタム処理があると勘違いしてしまいました。
アンデルスアベル

4

ジェネリックスを使用する場合は、ArrayListの代わりにListを使用してください。リストには、まさに必要なGetEnumeratorメソッドがあります。

List<MyObject> myList = new List<MyObject>();

0

マイリストをにするList<MyObject>、1つのオプション


0

注意IEnumerable<T>出回っがで実装System.Collections別のアプローチは、あなたの得ることであるので、MyObjectsからクラスをSystem.Collections基底クラス(などのドキュメント):

System.Collections:ジェネリックコレクションの基本クラスを提供します。

私たちは、後で仮想オーバーライドするために私たち自身のimplemenationを作ることができますSystem.Collections(唯一のカスタム動作を提供するためのメソッドClearItemsInsertItemRemoveItem、とSetItem一緒にEqualsGetHashCode、およびToStringからObject)を。とは異なり、List<T>簡単に拡張できるように設計されていません。

例:

public class FooCollection : System.Collections<Foo>
{
    //...
    protected override void InsertItem(int index, Foo newItem)
    {
        base.InsertItem(index, newItem);     
        Console.Write("An item was successfully inserted to MyCollection!");
    }
}

public static void Main()
{
    FooCollection fooCollection = new FooCollection();
    fooCollection.Add(new Foo()); //OUTPUT: An item was successfully inserted to FooCollection!
}

collectionまれに発生するカスタムコレクションの動作が必要な場合にのみ、推奨からの駆動に注意してください。使用法を参照してください。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.