Guida al Visual Basic .NET
Capitolo 6° - Tipi Reference e tipi Value
Tutti i tipi di variabile che possono essere creati si raggruppano
sotto due grandi categorie: Reference e Value. I primi si comportano
come oggetti, mentre i secondi rappresentano tipi scalari o numerici,
ma vediamo di mettere un po' ordine in tutti questi concetti. Differenza tra Classi e OggettiAll'inizio della guida mi sono soffermato ad elogiare le classi e la loro enorme importanza nell'ambiente .NET. Successivamente ho fatto menzione al tipo System.Object e al fatto che ogni cosa sia un oggetto. La differenza tra oggetto e classe è di vitale importanza per capire come vanno le cose nell'ambito della programmazione OO. Una classe rappresenta l'astrazione di qualcosa di concreto; un oggetto, invece, è qualcosa di concreto e viene rappresentato da una classe. Per fare un esempio banale, sappiamo benissimo che esiste il concetto di "uomo", ma ogni individuo sul pianeta, pur mantenendo alcune caratteristiche simili e costanti, è differente rispetto agli altri. Facendo un parallelismo con la programmazione, quindi, il singolo individuo, ad esempio io stesso, è un oggetto, mentre il generale concetto di "uomo" che ognuno di noi conosce è la classe. Se qualcuno dei lettori ha studiato filosofia, riconoscerà in questa differenza la stessa che Platone identificava nella discrepanza tra mondo sensibile e Iperuranio. Avendo ben chiari questi concetti, si può ora introdurre un po' di gergo tecnico. Ogni oggetto è anche detto istanza della classe che lo rappresenta (voi siete istanze della classe Uomo XD) e istanziare un oggetto significa crearlo.
O1 e O2 sono entrambe istanze della classe Object, ma sono diversi fra
di loro: in comune hanno solo l'appartenenza allo stesso tipo. Tipi Reference
Ogni cosa nel Framework è un oggetto e la maggior parte di essi sono tipi reference. Si dicono tipi reference
tutti quei tipi che derivano direttamente dalla classe System.Object
(la "derivazione" appartiene a un concetto che spiegherò più avanti):
questa classe è dichiarata all'interno di una libreria della Base Class
Library, ossia l'archivio di classi del Framework. Nel capitolo
precedente si è visto come sia possibile assegnare un valore ad una
variabile utilizzando l'operatore uguale "=". Con questo meccanismo, un
determinato valore viene depositato nella casella di memoria che la
variabile occupa. Ebbene, facendo uso dei tipi reference, questo non
avviene. Quando si utilizza l'uguale per assegnare un valore a tali
variabili, quello che effettivamente viene riposto nella loro parte di
memoria è un puntatore intero a 32bit (su sistemi operativi a 32bit).
Per chi non lo sapesse, un puntatore è una speciale variabile che,
invece di contenere un proprio valore, contiene l'indirizzo di un'area
di memoria contenente altri dati. Il puntatore viene memorizzato come
al solito sullo stack, mentre il vero oggetto viene
creato e deposto in un'area di memoria differente, detta heap managed,
dove esiste sotto la supervisione del CLR. Quando una variabile di
questo tipo viene impostata a Nothing (una costante che vedremo tra
poco), la parte dell'heap managed che l'oggetto occupa viene rilasciata
durante il processo di garbage collection
("raccolta dei rifiuti"). Tuttavia, ciò non avviene subito, poichè il
meccanismo del Framework fa in modo di avviare la garbage collection
solo quando è necessario, quindi quando la memoria comincia a
scarseggiare: supponendo che un programma abbia relativamente pochi
oggetti, questi potrebbero "vivere" indisturbati fino alla fine del
programma anche dopo essere stati
logicamente distrutti, il che significa che è stato eliminato manualmente qualsiasi riferimento ad essi (vedi paragrafo
successivo). Data l'impossibilità di
determinare a priori quando un oggetto verrà distrutto, si ha un fenomeno che va sotto il nome di finalizzazione non deterministica
(il termine "finalizzazione" non è casule: vedere il capitolo sui distruttori per maggiori informazioni). NothingNothing è una costante di tipo reference che rappresenta l'assenza di un oggetto piuttosto che un oggetto nullo. Infatti, porre una variabile oggetto uguale a Nothing equivale a distruggerla logicamente.
La distruzione logica non coincide con la distruzione fisica
dell'oggetto (ossia la sua rimzione dalla memoria), poiché, come detto
prima, il processo di liberazione della memoria viene avviato solo
quando è necessario. Non è possibile assegnare Nothing a un tipo value,
ma è possibile usare speciali tipi value che supportano tale valore:
per ulteriori dettagli, vedere "Tipi Nullable". Tipi Value
Ogni tipo value
deriva dalla classe System.ValueType, che deriva a sua volta da
System.Object, ma ne ridefinisce i metodi. Ogni variabile di questo
tipo contiene effettivamente il proprio valore e non un puntatore ad
esso. Inoltre, esse hanno dei vantaggi in termini di memoria e
velocità: occupano in genere meno spazio; data la loro posizione sullo
stack non vi è bisogno di referenziare un puntatore per ottenere o
impostarne i valori (referenziare un puntatore significa recarsi
all'indirizzo di memoria puntato e leggerne il contenuto); non c'è
necessità di occupare spazio nello heap managed: se la variabile viene
distrutta, cessa di esistere all'istante e non si deve attuare nessuna
operazione di rilascio delle risorse. Notare che non è possibile
distruggere logicamente una variabile value, fatta eccezione per certi
tipi derivati. Is e =Nel lavorare con tipi reference e value bisogna prestare molta attenzione a quando si utilizzano gli operatori di assegnamento. Come già detto, i reference contengono un puntatore, perciò se si scrive questo codice:
quello che O2 conterrà non sarà un valore identico a O1, ma un puntatore alla stessa area di memoria di O1. Questo provoca un fatto strano, poichè sia O1 che O2 puntano alla stessa area di memoria: quindi O1 e O2 sono lo stesso oggetto, soltanto riferito con nomi difersi. In casi simili, si può utilizzare l'operatore Is per verificare che due oggetti puntino allo stesso valore:
La scritta che apparirà sullo schermo sarà "True", ossia "Vero". Utilizzare Is per comparare un oggetto a Nothing equivale
a verificare che tale oggetto sia stato distrutto. Boxing e UnboxingConsideriamo il seguente codice:
I è un tipo value, mentre O è un tipo reference. Quello che succede dietro le quinte è semplice: il .NET crea un nuovo oggetto, perciò un tipo reference, con il rispettivo puntatore, e quindi gli assegna il valore di I: quando il processo è finito assegna il puntatore al nuovo oggetto a O. Questa conversione spreca tempo e spazio nello heap managed e viene definita come boxing. L'operazione inversa è l'unboxing e consiste nell'assegnare un tipo reference a un tipo value. Le operazioni che si svolgono sono le stesse, ma al contrario: entrambe sprecano tempo e cpu, quindi sono da evitare se non strettamente necessarie. Quando si può scegliere, quindi, sono meglio di tipi value.
C#, TypeScript, java, php, EcmaScript (JavaScript), Spring, Hibernate, React, SASS/LESS, jade, python, scikit, node.js, redux, postgres, keras, kubernetes, docker, hexo, etc...
|