C# |
|
Introduzione
Il framework .Net è un complesso ambiente per lo sviluppo di applicazioni che costituisce la base di tutte le tecnologie recenti e, si presume future, di casa Microsoft. Nasce in via ufficiale all'inizio degli anni 2000 con l'intento di fare un po' un punto e a capo nel caotico mondo della programmazione e anche nell’ambito dei numerosi linguaggi e tecnologie che avevano preso piede nella grande software house americana stessa. La spinta data da Internet in particolare e dall'emergere prepotente di nuove tecnologie esigeva una risposta chiara forte ma, soprattutto, efficiente. .Net è quindi questa risposta, una piattaforma potente e ad alta produttività orientata certamente allo sviluppo di applicazioni tradizionali ma con una fortissima orientazione al Web, all'integrazione con i database, alla gestione di contenuti XML ecc.. Il tutto fortemente integrato nelle sue componenti. Uno sforzo davvero in grande stile che ha concentrato molte risorse della software house di Redmond. ma i risultati, a parer mio, sono stati eccellenti. Sigle come DNA, MFC ecc... appartengono per lo più al passato. Anche COM viene ridimensionato e proprio COM (che doveva diventare COM+) era stato inzialmente il concorrente interno alla nascita di un nuovo framework. Tutte quelle citate non erano di tecnologie di scarso valore, tutt'altro. Ma, soprattutto se si pensa allo sviluppo Web oriented, mancava qualche cosa di unificato, di realmente robusto, che unisse potenza ad una certa semplicità sia in termini di modellazione che di mera stesura del codice. Ora questo qualche cosa c'è. Il progetto inizialmente si chiamava Lightning ed ora è cresciuto diventando uno dei protagonisti nel mondo della programmazione. Il miglior modo di pensare .Net è comunque che esso non è valido perchè i suoi predecessori non lo erano ma perchè è stato pensato dall’inizio in modo coerente e ambizioso.
Net è costituito fondamentalmente, o meglio da un punto di vista pratico,
dell'unione di 3 parti:
·
una serie di tools per lo sviluppo e la gestione di applicazioni multi purpose
·
una potente libreria di classi pronte all'uso
·
un ambiente run-time per l'esecuzione dei programmi Lo scopo che Microsoft perseguiva (insieme evidentemente a quelli commerciali peraltro irraggiungibili senza mettere in pista un prodotto più che valido e questo ultimo è un punto che tanti dimenticano...) è quello invogliare uno sviluppo software razionale che segua linee guida standard e che permetta una forte cooperazione tra le varie entità in gioco. La storia di .Net comincia di fronte al mondo nel 2002 quando fu distribuita la versione 1.0 che ottenne curiosità interesse, consensi e le solite critiche che accompagnano i nuovi prodotti destinati ad avere un certo impatto, specialmente se arrivano da Redmond. L'adozione di questa nuova tecnologia fu nel complesso tiepido soprattutto nei primissimi mesi ma, poco alla volta, essa cominciò a far breccia anche per l'imponente spinta proveniente dalla casa madre. Importante, anche da un punto di vista prettamente mediatico, fu l'introduzione di un linguaggio di programmazione del tutto nuovo, C# e anche le profonde modifiche fatte al caro vecchio Visual Basic, stravolto quasi dalle fondamenta e divenuto un linguaggio a oggetti in piena regola. Alle legioni di programmatori in VB fu dato ampio supporto affichè non si sentissero in qualche modo abbandonati in un mondo, quello apputo a oggetti, per molti di loro quasi ignoto. Nel 2003 debuttò la versione 1.1 che rimediava a qualche debolezza e qualche bug della precedente mentre pesanti cambiamenti si ebbero nel 2005 con la versione 2.0 che introduceva novità importanti come le funzioni anonime ed i generics. Successivamente, siamo nel 2006, arrivò la versione 3.0 e .Net cominciò a diventare veramente un prodotto non solo maturo ma anche molto sofisticato con una diffusione in rapida espansione.Nel 2007 si passò alla versione 3.5 e il 2010 è stato l'anno di un altro grosso passo in avanti con .Net 4.0 a cui ha fatto seguito nel 2012 la 4.5 e nel 2013 la 4.5.1. Ogni volta con affinamenti, migliorie e nuove funzionalità, queste ultime sempre ampiamente documentate, fattore questo decisamente importante.... troppe volte incontriamo ottimi prodotti con un documentazione scarsa, datata, incompleta. Con Microsoft questo problema è certamente molto mitigato, grazie alle enormi risorse del colosso fondato da Bill Gates. Ma quali sono i concetti cardine di .Net? Fondamentalmente Microsoft si è prefissata lo scopo di creare un ambiente che favorisse lo sviluppo pienamente object oriented, che semplificasse la vita allo sviluppatore, ad esempio con un miglior controllo delle versioni e, soprattutto una avanzata gestione della memoria, consentendogli di scrivere codice più sicuro e fornendo strumenti di sviluppo all'avanguardia (avete presente Visual Studio?). Si sono inoltre definite delle linee di sviluppo che hanno fortemente facilitato l'inserimento nel filone .Net di numerosi altri linguaggi. Tra questi ho seguito, per interesse personale IronPython e IronRuby (quest’ultimo però sembra un progetto ormai defunto e me ne dispiace), porting nel mondo .Net di Python e Ruby, forse i due linguaggi di scripting più evoluti e conosciuti, ma ce ne sono veramente tanti vale la pena segnalare la "traduzione" nel mondo di .Net del Cobol e del Fortran, pietre miliari nella storia della programmazione, ma anche Ada, Delphi, oppure la nascita di nuovissimi strumenti decisamente interessanti come Boo, Cobra, Nemerle. Proprio la possibilità che un gran numero di linguaggi potessero collaborare sotto un ombrello comune era una delle finalità principali in fase di progettazione del framework e direi che è stata pienamente raggiunta. Non solo questo però: lo sviluppo di un framework nuovo e lontano da COM ha permesso la una serie di vantaggi collaterali quali la possibilità di sviluppare componenti in modo molto più semplice rispetto al passato (“contra” COM). Un’altra cosa interessante è che comunque fin dalle origini .Net non ha tagliato i ponti colo passato ed è pur sempre possibile interagire con COM, Win32. Questo potente framework, nel tempo, ha dimostrato, lo abbiamo accennato parlando delle diverse versioni che si sono succedute, anche una scalabilità fuori del comune, permettendo la nascita ed il supporto di nuove tecnologie.
E veniamo ora ad analizzare il framework da un punto di vista per vosì dire
anatomico e fisiologico.Gli strumenti ed i tools messi a disposizione da MS sono
veramente numerosi, anche solo semplicemente considerando ciò che l'SDK base di
.Net ci mette gratuitamente nelle mani. Di essi tuttavia parleremo quando sarà
utile farlo, per ora è importante capire quale è il ruolo di .Net. Lo sviluppo di un software avviene normalmente secondo questo schema almeno per i linguaggi compilati costituito da 3 fasi successive:
La prima fase, la scrittura del codice è normale per tutti i
linguaggi anche nell'ambito del nostro framework. Va sottolineato
come per quanto riguarda i linguaggi da essa supportati ovvero C#,
VBNet e da ultimo anche F#, il linguaggio funzionale ispirato a
OCaml, Microsoft mette a disposizione, come già accennato, l'ottimo
ambiente di sviluppo Visual Studio, disponibile anche gratuitamente
nella comunque valida versione
Express; tuttavia potete usare
l'editor che più vi aggrada, ovviamente, i programmi sono pur sempre
solo dei files testuali pur se è evidente che strumenti evoluti
usati correttamente garantiscono una produttività di altissimo
livello. * una parte di codice in Intermediate Language (IL che altrove è
indicato come CIL o MSIL). Si tratta, come dice il nome, di un
linguaggio intermedio In fase di creazione viene importata dal framework una
funzione che si chiama _CorExeMain (nel caso di una DLL la
funzione è _CorDllMain) che fa da tramite tra il
sistema operativo e il runtime di .Net. Quando l'utente lancia
l'esecuzione del programma il loader del Sistema Operativo carica il
.exe e cerca l'entry point (vedremo nel terzo capitolo, come
definire l'entry point per ogni programma). Di qui viene rimandato alla funzione
_CorExeMain il quale manda in esecuzione il codice IL nel quale è
stato tradotto il nostro programma. Di tutti questi problemi
relativi all’esecuzione di occupa il Virtual Execution System (VES) Come si vede, quando scriviamo codice siamo ad alto livello, con
una astrazione piuttosto marcata rispetto alla macchina. Il codice
IL non è eseguibile nativamente ma .Net come detto mette a
disposizione, come abbiamo appena visto, tutto ciò che serve per la nostra applicazione. In
particolare, ora che il nostro codice è stato riconosciuto corretto
ed il file .exe è stato generato, entra in azione un JIT (= Just In Time
Compiler) che si preoccupa di prendere il nostro codice IL e
trasformarlo in eseguibile per l'architettura sottostante. Per
ottimizzare il lavoro il JIT compila al volo solo le parti che sono
effettivamente chiamate in causa nel momento dell'esecuzione, ecco
in questo modo chiarito il concetto di just-in-time o meglio, di
compilazione al volo. Ovviamente il codice compilato dal JIT viene
posto in cache in memoria così che non sia necessario ricorrere a
più compilazioni della stessa porzione di codice. In realtà
nell'ambito del framework esistono almeno 3 Jitters, uno "normale"
appena descritto ed un altro detto "EconoJit" più veloce ma
che genera codice meno ottimizzato e quindi di norma più lento in
esecuzione ed il PreJit che compila tutto il codice in un
passaggio solo. In breve se il primo passaggio nella figura
precedente tutto sommato resta immutato il secondo, tra compilazione
ed esecuzione, andrebbe spezzettato in più fasi ad esempio come
nell’immagine seguente tenendo tuttavia presente, vale la pena
ribadirlo, che il programmatore e soprattutto l’utente non avranno
alcun coinvolgimento diretto in quello che combina il compilatore e
su quanto accade in fase di esecuzione:
Non bisogna confondersi però: il JIT non è il compilatore che genera il codice dal vostro sorgente; si può dire che fa già parte del runtime per eseguire il programma, è una specie di motorino di avviamento per consentire al runtime, vero motore del framework, di eseguire il programma. A questo punto risulta fondamentale il ruolo dell'ambiente run
time provvisto da .Net; esso è noto con il nome di Common Language Runtime
(di seguito CLR). Questo componente è il vero fulcro del framework
pensato da Microsoft ed è una utile piattaforma, una
macchina virtuale, comune a qualunque
linguaggio si interfacci con esso. E' in grado di gestire, tramite
il JIT, l'Intermediate Language che
sia stato generato secondo lo schema proposto qui sopra; pertanto e
questa è la chiave di tutto il discorso, qualunque compilatore
produca IL vedrà il proprio codice funzionare
grazie allo strato run-time. Ecco spiegato perchè, o
meglio come .Net è aperto verso qualuque linguaggio, come in fondo
ci suggerisce quel "Common" messo come prima parola per il CLR;
perchè esso fa girare IL senza riguardo al linguaggio di
provenienza. Il contributo di questo strato per il runtime si
estrinseca anche in tutta serie di servizi che vengono messi a
disposizione per il codice IL. In particolare esso si preoccupa di
astrarre il programmatore da alcune importanti questioni tra le
quali segnalo le più significative: ·
gestione della sicurezza (molto importante, ne parleremo a
parte) ·
gestione dei thread ·
gestione della memoria ·
gestione delle eccezioni ·
gestione delle operazioni di pulizia dello heap (garbage
collection) Tutto questo ed anche altro è controllato direttamente dal CLR.
In realtà non siamo di fronte solo ad un run-time inteso come
ambito di esecuzione, sarebbe stato forse un po’ poco, ma ad un
insieme di
servizi forniti al software. Il codice che gira sotto il diretto
controllo del CLR viene definito "managed" (appunto "gestito" ma
preferisco usare il più comune termine inglese). Per esempio usando
codice IL managed non sarà più necessario liberare manualmente la
memoria occupata da oggetti non più utilizzati perchè se ne occuperà
il CLR; oppure le eccezioni saranno intercettate attraverso
meccanismi del runtime stesso con un sistema comune a tutti i
linguaggi che seguono le specifiche di .Net. E sappiamo quanto sia
importante. Peraltro, in sezioni
appositamente definite è possibile far girare codice unmanaged,
a rischio e pericolo del programmatore. La generazione di IL managed è condizione necessaria per ricevere le
attenzioni complete del CLR. Ma detto così non basta. Come si crea
il codice managed? Bisogna ora presentare due sigle
per così dire magiche: CTS e CLS. La prima sta per
Common Type
System. Evidentemente si tratta di una serie di regole che
definiscono i tipi ai quali devono appartenere le variabili usate
nei programmi. La parola Common ci fa per prima cosa capire che si
tratta di una struttura che faciliterà la cooperazione tra quanti
(linguaggi) ne seguiranno le linee guida. Proprio la cooperazione
tra linguaggi è obiettivo primario del CTS il quale mantiene
l'impostazione object oriented tipica del mondo .Net. Il CTS prevede
due gandi famiglie di tipi: i reference types (tipi per riferimento
o tipi di riferimento) e value types (tipi valore). Di essi parleremo
in dettaglio nel capitolo 3 della sezione dedicata a C# dove
verranno evidenziate le dinamiche peculiari nell'utilizzo delle due
tipologie. In questa sede ci basta sapere che sono profondamente
diversi e che è possibile passare da un tipo all'altro attraverso il
meccanismo di boxing e unboxing di cui parleremo nello stesso
capitolo. L'immagine che segue fornisce una visione di massima della
suddivsione dei tipi a livello così come intesa da .Net. Premesso
che i tipi in se stessi saranno più chiari più avanti è evidente la
netta suddivisione tra le due categorie, che pure hanno una origine
comune. Il concetto che qui è fondamentale comprendere è che questa
figura illustra la base comune a tutti i linguaggi che ambiscono ad
essere.Net compliant. Lo so, il concetto
della interoperabilità è ripetuto alla nausea ma credo sia utile far
vedere quali sono i punti in cui questa convergenza, o meglio
convivenza è stata realizzata. Se avete intenzione di scrivere il
prossimo grande linguaggio che si basa su .Net questi concetti vi
serviranno, che diamine! Lo schema che segue è simile ad altri che
potete trovare sulla rete.
L'immagine qui sopra identifica le due grandi famiglie di tipi.
Vale la pena sottolineare come i puntatori in stile C++ non fanno
parte del CTS (le loro veci vengono svolte dai delegati) mentre
esiste un tipo di managed pointers che è possibile usare nell'ambito
CTS. Ne riparleremo. Essere conforme alle specifiche è necessario
anche in questo caso per essere compatibili con le richieste del
CLR. Più complessa è la comprensione del secondo acronimo che abbiamo
dianzi citato ovvero CLS che sta per Common Language Specification.
Eppure si tratta di qualche cosa di concettualmente molto semplice;
il CLR è un terreno comune di conversazione tra linguaggi, nonchè
uno strato che si pone come intermediario tra il programma e la
macchina. Ebbene è necessario definire le regole affichè questo
colloquio possa funzionare, quindi stabilire cosa ciascun elemento
può esporre e come deve esporlo affiche sia comprensibile. Anche in
questo caso siamo di fronte ad una serie di regole che garantiscono
l'interoperabilità tra linguaggi. Ovvero si tratta di un insieme
minimale (e sottolineo quel "minimale"), di regole alle quali devono
rispondere i linguaggi .Net compliant. Questo non è limitativo dei
linguaggi stessi: essi possono supportare tutti i tipi di dati e le
istruzioni che preferiscono l'importante è che supportino anche
quelle che fanno parte del CLS e, qualora sia necessario interagire
con altri linguaggi, espongano, almeno nelle parti che dovranno
essere comuni, solo le funzionalità riconosciute dal CLS. Questo non
si riferisce ovviamente alla grammatica interna dei singoli
linguaggi. Un esempio tipico che troverete un po' ovunque su questo
argomento è la concatenazione di stringhe che viene ottenuta con "+"
in C# e con l'operatore "&" in VBNet. Il codice IL generato è
praticamente identico perchè l'operazione di concatenazione è
comunque accettabile. Il CLS con le sue regole impatta anche sul CTS
il quale accetta una tipologia di dati più ampia rispetto allo
stretto sottoinsieme che il CLS accetta per tutti i linguaggi. Ad
esempio il tipo enum fa parte del CTS ma non è riconosciuto come
universale dal CLS. Oppure Uint32, l'intero senza segno 32 bit non è
CLS compliant in quanto non tutti i linguaggi hanno un tipo
equivalente. Esiste una complessa tabella, disponibile su MSDN, che
spiega, una per una le regole che compongono il CLS. Queste sono
molto ampie e coinvolgono sia i dati in se stessi sia la loro
nomenclatura, ad esempio non è ammissibile che due variabili
differiscano tra di loro solo per il "case" (cioè maiuscolo /
minuscolo) dei loro identificatori quindi "gatto" e "Gatto" sono due
variabili diverse, ma non CLS ammissibili. Altra regola è
l'esclusione dei puntatori dal mondo Common Language Specification.
Insomma siamo di fronte ad un tentativo di mettere un po' di ordine
tra le features dei linguaggi senza peraltro azzopparne le
potenzialità. Giusto per completezza di informazione segnalo che CLS e CTS sono
parte del CLI, Common Language Infrastructure, ovvero l'insieme
globale delle caratteristiche definite per l'interoperabilità dei
linguaggi in ambito .Net. Si tratta di un insieme di specifiche
standardizzate ISO ed ECMA. Ancora una volta il concetto cardine che
giustifica l'esistenza del CLI è l'interoperabilità. Il CLR ne è la
rappresentazione pratica. Visivamente:
Abbiamo compreso perchè il CLR, ovvero la libreria di runtime è importante e quali sono le regole per goderne i vantaggi. Tuttavia .Net non è solo questo e un aspetto che abbiamo soltanto sfiorato finora è la Base Class Library (di seguito BCL) ovvero l'insieme di classi predefinite che accompagnano .Net e di cui possiamo ampiamente usufruire lavorando in modalità managed. Lo scopo di simili librerie, come le vecchie MFC o le OWL di casa Borland, è sempre quello di sollevare il programmatore dalla necessità di creare o ricreare strumenti per i vari problemi che si incontrano nella programmazione reale fornendo una base comune che viene incontro a varie necessità che si vada dalla gestione di contenuti XML, all’accesso ad un database, all‘uso spinto della grafica. Il tutto favorendo uno sviluppo pienamente object oriented. La libreria fornita da Microsoft è amplissima ed offre utili servizi e features praticamente per ogni possibile problematica dello sviluppo. Per uno strumento di sviluppo moderno avere una base di classi come questa è essenziale per avere successo. Anche la BCL può, volendo, essere vista come uno strato, un'interfaccia, che viene interposto tra il programmatore e il sistema, ovviamente quando viene utilizzata. Questa libreria, che è ovviamente disponibile per tutti i linguaggi predisposti per girare tramite .Net, è cresciuta enormemente nel tempo, ammodernandosi ed adattandosi alle evoluzioni informatiche, al punto che è praticamente impossibile conoscerla tutta. Comunque la documentazione è eccellente per cui non ci dovrebbero essere problemi particolari nel cercare quel che vi serve in particolare grazie alla sua chiamiamola "morfologia". La nostra libreria infatti è organizzata in "gusci" o se preferite in livelli tra i quali i più esterni contengono quelli più interni, tramite namespaces. Ce ne sono una miriade e, se volete farvi una cultura, trovate tutti i namespace presenti nella apposita pagina su MSDN. Ma che cos'è un namespace? Ne parleremo ancora nel capitolo 3, per ora vi basti sapere che è una sorta di contenitore che tiene insieme quelle classi che hanno una finalità di uso comune ed è gerarchizzato in modo da contenere eventualmente oltre alle classi anche altri namespaces, un po' il gioco delle scatole cinesi o delle matrioske. Comunque già un'occhiata alla pagina indicata dovrebbe garantirvi una buona comprensione. In particolare noterete che vi sono tre namespace principali: System, Microsoft e Windows, l'ultimo aggiunto al framework dopo l'avvento di Windows 8. I tre namespace hanno campi di applicazioni specifici: System - offre funzionalità che dovrebbero avere una validità
indipendente dalla piattaforma Avremo naturalmente modo di interagire molto ma molto spesso con la BCL, fin dal capitolo 3, quando cominceremo coi primi semplici programmi. Non è obbligatorio servirsi della BCL, ognuno si può scrivere le classi che vuole, ma è chiaro che essa è stata creata per lavorare armoniosamente con le altre componenti ed usarla fa risparmiare evidentemente parecchio tempo trattandosi di codice, in soldoni, ben scritto ed ottimizzato per il framework. Ora che abbiamo visto le componenti essenziali del framework, resterebbero da illustrare i numerosi e potenti tools che fanno da corredo agli altri componenti ma di essi parlremo man mano che serviranno, non mancherà l'occasione. Ttorniamo indietro ai primi concetti espressi ed analizziamo il processo di creazione di un file eseguibile. Già sappiamo che si tratta di un file in un formato un po' particolare che contiene del codice IL. al fianco del qualo abbiamo accennato alla presenza di un "manifesto". Questo perchè in realtà quando il compilatore termina la sua opera viene creata una entità che si chiama assembly e che, se generalmente è costituita dal solo nostro eseguibile, almeno per le piccole applicazioni di prova che useremo (come vedremo non è poi così corretto ma in prima istanza questo "quadro" può andare bene), può in realtà essere costituito da più files e più risorse. L'assembly è una struttura complessa, vera anima delle nostre applicazioni. In questa fase di presentazione basteranno poche informazioni di base; come prima cosa specifichiamo che tipo di informazioni porta dentro di sè un assembly. In sostanza al suo interno abbiamo:
Il primo reca informazioni per il runtime, come il nome, la
versione, la collaborazione di altri assembly ecc... Questo schema vale particolarmente per assembly composti da un file, mentre uno che comprenda più entità costituitive potrebbe ad esempio avere la parte relativa alle risorse racchiuse in un file esterno, puntare ad una DLL, o ad un un file grafico ecc... e anche comprendere più binari .Net.; in questo caso i vari files binari prendono il nome di moduli (ogni assembly ne ha almeno uno) e tra di essi ve ne deve essere uno primario che contiene il manifesto nel quale sono elencati tutti i necessari riferimenti. Tale manifesto, come già visto, contiene infatti i metadata dell'assembly stesso quindi informazioni ad esempio sui files che ne fanno parte, o relative alle risorse ed ai loro riferimenti, stabilisce le versioni, ad esempio dello stesso framework, necessarie al funzionamento dell'applicazione, informazioni sulla sicurezza ecc... E' da sottolineare il fatto che il manifesto contiene informazioni sulle versioni di software (anche DLL) che devono essere usate per l'applicazione. Questa gestione avanzata delle versioni serve per risolvere una annosa questione relativa proprio al fatto che spesso l'installazione di un nuovo programma finiva cper creare fastidiosi e a volte gravi problemi; questo perchè tale installazione comprendeva anche il corredo di DLL che il nuovo programma si portava dietro e che, a volte, entrava in conflitto con altre, con lo stesso nome ma versione diversa, usate da altri software installati. Vi ricordate il famoso DLL-Hell (di cui Microsoft ha una certa responsabilità, peraltro)? Proprio quello. Beh, ora non vi tormenterà più e più versioni della stessa DLL possono convivere senza pestarsi i piedi perchè il richiamare l'una o l'altra, a seconda dell'applicazione, è gestito tramite i manifesti di cui abbiamo parlato. A titolo informativo sottolineo che Net mette a disposizione degli strumenti per indagare sui manifesti di una applicazione. I type metadata invece riportano informazioni accurate sui tipi usati nell'assembly, quindi la loro natura, gli eventuali membri che ne fanno parte se si tratta di classi o strutture e così via. Possiamo assimilare nel complesso i metadati a delle tabelle descrittive precise e accurate. In generale i metadata sono un collante fondamentale per le applicazioni .Net. Infine le risorse indicano le solite informazioni locali relative al file, un esempio classico è l'icona specificata per rappresentare l'eseguibile. Un assembly ha in buona sostanza molti vantaggi in quanto è autodescritto, facilita l'installazione e realmente vale la pena di ribadire la sua importanza nel dirimere i conflitti tra versioni. Quando si parla di assembly si può rimanere un po' confusi in special modo quando ci si confronta con la visuale consueta e rassicurante dell'eseguibile monolitico. In realtà si tratta di un concetto logico abbastanza semplice che è del tutto trasparente all'utente e spesso anche al programmatore, quasi banale quando si parla di assembly costituito da un singolo files ma altrettanto semplice quando le risorse sono molteplici quando, come visto, in questo caso l'assembly è l'insieme di queste risorse riunite dal collante del manifesto che si trova nel file primario (nb: Visual Studio supporta solo assembly costituiti da un solo file; assembly multifile devono essere compilati dalla riga di comando). Nel complesso si tratta del passo successivo nella comprensione del concetto di compilazione in ambito .Net e può anche essere ignorato perchè non impatta sull'interazione tra utente e macchina almeno in maniera visibile. Tuttavia è pur sempre un concetto importantissimo che lavora pesantemente e silenziosamente dietro le quinte e costituisce una chiave primaria nella teoria di .Net. Sebbene tutto questo lavoro sia generalmente a carico del framework (non è comune andare ad interfacciarsi manualmente con gli assembly) può essere utile, probabilmente doveroso, conoscere queste nozioni e sapersi destreggiare tra di esse, in seguito vedremo l'uso di risorse atte ad indagare attentamente su assembly e compagnia. A proposito di applicazioni costruite nell’ambito .Net non può essere taciuto il concetto di Application Domain. Alla base della loro definizione sta l’idea di isolamento, ovvero il fatto che ogni applicazione deve essere in grado di girare senza influenzare involontariamente in alcun modo le altre. Questo era già possibile in precedenza attraverso l’indipendenza dei processi ma a prezzo di un decadimento delle prestazioni in particolare in caso di necessità di comunicazione tra un processo e l’altro quando la totale impossibilità di condividere memoria (il sistema operativo divide rigorosamente i singoli processi) rendeva necessari complessi meccanismi di marshalling con tutte le conseguenze del caso. Usando il modello basato sugli application domain ogni processo può contenere (un po’ improprio ma rende l’idea) più applicazioni che quindi essendo nella stessa area di memoria possono più facilmente parlare tra di loro per quanto il CLR si occupi comunque di nascondere i dati di una applicazione rispetto alle altre. In questo senso il CLR sta alle applicazioni come il sistema operativo ai processi. Attaverso meccanismi specifici sarà ogni singola applicazione che metterà eventualmente a disposizione i dati da condividere. Quanto detto è solo un accenno ma questa modo di funzionamento reso possibile dal framework è assai importante, tecnologicamente molto comodo e costituisce un buon passo avanti rispetto alle tecnologie precedenti. Anche in questo caso è possibile lavorare manualmente con gli application domain, si tratta di un altro argomento avanzato di cui parleremo a tempo debito. Un’altra feature disponibile per tutti i linguaggi che usano come runtime il CLR di .Net è il cosiddetto Garbage Collector (GC) ovvero lo strumento dedicato a ripulire la memoria (in particolare quella zona identificata come heap, come vedremo) da quegli oggetti non più in uso. La presenza di questo strumento viene incontro alla necessità di rilasciare la memoria non più utilizzata in modo da prevenire i memory leaks e aiutare a prevenire situazioni di errore. Molti programmatori “giovani” di oggi probabilmente non avranno il polso del problema in quanto una gestione automatizzata della memoria è comune a quasi tutti i linguaggi che usiamo. In passato la questione era però spinosa e veniva affrontata principalmente con due tecniche. In C++, per esempio, questo lavoro era svolto manualmente dal programmatore il che è certamente fattibile è può essere reso molto efficiente ma è un approccio estremamente complesso ed esposto a bug insidiosi, nonostante le migliorie nel supporto ai programmatori dei vari strumenti di sviluppo. L’approccio COM invece prevedeva la presenza di una sorta di “contatore di riferimenti” ovvero, grossolanamente, ogni oggetto manteneva una lista di contatti da parte dei vari client autodistruggendosi quando questo valore era pari a zero. Questo è senza dubbio interessante ma prevede una precisa cooperazione tra le varie entità che non è così scontata e banale. Ecco quindi che la risposta in .Net come in Java e molti altri è l’introduzione di una modalità gestita automaticamente dal runtime. Il garbage collector di .Net funziona in modo eccellente nel CLR anche perchè l'Intermediate Language ne favorisce l'utilizzo. Non si tratta, di un pensata esclusiva di casa Microsoft come detto, l’idea è anzi piuttosto datata, ma sicuramente l'interpretazione che ne è offerta è di qualità. Ovviamente questo strumento viene utilizzato se si lavora con codice managed, diversamente ad esempio cominciando ad infarcire il codice di puntatori “vecchio stile” questo ed altri vantaggi vengono persi. Spendiamo solo un attimo relativamente al discorso delle prestazioni della macchina virtuale che sono di ottimo livello e che possono essere ulteriormente perfezionate attraverso i numerosi strumenti di profiling che aiutano ad individuare le inefficienze del proprio codice. Sono stati fatti vari raffronti con la JVM, Java Virtual Machine che è considerata la piattaforma più direttamente rivale di .Net. Sinceramente non mi sento di esprimere sentenze, anche considerando che le mie esperienze con Java sono troppo datate per essere significative in questo senso, lasciando l’approfondimento alla consultazione dell'ampio materiale disponibile in rete a cui rimando anche per analisi prestazionali relative a .Net aggiornate ed approfondite, suggerendo di allargare quanto più possibile la ricerca senza fermarsi ai primi esiti, non tutti i siti sono “sinceri”. Come abbiamo accennato in precedenza alcuni linguaggi come Ruby e Python sono entrati a far parte del mondo .Net tramite le loro incarnazioni IronRuby e IronPython. Si tratta di linguaggi dinamici, profondamente diversi in questo senso da C# che, come vedremo ha una tipizzazione statica. A supporto dei linguaggi dinamici il nostro framework è stato dotato del DLR, ovvero Dynamic Language Runtime che assolve in realtà un duplice compito: da un lato consente l’interfacciamento del CLR con i linguaggi citati e altri (siano noti come LISP e SmallTalk o meno come Groovy o l’ottimo Cobra), dall’altro, come vedremo, permette di inserire caratteristiche dinamiche in linguaggi per natura statici, proprio come C#. La definizione testuale di Microsoft per questo progetto (che è Open Source) è la seguente: “The Dynamic Language Runtime (DLR) is a set of libraries built on the Common Language Runtime to support dynamic language implementations on .NET.” Chiaro e semplice ma anche pofondo: non si tratta di supporto puro e semplice ai linguaggi dinamici ma alle implementazioni di li linguaggi dinamici, ciò che rende possibili le estensioni di C#. Il DLR, da un punto di vista architetturale, si pone come strato intermedio tra i linguaggi dinamici ed il CLR; attraverso questo strato devono passare i linguaggi dinamici mentre ad esso possono accedere quelli statici. Questa è una novità che troveremo ed approfondiremo parlando delle nuove caratteristiche di C# 4.0 e oltre. Ulteriore conseguenza è un elevato grado di interoperabilità tra linguaggi aventi natura diversa il che è positivo anche in considerazione del buon successo che stanno riscuotendo alcuni dei citati linguaggi di scripting. Alcuni puristi non vedono di buon occhio le nuove caratteristiche dinamiche di un linguaggio originariamente statico come C#. Sinceramente, per quanto mi riguarda, li lascio volentieri alle loro elucubrazioni, degnissime, per carità e abbraccio ancor più volentieri le nuove interessanti caratteristiche che questo nuovo approccio consente. Terminato il discorso, certamente solo introduttivo, intorno al funzionamento di .Net parliamo ora, anche in questo caso sommariamente, degli aspetti applicativi. Certamente l’idea orginale del progetto guardava al mondo desktop su piattaforma Windows (naturalmente!) e al mondo Web come target principali. Tuttavia questa piattaforma è stata implementata per non tralasciare nulla che possa aiutare il lavoro dei programmatori. Si è sviluppato non solo un enorme ecosistema ma la stessa Microsoft ha sviluppato dei framework a corredo che sono nati per coordinarsi perfettamente con .Net. Ecco quindi che se, per esempio, vi chiedete se esistono delle features verso il mondo mobile la risposta non può che essere affermativa. La cosa è ovvia se si pensa che Microsoft rilascia un sistema operativo espressamente dedicato ai cellulare ed agli smartphone, il controverso, almeno inizialmente, Windows Mobile. Ecco allora che possiamo parlare di .Net Mobile (qualche volta chiamato, a mio avviso un po’ impropriamente, Microsoft Mobile Internet Toolkit) un ampio set di strumenti e controlli specificamente pensati per il promettente e dinamico mondo mobile. Si tratta di un aiuto importante per chi sviluppa viste le problematiche peculiari e critiche che si incontrano, le più banali e macroscopiche sono la ridotta area di visualizzazione e le criticità di interfacciamento con l’ambiente operativo. Avremo modo di parlare in via superficiale di questo ramo di .Net che mantiene gran parte delle maggiori caratteristiche citate in precedenza. E non finisce qui. Nell’ambito del framework è disponibile un namespace che contiene una serie di classi atte a favorire lo sviluppo di animazioni e giochi. L’acronimo con cui è noto questo ambito e i tools di sviluppo associati è XNA (strana sigla: 'XNA's Not an Acronym' però è orecchiabile ) per quanto il suo futuro sia molto incerto, ed esiste anche un IDE dedicato. Per quanto lo sviluppo di giochi non sia evidentemente una cosa banale siamo di fronte ad un altro ramo molto interessante e che, se non altro, promette di facilitare alcune cose, restando nell’ambito Microsoft e, in particolare, se ci si basa su DirectX. E se volete andare oltre, sia pure allargando un po’ l’ambito anche al di fuori di .Net, indagate su cosa sia Microsoft Robotics. Ovviamente non si può sapere e conoscere bene tutto ma occorre concentrarci su ciò che ci serve. Sappiamo però che con .Net saremo più o meno sempre a casa nostra. Molto importante è ricordare che la versione 4 del framework ci porta direttamente nel mondo della elaborazione parallela per poter sfruttare adeguatamente le CPU multicore ormai diffuse nel campo dei dispositivi informatici. Si tratta di un argomento piuttosto avanzato che affronteremo nella parte finale del nostro percorso in maniera completa e che costituisce la vera grande frontiera attuale nell’ambito della programmazione, aprendo veramente grandi prospettive che verranno ulteriormente sviluppate nelle prossime versione del framework. Il futuro si preannuncia davvero molto interessante e ricco ma l’importante per ora è apprezzare il notevole aiuto che il framework ci può fornire già da adesso per affrontare questa nuova avvincente sfida (almeno se ci si riferisce al “largo consumo”) con la quale ci confronteremo presto nell’immediato futuro. Quindi, dopo aver appreso per bene le basi del linguaggio affronteremo le criticità dello sviluppo multi threading e multi tasking. Infine giova ricordare, per chi non ama Windows o ha comunque orizzonti più ampi, che esiste il progetto Mono, porting nel mondo Linux e Mac del framework. Tramite questo è possibile scrivere programmi ad esempio in C# per il sistema operativo caro a Bill Gates e vederlo comunque girare, teoricamente senza modifiche, anche sulla vostra distribuzione Linux preferita o su Mac. La cosa non è così semplice in realtà e va inoltre considerato che, per forza di cose, Mono è sempre un po' indietro rispetto al progetto ufficiale ed ha un supporto non alla pari di quanto troviamo per Windows. Tuttavia è pur sempre un interessante e ben avviato tentativo di allargare il bacino di utenza di .Net e va seguito con curiosità e attenzione specialmente se volete che le vostre applicazioni abbiano come target più di un sistema operativo. Vedremo, molto più avanti in apposita sezione, qualche suggerimento che dovrebbe facilitare la scrittura di applicazioni cross-OS. In conclusione quali sono schematicamente i punti di forza di .Net? In giro sulla rete e nei vari testi ne troverete parecchie di tabelle e pareri personali che chiariscono questo aspetto; da parte mia sono concorde su questi punti che ho personalmente apprezzato: • facile (relativamente, si capisce) ed uniforme sviluppo verso
l'ambito desktop e quello Web con possibili
espansioni anche in altri ambiti (quello ludico ad esempio e ancora
di più quello mobile).
|