recorrer create array c# .net foreach ienumerable

create - ¿Por qué el compilador de C#está contento con IEnumerable doble<T> y foreach T?



ienumerable c# stack overflow (4)

Esto se debe a que foreach no realiza la verificación del tiempo de compilación en su caso específico. Si InvalidCastException un código de trabajo, obtiene una InvalidCastException en tiempo de ejecución .

using System.Collections.Generic; public class Test { internal class Program { public static void Main() { var item = new Foo(); var inner = new List<Foo>(); var outer = new List<List<Foo>>(); inner.Add(item); outer.Add(inner); IEnumerable<IEnumerable<Foo>> data = outer; foreach (Foo foo in data) { foo.Bar(); } } } public class Foo { public void Bar() { } } }

hacer foreach (Foo foo in data) es equivalente a llamar

IEnumerator enumerator = ((IEnumerable)data).GetEnumerator(); Foo foo; //declared here in C# 4 and older while(enumerator.MoveNext()) { //Foo foo; //declared here in C# 5 and newer foo = (Foo)enumerator.Current; //Here is the run time error in your code. //The code inside the foreach loop. { foo.Bar(); } }

Así que ya ves, no importa qué tipo de foo = (Foo)enumerator.Current; , siempre y cuando foo = (Foo)enumerator.Current; llamada tiene éxito.

La razón por la que no arroja ningún error de tiempo de compilación es que IEnumerable<T> es covariant . Eso significa que puedo pasar cualquier clase basada en Foo o más derivada de Foo . Entonces, si potencialmente pudiera hacer una segunda clase que hereda de Foo que también sea compatible con IEnumerable<Foo> y que mi lista contenga eso, en su lugar causaría un error en la conversión.

//This code compiles fine in .NET 4.5 and runs without throwing any errors. internal class Program { public static void Main() { var item = new Baz(); var inner = new List<Baz>(); inner.Add(item); IEnumerable<IEnumerable<Foo>> data = inner; foreach (Foo foo in data) { foo.Bar(); } } } public class Foo { public void Bar() { } } public class Baz : Foo, IEnumerable<Foo> { IEnumerator IEnumerable.GetEnumerator() { throw new NotImplementedException(); } IEnumerator<Foo> IEnumerable<Foo>.GetEnumerator() { throw new NotImplementedException(); } }

Sin embargo, si marca Foo como sealed el compilador ahora sabe que no pueden existir más clases derivadas y luego arrojará el error del compilador

Sé que este código no funciona (y no tengo problemas para escribirlo de una manera que funcione). Me preguntaba cómo el compilador puede construir sin errores. ¿Y obtienes errores de tiempo de ejecución si lo ejecutas? (suponiendo que los datos no fueran nulos)

using System; using System.Collections.Generic; public class Class1 { public void Main() { IEnumerable<IEnumerable<Foo>> data = null; foreach(Foo foo in data){ foo.Bar(); } } } public class Foo { public void Bar() { } }



Puedes hacer que se produzca el error de tiempo de compilación marcando tu clase de Foo como sealed :

public sealed class Foo { public void Bar() { } }

De lo contrario, el compilador no puede estar seguro de que la conversión es posible.


Ya que especificó un tipo explícitamente, cada iteración de foreach intentará convertir (en tiempo de ejecución) el elemento actual a Foo . No es diferente a escribir esto:

IEnumerable<IEnumerable<Foo>> data = null; foreach (object item in data) { Foo foo = (Foo)item; foo.Bar(); }

O más directamente, esto:

IEnumerable<Foo> data = null; Foo foo = (Foo)data;

El compilador no se queja por la misma razón por la que no se queja de una conversión ao desde una interfaz: no puede probar que la conversión no es válida (vea here un ejemplo de por qué).

Tenga en cuenta que, en cambio, si trabaja con una clase concreta en lugar de la interfaz IEnumerable , obtendrá un error en tiempo de compilación. Por ejemplo:

IEnumerable<List<Foo>> data = null; foreach(Foo foo in data){ // compile-time error here: "cannot convert List<Foo> to Foo" foo.Bar(); }