CodeGym /Corsi /C# SELF /Contratto per l’accesso tramite indice:

Contratto per l’accesso tramite indice: IList<T>

C# SELF
Livello 28 , Lezione 3
Disponibile

1. Introduzione

Allora, immagina: hai una collezione... e ti serve prendere al volo il terzo, il settimo o, che ne so, lo zero-esimo elemento. O magari scambiarli di posto. Con un array è facile — basta l’indice (array[3]). Ma con le collezioni? Non tutte sono indicizzabili allo stesso modo!

Qui entra in gioco l’interfaccia IList<T> — un contratto universale che chiede alla collezione di supportare l’accesso agli elementi tramite indice. In breve: se hai una collezione che implementa IList<T>, puoi tranquillamente accedere ai suoi elementi tramite indice (come in un array) e modificarli al volo.

Analogia:
Pensa a una tessera della biblioteca: ogni libro ha il suo numero d’ordine sullo scaffale, e puoi sempre andare a prendere il "terzo in fila". Così si comporta una collezione che supporta IList<T>.

2. Struttura generale e metodi di IList<T>

L’interfaccia IList<T> è l’eroe di tante collezioni. Estende ICollection<T> (che a sua volta estende IEnumerable<T>, così puoi ciclare la collezione), e aggiunge la cosa più importante: il lavoro con l’indice.

Schema di ereditarietà delle interfacce:


IEnumerable<T>
      ▲
      │
ICollection<T>
      ▲
      │
  IList<T>
Ereditarietà delle interfacce delle collezioni in .NET

Membri principali dell’interfaccia IList<T>

Membro Scopo
T this[int index] { get; set; }
Prendere o impostare un elemento tramite indice
int IndexOf(T item)
Trovare l’indice della prima occorrenza di un elemento
void Insert(int index, T item)
Inserire un elemento in una posizione specifica
void RemoveAt(int index)
Rimuovere un elemento tramite indice

Tutti gli altri membri, tipo Add, Remove, Clear, Contains, arrivano dall’interfaccia ICollection<T>.

La chicca: l’indexer

La vera "chicca" di IList<T> è l’indexer. È una specie di zucchero sintattico che ti permette di scrivere:


var myList = new List<int> { 10, 20, 30 };
int secondValue = myList[1]; // Prendiamo 20
myList[2] = 42;              // Cambiamo il terzo elemento

3. Quali collezioni implementano IList<T>

Nella libreria standard .NET tante strutture note supportano l’interfaccia IList<T>. Vediamo le più popolari:

Collezione Indicizzazione Descrizione
List<T>
Array dinamico
T[]
Gli array normali sono indicizzabili
BindingList<T>
Usata per il data binding
ObservableCollection<T>
Lista con notifiche
Collection<T>
Classe base per collezioni

Attenzione:

LinkedList<T>
e
HashSet<T>
NON implementano IList<T>, perché non hanno indicizzazione veloce (sì, LinkedList<T> non ha list[5]!).

4. Esempi di utilizzo di IList<T>

Prendere e impostare tramite indice


using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        IList<string> fruits = new List<string> { "Mela", "Banana", "Pera" };

        // Prendere il secondo elemento
        string fruit = fruits[1];
        Console.WriteLine(fruit); // Banana

        // Sostituire il terzo elemento
        fruits[2] = "Arancia";
        Console.WriteLine(fruits[2]); // Arancia
    }
}

Inserimento e rimozione tramite indice


fruits.Insert(1, "Kiwi");    // Inseriamo "Kiwi" al secondo posto
// Lista ora: "Mela", "Kiwi", "Banana", "Arancia"

fruits.RemoveAt(0);          // Rimuoviamo il primo elemento ("Mela")
// Lista ora: "Kiwi", "Banana", "Arancia"

Ricerca dell’indice di un elemento


int index = fruits.IndexOf("Arancia"); // Restituisce l’indice (2) o -1 se non trovato
if (index != -1)
    Console.WriteLine("Arancia si trova alla posizione: " + index);
else
    Console.WriteLine("Arancia non trovata");

5. Caratteristiche di implementazione ed errori tipici

Quando lavori con IList<T>, è facile cadere in qualche "trappola", soprattutto se ti dimentichi che l’indicizzazione parte da zero e la lunghezza della collezione è il numero attuale di elementi.

Per esempio, se provi ad accedere a un elemento che non esiste:


Console.WriteLine(fruits[100]); // IndexOutOfRangeException!

Gli indici in C# sono come le galline ovaiole: partono da zero, non da uno. Se la lista ha 4 elementi, l’ultimo indice valido è 3.

Ricorda anche che non tutte le implementazioni di IList<T> sono ugualmente veloci. Per esempio, con un array o una List<T> l’accesso tramite indice è istantaneo (O(1)), ma se ti inventi una collezione tua basata su una lista collegata e ci appiccichi sopra IList<T>, l’operazione potrebbe diventare lenta. Anche se la libreria standard non lo fa.

E ancora: se lavori con un array come IList<T>, puoi cambiare gli elementi, ma non la dimensione dell’array. I metodi Add, Remove, Insert ecc. su un array lanceranno una NotSupportedException.


int[] myArray = { 1, 2, 3 };
IList<int> listView = myArray; // Upcast

listView[0] = 42; // Funziona!
listView.Add(99); // Lancia NotSupportedException

6. Applicazione pratica e a cosa serve

Nei progetti veri quasi una collezione su due è qualcosa che implementa IList<T>, perché è comodo poter accedere velocemente agli elementi tramite numero, modificarli, inserirli e rimuoverli per posizione. Per esempio:

  • Proprietà di interfaccia nelle ViewModel di WPF o WinForms, dove si fanno i binding delle liste di elementi UI.
  • Implementazione di algoritmi di ordinamento, ricerca, permutazione, dove serve l’accesso tramite indice.
  • Moduli di import/export dati che lavorano con una lista dinamica di oggetti.

Al colloquio la domanda sulle differenze tra IEnumerable<T>, ICollection<T> e IList<T> è un classico. Sapendo a cosa serve ogni livello, puoi spiegare senza problemi all’intervistatore perché HashSet<T> non implementa IList<T> (perché l’unicità conta più dell’ordine e degli indici!).

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