Pagina personale di:
Carlo Vecchio
appunti di C#, R, SQL Server, ASP.NET, algoritmi, numeri
Vai ai contenuti

C# - Generics

C#
Generics
  • I Generics, permettono di progettare classi senza indicare esplicitamente i tipi di dati.
  • Sono utili per esempio quando si deve costruire un metodo che possa funzionare con più tipi di dato in input e non si voglia (o non si possa) scrivere più metodi che facciano le stesse operazioni con tipi di dato differenti.
  • Quando però si istanzia un oggetto da una classe che utilizza i Generics, bisogna indicarne il tipo.
  • In .NET è possibile anche utilizzare il tipo ‘object’ che lascia al programmatore molta libertà nella definizione di classi generiche. L’aspetto negativo è però che il compilatore non rivela errori se nel codice sono presenti errori di conversione implicita. E, se tutto funziona, il programma è più lento a causa di dispendiose operazioni di boxing – unboxing. Con i Generics invece questi due problemi non ci sono.
  • Si possono definire metodi con uno o più parametri Generic o classi che istanziano oggetti Generic: si vedano gli esempi che seguono.
  • Nel caso di classi generiche è anche possibile limitare i tipi utilizzabili a quelli che implementano una o più interfacce: su questo argomento al momento non ho esempi.

Esempio: metodo con Generic
  • Si vuole un metodo che effettui lo scambio tra due variabili dello stesso tipo, senza indicarne il tipo.
  • Una tecnica per scambiare due variabili consiste nell’utilizzare una terza variabile che conserva temporaneamente il valore di una di queste.
  • Una possibile soluzione è quella di scrivere diverse versioni del metodo con diversi valori di input coerenti, per esempio Swap(int, int), Swap(string, string), e così via. Questa soluzione è ovviamente lunga e incompleta.
  • Un’altra possibile soluzione è quella di scrivere la funzione con i due argomenti di tipo ‘object’: Swap(object, object). Il tutto funziona, anche se si passano due argomenti non coerenti, ma non si avrà nessun errore in fase di compilazione se i due argomenti hanno tipo diverso (cosa invece richiesta dal problema).
  • Con i Generics invece si può scrivere il seguente codice.
  • La classe seguente definisce il metodo generico ‘Swap’.

   static class EsempioGenerics
   {
       public static void Swap<T>(ref T a, ref T b)
       {
           T temp = a;
           a = b;
           b = temp;
       }
   }

  • Il metodo è utilizzato così:

   // Scambio di due variabili 'int'
   int a = 10;
   int b = 20;
   EsempioGenerics.Swap(ref a, ref b);
   Console.WriteLine(a + " - " + b);

   // Scambio di due variabili 'string'
   string s1 = "aaa";
   string s2 = "bbb";
   EsempioGenerics.Swap(ref s1, ref s2);
   Console.WriteLine(s1 + " - " + s2);

  • Se si prova ad utilizzare il metodo ‘Swap’ passando due argomenti diversi tra loro, si ottiene un errore in fase di compilazione.

Esempio: classe con Generic
  • Si vuole definire una classe che lavori con oggetti di diverso tipo, per esempio ‘int’, ‘string’, eccetera.
  • Quando però si definisce l’oggetto istanza della classe, occorre specificare il tipo e il compilatore indica eventuali errori nell’utilizzo dell’oggetto stesso.
  • In questo esempio si definisce una classe che gestisce una lista di oggetti e ha dei metodi per inserire gli oggetti, ordinarli e scriverli in Console.

   class MySortedList<T>
   {
       List<T> lst = new List<T>();

       public void Add(T item)
       {
           lst.Add(item);
       }

       public void Sort()
       {
           lst.Sort();
       }

       public void PrintItems()
       {
           foreach (var item in lst)
           {
               Console.WriteLine(item);
           }
       }
   }

  • Come si vede nella definizione della classe è presente ‘<T>’ che indica il tipo di dato che l’utente assegna all’oggetto che istanzia.
  • L’utente può assegnare all’oggetto un tipo primitivo (‘int’, ‘string’, eccetera) ma anche un tipo definito dall’utente stesso (per esempio se ci fosse la classe ‘Persona’, la si può assegnare).
  • Di seguito, l’utilizzo della classe con i due tipi ‘int’ e ‘string’.

   MySortedList<int> lst1 = new MySortedList<int>();
   lst1.Add(20);
   lst1.Add(8);
   lst1.Add(65);
   lst1.Sort();
   lst1.PrintItems();

   MySortedList<string> lst2 = new MySortedList<string>();
   lst2.Add("20");
   lst2.Add("8");
   lst2.Add("65");
   lst2.Sort();
   lst2.PrintItems();

