liste e generics - www3
Transcript
liste e generics - www3
Listee generics (C#) Vittorio Maniezzo Università di Bologna I generici I generics sono una funzionalità della versione 2.0 del linguaggio C# (e del CLR). Introducono il concetto di parametri di tipo, grazie ai quali è possibile progettare classi e metodi che rinviano la specifica di uno più tipi fino a quando un codice che li utilizza non ne dichiara o crea un'istanza. I generics rappresentano la soluzione a una limitazione presente nelle versioni precedenti di CLR e C#, in cui la generalizzazione viene effettuata mediante il cast di tipi in e dal tipo base universale Object. Una classe generica consente invece di creare un insieme indipendente dai tipi in fase di compilazione. Vittorio Maniezzo – Università di Bologna 2 Array Gli array sono i più semplici contenitori di dati predefiniti in C#. Hanno più funzionalità degli array in C / C++, ma restano poco adatti per ricerche e modifiche strutturali. Esempio: incrementare la dimensione di un array per metterci Pippo. string [] newArray = new string[array.Length+1]; for (int i=0; i<array.Length; i++) newArray[i] = array[i]; Dovrebbe essere newArray[newArray.Length] = “Pippo"; newArray.Length-1 array = newArray; Dov’è l’errore? Vittorio Maniezzo – Università di Bologna 3 Generici • Il C# può utilizzare strutture dati simili agli STL containers del C++. • Il C# le chiama generics (generici), dato che possono contenere dati di un qualunque tipo. • I generici sono definiti nella libreria System.CoIIections.Generics, che è referenziata automaticamente per i nuovi progetti. • L’uso è uguale al C++, ma i nomi sono diversi. Vittorio Maniezzo – Università di Bologna 4 Generici List dynamic array C++ STL container vector SortedList binary tree set Dictionary hash table map LinkedList linked list list Queue queue queue Stack stack stack C# Generic Data Structure In C# in più c’è anche un generico che si chiama SortedDictionary, è un ibrido fra una hash table e un binary tree. Vittorio Maniezzo – Università di Bologna 5 Il generico List La classe List è un array dinamico. Viene istanziata con il comando new, come qualunque altro oggetto Come in C++, il tipo viene definito fra <parentesi angolari>. List<string> words = new List<string>(); List<Employee> staff = new List<Employee>(); Senza il comando new non si compila! List<string> words; //Errore! Come con i vettori, si può definire la dimenzione iniziale. List<string> words = new List<string>(100); Come con i vettori, è possibile accedere agli elementi con []. words[3] = “Pippo"; Vittorio Maniezzo – Università di Bologna 6 Variazioni di una lista Si inseriscono elementi con il comando Add(). words.Add(“Pippo"); staff.Add(new Employee(“Pippo“, 23432)); Il comando Remove(object) cerca l’oggetto passato come parametro e lo cancella dalla lista. words.Remove(“Pippo"); Employee pippo = new Employee(“Pippo",23432.88); staff.Remove(pippo); Il comando RemoveAt(int) elimina l’elemento nella posizione specificata. words.RemoveAt(0); // Elimina il primo elemento. words.RemoveAt(words.Count-1); // elimina l’ultimo. Il comando Clear() elimina tutti gli elementi di una lista. words.Clear(); Vittorio Maniezzo – Università di Bologna 7 Ricerca in una lista Ci sono molte funzioni per ricercare in una lista. La più semplice è Contains(object). bool ListName.Contains( object ) Ritorna true se l’oggetto dato è nella lista. If (words.Contains(“Pippo")) Console.WriteLine(“Pippo è nella lista."); Bisogna fare attenzione nell’utilizzo: controlla la presenza dell’oggetto (l’indirizzo di memoria deve combaciare). Vittorio Maniezzo – Università di Bologna 8 Predicati Un predicato è una funzione booleana che accetta un parametro tipizzato (stesso tipo di quello della List). La funzione Find prende in input il nome del predicato. Es. Trova tutti gli studenti il cui nome comincia con M. static bool StartsWithM (string s) { return (s[O] == 'M’); } Ora, avendo una lista dei nomi degli studenti. List<string> names = new List<string>(5); Una chiamata alla funzione Find ritorna il primo studente nella lista per cui il predicato ritorna true. string firstMName = names.Find(StartsWithM); Per avere l’ultimo si usa FindLast. Vittorio Maniezzo – Università di Bologna 9 Funzioni di ricerca Ci sono diverse funzioni di ricerca basate su predicati. Exists(predicate) ritorna true se il predicato è soddisfatto per qualche elemento. if (names.Exists(StartsWithM)) Console.WriteLine("Somebody starts with a M."); RemoveAlI(predicate) cancella ogni elemento per cui il predicato ritorna true. names.RemoveAll(StartsWithM); FindAll(predicate) riturna una lista con tutti gli elementi per cui il predicato è vero. List<string> names2 = names.FindAll(StartsWithM); foreach (string s in names2) //Nota: foreach è applicabile al tipo List Console.WriteLine(s); Vittorio Maniezzo – Università di Bologna 10 Ordinamento di una lista La funzione Sort assume che sia definito l’operatore < per gli oggetti da ordinare. names.Sort(); //Ordina le stringhe alfabeticamente. Si può anche fornire un predicato alla funzione Sort per far ordinare in qualche modo specifico. Es., predicato che prende in input due item e ritorna un int int comparison (T x, T y) return 0 if x == y, return -1 if x<y, return 1 if x>y Esempio: che ordinamento è questo? names.Sort(nomi); static int nomi(string s1, string s2) { if (s1.Length < s2.Length) return 1; if (s1.Length > s2.Length) return -1; else return 0; } Vittorio Maniezzo – Università di Bologna 11 Il generico Dictionary Dictionary è una hash table (map). Memorizza un dato e la sua chiave. Per dichiarare un Dictionary, si specifica il tipo della chiave e il tipo del dato. Dictionary<int,string> words = new Dictionary<int,string>(); Dictionary<string, Employee> staff = new Dictionary<string, Employee> (); Tutte le chiavi in un Dictionary devono essere uniche. Vittorio Maniezzo – Università di Bologna 12 Dictionary Functions Per aggiungere nuovi elementi al Dictionary, si specificano chiave e dato. staff.Add(“Pippo", new Employee(“Pippo Goofy", 23883)); Si può cercare o aggiungere elementi usando le parentesi quadre sul tipo della chiave. Employee pippo = staff[“Pippo"]; staff[“Pluto"] = new EmpIoyee(“Pluto Topolino”, 1000000); Si può cercare un elemento nel dizionario sia con la chiave che con il valore del dato. if (staff.ContainsKey(“Pippo")) Console.WriteLine(“Ho trovato Pippo."); Employee pluto = new EmpIoyee(“Pluto Topolino”, 1000000); if (staff.ContainsValue(pluto)) Console.WriteLine(“Ho trovato Pluto."); Vittorio Maniezzo – Università di Bologna 13 Il generico Stack Stack Push(T) T Pop() T Peek() – struttura dati LIFO – mette un elemento in cima allo stack – toglie l’elemento in cima e lo ritorna – ritorna l’elemento in cima senza toglierlo Esempio: Stack<string> s = new Stack<string>(); s.Push(“Pippo"); s.Push(“Pluto"); s.Push(s.Pop()); Console.WriteLine( s.Peek() + s.Pop() + s.Peek() ); Vittorio Maniezzo – Università di Bologna 14 Il generico Queue Queue – struttura dati FIFO Enqueue(T) – nuovo elemento accodato T Dequeue()– elimina il primo elemento della coda e lo ritorna T Peek() – ritorna il primo elemento della coda senza eliminarlo Esempio: Queue<string> q = new Queue<string>(); q.Enqueue(“Pippo"); q.Enqueue(“Pluto"); q.Enqueue(q.Dequeue); Console.WriteLine(q.Peek()); Vittorio Maniezzo – Università di Bologna 15 Generici fai da te // Dichiara la classe generica public class GenericList<T> { void Add(T input) { } } class TestGenericList { static void Main() { // Dichiara una lista di tipo int GenericList<int> list1 = new GenericList<int>(); // Dichiara una lista di tipo string GenericList<string> list2 = new GenericList<string>(); } } Vittorio Maniezzo – Università di Bologna 16 Delegate generici delegate bool Check<T>(T value); class Payment { public DateTime date; public int amount; } internal class Account { private IList<Payment> payments = new List<Payment >(); public void Add(Payment p) { payments.Add(p); } public int AmountPayed( Check<Payment> matches ) { int val = 0; foreach (Payment p in payments) if ( matches(p) ) val += p.amount; Si passa il metodo return val; check, che controlla } se i pagamenti sono } accetabili Vittorio Maniezzo – Università di Bologna 17 Riassumendo (cfr. MSDN) • Utilizzare i tipi generici per massimizzare il riutilizzo del codice, l'indipendenza dai tipi e le prestazioni. • I generics vengono generalmente utilizzati per creare classi di insiemi. • La libreria di classi .NET Framework contiene diverse classi di insiemi generiche nello spazio dei nomi System.Collections.Generic. • È possibile creare interfacce, classi, metodi, eventi e delegati generici personalizzati. • Le classi generiche possono essere vincolate in modo da consentire l'accesso ai metodi su solo determinati tipi di dati. • È possibile ottenere informazioni sui tipi utilizzati in un tipo di dati generico in fase di esecuzione tramite la reflection. Vittorio Maniezzo – Università di Bologna 18