получение универсального перечислителя из массива
в C#, как получить универсальный перечислитель из данного массива?
в коде ниже,MyArray
массив MyType
объекты. Я хотел бы получить MyIEnumerator
в моде показано,
но кажется, что я получаю пустой перечислитель (хотя я подтвердил, что MyArray.Length > 0
).
MyType [ ] MyArray = ... ;
IEnumerator<MyType> MyIEnumerator
= ( MyArray.GetEnumerator() as IEnumerator<MyType> ) ;
7 ответов:
работает на 2.0+:
((IEnumerable<MyType>)myArray).GetEnumerator()
работает на 3.5+ (fancy 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[]
выбирает номера-genericIEnumerable
интерфейс для его реализации по умолчанию (т. е. неявной)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
--внутренний класс, который в настоящее время помечено "запечатано" - поскольку это позволит по умолчанию возвращать (ковариантный) общий перечислитель?
чтобы сделать его как можно более чистым, мне нравится, чтобы компилятор выполнял всю работу. Там нет слепков (так что его на самом деле тип-безопасный). Нет сторонних библиотек (System.Linq) используются (без накладных расходов во время выполнения).
public static IEnumerable<T> GetEnumerable<T>(this T[] arr) { return arr; }
// и использовать код:
String[] arr = new String[0]; arr.GetEnumerable().GetEnumerator()
Это использует некоторые компилятор магии, которая держит все в чистоте.
другой момент, чтобы отметить, что мой ответ является единственным ответом, который будет делать проверку времени компиляции.
для любого из других решений если тип "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. */} } }
Это более или менее равно реализации .NET SZGenericArrayEnumerator
, как упоминалось Гленном Слайденом. Вы должны, конечно, только это делать, это случаи, когда это стоит усилий. В большинстве случаев это не так.