Clase Nro 7

Colecciones en C#

Trabajar con un grupo de objetos: arreglos, tipos genéricos, colecciones y enum.

Arreglos

Grupo fijo, fuertemente tipado.

Genéricos

Contenedor para un tipo parametrizable.

Colecciones

Grupos de tamaño dinámico.

Enum

Listas de constantes con nombre.

Programación: laptop con código y apuntes
Introducción

Trabajando con grupos de datos

En muchas aplicaciones es necesario crear y administrar grupos de objetos relacionados. En C# hay dos formas de agruparlos.

Arreglos

Útiles para crear y trabajar con un número fijo de objetos fuertemente tipados.

El tamaño se define al crear la instancia y permanece constante durante toda su vida útil.

tamaño fijo

Colecciones

El grupo de objetos puede crecer y reducirse dinámicamente en tiempo de ejecución.

Más flexibles: se adaptan a las necesidades cambiantes de la aplicación.

tamaño dinámico
Arreglos

¿Qué es un arreglo?

Un arreglo es un grupo de variables del mismo tipo de dato a las que nos referimos con un nombre común. A cada variable la llamamos elemento.

Características

  • El tipo de los elementos puede ser cualquier tipo válido: char, int, float, etc.
  • Se almacenan en ubicaciones contiguas de memoria.
  • También llamados array, vector o matriz.
  • Un escalar es un tensor de orden 0; un vector, de orden 1; una matriz, de orden 2…
Vector, Matriz, Array y Listas
Vector → Matriz → Array (tensores de orden creciente) y listas.
Arreglos · En el mundo real

Una imagen es un arreglo

Cualquier imagen en color se representa como un tensor de tercer orden: sus tres dimensiones son alto, ancho y color (canales RGB).

Imagen descompuesta en canales RGB y tensor 3D
Alto (H) × Ancho (W) × Canales (C). Cada canal es una matriz de intensidades.
Imagen muestreada en una grilla de píxeles
Al muestrear en una grilla, cada celda es un elemento del arreglo.
Arreglos · Sintaxis

Declaración de un arreglo

<Tipo de dato> [ ] Nombre_del_arreglo;

Las partes

  • <Tipo de dato> — define el tipo de cada elemento.
  • [ ] — entre corchetes se define el tamaño y las dimensiones.
  • Nombre_del_arreglo — el nombre del arreglo.

Ejemplos de declaración

int[] x;          // elementos int
string[] s;       // elementos string
double[] d;       // elementos double
Persona[] Estudiantes; // instancias de Persona

Fuente: geeksforgeeks.org/c-sharp-arrays

Arreglos · Inicialización

Declaración e inicialización

Un arreglo es un reference type (tipo por referencia), por lo que se usa la palabra clave new para crear una instancia.

type [ ] <Name_Array> = new <datatype>[size];

int[] intArray1 = new int[5];
int[,] b = new int[7,8];               // arreglo bidimensional
string[] Autos = {"Volvo", "BMW", "Ford", "Mazda"};
Importante: al crear la instancia, el rango y la longitud de cada dimensión quedan fijos y constantes durante toda su vida útil. Cada instancia de tipo Array hereda los miembros declarados por System.Array.
Arreglos · A tener en cuenta

Puntos clave de los arreglos

Genéricos

¿Qué es un genérico?

Es un contenedor para un tipo de dato específico, usado al crear una instancia de una variable genérica. El tipo se define recién al instanciar.

Declaración

public class MiClase<T>
{
   T value;
}

Por convención, los parámetros de tipo se prefijan con la letra T y deben ser únicos dentro de la declaración para evitar conflictos de nombres.

Ventajas

  • Rendimiento — evita boxing/unboxing.
  • Seguridad de tipos — errores en tiempo de compilación.
  • Reutilización de código — una misma clase para muchos tipos.
Genéricos · Implementación

Sustitución del tipo genérico

Implementación

public class MiClase<T>
{
   T value;
   public MiClase(T t)
   {
      value = t;
   }
   public T GetValor()
   {
      return value;
   }
}

Uso

MiClase<int> Instancia =
   new MiClase<int>();

MiClase<string> Instancia =
   new MiClase<string>();

Dentro de la implementación, el parámetro T se sustituye por el tipo específico: en el primer caso por int, en el segundo por string.

Genéricos · Ejemplo práctico

Una pila genérica reutilizable

Definimos una sola vez una Pila<T> y la usamos con cualquier tipo: string, int, objetos propios…

La clase genérica

public class Pila<T>
{
   private List<T> items = new List<T>();

   public void Apilar(T item) => items.Add(item);

   public T Desapilar()
   {
      T ultimo = items[items.Count - 1];
      items.RemoveAt(items.Count - 1);
      return ultimo;
   }

   public int Cantidad => items.Count;
}

Usarla con distintos tipos

