Según lo que he leído, se tomó una decisión de diseño para ciertos tipos de enumeradores de colecciones para que sean estructuras mutables en lugar de tipos de referencia por motivos de rendimiento.
Buena pregunta.
En primer lugar, estás en lo cierto. Aunque, en general, los tipos de valores mutables son un mal olor de código, en este caso están justificados:
- La mutación se oculta casi por completo por el usuario.
- Es muy poco probable que alguien use el enumerador de manera confusa.
- El uso de un tipo de valor mutable en realidad resuelve un problema de rendimiento realista en un escenario extremadamente común.
Me pregunto si alguien sabe por qué iterador genérico de matriz se implementó como un tipo de referencia cuando tantas colecciones críticos otro rendimiento utilizan estructuras mutables en su lugar.
Porque si eres el tipo de persona que se preocupa por el rendimiento de enumerar una serie entonces ¿Por qué utiliza un enumerador en el primer lugar? Es una matriz por el amor del cielo; simplemente escriba un ciclo for
que itere sobre sus índices como una persona normal y nunca asigne el enumerador. (O un foreach
bucle; el compilador de C# volverá a escribir el bucle foreach
en el circuito equivalente for
si se sabe que la colección de bucle es una matriz.)
La única razón por la que le obtener un enumerador de una matriz en el El primer lugar es si lo pasa a un método que toma un IEnumerator<T>
, en cuyo caso si el enumerador es una estructura, entonces va a estar encajándolo de todos modos. ¿Por qué tomar el gasto de hacer el tipo de valor y luego boxearlo? Simplemente haga que sea un tipo de referencia para comenzar.
Dada la prevalencia (y potencia expresiva) de LINQ, es probable que los enumeradores se recuperen para matrices con bastante frecuencia. ejemplo, las clases de reflexión en arrays de retorno .NET para muchas propiedades/métodos - y usar LINQ w/reflection es bastante útil. Que yo sepa, la mayoría de los operadores de LINQ no realizan comprobaciones de casos especiales para determinar si el IEnumerable <> con el que están tratando es realmente una matriz. – LBushkin
@LBushkin: Correcto. Si no está dispuesto a asumir el costo de la asignación de montón y luego recolectar un * enumerador * de basura, entonces también es probable que no esté dispuesto a asumir el costo de la asignación de montón y recolección de basura * la consulta *, y todos los objetos del enumerador que crea y todos los delegados que tienes que crear Sin mencionar todos los gastos generales de llamar a esa consulta (verificar argumentos para ser correctos, etc.). LINQ to objects es razonablemente eficiente, ¡pero ciertamente no fue diseñado para minimizar las asignaciones de montón! LINQ asigna memoria de montón por todo el lugar. –
Otro factor, sospecho, es que el enumerador de la 'Lista' original fue diseñado en los días previos a los genéricos. La estructura de 'List.Enumerator' solo afecta a tres tipos de código: código que usa explícitamente el tipo' List.Enumerator', código que acepta un parámetro genérico restringido a 'IEnumerable', o código que usa' var' para declarar un variable que contendrá el enumerador de una lista. Si un programador va a especificar 'List.Enumerator', el programador debe saber lo que está haciendo. En cuanto a los otros dos tipos de código, no podrían existir cuando se diseñó 'List.Enumerator'. – supercat