web-dev-qa-db-ja.com

配列から汎用列挙子を取得する

C#では、特定の配列から汎用列挙子をどのように取得しますか?

以下のコードでは、MyArrayMyTypeオブジェクトの配列です。表示されている方法でMyIEnumeratorを取得したいのですが、空の列挙子を取得しているようです(ただし、MyArray.Length > 0)。

MyType[] MyArray = ... ;
IEnumerator<MyType> MyIEnumerator = MyArray.GetEnumerator() as IEnumerator<MyType>;
86
JaysonFix

2.0以降で動作します:

((IEnumerable<MyType>)myArray).GetEnumerator()

3.5+で動作します(派手なLINQy、少し効率が悪い):

myArray.Cast<MyType>().GetEnumerator()   // returns IEnumerator<MyType>
93
Mehrdad Afshari

キャストが醜いかどうかを判断して、無関係なライブラリ呼び出しを正当化できます。

_int[] arr;
IEnumerator<int> Get1()
{
    return ((IEnumerable<int>)arr).GetEnumerator();  // <-- 1 non-local call

    // ldarg.0 
    // ldfld int32[] foo::arr
    // castclass System.Collections.Generic.IEnumerable`1<int32>
    // callvirt instance class System.Collections.Generic.IEnumerator`1<!0> System.Collections.Generic.IEnumerable`1<int32>::GetEnumerator()
}

IEnumerator<int> Get2()
{
    return arr.AsEnumerable().GetEnumerator();   // <-- 2 non-local calls

    // ldarg.0 
    // ldfld int32[] foo::arr
    // call class System.Collections.Generic.IEnumerable`1<!!0> System.Linq.Enumerable::AsEnumerable<int32>(class System.Collections.Generic.IEnumerable`1<!!0>)
    // callvirt instance class System.Collections.Generic.IEnumerator`1<!0> System.Collections.Generic.IEnumerable`1<int32>::GetEnumerator()
}
_

また、完全を期すために、以下は正しくなく、実行時にクラッシュすることにも注意してください。これは、_T[]_がnon-generic IEnumerableインターフェースを選択するためですGetEnumerator()のデフォルト(明示的ではない)実装。

_IEnumerator<int> NoGet()                    // error - do not use
{
    return (IEnumerator<int>)arr.GetEnumerator();

    // ldarg.0 
    // ldfld int32[] foo::arr
    // callvirt instance class System.Collections.IEnumerator System.Array::GetEnumerator()
    // castclass System.Collections.Generic.IEnumerator`1<int32>
}
_

謎は、なぜ_SZGenericArrayEnumerator<T>_がSZArrayEnumeratorから継承しないのか-現在「封印済み」とマークされている内部クラスです-これにより、(共変)汎用列挙子をデフォルトで返すことができるためです?

52
Glenn Slayden

私はキャストが好きではないので、少し更新します:

your_array.AsEnumerable().GetEnumerator();
23
greenoldman

できるだけクリーンにするために、コンパイラーにすべての作業を任せたいと思います。キャストはありません(そのため、実際にはタイプセーフです)。サードパーティのライブラリ(System.Linq)は使用されません(実行時のオーバーヘッドはありません)。

    public static IEnumerable<T> GetEnumerable<T>(this T[] arr)
    {
        return arr;
    }

//そしてコードを使用するには:

    String[] arr = new String[0];
    arr.GetEnumerable().GetEnumerator()

これは、すべてをクリーンに保つコンパイラーの魔法を利用しています。

もう1つ注意すべき点は、私の答えはコンパイル時のチェックを行う唯一の答えであることです。

その他のソリューションでは、「arr」のタイプが変更されると、呼び出しコードがコンパイルされ、実行時に失敗し、実行時のバグが発生します。

私の答えはコードがコンパイルされない原因となるため、間違った型を使用していることを通知するので、コードにバグが含まれる可能性が低くなります。

6
leat

YourArray.OfType()。GetEnumerator();

タイプをチェックするだけでキャストは不要なので、パフォーマンスが少し向上する可能性があります。

2
Andrew Dennison
    MyType[] arr = { new MyType(), new MyType(), new MyType() };

    IEnumerable<MyType> enumerable = arr;

    IEnumerator<MyType> en = enumerable.GetEnumerator();

    foreach (MyType item in enumerable)
    {

    }
1
Maxim Q

もちろん、できることは、配列用の独自の汎用列挙子を実装することだけです。

using System.Collections;
using System.Collections.Generic;

namespace SomeNamespace
{
    public class ArrayEnumerator<T> : IEnumerator<T>
    {
        public ArrayEnumerator(T[] arr)
        {
            collection = arr;
            length = arr.Length;
        }
        private readonly T[] collection;
        private int index = -1;
        private readonly int length;

        public T Current { get { return collection[index]; } }

        object IEnumerator.Current { get { return Current; } }

        public bool MoveNext() { index++; return index < length; }

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

        public void Dispose() {/* Nothing to dispose. */}
    }
}

これは、Glenn Slaydenが述べたSZGenericArrayEnumerator <T>の.NET実装とほぼ同じです。もちろん、これだけを行う必要があります。これは、努力する価値がある場合です。ほとんどの場合、そうではありません。

0
Corniel Nobel