List<T>

  • La lista è una evoluzione dell’ArrayList, ma supporta in modo nativo l’aggiunta e l’eliminazione degli elementi. Sia la ricerca sia l’ordinamento sono operazioni molto veloci.
  • Ecco alcuni esempi.

   // Definizione della lista e inserimento di alcuni elementi
   
List<string> _Lista = new List<string>();
   _Lista.Add("Primo elemento");

   _Lista.Add("Secondo elemento");
   _Lista.Add(
"Quarto elemento");
   _Lista.Insert(2,
"Terzo elemento");

   // Proprietà / Metodi utili

   int totElementi = _Lista.Count;                    // 4
   
string elemento = _Lista[2];                       // "Terzo elemento"
   
int index1 = _Lista.IndexOf("Secondo elemento");   // 1
   
int index2 = _Lista.IndexOf("Quinto elemento");    // -1, non esiste
   
bool trovato = _Lista.Contains("Primo elemento");  // true
   _Lista.Sort();                                     
// la lista è ora lista ordinata
   
string[] array = _Lista.ToArray();                 // array con gli elementi della lista

   
// Ricerca di un elemento con un delegate
   
string s = _Lista.Find(FindLength14);              // "Primo elemento"

  • L'struzione precedente necessita della definizione del metodo seguente:

   private static bool FindLength14(string item)
   {
       
if (item.Length == 14)
           
return true;
       
else
           
return false;
   }


Dictionary<TKey, TValue>
  • I dictionary permettono di organizzare un elenco di chiavi-valori. Le ricerche tramite chiave sono molto veloci.
  • I tipi di variabili da utilizzare per le chiavi e per i valori, sono definibili dal programmatore.
  • I valori utilizzati come chiave, devono essere univoci.
  • I dictionary sono nel namespace System.Collections.Generic.
  • Esempio con una applicazione Console.
  • In questo esempio la chiave è un int, mentre il valore è string.

   // Definizione del dictionary e inserimento di alcuni elementi
   
Dictionary<int, string> _Dic = new Dictionary<int, string>();
   _Dic.Add(123,
"Prima stringa");
   _Dic.Add(234,
"Seconda stringa");
   _Dic.Add(456,
"Quarta stringa");
   _Dic.Add(345,
"Terza stringa");

   
// Proprietà / Metodi utili
   
int totElementi = _Dic.Count;                // 4
   
bool findKey = _Dic.ContainsKey(123);        // true
   
bool findValue = _Dic.ContainsValue("aaa");  // false
   
string elemento = _Dic[123];                 // "Prima stringa"
   
if (_Dic.TryGetValue(234, out elemento))
   { }                                          
// in 'elemento' c'è "Seconda stringa"

   // Molte proprietà hanno anche una corrispondente funzione
   // che accetta come argomento una espressione LINQ.
   // Per esempio la Count().
   Console.WriteLine(_Dic.Count(p => p.Key > 300));

   // Ciclare tutti gli elementi
   
foreach (KeyValuePair<int, string> kv in _Dic)
   {
       
Console.WriteLine("{0}, {1}", kv.Key, kv.Value);
   }
 
   // Ciclare solo le chiavi
   foreach (int k in _Dic.Keys)
   {
       Console.WriteLine("{0}", k);
   }

   // Ciclare solo i valori
   foreach (string v in _Dic.Values)
   {
       Console.WriteLine("{0}", v);
   }

   // Ordinare le chiavi
   
