配列からジェネリック列挙器を取得します
-
13-09-2019 - |
質問
C#では、特定の配列から一般的な列挙器をどのように取得しますか?
以下のコードでは、 MyArray
の配列です MyType
オブジェクト。入手したいのですが MyIEnumerator
示されているファッションでは、空の列挙器を取得しているようです(ただし、それを確認しましたが MyArray.Length > 0
).
MyType [ ] MyArray = ... ;
IEnumerator<MyType> MyIEnumerator
= ( MyArray.GetEnumerator() as IEnumerator<MyType> ) ;
解決
2.0+で動作します:
((IEnumerable<MyType>)myArray).GetEnumerator()
3.5+で動作します(派手なLinqy、少し効率が低い):
myArray.Cast<MyType>().GetEnumerator() // returns IEnumerator<MyType>
他のヒント
キャスティングが無関係なライブラリコールを保証するのに十分なほど醜いかどうかを自分で決定できます。
int[] arr;
IEnumerator<int> Get1()
{
return ((IEnumerable<int>)arr).GetEnumerator(); // <-- 1 non-local call
// L_0001: ldarg.0
// L_0002: ldfld int32[] foo::arr
// L_0007: castclass [mscorlib]System.Collections.Generic.IEnumerable`1<int32>
// L_000c: callvirt instance class [mscorlib]System.Collections.Generic.IEnumerator`1<!0> [mscorlib]System.Collections.Generic.IEnumerable`1<int32>::GetEnumerator()
// L_0011: stloc.0
}
IEnumerator<int> Get2()
{
return arr.AsEnumerable().GetEnumerator(); // <-- 2 non-local calls
// L_0001: ldarg.0
// L_0002: ldfld int32[] foo::arr
// L_0007: call class [mscorlib]System.Collections.Generic.IEnumerable`1<!!0> [System.Core]System.Linq.Enumerable::AsEnumerable<int32>(class [mscorlib]System.Collections.Generic.IEnumerable`1<!!0>)
// L_000c: callvirt instance class [mscorlib]System.Collections.Generic.IEnumerator`1<!0> [mscorlib]System.Collections.Generic.IEnumerable`1<int32>::GetEnumerator()
// L_0011: stloc.0
}
また、完全性のために、以下が正しくないことにも注意する必要があります - そして、実行時にクラッシュします - T[]
を選択します 非-ジェネリック IEnumerable
デフォルトの(すなわち非推定)実装のためのインターフェイス GetEnumerator()
.
IEnumerator<int> NoGet() // error - do not use
{
return (IEnumerator<int>)arr.GetEnumerator();
// L_0001: ldarg.0
// L_0002: ldfld int32[] foo::arr
// L_0007: callvirt instance class [mscorlib]System.Collections.IEnumerator [mscorlib]System.Array::GetEnumerator()
// L_000c: castclass [mscorlib]System.Collections.Generic.IEnumerator`1<int32>
// L_0011: stloc.0
}
謎は、なぜそうではないのですか SZGenericArrayEnumerator<T>
継承から SZArrayEnumerator
- 現在「密閉」とマークされている内部クラス - これにより、(共変動)ジェネリック列挙器がデフォルトで返される可能性があるためですか?
私はキャストが好きではないので、少しの更新:
your_array.AsEnumerable().GetEnumerator();
できるだけきれいにするために、私はコンパイラにすべての作業をさせるのが好きです。キャストはありません(そのため、実際にはタイプセーフ)。サードパーティライブラリ(System.linq)は使用されません(ランタイムオーバーヘッドはありません)。
public static IEnumerable<T> GetEnumerable<T>(this T[] arr)
{
return arr;
}
//コードを使用するには:
String[] arr = new String[0];
arr.GetEnumerable().GetEnumerator()
これは、すべてをきれいに保つコンパイラマジックを利用します。
注意すべきもう1つのポイントは、私の答えがコンパイル時間チェックを行う唯一の答えであるということです。
「arr」のタイプが変更された場合、他のソリューションのいずれかについて、コードを呼び出すと実行時に故障して実行時のバグが生じます。
私の答えは、コードがコンパイルされないため、コードにバグを出荷する可能性が低くなります。
yourarray.oftype()。getEnumerator();
キャストではなく、タイプをチェックするだけであるため、少し良くパフォーマンスを発揮する可能性があります。
MyType[] arr = { new MyType(), new MyType(), new MyType() };
IEnumerable<MyType> enumerable = arr;
IEnumerator<MyType> en = enumerable.GetEnumerator();
foreach (MyType item in enumerable)
{
}
もちろん、できることは、アレイ用の独自の汎用列挙器を実装するだけです。
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. */}
}
}
これは、SzgenericArrayenumeratorの.NETインプレメンティングとほぼ同等ですu003CT>グレン・スレイデンが述べたように。もちろん、これを行う必要があります。これは努力する価値がある場合です。ほとんどの場合、そうではありません。