Capítulo 9: Colecciones, listas y arrays en C#
5 min de lectura
Parte I - Sección 2: Programación desde cero - Nivel: Principiante
🎯 Objetivos de aprendizaje
Al finalizar este capítulo serás capaz de:
- Entender qué es una colección y por qué necesitamos más que variables simples
- Trabajar con arrays de tamaño fijo
- Usar List
para colecciones dinámicas que crecen y decrecen - Manejar Dictionary<K,V> para pares clave-valor
- Elegir la colección adecuada según el problema
- Recorrer colecciones con foreach, for, y acceso directo
- Construir un gestor completo de lista de materiales con búsqueda y filtrado
📋 Requisitos previos
Antes de comenzar este capítulo debes:
- ✅ Haber completado el Capítulo 8
- ✅ Entender funciones y métodos
- ✅ Conocer bucles (for, foreach, while)
- ✅ Saber trabajar con variables y tipos de datos
🏆 Proyecto del capítulo
Gestor Inteligente de Lista de Materiales
Al final de este capítulo habrás construido un sistema completo para:
- Gestionar componentes eléctricos (agregar, eliminar, buscar)
- Buscar por código, tipo, o designación
- Calcular estadísticas (total de componentes, corriente total, etc.)
- Filtrar por criterios (corriente, voltaje, fabricante)
- Generar reportes formateados
Tiempo estimado: 60-70 minutos
⚠️ Nota Importante: En este capítulo, los datos de los componentes (designación, código, etc.) se crearán manualmente en el código. Esto se hace intencionalmente para centrarnos en el aprendizaje de Colecciones (Listas y Diccionarios) sin distraernos con la complejidad de leer datos reales de la base de datos de EPLAN, lo cual aprenderemos en capítulos posteriores.
📚 Contenido
1. Introducción: El problema de las variables individuales
Sin colecciones (código difícil de mantener):
// Necesitamos almacenar 100 componentes...
string componente1 = "K1";
string componente2 = "K2";
string componente3 = "K3";
// ... 97 variables más
string componente100 = "K100";
// ¿Cómo buscamos? ¿Cómo agregamos uno nuevo?
// Imposible de mantener
Con colecciones (código escalable):
// Array simple de 100 elementos
string[] componentes = new string[100];
componentes[0] = "K1";
componentes[1] = "K2";
// ...
// O mejor: List que crece dinámicamente
List<string> componentes = new List<string>();
componentes.Add("K1");
componentes.Add("K2");
// Fácil agregar, eliminar, buscar
Las colecciones nos permiten:
- Almacenar múltiples valores relacionados
- Agregar y eliminar elementos dinámicamente
- Buscar y filtrar datos eficientemente
- Iterar sobre todos los elementos fácilmente
- Organizar datos complejos
2. Arrays: Colecciones de tamaño fijo
Un array es una colección de elementos del mismo tipo con tamaño fijo.
2.1 Declaración y creación
// Sintaxis
tipo[] nombreArray = new tipo[tamaño];
// Ejemplos
string[] componentes = new string[5];
double[] corrientes = new double[10];
int[] cantidades = new int[20];
2.2 Inicialización
Forma 1: Declarar y luego asignar
string[] componentes = new string[3];
componentes[0] = "K1";
componentes[1] = "Q1";
componentes[2] = "F1";
Forma 2: Inicializar directamente
string[] componentes = {"K1", "Q1", "F1"};
// O más explícito:
string[] componentes = new string[] {"K1", "Q1", "F1"};
Forma 3: Con valores calculados
double[] voltajes = new double[5];
for (int i = 0; i < voltajes.Length; i++)
{
voltajes[i] = 230.0 + i * 10.0; // 230, 240, 250, 260, 270
}
2.3 Acceso a elementos
string[] componentes = {"K1", "Q1", "F1", "M1", "X1"};
// Acceso por índice (comienza en 0)
string primero = componentes[0]; // "K1"
string ultimo = componentes[4]; // "X1"
// Modificar elemento
componentes[2] = "F2"; // Cambia "F1" por "F2"
// Longitud del array
int total = componentes.Length; // 5
2.4 Recorrer arrays
Con for:
string[] componentes = {"K1", "Q1", "F1", "M1"};
for (int i = 0; i < componentes.Length; i++)
{
MessageBox.Show(string.Format("[{0}] = {1}", i, componentes[i]));
}
Con foreach (más simple):
foreach (string componente in componentes)
{
MessageBox.Show(componente);
}
2.5 Ejemplo práctico: Análisis de corrientes
string[] designaciones = {"K1", "K2", "Q1", "Q2", "F1"};
double[] corrientes = {25.0, 40.0, 16.0, 20.0, 10.0};
// Buscar la corriente máxima
double maxCorriente = corrientes[0];
string designacionMax = designaciones[0];
for (int i = 1; i < corrientes.Length; i++)
{
if (corrientes[i] > maxCorriente)
{
maxCorriente = corrientes[i];
designacionMax = designaciones[i];
}
}
MessageBox.Show(string.Format("Corriente máxima: {0}A en {1}",
maxCorriente, designacionMax));
// Corriente máxima: 40A en K2
2.6 Limitaciones de los arrays
❌ Tamaño fijo: No se puede cambiar después de crear
string[] componentes = new string[5];
// No puedes agregar un sexto elemento
❌ No se pueden eliminar elementos: Solo sobrescribir
componentes[2] = null; // "Elimina" pero deja un hueco
❌ No hay métodos convenientes como Add(), Remove(), Contains()
Solución: Usar List<T> para colecciones dinámicas.
3. List: Colecciones dinámicas
List<T> es una colección dinámica que puede crecer y decrecer automáticamente.
Ventajas sobre arrays:
- Tamaño dinámico (crece automáticamente)
- Métodos convenientes: Add(), Remove(), Insert(), Contains()
- Más flexible para la mayoría de casos
3.1 Declaración y creación
// Sintaxis
List<tipo> nombreLista = new List<tipo>();
// Ejemplos
List<string> componentes = new List<string>();
List<double> corrientes = new List<double>();
List<int> cantidades = new List<int>();
3.2 Agregar elementos
List<string> componentes = new List<string>();
// Add: Agrega al final
componentes.Add("K1");
componentes.Add("Q1");
componentes.Add("F1");
// Insert: Agrega en posición específica
componentes.Insert(1, "K2"); // Inserta "K2" en posición 1
// Resultado: ["K1", "K2", "Q1", "F1"]
3.3 Acceso a elementos
List<string> componentes = new List<string> {"K1", "Q1", "F1", "M1"};
// Acceso por índice
string primero = componentes[0]; // "K1"
string segundo = componentes[1]; // "Q1"
// Count: número de elementos
int total = componentes.Count; // 4
// Contains: verificar si existe
bool existe = componentes.Contains("K1"); // true
3.4 Eliminar elementos
List<string> componentes = new List<string> {"K1", "Q1", "F1", "M1"};
// Remove: elimina la primera ocurrencia
componentes.Remove("Q1");
// Resultado: ["K1", "F1", "M1"]
// RemoveAt: elimina por índice
componentes.RemoveAt(0);
// Resultado: ["F1", "M1"]
// Clear: elimina todos
componentes.Clear();
// Resultado: []
3.5 Buscar y filtrar
List<string> componentes = new List<string> {"K1", "K2", "Q1", "Q2", "F1", "M1"};
// IndexOf: encontrar posición
int indice = componentes.IndexOf("Q1"); // 2
if (indice != -1)
{
MessageBox.Show("Encontrado en posición: " + indice);
}
// Find: buscar con condición
string primerK = componentes.Find(c => c.StartsWith("K"));
MessageBox.Show("Primer contactor: " + primerK); // "K1"
// FindAll: buscar todos que cumplan condición
List<string> contactores = componentes.FindAll(c => c.StartsWith("K"));
// Resultado: ["K1", "K2"]
3.6 Ejemplo práctico: Gestión de componentes
List<string> componentes = new List<string>();
// Agregar componentes
componentes.Add("K1");
componentes.Add("K2");
componentes.Add("Q1");
componentes.Add("F1");
// Mostrar todos
string lista = "Componentes:\n";
foreach (string comp in componentes)
{
lista += "- " + comp + "\n";
}
MessageBox.Show(lista);
// Buscar si existe un componente
if (componentes.Contains("Q1"))
{
MessageBox.Show("Guardamotor Q1 encontrado");
}
// Contar contactores
int numContactores = componentes.FindAll(c => c.StartsWith("K")).Count;
MessageBox.Show("Número de contactores: " + numContactores); // 2
// Eliminar componente
componentes.Remove("F1");
MessageBox.Show("Componentes después de eliminar F1: " + componentes.Count); // 3
3.7 Recorrer List
Con foreach (recomendado):
List<double> corrientes = new List<double> {10.0, 16.0, 25.0, 32.0};
foreach (double corriente in corrientes)
{
MessageBox.Show(string.Format("Corriente: {0}A", corriente));
}
Con for (cuando necesitas el índice):
for (int i = 0; i < corrientes.Count; i++)
{
MessageBox.Show(string.Format("[{0}] = {1}A", i, corrientes[i]));
}
4. Dictionary<K,V>: Colecciones clave-valor
Dictionary<K,V> almacena pares clave-valor para búsquedas rápidas.
Analogía: Un diccionario de idiomas
- Clave: Palabra en español
- Valor: Traducción en inglés
- Búsqueda rápida por clave
4.1 Declaración y creación
// Sintaxis
Dictionary<tipoClave, tipoValor> nombreDict = new Dictionary<tipoClave, tipoValor>();
// Ejemplos
Dictionary<string, double> corrientes = new Dictionary<string, double>();
Dictionary<string, string> fabricantes = new Dictionary<string, string>();
Dictionary<int, string> componentes = new Dictionary<int, string>();
4.2 Agregar elementos
Dictionary<string, double> corrientes = new Dictionary<string, double>();
// Add: agregar par clave-valor
corrientes.Add("K1", 25.0);
corrientes.Add("K2", 40.0);
corrientes.Add("Q1", 16.0);
// O inicialización directa
Dictionary<string, double> corrientes = new Dictionary<string, double>
{
{"K1", 25.0},
{"K2", 40.0},
{"Q1", 16.0}
};
4.3 Acceso a valores
Dictionary<string, double> corrientes = new Dictionary<string, double>
{
{"K1", 25.0},
{"K2", 40.0},
{"Q1", 16.0}
};
// Acceso directo por clave
double corrienteK1 = corrientes["K1"]; // 25.0
// Verificar si existe una clave
if (corrientes.ContainsKey("K2"))
{
MessageBox.Show("K2 tiene corriente: " + corrientes["K2"]);
}
// TryGetValue: forma segura (no lanza excepción)
double valor;
if (corrientes.TryGetValue("Q1", out valor))
{
MessageBox.Show("Q1: " + valor + "A");
}
else
{
MessageBox.Show("Q1 no encontrado");
}
4.4 Modificar y eliminar
Dictionary<string, double> corrientes = new Dictionary<string, double>
{
{"K1", 25.0},
{"K2", 40.0}
};
// Modificar valor existente
corrientes["K1"] = 32.0;
// Eliminar entrada
corrientes.Remove("K2");
// Count: número de entradas
int total = corrientes.Count; // 1
4.5 Recorrer Dictionary
Con foreach (pares clave-valor):
Dictionary<string, double> corrientes = new Dictionary<string, double>
{
{"K1", 25.0},
{"K2", 40.0},
{"Q1", 16.0}
};
foreach (KeyValuePair<string, double> par in corrientes)
{
MessageBox.Show(string.Format("{0}: {1}A", par.Key, par.Value));
}
Forma simplificada con var:
foreach (var par in corrientes)
{
MessageBox.Show(string.Format("{0}: {1}A", par.Key, par.Value));
}
Solo claves o solo valores:
// Solo claves
foreach (string clave in corrientes.Keys)
{
MessageBox.Show(clave);
}
// Solo valores
foreach (double valor in corrientes.Values)
{
MessageBox.Show(valor + "A");
}
4.6 Ejemplo práctico: Base de datos de componentes
// Diccionario: Código → Descripción
Dictionary<string, string> baseDatos = new Dictionary<string, string>
{
{"3RT2026-1BB40", "Contactor 3P 25A AC3"},
{"3RT2036-1BB40", "Contactor 3P 40A AC3"},
{"3RV2011-1JA10", "Guardamotor 3.5-5A"},
{"LC1D09BD", "Contactor TeSys D 9A"}
};
// Buscar componente
string codigoBuscar = "3RT2026-1BB40";
if (baseDatos.ContainsKey(codigoBuscar))
{
string descripcion = baseDatos[codigoBuscar];
MessageBox.Show(string.Format("Código: {0}\nDescripción: {1}",
codigoBuscar, descripcion));
}
else
{
MessageBox.Show("Código no encontrado");
}
// Listar todos los componentes
string reporte = "BASE DE DATOS DE COMPONENTES\n\n";
foreach (var item in baseDatos)
{
reporte += string.Format("{0}\n → {1}\n\n", item.Key, item.Value);
}
MessageBox.Show(reporte);
5. Comparación de colecciones
| Característica | Array | List | Dictionary<K,V> |
|---|---|---|---|
| Tamaño | Fijo | Dinámico | Dinámico |
| Acceso | Por índice | Por índice | Por clave |
| Búsqueda | Lenta (O(n)) | Lenta (O(n)) | Rápida (O(1)) |
| Agregar | No | Sí (Add) | Sí (Add) |
| Eliminar | No | Sí (Remove) | Sí (Remove) |
| Uso típico | Datos fijos | Listas dinámicas | Búsquedas por clave |
Cuándo usar cada una:
Array string[]:
- Tamaño conocido y fijo
- No necesitas agregar/eliminar
- Ejemplo: Días de la semana, meses del año
ListList<string>:
- Tamaño variable
- Necesitas agregar/eliminar frecuentemente
- Ejemplo: Lista de componentes en un proyecto
Dictionary<K,V> Dictionary<string, double>:
- Búsquedas rápidas por clave única
- Asociar un valor con otro
- Ejemplo: Código de artículo → Descripción
💻 Manos a la obra: Construyendo el proyecto
Vamos a crear un Gestor Inteligente de Lista de Materiales que combina todas las colecciones.
Estructura del proyecto
using System;
using System.Collections.Generic;
using Eplan.EplApi.Base;
using Eplan.EplApi.ApplicationFramework;
public class GestorListaMateriales
{
// ========================================
// ESTRUCTURAS DE DATOS
// ========================================
// Lista principal de componentes
private List<Componente> componentes;
// Diccionario para búsqueda rápida por código
private Dictionary<string, Componente> indiceCodigo;
// Estadísticas
private Dictionary<string, int> contadorPorTipo;
[Start]
public void Ejecutar()
{
// Código principal
}
}
// Clase auxiliar para representar un componente
public class Componente
{
public string Designacion { get; set; }
public string Codigo { get; set; }
public string Tipo { get; set; }
public double Corriente { get; set; }
public double Voltaje { get; set; }
public string Fabricante { get; set; }
public Componente(string designacion, string codigo, string tipo,
double corriente, double voltaje, string fabricante)
{
this.Designacion = designacion;
this.Codigo = codigo;
this.Tipo = tipo;
this.Corriente = corriente;
this.Voltaje = voltaje;
this.Fabricante = fabricante;
}
public override string ToString()
{
return string.Format("{0} - {1} ({2}A, {3}V)",
Designacion, Codigo, Corriente, Voltaje);
}
}
Implementación completa
El código completo está disponible en code/cap-09/GestorListaMateriales.cs.
Funcionalidades incluidas:
- Agregar componentes con validación
- Buscar por designación, código, o tipo
- Eliminar componentes
- Filtrar por corriente, voltaje, fabricante
- Estadísticas: Total, corriente total, por tipo
- Reportes formateados
- Validaciones (duplicados, valores incorrectos)
🔍 Deep Dive: Entendiendo en profundidad
Complejidad de búsqueda
Array y List
// Tiene que revisar todos los elementos
List<string> componentes = new List<string> {"K1", "K2", "Q1", "F1"};
bool existe = componentes.Contains("F1"); // O(n) - revisa 4 elementos
Dictionary<K,V>: Búsqueda por hash O(1)
// Acceso directo mediante hash
Dictionary<string, string> componentes = new Dictionary<string, string>
{
{"K1", "Contactor"},
{"Q1", "Guardamotor"}
};
bool existe = componentes.ContainsKey("K1"); // O(1) - acceso directo
Conclusión: Para búsquedas frecuentes, usa Dictionary.
Colecciones anidadas
Puedes combinar colecciones:
// Dictionary de Lists
Dictionary<string, List<string>> componentesPorTipo = new Dictionary<string, List<string>>
{
{"Contactor", new List<string> {"K1", "K2", "K3"}},
{"Guardamotor", new List<string> {"Q1", "Q2"}},
{"Motor", new List<string> {"M1", "M2", "M3"}}
};
// Acceso
List<string> contactores = componentesPorTipo["Contactor"];
foreach (string contactor in contactores)
{
MessageBox.Show(contactor);
}
LINQ: Consultas avanzadas
LINQ (Language Integrated Query) permite consultas poderosas:
List<Componente> componentes = ObtenerComponentes();
// Filtrar por corriente > 20A
var altaCorriente = componentes.Where(c => c.Corriente > 20.0).ToList();
// Ordenar por corriente
var ordenados = componentes.OrderBy(c => c.Corriente).ToList();
// Obtener solo designaciones
var designaciones = componentes.Select(c => c.Designacion).ToList();
// Suma total de corrientes
double corrienteTotal = componentes.Sum(c => c.Corriente);
// Corriente promedio
double corrientePromedio = componentes.Average(c => c.Corriente);
📝 Resumen
En este capítulo aprendiste:
- ✅ Arrays: Colecciones de tamaño fijo, acceso rápido por índice
- ✅ List
: Colecciones dinámicas con Add/Remove/Insert - ✅ Dictionary<K,V>: Pares clave-valor para búsquedas rápidas
- ✅ Cuándo usar cada una: Según necesidades de tamaño y búsqueda
- ✅ Recorrer colecciones: foreach, for, acceso directo
- ✅ Proyecto construido: Gestor completo de lista de materiales
Conceptos clave:
- Las colecciones permiten manejar múltiples datos relacionados
- List
es la más versátil para la mayoría de casos - Dictionary<K,V> es ideal para búsquedas rápidas
- Siempre valida antes de acceder (ContainsKey, TryGetValue)
🔗 Conexiones
⬅️ Capítulo anterior
Capítulo 8: Funciones (Métodos)
➡️ Próximo capítulo
Capítulo 10: Clases y objetos (POO)
En el próximo capítulo aprenderás los fundamentos de Programación Orientada a Objetos (POO): clases, objetos, propiedades, constructores, y métodos. Construirás un sistema completo de gestión de circuitos eléctricos usando principios de POO.
📚 Recursos adicionales
- 📖 Documentación de C# - Arrays
- 📖 Documentación de C# - List
- 📖 Documentación de C# - Dictionary<K,V>
- 💻 Código completo:
code/cap-09/
❓ Preguntas frecuentes
P: ¿Cuándo debería usar un array en lugar de List?
R: Usa arrays solo cuando el tamaño es fijo y conocido de antemano (ej. días de la semana: 7). En la mayoría de casos, List
P: ¿Puedo usar objetos complejos en colecciones?
R: Sí. Puedes tener List<Componente>, Dictionary<string, Componente>, etc.
P: ¿Qué pasa si accedo a un índice fuera de rango?
R: Se lanza una excepción IndexOutOfRangeException. Siempre verifica con if (i < lista.Count).
P: ¿Puedo modificar una lista mientras la recorro con foreach?
R: No. Esto causa una excepción. Si necesitas modificar, usa for en orden inverso o crea una lista temporal.
// Mal - causa excepción
foreach (var item in lista)
{
lista.Remove(item); // ❌ ERROR
}
// Bien - usar for inverso
for (int i = lista.Count - 1; i >= 0; i--)
{
lista.RemoveAt(i); // ✓ OK
}
P: ¿Dictionary permite claves duplicadas?
R: No. Cada clave debe ser única. Si intentas agregar una clave que ya existe, se lanza excepción. Usa ContainsKey() antes de agregar.
📋 Checklist de completitud
Antes de pasar al siguiente capítulo, asegúrate de:
- Entender la diferencia entre Array, List, y Dictionary
- Poder crear y manipular List
(Add, Remove, Contains) - Poder usar Dictionary<K,V> para búsquedas rápidas
- Saber recorrer colecciones con foreach y for
- Comprender cuándo usar cada tipo de colección
- Haber ejecutado el gestor de lista de materiales
- Poder agregar nuevas funcionalidades al gestor
- (Opcional) Haber intentado al menos un desafío
¿Listo para aprender Programación Orientada a Objetos? Vamos al Capítulo 10! 🚀
Última actualización: Enero 2025
Tiempo de lectura estimado: 60-70 minutos
Código de ejemplo: code/cap-09/