CodeGym /Cursos /C# SELF /El papel de las interfaces en las colecciones de C#

El papel de las interfaces en las colecciones de C#

C# SELF
Nivel 28 , Lección 0
Disponible

1. ¿Para qué sirven las interfaces en las colecciones?

Seamos sinceros: mientras escribíamos código usando un tipo concreto (List<int>, Dictionary<string, string>), todo era fácil y claro. Pero imagina que de repente decides cambiar List<T> por HashSet<T> porque ahora solo necesitas valores únicos. O quieres crear un método que funcione tanto con uno como con otro tipo de colección. ¿Qué haces?

Para tener un único punto de entrada — necesitas un “conector universal”. En C#, ese conector son las interfaces. Definen un conjunto de reglas (métodos, propiedades) que debe tener una colección. Todo lo que implemente la interfaz — soporta esas operaciones.

Es como el cargador del móvil: si tienes un conector estándar USB-C, puedes cargar el móvil, una aspiradora moderna e incluso un cepillo de dientes eléctrico (lo importante es no confundirlos en la oscuridad).

Ejemplo práctico

Supón que quieres escribir un método que sume todos los números de cualquier colección de números. Así sería sin interfaces:


// Este método solo funciona con arrays:
static int SumArray(int[] array)
{
    int sum = 0;
    for (int i = 0; i < array.Length; i++)
    {
        sum += array[i];
    }
    return sum;
} 

// Y este - solo con List<int>:
static int SumList(List<int> list)
{
    int sum = 0;
    for (int i = 0; i < list.Count; i++)
        sum += list[i];
    return sum;
}

¡Incómodo! El código es casi igual. Usando la interfaz IEnumerable<int> puedes hacerlo universal:


static int SumAll(IEnumerable<int> collection)
{
    int sum = 0;
    foreach (var number in collection)
        sum += number;
    return sum;
}

Y ahora este método acepta cualquier tipo de colección que implemente IEnumerable<int>: array, lista, conjunto, lo que sea.

2. Interfaces principales de las colecciones

En .NET hay varias interfaces clave para colecciones. Vamos a verlas por encima — en las siguientes lecciones verás los detalles de cada una.

Interfaz ¿Para qué sirve? Ejemplo de colecciones que la implementan
IEnumerable<T>
Enumerar elementos (foreach) Todas:
List<T>
,
HashSet<T>
, arrays
ICollection<T>
Conjunto modificable (Add, Remove, Count)
List<T>
,
HashSet<T>
,
Dictionary<K,V>
IList<T>
Acceso por índice, modificación
List<T>
, arrays
IDictionary<K,V>
Trabajar con pares "clave-valor"
Dictionary<K,V>
,
SortedDictionary<K,V>
ISet<T>
Conjunto (elementos únicos)
HashSet<T>
,
SortedSet<T>

P.D. Para simplificar, de momento ignoramos los “varios niveles” de herencia entre estas interfaces. Quién hereda de quién — eso lo veremos más adelante.

Visualización: árbol de interfaces de colecciones


           IEnumerable<T>
                 ^
                 |
          ICollection<T>
              ^       ^
              |       |
         IList<T>  ISet<T>
              |       |
         List<T>   HashSet<T>
Jerarquía de interfaces para colecciones tipo lista y conjuntos

Para diccionarios:


           IEnumerable<KeyValuePair<K,V>>
                      ^
                      |
               ICollection<KeyValuePair<K,V>>
                      ^
                      |
               IDictionary<K,V>
                      |
                Dictionary<K,V>
Jerarquía de interfaces para diccionarios

3. ¿Para qué sirven las interfaces en la vida real?

Flexibilidad

Cuando tu método o clase trabaja no con un tipo concreto (List<int>), sino con una interfaz (IEnumerable<int>), puedes pasarle cualquier colección que implemente esa interfaz. Al final, tu programa es flexible y ampliable. (En vez de “Solo iPhone” — ahora vale cualquier móvil con Bluetooth.)


