Microsoft .NET è un framework per la generazione e l’esecuzione di applicazioni di nuova generazione inclusi i servizi Web XML.
Fondamentalmente .NET fornisce un ambiente di programmazione orientato agli oggetti in grado di:
- ridurre i problemi relativi ai conflitti di versione
- garantire l’esecuzione sicura del codice
- risolvere i problemi di prestazione dei linguaggi interpretati
- rendere il codice prodotto riutilizzabile da tutti i linguaggi compatibili con il framework stesso.
I componenti principali del framework .NET sono il Common Language Runtime (CLR) e la libreria di classi .NET.
Il Common Language Runtime sta alla base di .NET e può essere considerato un agente che gestisce il codice in esecuzione fornendo servizi quali la gestione della memoria, dei thread e dei servizi remoti: per questa ragione il codice destinato al CLR è definito codice gestito.
La libreria di classi .NET è invece un insieme di oggetti riutilizzabili nello sviluppo delle applicazioni tradizionali (a riga di comando o con GUI) oppure basate sulle recenti innovazioni (servizi Web).
Common Language Runtime
Affinchè il CLR possa fornire dei servizi al codice gestito è necessario che i compilatori generino insieme al codice dei metadati descrittivi dei tipi, membri e riferimenti presenti nel codice stesso.
Il processo che porta all’esecuzione del codice nativo è costituito dalle seguenti fasi:
- viene sviluppato il codice sorgente utilizzando un linguaggio in grado di supportare le funzionalità offerte dal CLR (ad esempio C#)
- il codice sorgente viene compilato in un Portable Executable (PE) contenente metadati e codice MSIL (Microsoft Intermediate Language)
- il codice MSIL viene progressivamente tradotto in codice nativo da un compilatore JIT (Just In Time)
- il codice nativo viene eseguito
 |
Processo di compilazione
In ambiente .NET dalla compilazione del codice sorgente non si ottiene subito il codice nativo ma si ottiene un linguaggio intermedio (Microsoft Intermediate Language) che essendo indipendente dall’architettura è assolutamente portabile da un sistema all’altro.
Il codice nativo viene ottenuto mediante una seconda compilazione detta JIT (Just In Time) che converte il linguaggio intermedio in codice nativo per l’architettura sulla quale il programma sta "girando".
|
Struttura di un Portable Executable
Un Portable Executable costituisce un file eseguibile trasportabile contenente metadati e codice MSIL.
La presenza dei metadati insieme al codice MSIL permette al codice di autodescriversi per cui non vi è nessuna necessità di utilizzare librerie dei tipi o linguaggi di definizione dell’interfaccia (IDL):
 |
Struttura di un Portable Executable
All’interno di un Portable Executable i metadati forniscono una rappresentazione indipendente dal linguaggio dei tipi utilizzati all’interno del codice MSIL.
Il codice MSIL costituisce una traduzione intermedia, comprensibile al Common Language Runtime, del codice sorgente.
|
I metadati descrivono i tipi e membri contenuti nel codice in maniera indipendente dal linguaggio: quando il codice viene eseguito i metadati vengono caricati in memoria ed il runtime vi fa riferimento per ottenere informazioni sulle classi utilizzate nel codice.
All’interno di un PE i metadati sono organizzati in tabelle alle quali il codice MSIL fa riferimento utilizzando dei token che identificano univocamente una tabella di metadati e per questa ragione sono concettualmente molto simili a dei puntatori.
Il codice MSIL (Microsoft Intermediate Language) è costituito da una serie di istruzioni indipendenti dalla CPU e facilmente convertibili in linguaggio nativo.
Tali istruzioni comprendono istruzioni per il caricamento, la memorizzazione, l’inizializzazione e il richiamo di metodi su oggetti, per le operazioni aritmetiche e logiche, il flusso di controllo, l’accesso diretto alla memoria, la gestione delle eccezioni e altre operazioni ancora.
Compilazione JIT
Dal momento che il codice MSIL è costituito da istruzioni indipendenti dalla CPU non può essere eseguito direttamente ma occorre eseguire un’ulteriore compilazione in linguaggio nativo.
I compilatori che effettuano la conversione da Microsoft Intermediate Language a codice nativo prendono il nome di compilatori JIT (Just In time): il Common Language Runtime fornisce un compilatore JIT per ogni architettura supportata questo rende il codice MSIL altamente portabile da un sistema all’altro (è ciò che accade anche con Java e le Java Virtual Machine).
I compilatori JIT prendono questo nome perchè prendono in cosiderazione l’ipotesi che parte del codice MSIL non venga mai chiamato durante l’esecuzione pertanto piuttosto che convertire tutto il codice contenuto in un PE in codice nativo, viene convertito soltanto il codice MSIL necessario in fase di esecuzione ed il codice nativo risultate viene memorizzato per soddisfare le eventuali chiamate successive.
 |
Compilazione JIT
La compilazione in .NET è molto simile concettualmente alla compilazione che avviene in ambiente Java: il bytecode, risultato della compilazione Java, viene di volta in volta interpretato dalla Java Virtual Machine e tradotto nel codice nativo appropriato per l’architettura che si sta utilizzando.
La compilazione Just In Time non fa altro che tradurre il codice espresso in Microsoft Intermediate Language (MSIL) in codice nativo eseguibile dall’architettura utilizzata.
Per ottimizzare le prestazioni il codice compilato Just In Time viene memorizzato per far fronte a chiamate future dello stesso.
|
Interoperabilità
Common Language Runtime fornisce il supporto per l’interoperabilità dei linguaggi ovvero la capacità di interagire con codice scritto in un linguaggio di programmazione differente.
Fondamentalmente l’interoperabilità è garantita dall’utilizzo di un sistema di tipi comuni e dall’utilizzo dei metadati che definiscono un meccanismo unico e soprattutto indipendente dal linguaggio per l’archiviazione ed il recupero delle informazioni sui tipi utilizzati.
Per essere certi che il proprio codice gestito sia accessibile ad altri indipendentemente dal linguaggio utilizzato .NET fornisce delle specifiche note col nome di Common Language Specification (CLS) che definiscono un set di funzionalità fondamentali dei linguaggi: se il codice sviluppato utilizza sclusivamente le funzionalità CLS nelle API che espone all’esterno allora l’accesso a tale codice è garantito a tutti i linguaggi che supportano le specifiche stesse.
E’ possibile specificare la compatibilità con CLS dei propri moduli, membri, tipi utilizzando nel proprio codice l’attributo CLSCompliantAttribute.
Se si vuole rendere compatibile con CLS codice che contiene parti non compatibili occorre che:
- le parti non compatibili utilizzino l’attributo CLSCompliantAttribute con argomento false
- per ogni membro non compatibile venga fornito un corrispondente membro compatibile con le specifiche.