Optimización de Listas: Buenas Prácticas en C#

Clase 14 de 35Curso de C# con .Net Core 2.1

Resumen

Exponer colecciones mutables puede romper la integridad de tu dominio. Aquí aprenderás a proteger tus datos con listas de solo lectura, a preferir interfaces genéricas como IEnumerable, y a evitar que otros desarrolladores inserten objetos fuera de la estructura correcta. La meta: APIs seguras, claras y fáciles de usar.

¿Por qué evitar listas modificables públicas?

Cuando devuelves una lista concreta, cualquier consumidor puede modificarla. El ejemplo es claro: alguien podría hacer listaObjetos.agregar y crear un "curso loco" que no pertenece a la escuela ni a ningún curso o alumno. Ese objeto sería huérfano y rompería la relación entre cursos, asignaturas y evaluaciones.

  • Evita que agreguen cursos fuera de la escuela.
  • Impide evaluaciones sin curso ni alumno asociado.
  • Protege la integridad de tus estructuras.

Código ilustrativo del problema:

// Riesgo: lista pública y modificable
listaObjetos.Add(new Curso { Nombre = "curso loco" }); // Se agrega fuera de la escuela.

La práctica recomendada es no exponer una lista modificable cuando el objetivo no es permitir cambios.

¿Cómo implementar listas de solo lectura en C#?

La solución: devolver una lista de solo lectura. Se menciona usar ReadOnlyList y IReadOnlyList, y convertir con "as read only" al momento de retornar. Así, el consumidor no podrá adicionar elementos.

  • Devuelve ReadOnlyList en los métodos públicos.
  • Usa IReadOnlyList como tipo de recepción cuando consumas.
  • Aplica la conversión "as read only" al retornar la colección.

Ejemplo de implementación y consumo:

// En tu *engine*: exponer en solo lectura
ReadOnlyList<Curso> ObtenerCursos()
{
    // ... construir lista interna
    return listaCursos.AsReadOnly(); // "as read only" al devolver.
}

// Consumo con var: el tipo se infiere como IReadOnlyList
var cursos = ObtenerCursos();

// Intento de modificación: producirá advertencia/error en tiempo de compilación
cursos.Add(new Curso { Nombre = "curso loco" }); // No permitido: colección de solo lectura.

Punto clave: si declaras con var, no necesitas cambiar el tipo explícito cuando el método pase a devolver una lista de solo lectura. El compilador inferirá el tipo correcto.

¿Qué error previenes al usar read only?

  • Evitas inserciones imprevistas en colecciones compartidas.
  • Bloqueas cambios que no pasan por las reglas del dominio.
  • Garantizas que la colección expuesta es inmutable para el consumidor.

¿Qué interfaces genéricas conviene devolver?

Regla de oro: no devuelvas un tipo de lista específico. Prefiere interfaces genéricas. En particular, IEnumerable permite que cada consumidor convierta la secuencia al tipo de colección que necesite, sin atarse a una implementación concreta.

  • Devuelve IEnumerable cuando solo necesitas iteración.
  • Devuelve IReadOnlyList si quieres iteración con acceso indexado sin modificaciones.
  • Reserva la lista concreta para uso interno.

Ejemplo de firma genérica:

IEnumerable<Curso> ObtenerCursos(); // Flexible para el consumidor.

Conceptos y habilidades clave integrados:

  • IEnumerable: interfaz para enumerar colecciones sin exponer implementación.
  • IReadOnlyList: colección indexable que no permite modificaciones externas.
  • ReadOnlyList: colección expuesta en modo de solo lectura.
  • as read only: conversión al retornar para bloquear Add/Remove.
  • var: inferencia de tipos que simplifica el consumo tras cambiar el tipo de retorno.
  • Buenas prácticas de API: encapsular colecciones y evitar efectos secundarios.

¿Tienes un caso real en el que necesites exponer colecciones sin perder control? Cuéntalo en los comentarios y revisamos juntos la mejor estrategia.