1. Introducción
Imagínate esto: tienes una colección... y necesitas pillar rápido el tercer, séptimo o, yo qué sé, el elemento cero. O intercambiarlos. En un array esto es fácil — por índice (array[3]). ¿Pero qué pasa con las colecciones? ¡No todas las colecciones son igual de indexables!
Aquí entra en juego la interfaz IList<T> — un contrato universal que exige que la colección soporte trabajar con elementos por índice. En resumen: si tienes una colección que implementa IList<T>, puedes acceder a sus elementos por índice (como en un array) y cambiarlos al vuelo.
Analogía:
Piensa en una ficha de biblioteca: cada libro tiene su número de orden en la estantería, y siempre puedes ir a por el "tercero en la fila" y cogerlo. Así se comporta una colección que soporta IList<T>.
2. Estructura general y métodos de IList<T>
La interfaz IList<T> es la heroína de muchas colecciones. Extiende ICollection<T> (que a su vez extiende IEnumerable<T>, lo que permite recorrer la colección con bucles), y añade lo más importante: el trabajo con el índice.
Esquema de herencia de interfaces:
IEnumerable<T>
▲
│
ICollection<T>
▲
│
IList<T>
Miembros principales de la interfaz IList<T>
| Miembro | Propósito |
|---|---|
|
Obtener o establecer un elemento por índice |
|
Buscar el índice de la primera aparición del elemento |
|
Insertar un elemento en la posición indicada |
|
Eliminar un elemento por índice |
Todos los demás miembros, como Add, Remove, Clear, Contains, vienen de la interfaz ICollection<T>.
La clave: el indexador
La "gracia" principal de IList<T> es tener un indexador. Es una especie de azúcar sintáctico que te permite escribir:
var myList = new List<int> { 10, 20, 30 };
int secondValue = myList[1]; // Pillamos el 20
myList[2] = 42; // Cambiamos el tercer elemento
3. Qué colecciones implementan IList<T>
En la biblioteca estándar de .NET, muchas estructuras conocidas soportan la interfaz IList<T>. Vamos a ver las más populares:
| Colección | Indexación | Descripción |
|---|---|---|
|
Sí | Array dinámico |
|
Sí | Los arrays normales son indexables |
|
Sí | Se usa para data binding |
|
Sí | Lista con notificaciones |
|
Sí | Clase base para colecciones |
Ojo:
LinkedList<T> y
HashSet<T>
NO implementan
IList<T>, porque no tienen indexación rápida (sí,
LinkedList<T> no tiene
list[5]!).
4. Ejemplos de uso de IList<T>
Obtener y establecer por índice
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
IList<string> fruits = new List<string> { "Manzana", "Plátano", "Pera" };
// Obtener el segundo elemento
string fruit = fruits[1];
Console.WriteLine(fruit); // Plátano
// Cambiar el tercer elemento
fruits[2] = "Naranja";
Console.WriteLine(fruits[2]); // Naranja
}
}
Insertar y eliminar por índice
fruits.Insert(1, "Kiwi"); // Insertamos "Kiwi" en la segunda posición
// Lista ahora: "Manzana", "Kiwi", "Plátano", "Naranja"
fruits.RemoveAt(0); // Eliminamos el primer elemento ("Manzana")
// Lista ahora: "Kiwi", "Plátano", "Naranja"
Buscar el índice de un elemento
int index = fruits.IndexOf("Naranja"); // Devuelve el índice (2) o -1 si no lo encuentra
if (index != -1)
Console.WriteLine("Naranja está en la posición: " + index);
else
Console.WriteLine("Naranja no encontrada");
5. Particularidades de implementación y errores típicos
Trabajando con IList<T> es fácil caer en un par de "trampas", sobre todo si olvidas que la indexación empieza en cero y la longitud de la colección es el número real de elementos.
Por ejemplo, intentar acceder a un elemento que no existe:
Console.WriteLine(fruits[100]); // IndexOutOfRangeException!
Los índices en C# son como gallinas ponedoras: empiezan en cero, no en uno. Si hay 4 elementos en la lista, el último índice disponible es 3.
También hay que recordar que no todas las implementaciones de IList<T> son igual de rápidas. Por ejemplo, en un array o en List<T> el acceso por índice es instantáneo (O(1)), pero si te montas tu propia colección basada en una lista enlazada y le pones IList<T>, la operación puede ser lenta. Aunque la biblioteca estándar no hace eso.
Y otra cosa: si trabajas con un array como IList<T>, puedes cambiar los elementos, pero no el tamaño del array. Los métodos Add, Remove, Insert, etc. en un array lanzarán NotSupportedException.
int[] myArray = { 1, 2, 3 };
IList<int> listView = myArray; // Upcast
listView[0] = 42; // ¡Funciona!
listView.Add(99); // Lanza NotSupportedException
6. Aplicación práctica y para qué sirve
En proyectos reales, casi cada segunda colección es algo que implementa IList<T>, porque mola poder acceder rápido a los elementos por número, cambiarlos, insertarlos y eliminarlos por posición. Por ejemplo:
- Propiedades de interfaz en ViewModel de WPF o WinForms, donde se bindean listas de elementos de UI.
- Implementación de algoritmos de ordenación, búsqueda, permutación, donde necesitas acceso por índice.
- Módulos de importación/exportación de datos que trabajan con listas dinámicas de objetos.
En una entrevista la pregunta sobre las diferencias entre IEnumerable<T>, ICollection<T> y IList<T> es un clásico. Sabiendo para qué sirve cada nivel, puedes explicar con confianza al entrevistador por qué HashSet<T> no implementa IList<T> (¡porque la unicidad es más importante que el orden y los índices!).
GO TO FULL VERSION