for array c# foreach ienumerable

c# - array - ¿La clase necesita implementar IEnumerable para usar Foreach?



ienumerable c# (11)

Esto está en C #, tengo una clase que estoy usando desde la DLL de otra persona. No implementa IEnumerable pero tiene 2 métodos que devuelven un IEnumerator. ¿Hay alguna manera de usar un ciclo foreach en estos? La clase que estoy usando está sellada.


@Brian: No estoy seguro de que intente recorrer el valor devuelto por la llamada al método o la clase en sí. Si lo que desea es la clase, conviértala en una matriz que puede usar con foreach.


Dada la clase X con los métodos A y B que ambos devuelven IEnumerable, podrías usar un foreach en la clase como este:

foreach (object y in X.A()) { //... } // or foreach (object y in X.B()) { //... }

Presumiblemente, el significado para los enumerables devueltos por A y B está bien definido.


De acuerdo con MSDN :

foreach (type identifier in expression) statement

donde expresión es:

Objeto de colección o expresión de matriz. El tipo del elemento de recopilación debe ser convertible al tipo de identificador. No use una expresión que evalúe nulo. Evalúa a un tipo que implementa IEnumerable o un tipo que declara un método GetEnumerator. En el último caso, GetEnumerator debe devolver un tipo que implemente IEnumerator o declare todos los métodos definidos en IEnumerator.


No estrictamente Siempre que la clase tenga los necesarios GetEnumerator, MoveNext, Reset y miembros actuales, funcionará con foreach


No, no lo hace y ni siquiera necesita un método GetEnumerator, por ejemplo:

class Counter { public IEnumerable<int> Count(int max) { int i = 0; while (i <= max) { yield return i; i++; } yield break; } }

que se llama de esta manera:

Counter cnt = new Counter(); foreach (var i in cnt.Count(6)) { Console.WriteLine(i); }


Re: Si foreach no requiere un contrato de interfaz explícito, ¿encuentra GetEnumerator usando reflexión?

(No puedo comentar porque no tengo una reputación lo suficientemente alta).

Si está implicando la reflexión en tiempo de ejecución, entonces no. Lo hace todo en tiempo de compilación, otro hecho menos conocido es que también verifica si el objeto devuelto que podría implementar Implementador I es desechable.

Para ver esto en acción, considere este fragmento (ejecutable).

using System; using System.Collections.Generic; using System.Text; namespace ConsoleApplication3 { class FakeIterator { int _count; public FakeIterator(int count) { _count = count; } public string Current { get { return "Hello World!"; } } public bool MoveNext() { if(_count-- > 0) return true; return false; } } class FakeCollection { public FakeIterator GetEnumerator() { return new FakeIterator(3); } } class Program { static void Main(string[] args) { foreach (string value in new FakeCollection()) Console.WriteLine(value); } } }


Siempre puede envolverlo, y como un lado para ser "foreachable", solo necesita tener un método llamado "GetEnumerator" con la firma adecuada.

class EnumerableAdapter { ExternalSillyClass _target; public EnumerableAdapter(ExternalSillyClass target) { _target = target; } public IEnumerable GetEnumerator(){ return _target.SomeMethodThatGivesAnEnumerator(); } }


foreach no requiere IEnumerable , contrario a la creencia popular. Todo lo que se requiere es un método GetEnumerator que devuelva cualquier objeto que tenga el método MoveNext y la propiedad get- Current con las firmas apropiadas.

/ EDITAR: en tu caso, sin embargo, no tienes suerte. Sin embargo, puede envolver trivialmente su objeto para que sea enumerable:

class EnumerableWrapper { private readonly TheObjectType obj; public EnumerableWrapper(TheObjectType obj) { this.obj = obj; } public IEnumerator<YourType> GetEnumerator() { return obj.TheMethodReturningTheIEnumerator(); } } // Called like this: foreach (var xyz in new EnumerableWrapper(yourObj)) …;

/ EDITAR: El siguiente método, propuesto por varias personas, no funciona si el método devuelve un IEnumerator :

foreach (var yz in yourObj.MethodA()) …;


Para que una clase sea utilizable con foeach, todo lo que necesita hacer es tener un método público que devuelva e IEnumerator llamado GetEnumerator (), eso es:

Tome la siguiente clase, no implementa IEnumerable o IEnumerator:

public class Foo { private int[] _someInts = { 1, 2, 3, 4, 5, 6 }; public IEnumerator GetEnumerator() { foreach (var item in _someInts) { yield return item; } } }

Alternativamente, el método GetEnumerator () podría escribirse:

public IEnumerator GetEnumerator() { return _someInts.GetEnumerator(); }

Cuando se usa en un foreach (Tenga en cuenta que no se utiliza el envoltorio, solo una instancia de clase):

foreach (int item in new Foo()) { Console.Write("{0,2}",item); }

huellas dactilares:

1 2 3 4 5 6


Respuesta corta:

Necesitas una clase con un método llamado GetEnumerator , que devuelve el IEnumerator que ya tienes. Lograr esto con un simple contenedor:

class ForeachWrapper { private IEnumerator _enumerator; public ForeachWrapper(Func<IEnumerator> enumerator) { _enumerator = enumerator; } public IEnumerator GetEnumerator() { return _enumerator(); } }

Uso:

foreach (var element in new ForeachWrapper(x => myClass.MyEnumerator())) { ... }

De la especificación del lenguaje C # :

El procesamiento en tiempo de compilación de una instrucción foreach primero determina el tipo de colección, el tipo de enumerador y el tipo de elemento de la expresión. Esta determinación procede de la siguiente manera:

  • Si el tipo X de expresión es un tipo de matriz, entonces hay una conversión de referencia implícita de X a la interfaz System.Collections.IEnumerable (ya que System.Array implementa esta interfaz). El tipo de recopilación es la interfaz System.Collections.IEnumerable, el tipo de enumerador es la interfaz System.Collections.IEnumerator y el tipo de elemento es el tipo de elemento de la matriz tipo X.

  • De lo contrario, determine si el tipo X tiene un método GetEnumerator apropiado:

    • Realice la búsqueda de miembros en el tipo X con el identificador GetEnumerator y sin argumentos de tipo. Si la búsqueda de miembro no produce una coincidencia, o produce una ambigüedad, o produce una coincidencia que no es un grupo de métodos, verifique si hay una interfaz enumerable como se describe a continuación. Se recomienda que se emita una advertencia si la búsqueda de miembros produce algo excepto un grupo de métodos o ninguna coincidencia.

    • Realice una resolución de sobrecarga utilizando el grupo de métodos resultante y una lista de argumentos vacía. Si la resolución de sobrecarga no da lugar a métodos aplicables, genera una ambigüedad o da como resultado un único mejor método, pero dicho método es estático o no público, busque una interfaz enumerable como se describe a continuación. Se recomienda que se emita una advertencia si la resolución de sobrecarga produce algo, excepto un método de instancia público inequívoco o ningún método aplicable.

    • Si el tipo de retorno E del método GetEnumerator no es una clase, estructura o tipo de interfaz, se produce un error y no se realizan más pasos.

    • La búsqueda de miembros se realiza en E con el identificador Actual y sin argumentos de tipo. Si la búsqueda de miembros no produce coincidencias, el resultado es un error o el resultado es cualquier cosa, excepto una propiedad de instancia pública que permite la lectura, se produce un error y no se realizan más pasos.

    • La búsqueda de miembros se realiza en E con el identificador MoveNext y sin argumentos de tipo. Si la búsqueda de miembros no produce coincidencias, el resultado es un error o el resultado es cualquier cosa, excepto un grupo de métodos, se produce un error y no se realizan más pasos.

    • La resolución de sobrecarga se realiza en el grupo de métodos con una lista de argumentos vacía. Si la resolución de sobrecarga no da lugar a métodos aplicables, genera una ambigüedad o da como resultado un único mejor método, pero ese método es estático o no público o su tipo de devolución no es bool, se produce un error y no se toman más medidas.

    • El tipo de recopilación es X, el tipo de enumerador es E y el tipo de elemento es el tipo de la propiedad Actual.

  • De lo contrario, busca una interfaz enumerable:

    • Si hay exactamente un tipo T tal que hay una conversión implícita de X a la interfaz System.Collections.Generic.IEnumerable <T>, entonces el tipo de colección es esta interfaz, el tipo de enumerador es la interfaz System.Collections.Generic. IEnumerator <T>, y el tipo de elemento es T.

    • De lo contrario, si hay más de un tipo T, se produce un error y no se realizan más pasos.

    • De lo contrario, si hay una conversión implícita de X a la interfaz System.Collections.IEnumerable, entonces el tipo de colección es esta interfaz, el tipo de enumerador es la interfaz System.Collections.IEnumerator y el tipo de elemento es object.

    • De lo contrario, se produce un error y no se realizan más pasos.


El tipo solo requiere tener un método público / no estático / no genérico / sin parámetros llamado GetEnumerator que debe devolver algo que tenga un método público MoveNext y una propiedad Current pública. Como recuerdo en alguna parte el Sr. Eric Lippert, esto fue diseñado para acomodarse a la era pre-genérica tanto para la seguridad de tipo como para problemas de desempeño relacionados con el boxeo en el caso de tipos de valores.

Por ejemplo, esto funciona:

class Test { public SomethingEnumerator GetEnumerator() { } } class SomethingEnumerator { public Something Current //could return anything { get { } } public bool MoveNext() { } } //now you can call foreach (Something thing in new Test()) //type safe { }

Esto es traducido por el compilador a:

var enumerator = new Test().GetEnumerator(); try { Something element; //pre C# 5 while (enumerator.MoveNext()) { Something element; //post C# 5 element = (Something)enumerator.Current; //the cast! statement; } } finally { IDisposable disposable = enumerator as System.IDisposable; if (disposable != null) disposable.Dispose(); }

Desde la sección 8.8.4 de la especificación.

Algo que vale la pena destacar es la precedencia del enumerador involucrada: parece que si tiene un método public GetEnumerator , entonces esa es la opción predeterminada de foreach independientemente de quién lo implemente. Por ejemplo:

class Test : IEnumerable<int> { public SomethingEnumerator GetEnumerator() { //this one is called } IEnumerator<int> IEnumerable<int>.GetEnumerator() { } }

( Si no tiene una implementación pública (es decir, solo implementación explícita), entonces la precedencia es como IEnumerator<T> >> IEnumerator ) .