Esto es causado por el hecho de que, efectivamente, su lista de inicialización en línea es demasiado grande para la pila - ver este muyrelated question over on the msdn forums donde el escenario está cerca-en idéntica.
Un método en .Net tiene una profundidad y un tamaño de pila. Un StackOverflowException
es causado no solo por la cantidad de llamadas en la pila, sino por el tamaño total de la asignación de memoria de cada método en la pila. En este caso, su método es demasiado grande, y eso se debe a la cantidad de variables locales.
A modo de ejemplo, consideremos el siguiente código:
public class Foo
{
public int Bar { get; set;}
}
public Foo[] GetInts()
{
return new Foo[] { new Foo() { Bar = 1 }, new Foo() { Bar = 2 },
new Foo() { Bar = 3 }, new Foo() { Bar = 4 }, new Foo() { Bar = 5 } };
}
Ahora mira el lead-in IL de ese método cuando se compila (esta es una versión de lanzamiento, también):
.maxstack 4
.locals init (
[0] class SomeExample/Foo '<>g__initLocal0',
[1] class SomeExample/Foo '<>g__initLocal1',
[2] class SomeExample/Foo '<>g__initLocal2',
[3] class SomeExample/Foo '<>g__initLocal3',
[4] class SomeExample/Foo '<>g__initLocal4',
[5] class SomeExample/Foo[] CS$0$0000
)
Nota: el bit real anterior a /
, es decir, SomeExample
dependerá del espacio de nombres y la clase en que se definan el método y la clase anidada. He tenido que editar varias veces para eliminar los nombres tipo de algún código en progreso que 'Estoy escribiendo ¡en el trabajo!
¿Por qué todos esos lugareños? Debido a la forma en que se realiza la inicialización en línea.Cada objeto se actualiza y se almacena en un local "oculto", esto es necesario para que las asignaciones de propiedades se puedan realizar en las inicializaciones en línea de cada Foo
(la instancia del objeto es necesaria para generar la propiedad establecida para Bar
). Esto también demuestra cómo la inicialización en línea es solo algo de azúcar sintáctico C#.
En su caso, son estos locales los que están causando que la pila explote (hay al menos unos miles de ellos solo para los objetos de nivel superior, pero también tiene iniciadores anidados).
El compilador de C# podía alternativamente pre-cargar el número de referencias requeridas para cada sobre de la pila (apareciendo cada uno fuera para cada asignación de propiedad), pero luego de que está abusando de la pila donde el uso de los locales llevará a cabo mucho mejor.
También podría utilizar una sola locales, ya que cada uno es meramente escrito demasiado y se almacena en la lista de índice de matriz, el local nunca se necesita de nuevo. Eso podría ser algo que el equipo de C# considere: Eric Lippert puede tener algunas ideas al respecto si tropieza con este hilo.
Ahora, este examen también nos da una vía potencial alrededor de este uso de los locales para el método muy masiva: utilizar un iterador:
public Foo[] GetInts()
{
return GetIntsHelper().ToArray();
}
private IEnumerable<Foo> GetIntsHelper()
{
yield return new Foo() { Bar = 1 };
yield return new Foo() { Bar = 2 };
yield return new Foo() { Bar = 3 };
yield return new Foo() { Bar = 4 };
yield return new Foo() { Bar = 5 };
}
Ahora, la IL para GetInts()
ahora simplemente tiene .maxstack 8
en la cabeza, y no lugareños. En cuanto a la función de repetidor GetIntsHelper()
tenemos:
.maxstack 2
.locals init (
[0] class SomeExample/'<GetIntsHelper>d__5'
)
Así que ahora hemos dejado de usar todos esos locales en esos métodos ...
Pero ...
vistazo a la clase SomeExample/'<GetIntsHelper>d__5'
, que ha sido generado automáticamente por el compilador, y vemos que los locales todavía están allí; se han promocionado a campos en esa clase:
.field public class SomeExample/Foo '<>g__initLocal0'
.field public class SomeExample/Foo '<>g__initLocal1'
.field public class SomeExample/Foo '<>g__initLocal2'
.field public class SomeExample/Foo '<>g__initLocal3'
.field public class SomeExample/Foo '<>g__initLocal4'
Entonces, la pregunta es: ¿la creación de ese objeto también explotará si se aplica a su escenario? Probablemente no, porque en la memoria debería ser como intentar inicializar una gran matriz, donde las matrices de millones de elementos son bastante aceptables (suponiendo que hay suficiente memoria en la práctica).
Por lo tanto - que puede ser que poder fijar su código simplemente moviendo hacia el uso de un método que IEnumerable
yield
s cada elemento.
Pero la mejor práctica dice que si tiene que tener esto estáticamente definido, considere agregar los datos a un recurso o archivo incrustado en el disco (XML y Linq a XML podrían ser una buena opción) y luego cargarlo desde allí en demanda.
Mejor aún, pegarlo en una base de datos :)
¿Cómo está accediendo a la lista? – Oded
JEEEEZ, ¡realmente deberías considerar almacenarlos en otro lugar, codificando más de 3500 elementos! : | – mattytommo
@Oded - algo así como var sellers = DataHelper.Sellers; – Gaz