CodeGym /Cursos /C# SELF /Contrato de acceso por índice:

Contrato de acceso por índice: IList<T>

C# SELF
Nivel 28 , Lección 3
Disponible

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>
Herencia de interfaces de colecciones en .NET

Miembros principales de la interfaz IList<T>

Miembro Propósito
T this[int index] { get; set; }
Obtener o establecer un elemento por índice
int IndexOf(T item)
Buscar el índice de la primera aparición del elemento
void Insert(int index, T item)
Insertar un elemento en la posición indicada
void RemoveAt(int index)
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
List<T>
Array dinámico
T[]
Los arrays normales son indexables
BindingList<T>
Se usa para data binding
ObservableCollection<T>
Lista con notificaciones
Collection<T>
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!).

Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION