InteroperabilitàModifica
Perché i sistemi informatici richiedono comunemente l’interazione tra applicazioni più recenti e più vecchie, .NET Framework fornisce mezzi per accedere a funzioni implementate in programmi più recenti e più vecchi che vengono eseguiti fuori dall’ambiente .NET. L’accesso ai componenti Component Object Model (COM) è fornito in System.Runtime.InteropServices
e System.EnterpriseServices
namespace del framework. L’accesso ad altre funzioni è tramite Platform Invocation Services (P/Invoke). L’accesso alle funzioni .NET dalle applicazioni native è tramite la funzione P/Invoke inversa.
Indipendenza dalla linguaModifica
.NET Framework introduce un Common Type System (CTS) che definisce tutti i possibili tipi di dati e costrutti di programmazione supportati dal CLR e come possono interagire o meno in conformità alle specifiche CLI. Grazie a questa caratteristica, .NET Framework supporta lo scambio di tipi e istanze di oggetti tra librerie e applicazioni scritte usando qualsiasi linguaggio .NET conforme.
Type safetyEdit
CTS e il CLR usato in .NET Framework applicano anche la sicurezza dei tipi. Questo previene cast mal definiti, invocazioni di metodi sbagliati e problemi di dimensione della memoria quando si accede a un oggetto. Questo rende anche la maggior parte dei linguaggi CLI staticamente tipizzati (con o senza inferenza dei tipi). Tuttavia, a partire dal .NET Framework 4.0, il Dynamic Language Runtime ha esteso il CLR, permettendo ai linguaggi dinamicamente tipizzati di essere implementati sopra il CLI.
PortabilitàModifica
Mentre Microsoft non ha mai implementato il framework completo su nessun sistema eccetto Microsoft Windows, ha progettato il framework per essere multipiattaforma, e sono disponibili implementazioni per altri sistemi operativi (vedere Silverlight e § Implementazioni alternative). Microsoft ha presentato le specifiche per CLI (che include le librerie di classi di base, CTS e CIL), C# e C++/CLI sia a Ecma International (ECMA) che a International Organization for Standardization (ISO), rendendole disponibili come standard ufficiali. Questo rende possibile a terzi di creare implementazioni compatibili del framework e dei suoi linguaggi su altre piattaforme.
SecurityEdit
.NET Framework ha il proprio meccanismo di sicurezza con due caratteristiche generali: Code Access Security (CAS), e validazione e verifica. Il CAS si basa su prove che sono associate ad uno specifico assembly. Tipicamente la prova è la fonte dell’assembly (se è installato sulla macchina locale o è stato scaricato da Internet). Il CAS usa le prove per determinare i permessi concessi al codice. Altro codice può richiedere che al codice chiamante sia concesso un permesso specifico. La richiesta fa sì che il CLR esegua una passeggiata nello stack delle chiamate: ogni assembly di ogni metodo nello stack delle chiamate viene controllato per il permesso richiesto; se ad un assembly non viene concesso il permesso viene lanciata un’eccezione di sicurezza.
Il bytecode CIL gestito è più facile da reingegnerizzare del codice nativo, a meno che non sia offuscato. I programmi decompilatori .NET permettono agli sviluppatori senza alcuna abilità di reverse-engineering di vedere il codice sorgente dietro gli assembly .NET non offuscati. Al contrario, le applicazioni compilate in codice macchina nativo sono molto più difficili da decodificare, e il codice sorgente non viene quasi mai prodotto con successo, principalmente a causa delle ottimizzazioni del compilatore e della mancanza di riflessione. Questo crea preoccupazioni nella comunità aziendale per la possibile perdita di segreti commerciali e l’aggiramento dei meccanismi di controllo delle licenze. Per mitigare questo, Microsoft ha incluso Dotfuscator Community Edition con Visual Studio .NET dal 2002. Strumenti di offuscamento di terze parti sono anche disponibili da venditori come VMware, V.i. Labs, Turbo e Red Gate Software. Strumenti di crittografia a livello di metodo per il codice .NET sono disponibili da fornitori come SafeNet.
Gestione della memoriaModifica
CLR libera lo sviluppatore dall’onere di gestire la memoria (allocare e liberare quando fatto); gestisce la gestione della memoria stessa rilevando quando la memoria può essere liberata in sicurezza. Le istanziazioni dei tipi .NET (oggetti) sono allocate dall’heap gestito; un pool di memoria gestito da CLR. Finché esiste un riferimento a un oggetto, che può essere diretto o tramite un grafico di oggetti, l’oggetto è considerato in uso. Quando non esiste alcun riferimento a un oggetto, e non può essere raggiunto o utilizzato, diventa garbage, idoneo per la raccolta.
.NET Framework include un garbage collector (GC) che viene eseguito periodicamente, su un thread separato da quello dell’applicazione, che enumera tutti gli oggetti inutilizzabili e recupera la memoria ad essi allocata. È un garbage collector non deterministico, compattante, mark-and-sweep. GC viene eseguito solo quando una certa quantità di memoria è stata utilizzata o c’è abbastanza pressione per la memoria sul sistema. Poiché non è garantito quando le condizioni per recuperare la memoria sono raggiunte, le esecuzioni di GC sono non-deterministiche. Ogni applicazione .NET ha un insieme di radici, che sono puntatori a oggetti sull’heap gestito (oggetti gestiti). Questi includono riferimenti a oggetti statici, oggetti definiti come variabili locali o parametri di metodo attualmente in scope, e oggetti a cui fanno riferimento i registri della CPU. Quando GC viene eseguito, mette in pausa l’applicazione e poi, per ogni oggetto a cui si fa riferimento nella root, enumera ricorsivamente tutti gli oggetti raggiungibili dagli oggetti della root e li segna come raggiungibili. Utilizza i metadati CLI e la riflessione per scoprire gli oggetti incapsulati da un oggetto, e poi li percorre ricorsivamente. Poi enumera tutti gli oggetti sull’heap (che sono stati inizialmente allocati in modo contiguo) usando reflection. Tutti gli oggetti non contrassegnati come raggiungibili sono spazzatura. Questa è la fase di marcatura. Poiché la memoria tenuta dalla spazzatura non ha alcuna conseguenza, viene considerata spazio libero. Tuttavia, questo lascia pezzi di spazio libero tra gli oggetti che inizialmente erano contigui. Gli oggetti vengono poi compattati insieme per rendere lo spazio libero sull’heap gestito nuovamente contiguo. Qualsiasi riferimento ad un oggetto invalidato dallo spostamento dell’oggetto viene aggiornato dal GC per riflettere la nuova posizione. L’applicazione viene ripresa dopo la fine della garbage collection. L’ultima versione di .NET Framework usa la garbage collection concomitante insieme al codice utente, rendendo le pause impercettibili, perché è fatta in background.
Il garbage collector usato da .NET Framework è anche generazionale. Agli oggetti viene assegnata una generazione. Gli oggetti appena creati sono etichettati come Generazione 0. Gli oggetti che sopravvivono ad una garbage collection sono etichettati come Generazione 1. Gli oggetti di Generazione 1 che sopravvivono ad un’altra raccolta sono di Generazione 2. Il framework usa fino a oggetti di Generazione 2. Gli oggetti di generazione superiore sono raccolti meno spesso di quelli di generazione inferiore. Questo aumenta l’efficienza della garbage collection, poiché gli oggetti più vecchi tendono ad avere una vita più lunga rispetto agli oggetti più nuovi. Ignorando gli oggetti più vecchi nella maggior parte delle raccolte, sono necessari meno controlli e operazioni di compattazione in totale.
PerformanceEdit
Quando un’applicazione viene lanciata per la prima volta, il .NET Framework compila il codice CIL in codice eseguibile usando il suo compilatore just-in-time, e memorizza il programma eseguibile nella .NET Native Image Cache. Grazie alla cache, l’applicazione viene lanciata più velocemente per i lanci successivi, anche se il primo lancio è solitamente più lento. Per accelerare il primo lancio, gli sviluppatori possono usare l’utilità Native Image Generator per compilare manualmente prima del tempo e mettere in cache qualsiasi applicazione .NET.
Il garbage collector, che è integrato nell’ambiente, può introdurre ritardi imprevisti di esecuzione sui quali lo sviluppatore ha poco controllo diretto. “Nelle applicazioni di grandi dimensioni, il numero di oggetti con cui il garbage collector deve lavorare può diventare molto grande, il che significa che può richiedere molto tempo per visitarli e riorganizzarli tutti.”
.NET Framework fornisce il supporto per chiamare Streaming SIMD Extensions (SSE) tramite codice gestito da aprile 2014 in Visual Studio 2013 Update 2. Tuttavia, Mono ha fornito il supporto per le estensioni SIMD a partire dalla versione 2.2 all’interno del namespace Mono.Simd nel 2009. Lo sviluppatore principale di Mono, Miguel de Icaza, ha espresso la speranza che questo supporto SIMD sarà adottato dallo standard ECMA di CLR. Le estensioni SIMD in streaming sono state disponibili nelle CPU x86 dall’introduzione del Pentium III. Alcune altre architetture come ARM e MIPS hanno anche estensioni SIMD. Nel caso in cui la CPU non supporti queste estensioni, le istruzioni sono simulate nel software.