obter enumerador genérico de uma matriz
-
13-09-2019 - |
Pergunta
Em C#, como se obtém um enumerador genérico de uma determinada matriz?
No código abaixo, MyArray
é uma variedade de MyType
objetos. Eu gostaria de obter MyIEnumerator
Na moda mostrada, mas parece que eu obtive um enumerador vazio (embora eu tenha confirmado isso MyArray.Length > 0
).
MyType [ ] MyArray = ... ;
IEnumerator<MyType> MyIEnumerator
= ( MyArray.GetEnumerator() as IEnumerator<MyType> ) ;
Solução
Funciona em 2.0+:
((IEnumerable<MyType>)myArray).GetEnumerator()
Funciona no 3.5+ (Fancy Linqy, um pouco menos eficiente):
myArray.Cast<MyType>().GetEnumerator() // returns IEnumerator<MyType>
Outras dicas
Você pode decidir por si mesmo se o elenco é feio o suficiente para justificar uma chamada estranha da biblioteca:
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
}
E para a completude, também deve-se observar que o seguinte não está correto-e falhará no tempo de execução-porque T[]
escolhe o não-genérico IEnumerable
Interface para sua implementação padrão (ou seja, não explícita) de 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
}
O mistério é, por que não SZGenericArrayEnumerator<T>
herdar de SZArrayEnumerator
-Uma classe interna que atualmente está marcada "selada"-já que isso permitiria que o enumerador genérico (covariante) fosse devolvido por padrão?
Como não gosto de elenco, uma pequena atualização:
your_array.AsEnumerable().GetEnumerator();
Para torná -lo o mais limpo possível, gosto de deixar o compilador fazer todo o trabalho. Não há elencos (então é realmente seguro). Nenhuma biblioteca de terceiros (System.LinQ) é usada (sem sobrecarga de tempo de execução).
public static IEnumerable<T> GetEnumerable<T>(this T[] arr)
{
return arr;
}
// e para usar o código:
String[] arr = new String[0];
arr.GetEnumerable().GetEnumerator()
Isso tira proveito de alguma magia do compilador que mantém tudo limpo.
O outro ponto a ser observado é que minha resposta é a única resposta que fará a verificação do tempo de compilação.
Para qualquer uma das outras soluções se o tipo de "ARR" mudar, o código de chamada compilará e falhará no tempo de execução, resultando em um bug de tempo de execução.
Minha resposta fará com que o código não seja compilação e, portanto, tenho menos chances de enviar um bug no meu código, pois isso me indica que estou usando o tipo errado.
YourArray.oftype (). Getenumerator ();
pode ter um desempenho um pouco melhor, pois ele só precisa verificar o tipo e não ser lançado.
MyType[] arr = { new MyType(), new MyType(), new MyType() };
IEnumerable<MyType> enumerable = arr;
IEnumerator<MyType> en = enumerable.GetEnumerator();
foreach (MyType item in enumerable)
{
}
O que você pode fazer, é claro, é apenas implementar seu próprio enumerador genérico para matrizes.
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. */}
}
}
Isso é mais ou menos igual à implementação .NET de SzgeRericArrayenumeratoru003CT> Como mencionado por Glenn Slayden. É claro que você deve fazer isso apenas, são os casos em que vale a pena o esforço. Na maioria dos casos, não é.