// pila de tareas (string)
Pila<string> tareas = new Pila<string>();
tareas.Apilar("Estudiar");
tareas.Apilar("Practicar");

Console.WriteLine(tareas.Desapilar()); // "Practicar"
Console.WriteLine(tareas.Cantidad);     // 1

// la MISMA clase, ahora con int
Pila<int> numeros = new Pila<int>();
numeros.Apilar(10);
numeros.Apilar(20);
Console.WriteLine(numeros.Desapilar()); // 20
Colecciones

Colecciones

Proporcionan una forma más flexible de trabajar con grupos de objetos. A diferencia de los arreglos, el grupo puede crecer y reducirse dinámicamente a medida que cambian las necesidades de la aplicación.

Todas las colecciones genéricas comparten una jerarquía de interfaces: IEnumerable<T>ICollection<T>IList<T> / IDictionary<T,V>.
Jerarquía de interfaces de colecciones genéricas
Cada interfaz aporta sus operaciones: Add/Remove, IndexOf/Insert, Keys/Values…
Colecciones · Espacios de nombre

¿Dónde viven las colecciones?

El .NET Framework provee muchas colecciones comunes, cada una diseñada para un propósito específico. Pertenecen a estos espacios de nombre:

System.Collections.Generic

Colecciones genéricas fuertemente tipadas: todos los elementos tienen el mismo tipo. Mejor rendimiento y mayor seguridad de tipos.

recomendado

System.Collections.Concurrent

Desde .NET Framework 4+. Operaciones eficientes y seguras para trabajo asincrónico: acceso a los elementos desde múltiples hilos.

multi-hilo

System.Collections

Las clases guardan los elementos como objetos de tipo Object, no fuertemente tipados.

obsoleto
Colecciones · System.Collections.Generic

Tipos de colecciones más usadas

ClaseDescripción
Dictionary<TKey,TValue> Colección de pares clave/valor (key/value pair) a los que se accede por su clave.
List<T> Lista de objetos accesible por un índice. Provee métodos para buscar, ordenar y modificar la lista.
Queue<T> Colección organizada como FIFO (first in, first out). La conocemos como Fila o Cola.
Stack<T> Colección de elementos LIFO (last in, first out). La conocemos como Pila.
Colecciones · Uso

Instanciar y agregar elementos

Una colección es una clase: hay que declarar una instancia antes de poder agregarle elementos.

Crear una instancia

// lista de objetos int
List<int> MiLista =
   new List<int>();

// diccionario int → string
Dictionary<int, string> MiDiccionario =
   new Dictionary<int,string>();

Agregar elementos

// agrego el 5 a la lista
MiLista.Add(5);

// agrego al diccionario con
// la llave 1 → "Opción 1"
MiDiccionario.Add(1, "Opción 1");

En el diccionario, el primer parámetro es la clave y el segundo el valor asociado.

Colecciones · List<T>

Ejemplos con listas

List<T> es la colección genérica más usada: una lista de tamaño dinámico accesible por índice.

Crear y agregar

// crear una lista vacía
List<string> frutas = new List<string>();

frutas.Add("Manzana");
frutas.Add("Banana");
frutas.Add("Naranja");

// inicializar con valores
List<int> numeros =
   new List<int>() { 10, 20, 30 };

Acceder, recorrer y contar

// acceso por índice (base 0)
string primera = frutas[0];  // "Manzana"

// recorrer con foreach
foreach (string f in frutas)
{
   Console.WriteLine(f);
}

// cantidad de elementos
int total = frutas.Count;     // 3
Colecciones · List<T>

Listas: más operaciones

Además de agregar, List<T> provee métodos para buscar, insertar, quitar y ordenar.

Buscar, insertar y quitar

// ¿contiene un elemento?
bool hay = frutas.Contains("Banana");  // true

// posición de un elemento
int pos = frutas.IndexOf("Naranja");    // 2

// insertar en una posición
frutas.Insert(1, "Pera");

// quitar por valor y por índice
frutas.Remove("Manzana");
frutas.RemoveAt(0);

Ordenar y agregar en lote

List<int> numeros = new List<int>() { 30, 10, 20 };

// agregar varios de una vez
numeros.AddRange(new int[] { 40, 50 });

// ordenar de menor a mayor
numeros.Sort();      // 10, 20, 30, 40, 50

// invertir el orden
numeros.Reverse();

// vaciar la lista
numeros.Clear();
Colecciones · Iteradores

Formas de recorrer una lista

Una lista se puede recorrer de varias maneras. Todas terminan apoyándose en el mismo mecanismo: el enumerador.

for con índice

for (int i = 0;
     i < frutas.Count;
     i++)
{
   Console.WriteLine(
      frutas[i]);
}

Control total del índice; útil para recorrer al revés o de a saltos.

foreach

foreach (string f
         in frutas)
{
   Console.WriteLine(f);
}

La forma más simple y legible. No expone el índice.

Enumerador manual

IEnumerator<string> e =
   frutas.GetEnumerator();