static void PrintAll(IEnumerable<string> collection)
{
    foreach (var item in collection)
        Console.WriteLine(item);
}

¡Ahora puedes imprimir los elementos de cualquier lista, array, conjunto, incluso el resultado de una consulta LINQ!

Unificación

Si implementas tu propio tipo de colección (por ejemplo, una colección muy específica para alguna tarea), implementa las interfaces necesarias — y tu colección funcionará con código ajeno que ni sabe de tu colección. Por eso en .NET se suele decir: “programa a nivel de interfaces, no de implementaciones concretas”.

Compatibilidad con herramientas estándar

Gracias al soporte de interfaces estándar, tus colecciones pueden funcionar con LINQ, ordenación, serialización y otras cosas de .NET.

4. List<T> — es lista, colección y enumeración a la vez

Unas palabras sobre enumeración

IEnumerable<T> — es la interfaz más importante y básica de colecciones en .NET. Define un solo método — GetEnumerator(), que devuelve un enumerador para iterar la colección. Gracias a eso puedes escribir:


foreach (var name in names)
{
    Console.WriteLine(name);
}

Donde names puede ser lista, conjunto, incluso array. foreach es un azúcar sintáctico que por dentro usa el método GetEnumerator().

La interfaz IEnumerable (sin <T>) es más antigua, no genérica. Ahora casi siempre usa la versión genérica.

Sintaxis de declaración de la clase List

Mira cómo se declara la clase List<T>, si le quitas un poco de magia:


public class List<T> : IList<T>, ICollection<T>, IEnumerable<T>, ...

Eso significa que el objeto List<T> se puede usar como:

  • Lista con acceso aleatorio por índice (IList<T>)
  • Colección con posibilidad de añadir/eliminar elementos (ICollection<T>)
  • Simplemente un conjunto de elementos por el que puedes iterar (IEnumerable<T>)

Ejemplo:


List<string> names = new List<string> { "Anna", "Boris", "Bill" };

// Como IEnumerable - puedes recorrer en un bucle
IEnumerable<string> asEnumerable = names;
foreach (var n in asEnumerable) Console.WriteLine(n);

// Como ICollection - puedes saber el Count:
ICollection<string> asCollection = names;
int count = asCollection.Count;

// Como IList - puedes acceder por índice:
IList<string> asList = names;
string firstItem = asList[0];

5. Ejemplo de aplicación

Supón que tenemos una app sencilla para gestionar usuarios y sus roles (empezamos a hacerla en lecciones anteriores):


// Clase de usuario
public class User
{
    public string Name { get; set; }
    public int Age { get; set; }
}

Podemos guardar usuarios en cualquier colección que implemente IEnumerable<User>:


List<User> listUsers = new List<User> { new User { Name = "Anna", Age = 20 } };
HashSet<User> setUsers = new HashSet<User> { new User { Name = "Boris", Age = 25 } };

// Método que imprime los nombres de todos los usuarios
static void PrintUserNames(IEnumerable<User> users)
{
    foreach (var user in users)
        Console.WriteLine(user.Name);
}

// Usamos con distintos tipos de colecciones:
PrintUserNames(listUsers);
PrintUserNames(setUsers);

¡Ahora la app es mucho más flexible — sin magia ni sobrecargas personalizadas!

6. Errores típicos y trampas

Uno de los errores más comunes — usar tipos demasiado concretos cuando basta con una interfaz:


// Mal (te atas a la implementación)
void DoSomethingWithList(List<int> numbers) { ... }

// Mejor (el método funciona con cualquier colección)
void DoSomethingWithNumbers(IEnumerable<int> numbers) { ... }

Otra trampa — olvidar que la interfaz solo define las operaciones que describe explícitamente. Por ejemplo, en IEnumerable<T> no puedes saber el número de elementos (Count), para eso tienes que usar ICollection<T> — o contarlos tú mismo.

2
Tarea
C# SELF, nivel 28, lección 0
Bloqueada
Implementación de la interfaz IEnumerable para una colección propia
Implementación de la interfaz IEnumerable para una colección propia
Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION