🚀 Nuova versione beta disponibile! Feedback o problemi? Contattaci

Generici in C#: Guida Completa

Codegrind Team•Aug 28 2024

I generici in C# sono una potente funzionalità che consente di creare classi, metodi, interfacce e delegati che funzionano con qualsiasi tipo di dato, fornendo un alto grado di flessibilità e riutilizzabilità. Introdotti in C# 2.0, i generici hanno rivoluzionato il modo in cui è possibile scrivere codice, migliorando sia la sicurezza del tipo che le prestazioni. In questa guida esploreremo cosa sono i generici, come utilizzarli, e quali sono le best practices per sfruttarli al meglio.

Cos’è un Generico?

Un generico in C# è un tipo o un metodo che è definito con un parametro di tipo, permettendo di specificare il tipo esatto da utilizzare solo quando il tipo o il metodo viene effettivamente utilizzato. Questo consente di creare collezioni e metodi generici che funzionano con qualsiasi tipo di dato, evitando la necessità di duplicare il codice per diversi tipi.

Vantaggi dei Generici

  1. Sicurezza del Tipo: I generici garantiscono che solo i tipi appropriati siano utilizzati con le collezioni o i metodi generici, riducendo gli errori di runtime.
  2. Prestazioni: A differenza dell’uso di object e del boxing/unboxing, i generici evitano il sovraccarico di conversione dei tipi, migliorando le prestazioni.
  3. RiutilizzabilitĂ : I generici consentono di scrivere codice piĂą flessibile e riutilizzabile, riducendo la duplicazione del codice.

Sintassi di Base dei Generici

Classi Generiche

Una classe generica è una classe che è definita con uno o più parametri di tipo. Questi parametri vengono specificati tra angolari (<>).

Esempio di Classe Generica

public class Scatola<T>
{
    private T _contenuto;

    public Scatola(T contenuto)
    {
        _contenuto = contenuto;
    }

    public T OttieniContenuto()
    {
        return _contenuto;
    }

    public void SostituisciContenuto(T nuovoContenuto)
    {
        _contenuto = nuovoContenuto;
    }
}

public static void Main()
{
    Scatola<int> scatolaDiInteri = new Scatola<int>(10);
    Console.WriteLine(scatolaDiInteri.OttieniContenuto());  // Output: 10

    Scatola<string> scatolaDiStringhe = new Scatola<string>("Ciao");
    Console.WriteLine(scatolaDiStringhe.OttieniContenuto());  // Output: Ciao
}

In questo esempio, la classe Scatola<T> è generica e può contenere qualsiasi tipo di dato, specificato al momento dell’istanza della classe (int, string, ecc.).

Metodi Generici

Un metodo generico è un metodo definito con uno o più parametri di tipo. Anche i metodi generici possono essere definiti all’interno di una classe non generica.

Esempio di Metodo Generico

public class Utilita
{
    public static void StampaDoppio<T>(T valore)
    {
        Console.WriteLine(valore);
        Console.WriteLine(valore);
    }
}

public static void Main()
{
    Utilita.StampaDoppio<int>(42);  // Output: 42 42
    Utilita.StampaDoppio<string>("Ciao");  // Output: Ciao Ciao
}

In questo esempio, il metodo StampaDoppio<T> può essere utilizzato con qualsiasi tipo di dato, specificato al momento della chiamata.

Interfacce Generiche

Le interfacce generiche permettono di definire contratti che possono essere implementati da classi con diversi tipi di dati.

Esempio di Interfaccia Generica

public interface IContenitore<T>
{
    void Aggiungi(T elemento);
    T Rimuovi();
}

public class Coda<T> : IContenitore<T>
{
    private Queue<T> _coda = new Queue<T>();

    public void Aggiungi(T elemento)
    {
        _coda.Enqueue(elemento);
    }

    public T Rimuovi()
    {
        return _coda.Dequeue();
    }
}

public static void Main()
{
    IContenitore<int> codaDiInteri = new Coda<int>();
    codaDiInteri.Aggiungi(10);
    codaDiInteri.Aggiungi(20);
    Console.WriteLine(codaDiInteri.Rimuovi());  // Output: 10
}

In questo esempio, l’interfaccia IContenitore<T> viene implementata dalla classe Coda<T>, permettendo di creare una coda di qualsiasi tipo di dato.

Vincoli nei Generici

C# permette di definire vincoli sui tipi generici, limitando i tipi che possono essere utilizzati come argomento di tipo.

Esempio di Vincolo con where

public class Comparatore<T> where T : IComparable<T>
{
    public T Maggiore(T primo, T secondo)
    {
        return primo.CompareTo(secondo) > 0 ? primo : secondo;
    }
}

public static void Main()
{
    Comparatore<int> comparatoreInteri = new Comparatore<int>();
    Console.WriteLine(comparatoreInteri.Maggiore(5, 10));  // Output: 10

    Comparatore<string> comparatoreStringhe = new Comparatore<string>();
    Console.WriteLine(comparatoreStringhe.Maggiore("A", "B"));  // Output: B
}

In questo esempio, il vincolo where T : IComparable<T> assicura che T implementi l’interfaccia IComparable<T>, necessaria per utilizzare il metodo CompareTo.

Altri Vincoli Comuni

  • where T : class – Il tipo deve essere una classe.
  • where T : struct – Il tipo deve essere un valore (non nullable).
  • where T : new() – Il tipo deve avere un costruttore senza parametri.

Generici e Collezioni

Le collezioni generiche, come List<T>, Dictionary<TKey, TValue>, e Queue<T>, sono ampiamente utilizzate in C# perché offrono un modo tipizzato e sicuro per gestire insiemi di dati.

Esempio di Lista Generica

List<string> nomi = new List<string> { "Alice", "Bob", "Charlie" };
nomi.Add("Diana");

foreach (var nome in nomi)
{
    Console.WriteLine(nome);
}

In questo esempio, List<string> è una collezione generica che può contenere solo elementi di tipo string.

Best Practices

1. Utilizzare Generici per Migliorare la Sicurezza del Tipo

Sfrutta i generici per creare codice che sia sicuro dal punto di vista del tipo, evitando l’uso di tipi non specifici come object, che possono portare a errori di runtime.

2. Evitare il Polimorfismo Non Necessario

Evita di complicare troppo le gerarchie di classi generiche. Usa i generici dove migliorano la flessibilitĂ  e la riutilizzabilitĂ , ma senza aggiungere complessitĂ  inutile.

3. Sfruttare i Vincoli

Usa i vincoli per limitare i tipi che possono essere utilizzati come parametri generici. Questo ti permette di utilizzare funzioni specifiche del tipo, come metodi di interfaccia o costruttori.

4. Documentare i Vincoli

Assicurati di documentare chiaramente i vincoli sui tipi generici, in modo che gli utenti del tuo codice comprendano le aspettative e i requisiti.

Conclusione

I generici in C# sono uno strumento potente che ti consente di scrivere codice flessibile, riutilizzabile e sicuro dal punto di vista del tipo. Che tu stia creando collezioni personalizzate, metodi utilitari, o implementando interfacce generiche, i generici ti offrono un modo per evitare la duplicazione del codice e per garantire la coerenza del tipo in tutto il tuo codice. Con una comprensione approfondita dei generici e delle best practices, puoi sfruttare al massimo questa funzionalitĂ  per scrivere applicazioni C# robuste ed efficienti.