List<int> list = new List<int>(_Dic.Keys);
   list.Sort();
   
foreach (int item in list)
   {
       
Console.WriteLine("{0}, {1}", item, _Dic[item]);
   }
   
Console.ReadKey();

  • Esempio con una applicazione Windows Form.
  • In questo esempio la chiave è un int, mentre il valore è un oggetto (proprietà 'Nome' e 'Cognome') istanziato dalla classe seguente.

   // Creazione oggetti dalla classe 'Persone'
   
Persone _P1 = new Persone();
   _P1.Nome =
"Archimede"; _P1.Cognome = "Pitagorico";
   
Persone _P2 = new Persone();
   _P2.Nome =
"Leonardo"; _P2.Cognome = "Da Vinci";
   
Persone _P3 = new Persone();
   _P3.Nome =
"Michelangelo"; _P3.Cognome = "Buonarroti";

   
// Creazione del dictionary
   
Dictionary<int, Persone> _Dic = new Dictionary<int, Persone>();

   
// Aggiunta, rimozione, totale elementi del dictionay
   _Dic.Add(1, _P1);
   _Dic.Add(2, _P2);
   _Dic.Add(3, _P3);
   _Dic.Remove(3);
   _Dic.Remove(2);
   _Dic.Remove(1);
   _Dic.Clear();
   
int i = _Dic.Count;

   
// Visualizzazione di un elemento del dictionary
   
MessageBox.Show(_Dic[1].Nome + " " + _Dic[1].Cognome);

   
// Verifica se esiste una certa chiave nel dictionary
   
if (_Dic.ContainsKey(1))
       
MessageBox.Show("Esiste la chiave 1");
   
else
       
MessageBox.Show("Non esiste la chiave 1");

   
// Verifica se esiste un certo elemento nel dictionary
   
if (_Dic.ContainsValue(_P1))
       
MessageBox.Show("Esiste l'elemento " + _P1.Nome + " " + _P1.Cognome);
   
else
       
MessageBox.Show("Non esiste l'elemento " + _P1.Nome + " " + _P1.Cognome);

   
// Visualizzazione di tutti gli elementi del dictionary
   
foreach (KeyValuePair<int, Persone> item in _Dic)
   {
       
MessageBox.Show(item.Key + " " + item.Value.Nome + " " + item.Value.Cognome);
   }

   
// Preparazione di una lista con tutte le chiavi del dictionary
   
// Attenzione: è lento, meglio utilizzare il foreach su KeyValuePair
   
List<int> list = new List<int>(_Dic.Keys);
   
foreach (int k in list)
       
MessageBox.Show(k.ToString());


  • Esempio per ottenere un dictionary a partire da un array o da una lista di oggetti.
  • È data la classe seguente:

   class Auto
   {
       public int Id { get; set; }
       public string Marca { get; set; }
       public string Modello { get; set; }
   }

  • Da array di oggetti a dictionary:

   Auto[] arrayAuto = new Auto[3];
   arrayAuto[0] = new Auto() { Id = 1, Marca = "Fiat", Modello = "500" };
   arrayAuto[1] = new Auto() { Id = 2, Marca = "Ford", Modello = "Focus" };
   arrayAuto[2] = new Auto() { Id = 3, Marca = "Alfa Romeo", Modello = "Mito" };

   Dictionary<int, Auto> dic = arrayAuto.ToDictionary(p => p.Id, p => p);

  • Da lista di oggetti a dictionary:

   List<Auto> lstAuto = new List<Auto>();
   lstAuto.Add(new Auto() { Id = 1, Marca = "Fiat", Modello = "500" });
   lstAuto.Add(new Auto() { Id = 2, Marca = "Ford", Modello = "Focus" });
   lstAuto.Add(new Auto() { Id = 3, Marca = "Alfa Romeo", Modello = "Mito" });

   Dictionary<int, Auto> dic = lstAuto.ToDictionary(p => p.Id, p => p);

© 2022 Carlo Vecchio
Torna ai contenuti