while (e.MoveNext())
{
   Console.WriteLine(
      e.Current);
}

Es lo que hace foreach por dentro: MoveNext() + Current.

Todas las colecciones que implementan IEnumerable<T> se pueden recorrer con foreach — por eso funciona igual en List, Dictionary, Queue o Stack.
Colecciones · List<Persona>

Listas de objetos

Una lista no se limita a tipos simples: puede guardar instancias de una clase propia.

Una clase y su lista

class Persona
{
   public string Nombre;
   public int Edad;
}

List<Persona> estudiantes =
   new List<Persona>();

estudiantes.Add(new Persona { Nombre = "Ana", Edad = 20 });
estudiantes.Add(new Persona { Nombre = "Luis", Edad = 22 });

Recorrer y filtrar

// recorrer los objetos
foreach (Persona p in estudiantes)
{
   Console.WriteLine($"{p.Nombre} - {p.Edad}");
}

// acceder por índice
Persona primero = estudiantes[0];  // Ana

// filtrar manualmente (mayores de 21)
List<Persona> mayores = new List<Persona>();
foreach (Persona p in estudiantes)
   if (p.Edad > 21) mayores.Add(p);
Colecciones · LINQ

Consultar listas con LINQ

LINQ permite consultar colecciones de forma declarativa, reemplazando los foreach de filtrado por expresiones más cortas.

Filtrar y proyectar

using System.Linq;

// filtrar: mayores de 21
List<Persona> mayores = estudiantes
   .Where(p => p.Edad > 21)
   .ToList();

// proyectar: solo los nombres
List<string> nombres = estudiantes
   .Select(p => p.Nombre)
   .ToList();

Operaciones de agregado

// ¿hay algún mayor de edad?
bool hayMayor = estudiantes.Any(p => p.Edad >= 18);

// el primero que cumpla
Persona ana = estudiantes.First(p => p.Nombre == "Ana");

// contar y promediar
int cuantos = estudiantes.Count(p => p.Edad > 21);
double promedio = estudiantes.Average(p => p.Edad);
LINQ no es magia: internamente sigue iterando con un enumerador sobre la colección. Es azúcar sintáctico sobre IEnumerable<T>.
Enum

Enumeraciones

Estructura de datos de tipo valor. Permite declarar una lista de constantes con nombre y referirnos a cada una por un identificador que las agrupa.

Crear un enum

enum DiaDeLaSemana
{
   Domingo = 0,
   Lunes = 1,
   Martes = 2,
   Miercoles = 3,
   Jueves = 4,
   Viernes = 5,
   Sabado = 6
}

Utilizar un enum

DiaDeLaSemana MiDía =
   DiaDeLaSemana.Domingo;

Se puede definir el tipo subyacente, por ejemplo ushort, y asignar valores arbitrarios:

enum ErrorCode : ushort
{
   Ninguno = 0,
   PerdidaDeConexion = 100,
   RecursoNoEncontrado = 404
}
Enum · Flags

Enum como combinación de opciones

Con el decorador [Flags], un enum puede representar una combinación de elecciones. Cada miembro se comporta como un campo de bits (potencias de 2).

Declarar con [Flags]

[Flags]
enum Dias
{
   Ninguno = 0,
   Domingo = 1,
   Lunes = 2,
   Martes = 4,
   Miercoles = 8,
   Jueves = 16,
   Viernes = 32,
   Sabado = 64
}

Combinar con operadores lógicos

Dias FindeSemana =
   Dias.Domingo |
   Dias.Sabado;

Se usan los operadores OR (|) para combinar y AND (&) para intersecar / comprobar qué opciones están activas.

Buena práctica

Usá la documentación oficial

Sumamente importante: consultar siempre la documentación oficial del framework, librería o API con la que estés trabajando.

.NET API Browser: docs.microsoft.com/es-es/dotnet/api/
Ahí encontrás cada clase de colección, sus métodos, sobrecargas y ejemplos — la fuente de verdad por encima de cualquier tutorial.

Repaso & Bibliografía

En una línea

Usá arreglos cuando el tamaño es fijo, colecciones genéricas cuando crece dinámicamente, genéricos para reutilizar con seguridad de tipos y enum para constantes con nombre.

Decisión rápida

  • Tamaño fijo, mismo tipo → arreglo
  • Crece/se reduce, por índice → List<T>
  • Acceso por clave → Dictionary<K,V>
  • FIFO → Queue<T> · LIFO → Stack<T>

Bibliografía

  • Arreglos — geeksforgeeks.org/c-sharp-arrays
  • Colecciones — docs.microsoft.com/.../concepts/collections
  • Genéricos — docs.microsoft.com/es-es/dotnet/standard/generics
  • Generics (blog) — sparraguerra.wordpress.com

Navegación de este deck

siguiente · atrás · Espacio siguiente · Inicio primera · Fin última

¿Preguntas?