Il 0% ha trovato utile questo documento (0 voti)
3K visualizzazioni519 pagine

Guida Visual Basic

Caricato da

bianchetto
Copyright
© Attribution Non-Commercial (BY-NC)
Per noi i diritti sui contenuti sono una cosa seria. Se sospetti che questo contenuto sia tuo, rivendicalo qui.
Formati disponibili
Scarica in formato PDF, TXT o leggi online su Scribd
Il 0% ha trovato utile questo documento (0 voti)
3K visualizzazioni519 pagine

Guida Visual Basic

Caricato da

bianchetto
Copyright
© Attribution Non-Commercial (BY-NC)
Per noi i diritti sui contenuti sono una cosa seria. Se sospetti che questo contenuto sia tuo, rivendicalo qui.
Formati disponibili
Scarica in formato PDF, TXT o leggi online su Scribd

A1.

Introduzione
Benvenuti, aspir anti pr ogr ammator i! In questa guida dalla lunghezza chiolemtr ica impar er ete cosa significa e cosa compor ta pr ogr ammar e, e tutti i tr ucchi e gli espedienti per costr uir e solide e sicur e applicazioni.

Una veloc e panoramic a sulla programmazione


La pr ogr ammazione quella disciplina dell'infor matica che si occupa di idear e, costr uir e e mantener e il softw ar e. Queste sono le tr e pr incipali divisioni che si possono oper ar e all'inter no di questa speciale e affascinante br anca dell'ingegner ia. Infatti, un buon pr ogr ammator e deve pr ima di tutto analizzar e il pr oblema, quindi pensar e a una possibile soluzione, se esiste, e costr uir e mentalmente un'ipotetica str uttur a del softw ar e che dovr impegnar si a scr iver e: questa par te della pr ogettazione si chiama analisi. Successivamente, si viene alla fase pi tecnica, e che implica una conoscenza dir etta del linguaggio di pr ogr ammazione usato: in questa guida, mi occuper di descr iver e il Visual Basic .NET. Una volta sviluppato il pr ogr amma, lo si deve testar e per tr ovar e eventuali malfunzionamenti (bugs) - che, per inciso, si manifestano solo quando non dovr ebber o - e, come ultima oper azione, bisogna attuar e una manutenzione per iodica dello stesso, od or ganizzar e un efficiente sistema di aggior namento. Inutile dir e che l'ultima fase necessar ia solo nel caso di gr andi applicazioni commer ciali e non cer tamente nel contesto di piccoli pr ogr ammi amator iali. Pr ima di iniziar e, una br eve sintesi di alcuni dettagli tecnici: i ter mini da conoscer e, e gli ambienti di sviluppo da usar e.

Alc uni termini da c onosc ere


Co dice so r g ente o so r g ente: l'insieme di tutte le istr uzioni che il pr ogr ammator e scr ive e fa eseguir e al pr ogr amma. Il file testuale che contiene tali istr uzioni viene esso stesso chiamato sor gente Co m pilato r e: il softw ar e utilizzato per cr ear e il pr ogr amma finito (un eseguibile *.ex e) a par tir e dal solo codice sor gente Debug g er : il softw ar e usato per l'analisi e la r isoluzione degli er r or i (bugs) all'inter no di un pr ogr amma; Par o le r iser v ate o k eyw o r ds: di solito vengono evidenziate dai compilator i in un color e diver so e sono par ole pr edefinite intr inseche del linguaggio, che ser vono per scopi ben pr ecisi.

Ambiente di sviluppo
L'ambiente di sviluppo che pr ender come r ifer imento per questa guida Visual Basic Ex pr ess 2008 (scar icabile dal Sito Ufficiale della M icr o so ft; se si ha un pr ofilo Passpor t.NET possibile r egistr ar e il pr odotto e ottener e una ver sione completa). Potete comunque scar icar e Shar pDevelop da qui (vedi sezione dow nloads), un pr ogr amma gr atis e molto buono (tr over ete una r ecensione nella sezione Sofw tar e di Pier oTofy.it r edatta da me e HeDo qui). Dato che le ver sioni pr ecedenti della guida, dalle quali r ipr esa la maggior anza dei sor genti pr oposti, sono state r edatte pr endendo come esempio Visual Basic Ex pr ess 2005, potete scar icar e anche quello da qui.

A2. Classi, Moduli e Namespace


Objec t Oriented Programming
I linguaggi .NET sono orien tati agli oggetti e cos lo anche VB.NET. Questo appr occio alla pr ogr ammazione ha avuto molto successo negli ultimi anni e si basa fondamentalmente sui concetti di astr azione, oggetto e inter azione fr a oggetti. A lor o volta, questi ultimi costituiscono un potente str umento per la modellizzazione e un nuovo modo di avvicinar si alla r isoluzione dei pr oblemi. La par ticolar e mentalit che questa linea di sviluppo adotta favor evole alla r appr esentazione dei dati in modo ger ar chico, e per questo motivo il suo par adig m a di pr ogr ammazione - ossia l'insieme degli str umenti concettuali messi a disposizione dal linguaggio e il modo in cui il pr ogr ammator e concepisce l'applicativo - definito da tr e concetti car dine: l'er editar iet, il po lim o r fism o e l'incapsulam ento . Molto pr esto ar r iver emo ad osser var e nel par ticolar e le car atter istiche di ognuno di essi, ma pr ima vediamo di iniziar e con l'intr odur r e l'entit fondamentale che si pone alla base di tutti questi str umenti: la classe.

Le Classi
Come dicevo, una car atter istica par ticolar e di questa categor ia di linguaggi che essi sono basati su un unico impor tantissimo concetto fondamentale: gli o g g etti, i quali vengono r appr esentati da classi. Una classe non altr o che la r appr esentazio ne - o v v iam ente astr atta - di qualco sa di co ncr eto , mentr e l'oggetto sar una concr etizzazione di questa r appr esentazione (per una discussione pi appr ofondita sulla differ enza tr a classe e oggetto, veder e capitolo A7). Ad esempio, in un pr ogr amma che deve gestir e una videoteca, ogni videocassetta o DVD r appr esentato da una classe; in un pr ogr amma per la fattur azione dei clienti, ogni cliente e ogni fattur a vengono r appr esentati da una classe. Insomma, ogni cosa, ogni entit, ogni r elazione - per fino ogni er r or e - tr ova la sua r appr esentazione in una classe. Detto questo, viene spontaneo pensar e che, se ogni cosa astr atta da una classe, questa classe dovr anche contener e dei dati su quella cosa. Ad esempio, la classe Utente dovr contener e infor mazioni sul nome dell'utente, sulla sua passw or d, sulla sua data di nascita e su molto altr o su cui si pu sor volar e. Si dice che tutte queste infor mazioni sono espo ste dalla classe: ognuna di esse, inoltr e, r appr esentata da quello che viene chiamato m em br o. I membr i di una classe sono tutti quei dati e quelle funzionalit che essa espone. Per esser e usabile, per , una classe deve venir e pr ima dichiar ata, mediante un pr eciso codice. L'atto di dichiar ar e una qualsiasi entit le per mette di iniziar e ad "esister e": il pr ogr ammator e deve infatti ser vir si di qualcosa che gi stato definito da qualche par te, e senza di quello non pu costr uir e niente. Con la par ola "entit" mi r ifer isco a qualsiasi cosa si possa usar e in pr ogr ammazione: dato che le vostr e conoscenze sono limitate, non posso che usar e dei ter mini gener ici e piuttosto vaghi, ma in br eve il mio lessico si far pi pr eciso. Nella pr atica, una classe si dichiar a cos: 1. Class [NomeClasse] 2. ... 3. End Class dove [NomeClasse] un qualsiasi nome che potete decider e ar bitr ar iamente, a seconda di cosa debba esser e r appr esentato. Tutto il codice compr eso tr a le par ole sopr a citate inter no alla classe e si chiama co r po ; tutte le entit esistenti nel cor po sono dei membr i. Ad esempio, se si volesse idealizzar e a livello di codice un tr iangolo, si scr iver ebbe questo: 1. Class Triangolo 2. ... 3. End Class Nel cor po di Tr iangolo si potr anno poi definir e tutte le infor mazioni che gli si possono attr ibuir e, come la lunghezza

dei lati, la tipologia, l'ampiezza degli angoli, ecceter a...

I Moduli
Nonostante il nome, i moduli non sono niente altr o che dei tipi speciali di classi. La differ enza sostanziale tr a i due ter mini ver r chiar ita molto pi avanti nella guida, poich le vostr e attuali competenze non sono sufficienti a un completo appr endimento. Tuttavia, i moduli sar anno la tipologia di classe pi usata in tutta la sezione A.

I Namespac e
Possiamo definir e classi e moduli come un it fun zion ali: essi r appr esentano qualcosa, possono esser e usate, manipolate, istanziate, dichiar ate, ecceter a... Sono quindi str umenti attivi di pr ogr ammazione, che ser vono a r ealizzar e concr etamente azioni e a pr odur r e r isultati. I namespace, invece, appar tengono a tutt'altr o gener e di categor ia: essi sono solo dei r aggr uppamenti "passivi" di classi o di moduli. Possiamo pensar e a un namespace come ad una car tella, entr o la quale possono star e files, ma anche altr e car telle, ognuna delle quali r aggr uppa un par ticolar e tipo di infor mazione. Ad esempio, volendo scr iver e un pr ogr amma che aiuti nel calcolo geometr ico di alcune figur e, si potr ebbe usar e un codice str uttur ate come segue: 01. 02. 03. 04. 05. 06. 07. 08. 09. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. Namespace Triangoli Class Scaleno '... End Class Class Isoscele '... End Class Class Equilatero '... End Class End Namespace Namespace Quadrilateri Namespace Parallelogrammi Class Parallelogramma '... End Class Namespace Rombi Class Rombo '... End Class Class Quadrato '... End Class End Namespace End Namespace End Namespace

Come si vede, tutte le classi che r appr esentano tipologie di tr iangoli (Scaleno, Isoscele, Equilater o) sono all'inter no del namespace Tr iangoli; allo stesso modo esiste anche il namespace Quadr ilater i, che contiene al suo inter no un altr o namespace Par allelogr ammi, poich tutti i par allelogr ammi sono quadr ilater i, per definizione. In quest'ultimo esiste la classe Par allelogr amma che r appr esenta una gener ica figur a di questo tipo, ma esiste ancor a un altr o namespace Rombi: come noto, infatti, tutti i r ombi sono anche par allelogr ammi. Dall'esempio si osser va che i namespace categor izzano le unit funzionali, dividendole in insiemi di per tinenza. Quando un namespace si tr ova all'inter no di un altr o namespace, lo si definisce nidificato: in questo caso, Par alleloogr ammi e Rombi sono namespace nidificati. Altr a cosa: al contr ar io della classi, gli spazi di nomi (italianizzazione dell'inglese name-space) non possiedono un "cor po", poich questo ter mine si pu usar e solo quando si par la di qualcosa di attivo;

per lo stesso motivo, non si pu neanche par lar e di membr i di un namespace.

A3. Panoramica sul Framework .NET


Come ho spiegato nel pr ecedente capitolo, il concetto pi impor tante della pr ogr ammazione ad oggetti la classe. Quindi, per scr iver e i nostr i pr ogr ammi, utilizzer emo sempr e, bene o male, queste entit. Ma non possibile pensar e che si debba scr iver e tutto da zer o: per i pr ogr ammator i .NET, esiste un vastissimo inventar io di classi gi pr onte, r aggr uppate sotto una tr entina di namespace fondamentali. L'insieme di tutti questi str umenti di pr ogr ammazione il Fr am ew o r k .NET, l'ossatur a pr incipale su cui si r eggono tutti i linguaggi basati sulla tecnologia .NET (di cui Vb.NET solo un esponente, accanto al pi usato C# e agli altr i meno noti, come J#, F#, Delphi per .NET, ecceter a...). Sar ebbe tuttavia r iduttivo descr iver e tale piattafor ma come un semplice agglomer ato di libr er ie (vedi oltr e), quando essa contempla meccanismi assai pi complessi, che sovr intendono alla gener ale esecuzione di tutte le applicazioni .NET. L'inter a str uttur a del Fr amew or k si pr esente come str atificata in diver si livelli:

1. Sistema operativo
Il Fr amew or k .NET pr esenta una str uttur a str atificata, alla base della quale r isiede il sistema oper ativo, Window s. Pi pr ecisamente, si consider a il sistema oper ativo e l'API (Application Pr ogr amming Inter face) di Window s, che espone tutti i metodi r esi disponibili al pr ogr ammator e per svolger e un dato compito.

2. Common Language Runtime


Un gr adino pi in su c' il Common Language Runtime (CLR), r esponsabile dei ser vizi basilar i del Fr amew or k, quali la gestione della memor ia e la sua liber azione tr amite il meccanismo di Gar bage Collection (vedi capitolo r elativo), la gestione str uttur ata delle eccezioni (er r or i) e il multithr eading. Nessuna applicazione inter agisce mai dir ettamente con il CLR, ma tutte sono allo stesso modo contr ollate da esso, come se fosse il lor o super visor e. Pr opr io per questo si definisce il codice .NET M an aged o Safe ("Gestito" o "Sicur o"), poich questo str ato del Fr amew or k gar antisce che non vengano mai eseguite istr uzioni dannose che possano mandar e in cr ash il pr ogr amma o il sistema oper ativo stesso. Al contr ar io, il codice Unmanaged o Unsafe pu eseguir e oper azioni r ischiose per il computer : sor genti pr odotti in Vb6 o C++ possono pr odur r e tale tipo di codice.

3. Base Class Library


Lo str ato successivo denominato Base Class Libr ar y (BCL): questa par te contiene tutti i tipi e le classi disponibili nel Fr amew or k (il che cor r isponde in numer o a diver se migliaia di elementi), r aggr uppati in una tr entina di file pr incipali (assembly). In questi ultimi compr esa la definizione della classe System.Object, dalla quale der iva pr essoch ogni altr a classe. I dati contenuti nella BCL per mettono di svolger e ogni oper azione possibile sulla macchina.

4. X ML
Successivamente tr oviamo i dati, le r isor se. Per salvar e i dati viene usato quasi sempr e il for mato XM L (eXtensible Mar kup Language), che utilizza dei tag spesso nidificati per contener e i campi necessar i. La str uttur a di questo tipo di file, inoltr e, adatta alla r appr esentazione ger ar chica, un metodo che nell'ambiente .net impor tantissimo. I file di configur azione e quelli delle opzioni impostate dell'utente, ad esempio, vengono salvati in for mato XML. Anche la nuova tecnologia denominata Window s Pr esentation Foundation (WPF), intr odotta nella ver sione 3.5 del Fr amew or k, che per mette di cr ear e contr olli dalla gr afica accattivante e str avagante, si basa su un linguaggio di contr assegno (di mar kup) sur r ogato dell'XML.

5. W indow s Forms e ASP.NET


Al livello super ior e tr oviamo ASP.NET e Window s For ms, ossia le inter facce gr afiche che r icopr ono il codice dell'applicazione ver a e pr opr ia. La pr ima una tecnologia pensata per lo sviluppo sul Web, mentr e la seconda for nisce sostanzialmente la possibilit di cr ear e una inter faccia gr afica (Gr aphical User Inter face, GUI) in tutto e per tutto uguale a quella classica, a finestr e, dei sistemi oper ativi Window s. La costr uzione di una Window s For m (ossia una singola finestr a) semplice e avviene come nel Vb classico, e chi sta leggendo questa guida per passar e dal VB6 al VB.NET lo sapr bene: si pr endono uno o pi contr olli e li si tr ascinano sulla super ficie della finestr a, dopodich si scr ive il codice associato ad ognuno dei lor o eventi.

6. Common Language Spec ific ations


Il penultimo stadio della str atificazione del Fr amew or k coincide con le Common Language Specifications (CLS), ossia un insieme di specifiche che definiscono i r equisiti minimi r ichiesti a un linguaggio di pr ogr ammazione per esser e qualificato come .NET. Un esempio di tali dir ettive: il linguaggio deve saper e gestir e tipi base come str inghe e numer i inter i, vettor i e collezioni a base zer o e deve saper pr ocessar e un'eccezione scatenata dal Fr amew or k.

7. Linguaggi .NET
In cima alla str uttur a ci sono tutti i linguaggi .net: Vb, C#, J#, ecceter a.

V ersioni del Framew ork


Con il passar e degli anni, a par tir e dal 2002, Micr osoft ha r ilasciato ver sioni successive del Fr amew or k .NET e ognuna di queste r elease ha intr odotto nuovi concetti di pr ogr ammazione e nuove possibilit per lo sviluppator e. Par allelamente all'uscita di queste nuove ver sioni, sono state cr eate anche edizioni successive del linguaggio VB.NET, ognuna delle quali stata natur almente accostata alla ver sione del Fr amew or k su cui si r eggeva. Ecco una r apida panor amica dell'evoluzione del linguaggio: VB2002: si basa sulla ver sione 1.0 del Fr amew or k VB2003: si basa sulla ver sione 1.1 del Fr amew or k VB2005: si basa sulla ver sione 2.0 del Fr amew or k. Questa la ver sione maggior mente utilizzata in questa guida, sebbene cer ti capitoli si concentr er anno sull'intr oduzione di alcuni nuovi aspetti por tati da VB2008 VB2008: si basa sulla ver sione 3.5 del Fr amew or k. La ver sione 3.0 si fondava ancor a sulla 2.0 del CLR e per ci le modifiche consistevano sostanzialmente nell'aggiunta di alcuni componenti e nell'appor to di diver se miglior ie e cor r ezioni VB2010: si basa sulla ver sione 4.0 del Fr amew or k

A4. Utilizzo base dell'IDE


IDE? Me lo sono dimentic ato a c asa...
Non vi pr eoccupate: se avete seguito tutti i capitoli fino a questo punto, siete gi un possesso di un IDE: Visual Basic 2005 (o 2008) Ex pr ess. L'acr onimo IDE significa Integr ated Development Envir onment ("ambiente di sviluppo integr ato") ed indica un softw ar e che aiuta il pr ogr ammator e nella stesur a del codice. Il softw ar e che vi ho consigliato for nisce, sebbene sia la ver sione fr ee, un numer o molto alto di str umenti e tools. In pr imis, contiene, ovviamente, un editor di codice sor gente, pr ogettato in modo da evidenziar e in modo differ ente le keyw or ds e da suppor tar e molte funzioni di r icer ca e r aggr uppamento che vedr emo in seguito. Accanto a questo, i pr incipali componenti che non possono mancar e in un IDE sono il compilator e ed il debugger , di cui ho dato una veloce definizione nel capitolo intr oduttivo. Il pr imo ha lo scopo di legger e il sor gente scr itto dal pr ogr ammator e e pr odur r e da questo un eseguibile: i passi che vengono por tati a ter mine dur ante un pr ocesso di compilazione sono in r ealt pi di uno (di solito compilazion e e lin kin g), ma molto spesso si semplifica il tutto par lando semplicemente di compilazione. Il secondo, invece, il pr ogr amma che vi dar pi filo da tor cer e, anche se in r ealt sar il vostr o miglior e aiutante (diciamo che vi sfinir a fin di bene): il debugger ha la funzione di analizzar e e segnalar e i bugs (bachi, er r or i) che si ver ificano dur ante l'esecuzione; assieme ad un r appor to dettagliato del tipo di er r or e ver ificatosi, segnala par allelamente anche il punto del codice che ha dato pr oblemi, in modo da r ender e molto pi semplice individuar e e cor r egger e la falla.

Funzionamento del c ompilatore .NET


Il compilator e , come gi detto, quel softw ar e necessar io a "tr asfor mar e" il codice sor gente scr itto in un deter minato linguaggio in un pr ogr amma eseguibile. Nor malmente, un compilator e pr odur r ebbe un applicativo tr aducendo le istr uzioni testuali intr odotte dal pr ogr ammator e in linguaggio macchina, ossia una ser ie di bit univocamente inter pr etabile dal pr ocessor e. I compilator i .NET, invece, hanno un compor tamento differ ente, in quanto il lor o output non un "nor male pr ogr amma" scr itto in linguaggio macchina, ma si tr atta di una ser ie di istr uzioni codificate in un altr o linguaggio speciale, chiamato IL (Inter mediate Language). Come sugger isce il nome, esso si tr ova ad un livello inter medio tr a la macchina e l'astr azione: super ior e r ispetto al pur o codice binar io, ma allo stesso tempo un gr adino pi sotto r ispetto ai linguaggi .NET. Venendo a conoscenza di queste infor mazioni, dovr ebbe sor ger e spontaneamente una domanda: come fa allor a un pr ogr amma .NET ad esser e eseguito? La r isposta semplice: lo stesso Fr amew or k che si occupa di inter pr etar ne le istr uzioni e di eseguir le, sempr e sotto la super visione del CLR. Per questo motivo, si hanno tr e impor tanti conseguenze: Non possibile far cor r er e un'applicazione .NET su una macchina spr ovvista del Fr amew or k; Il codice .NET sempr e sicur o; Un pr ogr amma .NET sempr e disassemblabile: su questo punto mi soffer mer in seguito.

Creare una Console A pplic ation


Nei pr ossimi capitoli inizer ad intr odur r e la sintassi del linguaggio, ossia le r egole da r ispettar e quando si scr ive un codice. Per tutti gli esempi della sezione A, far uso di applicazioni conso le (avete pr esente la finestr ella con lo sfondo ner o?), che lavor ano in DOS. Per cr ear e una Applicazione Console bisogna selezionar e dal men File del compilator e, la voce New Pr oject, e quindi sceglier e il tipo di applicazione desider ata. Dopodich, il compilator e scr iver aumaticamente alcune r ighe di codice pr eimpostate, che possono esser e simili a queste: Module Module1

Sub Main() End Sub End Module

Nello scr eenshot pr oposto qui sopr a si possono veder e le tr e ar ee in cui solitamente divisa l'inter faccia del compilator e: non vi pr eoccupate se la vostr a appar e differ ente, poich, essendo modificabile a piacimento, la mia potr ebbe esser e diver sa dal layout pr eimpostato del compilator e. Per or a, le finestr e impor tanti sono due: quella del codice, dove andr emo a scr iver e le istr uzioni, e quella degli er r or i, dove potr ete tener e costantemente sott'occhio se avete commesso degli er r or i di sintassi. Nello scr eenshot la seconda di queste non visibile, ma la si pu por tar e in pr imo piano tenendo pr emuto Ctr l e digitando in successione "\" ed "E". Per quanto r iguar da il codice che appar e, ho gi specificato in pr ecedenza che i moduli sono dei tipi speciali di classe, e fin qui vi baster saper e questo. Quello che potr este non conoscer e la par te di sor gente in cui appaiono le par ole Sub ed End Sub: anche in questo caso, la tr attazione par ticolar e di queste keyw or ds sar r imandata pi in l. Per or a possiamo consider ar e la Sub Main() come il pr ogr amma inter o: ogni cosa che viene scr itta tr a "Sub Main()" ed "End Sub" ver r eseguita quando si pr emer il pulsante Star t (il tr iangolino ver de in alto sulla bar r a degli str umenti), o in alter nativa F5.

Compilazione del programma finito


Una volta finito di scr iver e il codice e di testar lo usando le funzioni dell'IDE (ivi compr esa l'esecuzione in modalit debug pr emendo F5), sar necessar io cr ear e il pr ogr amma finito. Quello che avete eseguito fin'or a non er a altr o che una ver sione pi lenta e meno ottimizzata del softw ar e scr itto, poich c'er a bisogno di contr ollar e tutti gli er r or i e i bugs, impiegando tempo e spazio per memor izzar e le infor mazioni r elative al debug, appunto. Per cr ear e l'applicazione r eale finita, necessar io compilar e il codice in modalit r elease. Apr ite la scheda delle pr opr iet di pr ogetto, dal men pr incipale Pr oject > [NomePr ogetto] Pr oper ties (l'ultima voce del sottomen); selezionate la scheda Compile e cambiate il campo Configur ation su Release, quindi pr emete Build > Build Pr oject (Build sempr e una voce del men pr incipale).

Tr over ete l'eseguibile compilato nella car tella Documenti\Visual Studio 2008\Pr ojects\[Nome pr ogetto]\bin\Release.

A5. Variabili e costanti


Le variabili
Una var iabile uno spazio di memor ia RAM (Random Access Memor y) in cui vengono allocati dei dati dal pr ogr amma, ed possibile modificar ne od ottener ne il valor e facendo r ifer imento ad un nome che si definisce ar bitr ar iamente. Questo nome si dice anche iden tificatore (o, pi r ar amente, mn emon ico), e pu esser e costituito da un qualunque insieme di car atter i alfanumer ici e under scor e: l'unica condizione da r ispettar e per cr ear e un nome valido che questo non pu iniziar e con un numer o. Per esempio "Pippo", "_Pluto", "Mar io78" o anche "_12345" sono identificator i validi, mentr e "0Luigi" non lo . Il pr incipale scopo di una var iabile contener e dati utili al pr ogr amma; tali dati possono r isieder e in memor ia per un tempo pi o meno lungo, a seconda di quando una var iabile viene cr eata o distr utta: ogni var iabile, comunque, cessa di esister e nel momento in cui il pr ogr amma viene chiuso. Essa, inoltr e, pu contener e una gr andissima var it di tipi di dato diver si: dai numer i alle str inghe (testo), dalle date ai valor i booleani, per allar gar si poi a tipi pi ampi, in gr ado di r appr esentar e un inter o file. Ma pr ima di ar r ivar e a spiegar e tutto questo, bisogna analizzar e in che modo si dichiar a una var iabile. La dichiar azione, tanto di una costante quanto di una classe, l'atto definitivo con cui si stabilisce l'esistenza di un'entit e la si r ende disponibile o accessibile alle altr i par ti del pr ogr amma. Ogni cosa, per esser e usata, deve pr ima esser e dichiar ata da qualche par te: questa oper azione equivale, ad esempio, a definir e un concetto in matematica: la definizione impor tantissima. Ecco un semplice esempio: 01. Module Module1 02. Sub Main() 03. Dim Ciao As Int16 04. Ciao = 78 05. Ciao = Ciao + 2 06. Console.WriteLine(Ciao) 07. Console.Readkey() 08. End Sub 09. End Module Facendo cor r er e il pr ogr amma avr emo una scher mata ner a su cui viene visualizzato il numer o 80. Per ch? Or a vediamo. Come avr ete notato, le var iabili si dichiar ano in un modo specifico, usando le keyw or ds Dim e A s: 1. Dim [nome] As [tipo] Dove [nome] l'identificator e con cui ci si r ifer isce ad una var iabile e [tipo] il tipo di dato contenuto nella var iabile. Esistono molteplici tipi di var iabile fr a cui possibile sceglier e. Ecco un elenco dei tipi base (che sono consider ati keyw or ds): Byte: inter o a 8 bit che pu assumer e valor i da 0 a 255; Char : valor e a 8 bit che pu assumer e i valor i di ogni car atter e della tastier a (compr esi quelli speciali); Int16 o Sho r t: inter o a 16 bit che pu assumer e valor i da -32768 a +32767; Int32 o Integ er : inter o a 32 bit da -2147483648 a +2147483647; Int64 o Lo ng : inter o a 64 bit da cir ca -922000000000000000 a +9220000000000000000; Sing le: decimale da cir ca -3,4e+38 a +3,4e+38, con un inter vallo minimo di cir ca 1,4e-45; Do uble: decimale da cir ca -1,79e+308 a +1,79e+308, con un inter vallo minimo di cir ca 4,9e-324; Bo o lean: dato a 4 bytes che pu assumer e due valor i, Tr ue (ver o) e False (falso). Nonostante la limitatezza del suo campo di azione, che concettualmente potr ebbe r estr inger si ad un solo bit, il tipo Boolean occupa 32bit di memor ia: sono quindi da evitar e gr andi quantit di questo tipo; Str ing : valor e di minimo 10 bytes, composto da una sequenza di car atter i. Se vogliamo, possiamo assimilar lo ad

un testo; Object: r appr esenta un qualsiasi tipo (ma non un tipo base). I tipi base vengono detti anche ato m ici o pr im itiv i, poich non possono esser e ulter ior mente scomposti. Esistono, quindi, anche tipi der iv ati, appar tenenti a svar iate tipologie che analizzer emo in seguito, fr a cui si annover ano anche le classi: ogni tipo der ivato scomponibile in un insieme di tipi base. Or a, quindi, possiamo estr apolar e delle infor mazioni in pi dal codice pr oposto: dato che segue la keyw or d Dim, "Ciao" l'identificator e di una var iabile di tipo Int16 (infatti dopo As stato specificato pr opr io Int16). Questo significa che "Ciao" pu contener e solo numer i inter i che, in valor e assoluto, non super ino 32767. Ovviamente, la scelta di un tipo di dato piuttosto che un altr o var ia in funzione del compito che si intende svolger e: maggior e la pr ecisione e l'or dine di gr andezza dei valor i coinvolti e maggior e sar anche l'uso di memor ia che si dovr sostener e. Continuando a legger e, si incontr a, nella r iga successiva, un'assegnazione, ossia una oper azione che pone nella var iabile un cer to valor e, in questo caso 78; l'assegnazione avviene mediante l'uso dell'oper ator e uguale "=". L'istr uzione successiva simile a questa, ma con una sostanziale differ enza: il valor e assegnato alla var iabile influenzato dalla var iabile stessa. Nell'esempio pr oposto, il codice: 1. Ciao = Ciao + 2 ha la funzione di incr ementar e di due unit il contenuto di Ciao. Questa istr uzione potr ebbe sembr ar e algebr icamente scor r etta, ma bisogna r icor dar e che si tr atta di un comando (e non di un'equazione): pr ima di scr iver e nella cella di memor ia associata alla var iabile il numer o che il pr ogr ammator e ha designato, il pr ogr amma r isolve l'espr essione a destr a dell'uguale sostituendo ad ogni var iabile il suo valor e, e ottenendo, quindi, 78 + 2 = 80. Le ultime due r ighe, invece, fanno visualizzar e a scher mo il contenuto di Ciao e fer mano il pr ogr amma, in attesa della pr essione di un pulsante. Come si visto dall'esempio pr ecedente, con le var iabili di tipo numer ico si possono eseguir e oper azioni ar itmetiche. Gli oper ator i messi a disposizione dal Fr amew or k sono: + : addizione; - : sottr azione; * : pr odotto; / : divisione; \ : divisione tr a inter i (r estituisce come r isultato un numer o inter o a pr escinder e dal tipo degli oper andi, che possono anche esser e decimali); Mod : r estituisce il r esto di una divisione inter a; = : assegna alla var iabile posta a sinistr a dell'uguale il valor e posto dopo l'uguale; & : concatena una str inga con un numer o o una str inga con un'altr a str inga. 01. Module Module1 02. Sub Main() 03. 'Interi 04. Dim Intero, Ese As Int16 05. 'Decimale 06. Dim Decimale As Single 07. 'Booleano 08. Dim Vero As Boolean 09. 'Stringa 10. Dim Frase As String 11. 12. Intero = 90 13. Ese = Intero * 2 / 68 14. Intero = Ese - Intero * Intero 15. Decimale = 90.76 16. Decimale = Ese / Intero 17. Vero = True 18. Frase = "Ciao." 19. 'L'operatore "+" tra stringhe concatena due stringhe. Dopo la 20.

'prossima istruzione, la variabile Frase conterr: 21. ' "Buon giornoCiao" 22. Frase = "Buon giorno" + "Ciao" 23. 'L'operatore "&" pu concatenare qualsiasi dato e 24. 'restituisce una stringa. Dopo la prossima istruzione, la 25. 'variabile Frase conterr: 26. ' "Il valore decimale : -0,0003705076" 27. Frase = "Il valore decimale : " & Decimale 28. End Sub 29. End Module Esistono poi degli speciali oper ator i di assegnamento, che velocizzano l'assegnazione di valor i, alcuni sono: 01. Module Module1 02. Sub Main() 03. Dim V, B As Int32 04. V += B 'Equivale a 05. 06. B -= V 'Equivale a 07. V *= B 'Equivale a 08. B /= V 'Equivale a 09. End Sub 10. End Module

V B V B

= = = =

V B V B

+ * /

B V B V

Le fr asi poste dopo un apice (') sono dette co m m enti e ser vono per spiegar e cosa viene scr itto nel codice. Il contenuto di un commento NON influisce in nessun modo su ci che scr itto nel sor gente, ma ha una funzione ESCLUSIVAMENTE esplicativa.

Le c ostanti
Abbiamo visto che il valor e delle var iabili pu esser e modificato a piacimento. Ebbene quello delle costanti, come il nome sugger isce, no. Esistono per semplificar e le oper azioni. Per esempio, invece di digitar e 3,1415926535897932 per il Pi g r eco , possibile dichiar ar e una costante di nome Pi che abbia quel valor e ed utilizzar la nelle espr essioni. La sintassi per dichiar ar e una costante la seguente: 1. Const [nome] As [tipo] = [valore] Ci sono due lampanti differ enze r ispetto al codice usato per dichiar ar e una var iabile. La pr ima , ovviamente, l'uso della keyw or d Con s t al posto di Dim; la seconda consiste nell'assegnazione posta subito dopo la dichiar azione. Infatti, una costante, per esser e tale, dev e contener e qualcosa: per questo motivo o bblig ato r io specificar e sempr e, dopo la dichiar azione, il valor e che la costante assumer . Questo valor e non potr mai esser e modificato. Esempio: 01. Module Module1 02. Sub Main() 03. Const Pi As Single = 3.1415926535897932 04. Dim Raggio, Area As Double 05. 06. 'Questa istruzione scrive sul monitor il messaggio posto tra 07. 'virgolette nelle parentesi 08. Console.WriteLine("Inserire il raggio di un cerchio:") 09. 10. 'Questa istruzione legg un valore immesso dalla tastiera e 11. 'lo deposita nella variabile Raggio 12. Raggio = Console.ReadLine 13. Area = Raggio * Raggio * Pi 14. 15. Console.WriteLine("L'Area : " & Area) 16. 17. 'Questa istruzione ferma il programma in attesa della pressione 18. 'di un pulsante 19. Console.ReadKey() 20. End Sub 21. End Module

N.B.: a causa della lor o stessa natur a, le costanti NON possono esser e inizializzate con un valor e che dipenda da una funzione. Scr ivo questo appunto per pur a utilit di consultazione: anche se or a potr non r isultar e chiar o, vi capiter pi avanti di imbatter vi in er r or i del gener e: 1. Const Sqrt2 As Single = Math.Sqrt(2) Sqr t2 dovr ebbe esser e una costante numer ica decimale che contiene la r adice quadr ata di due. Sebbene il codice sembr i cor r etto, il compilator e segnaler come er r or e l'espr essione Math.Sqr t(2), poich essa una funzione, mentr e dopo l'uguale r ichiesto un valor e sempr e costante. Il codice cor r etto 1. Const Sqrt2 As Single = 1.4142135

Le istruzioni
Tutti i comandi che abbiamo impar tito al computer e che abbiamo gener icamente chiamato con il nome di istr uzioni (come Console.Wr iteLine()) hanno dei nomi pi specifici: sono pr o cedur e o funzio ni, in sostanza sottopr ogr ammi gi scr itti. Pr ocedur e e funzioni possono esser e globalmente indicate con la par ola m eto do . I metodi accettano dei par am etr i passatigli tr a par entesi: se i par ametr i sono di pi di uno vengono separ ati da vir gole. I par ametr i ser vono per comunicar e al metodo i dati sui quali questo dovr lavor ar e. La differ enza tr a una pr ocedur a e una funzione r isiede nel fatto che la pr ima fa semplicemente eseguir e istr uzioni al computer , mentr e la seconda r estituise un valor e. Ad esempio: 01. Module Module1 02. Sub Main() 03. Dim F As Double 04. 05. 'Questa una funzione che restituisce la radice quadrata di 56 06. F = Math.Sqrt(56) 07. 08. 'Questa una procedura che scrive a video un messaggio 09. Console.WriteLine("La radice di 56 " & F) Console.ReadKey() 10. 11. End Sub 12. End Module Anche i metodi ver r anno tr attai successivamente in dettaglio.

A6. Tipi Reference e tipi Value


Tutti i tipi di var iabile che possono esser e cr eati si r aggr uppano sotto due gr andi categor ie: Refer ence e Value. I pr imi si compor tano come oggetti, mentr e i secondi r appr esentano tipi scalar i o numer ici, ma vediamo di metter e un po' or dine in tutti questi concetti. P.S.: per una miglior e compr esione di questo capitolo, consiglio solo a chi ha gi esper ienza nel campo della pr ogr ammazione (in qualsiasi altr o linguaggio) di legger e questo ar tico lo sull'utilizzo della memor ia da par te di un pr ogr amma.

Differenza tra Classi e Oggetti


All'inizio della guida mi sono soffer mato ad elogiar e le classi e la lor o enor me impor tanza nell'ambiente .NET. Successivamente ho fatto menzione al tipo System.Object e al fatto che ogni cosa sia un oggetto. La differ enza tr a o g g etto e classe di vitale impor tanza per capir e come vanno le cose nell'ambito della pr ogr ammazione OO. Una classe r appr esenta l'astr azione di qualcosa di concr eto; un oggetto, invece, qualcosa di concr eto e viene r appr esentato da una classe. Per far e un esempio banale, sappiamo benissimo che esiste il concetto di "uomo", ma ogni individuo sul pianeta, pur mantenendo alcune car atter istiche simili e costanti, differ ente r ispetto agli altr i. Facendo un par allelismo con la pr ogr ammazione, quindi, il singolo individuo, ad esempio io stesso, un oggetto, mentr e il gener ale concetto di "uomo" che ognuno di noi conosce la classe. Se qualcuno dei lettor i ha studiato filosofia, r iconoscer in questa differ enza la stessa che Platone identificava nella discr epanza tr a mondo sensibile e Iper ur anio. Avendo ben chiar i questi concetti, si pu or a intr odur r e un po' di ger go tecnico. Ogni oggetto anche detto istanza della classe che lo r appr esenta (voi siete istanze della classe Uomo XD) e is tan ziare un oggetto significa cr ear lo. 1. 'New serve per creare fisicamente degli oggetti in memoria 2. Dim O1 As New Object 3. Dim O2 As New Object O1 e O2 sono entr ambe istanze della classe Object, ma sono diver si fr a di lor o: in comune hanno solo l'appar tenenza allo stesso tipo. N.B.: come si notato, "tipo" e "classe" sono ter mini spesso equivalenti, ma non gener alizzate questa associazione.

Tipi Referenc e
Ogni cosa nel Fr amew or k un oggetto e la maggior par te di essi sono tipi r efer ence. Si dicono tipi r efer ence tutti quei tipi che der ivano dir ettamente dalla classe System.Object (la "der ivazione" appar tiene a un concetto che spiegher pi avanti): questa classe dichiar ata all'inter no di una libr er ia della Base Class Libr ar y, ossia l'ar chivio di classi del Fr amew or k. Nel capitolo pr ecedente si visto come sia possibile assegnar e un valor e ad una var iabile utilizzando l'oper ator e uguale "=". Con questo meccanismo, un deter minato valor e viene depositato nella casella di memor ia che la var iabile occupa. Ebbene, facendo uso dei tipi r efer ence, questo non avviene. Quando si utilizza l'uguale per assegnar e un valor e a tali var iabili, quello che effettivamente viene r iposto nella lor o par te di memor ia un puntator e inter o a 32bit (su sistemi oper ativi a 32bit). Per chi non lo sapesse, un puntator e una speciale var iabile che, invece di contener e un pr opr io valor e, contiene l'indir izzo di un'ar ea di memor ia contenente altr i dati. Il puntator e viene memor izzato come al solito sullo stack , mentr e il ver o oggetto viene cr eato e deposto in un'ar ea di memor ia differ ente, detta heap m anag ed, dove esiste sotto la super visione del CLR. Quando una var iabile di questo tipo viene impostata a Nothing (una costante che vedr emo tr a poco), la par te dell'heap managed che l'oggetto occupa viene r ilasciata dur ante il pr ocesso di g ar bag e co llectio n ("r accolta dei r ifiuti"). Tuttavia, ci non avviene subito, poich il meccanismo del Fr amew or k fa in modo di avviar e la gar bage collection solo quando necessar io, quindi quando la

memor ia comincia a scar seggiar e: supponendo che un pr ogr amma abbia r elativamente pochi oggetti, questi potr ebber o "viver e" indistur bati fino alla fine del pr ogr amma anche dopo esser e stati lo g icam ente distr utti, il che significa che stato eliminato manualmente qualsiasi r ifer imento ad essi (vedi par agr afo successivo). Data l'impossibilit di deter minar e a pr ior i quando un oggetto ver r distr utto, si ha un fenomeno che va sotto il nome di finalizzazio ne no n deter m inistica (il ter mine "finalizzazione" non casule: veder e il capitolo sui distr uttor i per maggior i infor mazioni).

Nothing
Nothing una costante di tipo r efer ence che r appr esenta l'assenza di un oggetto piuttosto che un oggetto nullo. Infatti, por r e una var iabile oggetto uguale a Nothing equivale a distr ugger la logicamente. 1. Dim O As New Object 'L'oggetto viene creato 2. O = Nothing 'L'oggetto viene logicamente distrutto La distr uzione logica non coincide con la distr uzione fisica dell'oggetto (ossia la sua r imzione dalla memor ia), poich, come detto pr ima, il pr ocesso di liber azione della memor ia viene avviato solo quando necessar io. Non possibile assegnar e Nothing a un tipo value, ma possibile usar e speciali tipi value che suppor tano tale valor e: per ulter ior i dettagli, veder e "Tipi Nullable".

Tipi V alue
Ogni tipo v alue der iva dalla classe System.ValueType, che der iva a sua volta da System.Object, ma ne r idefinisce i metodi. Ogni var iabile di questo tipo contiene effettivamente il pr opr io valor e e non un puntator e ad esso. Inoltr e, esse hanno dei vantaggi in ter mini di memor ia e velocit: occupano in gener e meno spazio; data la lor o posizione sullo stack non vi bisogno di r efer enziar e un puntator e per ottener e o impostar ne i valor i (r efer enziar e un puntator e significa r ecar si all'indir izzo di memor ia puntato e legger ne il contenuto); non c' necessit di occupar e spazio nello heap managed: se la var iabile viene distr utta, cessa di esister e all'istante e non si deve attuar e nessuna oper azione di r ilascio delle r isor se. Notar e che non possibile distr ugger e logicamente una var iabile value, fatta eccezione per cer ti tipi der ivati.

Is e =
Nel lavor ar e con tipi r efer ence e value bisogna pr estar e molta attenzione a quando si utilizzano gli oper ator i di assegnamento. Come gi detto, i r efer ence contengono un puntator e, per ci se si scr ive questo codice: 1. Dim O1, O2 As Object 2. '... 3. O1 = O2 quello che O2 conter r non sar un valor e identico a O1, ma un puntator e alla stessa ar ea di memor ia di O1. Questo pr ovoca un fatto str ano, poich sia O1 che O2 puntano alla stessa ar ea di memor ia: quindi O1 e O2 so no lo stesso o g g etto , soltanto r ifer ito con nomi difer si. In casi simili, si pu utilizzar e l'oper ator e Is per ver ificar e che due var iabili puntino allo stesso oggetto: 1. 'Scrive a schermo se vero oppure no che 2. 'O1 e O2 sono lo stesso oggetto 3. Console.WriteLine(O1 Is O2) La scr itta che appar ir sullo scher mo sar "Tr ue", ossia "Ver o". Utilizzar e Is per compar ar e un oggetto a Nothing equivale a ver ificar e che tale oggetto sia stato distr utto. Questo NON avviene per i tipi value: quando ad un tipo value si assegna un altr o valor e con l'oper ator e =, si passa

effettivamente una co pia del valor e. Non possibile utilizzar e Is con i tipi value poich Is definito solo per i r efer ence.

Boxing e Unboxing
Consider iamo il seguente codice: 1. Dim I As Int32 = 50 2. Dim O As Object 3. O = I I un tipo value, mentr e O un tipo r efer ence. Quello che succede dietr o le quinte semplice: il .NET cr ea un nuovo oggetto, per ci un tipo r efer ence, con il r ispettivo puntator e, e quindi gli assegna il valor e di I: quando il pr ocesso finito assegna il puntator e al nuovo oggetto a O. Questa conver sione spr eca tempo e spazio nello heap managed e viene definita come bo xing . L'oper azione inver sa l'unboxing e consiste nell'assegnar e un tipo r efer ence a un tipo value. Le oper azioni che si svolgono sono le stesse, ma al contr ar io: entr ambe spr ecano tempo e cpu, quindi sono da evitar e se non str ettamente necessar ie. Quando si pu sceglier e, quindi, sono meglio di tipi value.

Una pr ecisazione: in tutti i pr ossimi capitoli capiter fr equentemente che io dica cose del tipo "la var iabile X un oggetto di tipo Str ing" oppur e "le due var iabili sono lo stesso oggetto". Si tr atta solo di una via pi br eve per evitar e il for malismo tecnico, poich, se una var iabile dichiar ata di tipo r efer ence, essa pr opr iamente un riferimen to all'oggetto e non un oggetto. Gli oggetti "vivono" indistur bati nell'heap managed, quel magico posto che nessuno conosce: noi possiamo solo usar e r ifer imenti a tali oggetti, ossia possiamo solo indicar li ("eccolo l! guar da! l'hai visto? ma s, pr opr io l! non lo vedi?").

A7. Il costrutto If
Capita spessissimo di dover eseguir e un contr ollo per ver ificar e se vigono cer te condizioni. possibile attuar e tale oper azione tr amite un co str utto di co ntr o llo , la cui for ma pi comune e diffusa il costr utto If. Questo per mette di contr ollar e se una condizione ver a. Ad esempio: in un pr ogr amma che calcoli l'ar ea di un quadr ato si deve impor r e di visualizzar e un messaggio di er r or e nel caso l'utente immetta una misur a negativa, poich, come noto, non esistono lati la cui misur a un numer o negativo: 01. Module Module1 02. Sub Main() 03. Dim Lato As Single 04. Console.WriteLine("Inserire il lato di un quadrato:") 05. Lato = Console.ReadLine 06. 07. 08. If Lato < 0 Then 'Se Lato minore di 0... Console.WriteLine("Il lato non pu avere una misura negativa!") 09. Else 'Altrimenti, se non lo ... 10. Console.WriteLine("L'area del quadrato : " & Lato * Lato) 11. 12. End If 'Fine controllo 13. Console.ReadKey() 14. 15. End Sub 16. End Module Come sicur amente avr ete intuito, questo contr ollo si pu associar e al costr utto italiano "Se avviene qualcosa Allor a fai questo Altr imenti fai quell'altr o". Si pu eseguir e qualsiasi tipo di compar azione tr a If e Then utilizzando i seguenti oper ator i di confr onto: > : maggior e < : minor e = : uguaglianza <> : diver so >= : maggior e o uguale <= : minor e o uguale Is : identicit (solo per tipi r efer ence) IsNot : negazione di Is (solo per tipi r efer ence) Ma l'impor tante r icor dar si di attener si a questa sintassi: 1. If [Condizione] Then 2. [istruzioni] 3. Else 4. [istruzioni alternative] 5. End If

If nidific ati
Quando si tr ova un costr utto If all'inter no di un altr o costr utto If, si dice che si tr atta di un Co str utto If Nidificato . Questo avviene abbastanza spesso, specie se si ha bisogno di far e contr olli multipli: 01. Module Module1 02. Sub Main() 03. Dim Numero As Int16 04. 05.

Console.WriteLine("Inserisci un numero:") 06. Numero = Console.ReadLine 07. 08. If Numero > 0 Then 09. If Numero < 5 Then 10. Console.WriteLine("Hai indovnato il numero!") 11. End If 12. Else 13. Console.WriteLine("Numero errato!") 14. End If 15. 16. Console.ReadKey() 17. End Sub 18. End Module Se il numer o inser ito da tastier a compr eso fr a 0 e 5, estr emi esclusi, allor a l'utente ha indovinato il numer o, altr imenti no. Si pu tr ovar e un numer o illimitato di If nidificati, ma meglio limitar ne l'uso e, piuttosto, far e utilizzo di co nnettiv i lo g ici.

I c onnettivi logic i
I connettivi logici sono 4: And, Or , Xor e Not. Ser vono per costr uir e contr olli complessi. Di seguito un'illustr azione del lor o funzionamento: If A And B : la condizione r isulta ver ificata se sia A che B sono ver e co ntem po r aneam e nte If A Or B : la condizione r isulta ver ificata se ver a alm eno una delle due condizioni If A Xor B: la condizione r isulta ver a se una so la delle due condizioni ver a If Not A: la condizione r isulta ver ificata se falsa Un esempio pr atico: 01. Module Module1 02. Sub Main() Dim a, b As Double 03. 04. Console.WriteLine("Inserire i lati di un rettangolo:") 05. a = Console.ReadLine 06. 07. b = Console.ReadLine 08. 09. 'Se tutti e due i lati sono maggiori di 0 10. If a > 0 And b > 0 Then 11. Console.WriteLine("L'area : " & a * b) 12. Else Console.WriteLine("Non esistono lati con misure negative!") 13. 14. End If 15. Console.Readkey() 16. End Sub 17. End Module

Continuare il c ontrollo: ElseIf


Nei pr ecedenti esempi, la seconda par te del costr utto sempr e stata Els e, una par ola r iser vata che indica cosa far e se n on si ver ifica la condizione pr oposta dalla pr ima par te. Il suo valor e , quindi, di pur a alter nativa. Esiste, tuttavia, una var iante di Else che consente di continuar e con un altr o contr ollo senza dover r icor r er e ad If nidificati (a cui sempr e meglio supplir e con qualcosa di pi or dinato). Ammettiamo, ad esempio, di aver e un codice 'autocr itico' simile: 01. Module Module1 02. Sub Main() 03. Dim Voto As Single 04. 05. Console.WriteLine("Inserisci il tuo voto:") 06.

Voto = Console.ReadLine 07. 08. If Voto < 3 Then 09. Console.WriteLine("Sei senza speranze!") 10. Else 11. If Voto < 5 Then 12. Console.WriteLine("Ancora un piccolo sforzo...") 13. Else 14. If Voto < 7 Then 15. Console.WriteLine("Stai andando discretamente") 16. Else 17. If Voto < 9 Then 18. Console.WriteLine("Molto bene, continua cos") 19. Else 20. Console.WriteLine("Sei praticamente perfetto!") 21. End If 22. End If 23. End If 24. End If 25. 26. Console.ReadKey() 27. End Sub 28. End Module E' abbastanza disor dinato... La var iante ElseIf molto utile per miglior e la leggibilit del codice: 01. Module Module1 02. Sub Main() 03. Dim Voto As Single 04. Console.WriteLine("Inserisci il tuo voto:") 05. 06. Voto = Console.ReadLine 07. 08. If Voto < 3 Then 09. Console.WriteLine("Sei senza speranze!") 10. ElseIf Voto < 5 Then 11. Console.WriteLine("Ancora un piccolo sforzo...") 12. ElseIf Voto < 7 Then 13. Console.WriteLine("Stai andando discretamente") 14. ElseIf Voto < 9 Then 15. Console.WriteLine("Molto bene, continua cos") 16. Else 17. Console.WriteLine("Sei praticamente perfetto!") 18. End If 19. Console.ReadKey() 20. 21. End Sub 22. End Module Notate che tutti gli ElseIf fanno par te dello s tes s o costr utto: mentr e nell'esempio ogni If nidificato er a un blocco a s stante, dotato infatti di un pr opr io End If, in questo caso ogni alter nativa-selettiva fa comunque par te dell'unico If iniziale, pr otr atto solamente un poco pi a lungo.

Bloc c hi di istruzioni
Fino a questo punto, gli esempi pr oposti non hanno mai dichiar ato una var iabile dentr o un costr utto If, ma solo all'inizio del pr ogr amma, dopo Sub Main(). possibile dichiar ar e var iabili in altr i punti del codice che non siano all'inizio della Sub? Cer tamente s. A differ enza di altr i, i linguaggi .NET per mettono di dichiar ar e var iabili in qualunque punto del sor gente, dove occor r e, evitando un gigantesco agglomer ato di dichiar azioni iniziali, for temente disper sive per chi legge. Questo un gr ande vantaggio, ma bisogna far e attenzione ai blocchi di codice. Con questo ter mine ci si r ifer isce a par ti del sor gente compr ese tr a due par ole r iser vate, che in VB di solito sono accoppiate in questo modo: 1. [Keyword] 2. 'Blocco di codice 3. End [Keyword]

Ad esempio, tutto il codice compr eso tr a Sub ed End Sub costituisce un blocco, cos come lo costituisce quello compr eso tr a If ed End If (se non vi un Else), tr a If ed Else o addir ttur a tr a Module ed End Module. Facendo questa distinzione sar facile intuir e che una var iabile dichiar ata in un blocco no n v isibile al di fuor i di esso. Con questo voglio dir e che la sua dichiar azione vale solo all'inter no di quel blocco. Ecco una dimostr azione: 01. Module Module1 02. Sub Main() 03. 'a, b e c fanno parte del blocco delimitato da Sub ... 'End Sub 04. Dim a, b, c As Single 05. 06. 07. 'Semplice esempio di risoluzione di equazione di 08. 'secondo grado Console.WriteLine("Equazione: ax2 + bx + c = 0") 09. Console.WriteLine("Inserisci, in ordine, a, b e c:") 10. a = Console.ReadLine 11. b = Console.ReadLine 12. 13. c = Console.ReadLine 14. 15. If a = 0 Then 16. Console.WriteLine("L'equazione si abbassa di grado") 17. Console.ReadKey() 18. 'Con Exit Sub si esce dalla Sub, che in questo caso 'coincide con il programma. Equivale a terminare 19. 'il programma stesso 20. Exit Sub 21. End If 22. 23. 'Anche delta fa parte del blocco delimitato da Sub ... 24. 'End Sub 25. Dim delta As Single = b ^ 2 - 4 * a * c 26. 27. 'Esistono due soluzioni distinte 28. If delta > 0 Then 29. 'Queste variabili fanno parte del blocco di If ... 30. 'ElseIf 31. Dim x1, x2 As Single 32. ' possibile accedere senza problemi alla variabile 33. 'delta, poich questo blocco a sua volta 34. 'all'interno del blocco in cui dichiarato delta 35. x1 = (-b + Math.Sqrt(delta)) / (2 * a) 36. x2 = (-b - Math.Sqrt(delta)) / (2 * a) 37. Console.WriteLine("Soluzioni: ") 38. Console.WriteLine("x1 = " & x1) 39. Console.WriteLine("x2 = " & x2) 40. 41. 'Esiste una soluzione doppia 42. ElseIf delta = 0 Then 43. 'Questa variabile fa parte del blocco ElseIf ... Else 44. Dim x As Single 45. x = -b / (2 * a) 46. Console.WriteLine("Soluzione doppia: ") 47. Console.WriteLine("x = " & x) 48. 49. 'Non esistono soluzioni in R 50. Else 51. Console.WriteLine("Non esistono soluzioni in R") 52. End If 53. 54. Console.ReadKey() 55. End Sub 56. 57. End Module Se in questo codice, pr ima del Console.ReadKey(), finale pr ovassimo a usar e una fr a le var iabili x , x 1 o x 2, otter r emmo un er r or e:

Questo succede per ch nessuna var iabile dichiar ata all'inter no di un blocco accessibile al di fuor i di esso. Con questo schemino r udimentale sar pi facile capir e:

Le fr ecce ver di indicano che un codice pu acceder e a cer te var iabili, mentr e quelle r osse indicano che non vi pu acceder e. Come salta subito agli occhi, sono per messe tutte le r ichieste che vanno dall'inter no di un blocco ver so l'ester no, mentr e sono pr oibite tutte quelle che vanno dall'ester no ver so l'inter no. Questa r egola vale sempr e, in qualsiasi cir costanza e per qualsiasi tipo di blocco: non ci sono eccezioni.

A8. Il costrutto Select Case


Abbiamo visto nel capitolo pr ecedente come si possa far pr ocessar e al computer un contr ollo per ver ificar e cer te condizioni. Supponiamo, or a, di aver e 20 contr olli di uguaglianza del tipo: 01. 02. 03. 04. 05. 06. 07. 08. 09. 10. 11. '... If A = 1 Then 'istruzioni End If If A = 2 Then 'istruzioni End If If A = 3 Then 'istruzioni End If 'eccetera

In questo caso il costr utto If diventa non solo noioso, ma anche ingombr ante e disor dinato. Per eseguir e questo tipo di contr olli multipli esiste un costr utto apposito, Select Case, che ha questa sintassi: 01. '... 02. Select Case [Nome variabile da analizzare] Case [valore1] 03. 04. 'istruzioni Case [valore2] 05. 06. 'istruzioni 07. Case [valore3] 'istruzioni 08. 09. End Select Questo tipo di contr ollo r ende molto pi linear e, semplice e veloce il codice sor gente. Un esempio: 01. Module Module 1 02. Sub Main() 03. Dim a, b As Double 04. Dim C As Byte 05. 06. Console.WriteLine("Inserire due numeri: ") 07. a = Console.ReadLine 08. b = Console.ReadLine 09. Console.WriteLine("Inserire 1 per calcolare la somma, 2 per la differenza, 3 per il prodotto, 4 per il quoziente:") 10. C = Console.ReadLine 11. 12. Select Case C 13. Case 1 14. Console.WriteLine(a + b) 15. Case 2 16. Console.WriteLine(a - b) 17. Case 3 Console.WriteLine(a * b) 18. Case 4 19. 20. Console.WriteLine(a / b) End Select 21. 22. Console.ReadKey() 23. End Sub 24. 25. End Module Molto semplice, ma anche molto efficace, specialmente utile nei pr ogr ammi in cui bisogna consider ar e par ecchi valor i. Anche se nell'esempio ho utilizzato solamente numer i, possibile consider ar e var iabili di qualsiasi tipo, sia base (str inghe, date), sia der ivato (str uttur e, classi). Ad esempio: 1.

Dim S As String 2. '... 3. Select Case S 4. Case "ciao" 5. '... 6. Case "buongiorno" 7. '... 8. End Select

V arianti del c ostrutto


Anche in questo caso, esistono numer ose var ianti, che per mettono non solo di ver ificar e uguaglianze come nei casi pr ecedenti, ma anche di contr ollar e disuguaglianze e analizzar e insiemi di valor i. Ecco una lista delle possibilit: Uso della v ir g o la La vir gola per mette di definir e non solo uno, ma molti valor i possibili in un solo Case. Ad esempio: 01. 02. 03. 04. 05. 06. 07. 08. 09. 10. Dim A As Int32 '... Select Case A Case 1, 2, 3 'Questo codice viene eseguito 'contiene un valore pari a 1, Case 4, 6, 9 'Questo codice viene eseguito 'contiene un valore pari a 4, End Select

solo se A 2 o 3 solo se A 6 o 9

Il codice sopr a pr oposto con Select equivale ad un If scr itto come segue: 1. If A = 1 Or A = 2 Or A = 3 Then 2. '... 3. ElseIf A = 4 Or A = 6 Or A = 9 Then 4. '... 5. End If Uso di To Al contr ar io, la keyw or d To per mette di definir e un ran ge di valor i, ossia un inter vallo di valor i, per il quale la condizione r isulta ver ificata se la var iabile in analisi r icade in tale inter vallo. 1. Select Case A 2. Case 67 To 90 3. 'Questo codice viene eseguito solo se 4. 'contiene un valore compreso tra 67 e 5. Case 91 To 191 6. 'Questo codice viene eseguito solo se 7. 'contiene un valore compreso tra 91 e 8. End Select Questo cor r isponde ad un If scr itto come segue: 1. If A >= 67 And A <= 90 Then 2. '... 3. ElseIf A >= 91 And A <= 191 Then 4. '... 5. End If Uso di Is Is usato in questo contesto per ver ificar e delle condizioni facendo uso di nor mali oper ator i di confr onto (meggior e, minor e, diver so, ecceter a...). L'Is usato nel costr utto Select Case non ha assolutamente niente a che veder e con quello usato per ver ificar e l'identicit di due oggetti: ha lo stesso nome, ma la funzione completamente differ ente. 01.

A 90 (estremi inclusi) A 191

Select Case A 02. Case Is >= 6 03. 'Questo codice viene eseguito solo se A 04. 'contiene un valore maggiore o uguale di 6 05. Case Is > 1 06. 'Questo codice viene eseguito solo se A 07. 'contiene un valore maggiore di 1 (e minore di 6, 08. 'dato che, se si arrivati a questo Case, 09. 'significa che la condizione del Case precedente non 10. ' stata soddisfatta) 11. End Select Il suo equivalente If: 1. If A >= 6 Then 2. '... 3. ElseIf A > 1 Then 4. '... 5. End If Uso di Else Anche nel Select lecito usar e Else: il Case che include questa istr uzione solitamente l'ultimo di tutte le alter native possibili e pr escr ive di eseguir e il codice che segue solo se tutte le altr e condizioni non sono state soddisfatte: 01. Select Case A 02. Case 1, 4 03. 'Questo codice viene eseguito solo se A 04. 'contiene 1 o 4 05. Case 9 To 12 06. 'Questo codice viene eseguito solo se A 07. 'contiene un valore compreso tra 9 e 12 08. Case Else 09. 'Questo codice viene eseguito solo se A 10. 'contiene un valore minore di 9 o maggiore di 12, 11. 'ma diverso da 1 e 4 12. End Select Uso delle pr ecedenti alter nativ e in co m binazione Tutti i modi illustr ati fino ad or a possono esser e uniti in un solo Case per ottener e potenti condizioni di contr ollo: 1. Select Case A 2. Case 7, 9, 10 To 15, Is >= 90 3. 'Questo codice viene eseguito solo se A 4. 'contiene 7 o 9 o un valore compreso tra 10 e 15 5. 'oppure un valore maggiore o uguale di 90 6. Case Else 7. '... 8. End Select

A9. I costrutti iterativi: Do Loop


Abbiamo visto che esistono costr utti per ver ificar e condizioni, o anche per ver ificar e in modo semplice e veloce molte ugualiglianze. Or a vedr emo i cicli o costr utti iter ativi (dal latino iter , itiner is = "viaggio", ma anche "per la seconda volta"). Essi hanno il compito di r ipeter e un blocco di istr uzioni un numer o deter minato o indeter minato di volte. Il pr imo che analizzer emo , appunto, il costr utto Do Loop, di cui esistono molte var ianti. La pi semplice ha questa sintassi: 1. Do 2. 'istruzioni 3. Loop Il suo compito consiste nel r ipete delle istr uzioni compr ese tr a Do e Loop un numer o infinito di volte: l'unico modo per uscir e dal ciclo usar e una speciale istr uzione: "Ex it Do", la quale ha la capacit di inter r omper e il ciclo all'istante ed uscir e da esso. Questa semplice var iante viene usata in un numer o r idotto di casi, che si possono r icondur r e sostanzialmente a due: quando si lavor a con la gr afica e le libr er ie Dir ectX, per disegnar e a scher mo i costanti cambiamenti del mondo 2D o 3D; quando necessar io ver ificar e le condizioni di uscita dal ciclo all'inter no del suo blocco di codice. Ecco un esempio di questo secondo caso: 01. Module Module1 02. 03. Sub Main() 04. Dim a, b As Single 05. 06. Do 07. 'Pulisce lo schermo 08. Console.Clear() 09. 'L'underscore serve per andare a capo nel codice 10. Console.WriteLine("Inserire le misure di base e altezza " & _ 11. "di un rettangolo:") 12. a = Console.ReadLine 13. b = Console.ReadLine 14. 15. 'Controlla che a e b non siano nulli. In quel caso, esce 16. 'dal ciclo. Se non ci fosse questo If in mezzo al codice, 17. 'verrebbe scritto a schermo il messaggio: 18. ' "L'area del rettangolo : 0" 19. 'cosa che noi vogliamo evitare. Se si usasse un'altra 20. 'variante di Do Loop, questo succederebbe sempre. Ecco 21. 'perch, in questa situazione, meglio 22. 'servirsi del semplice Do Loop 23. If a = 0 Or b = 0 Then 24. Exit Do 25. End If 26. Console.WriteLine("L'area del rettangolo : " & (a * b)) 27. 28. Console.ReadKey() Loop 29. End Sub 30. 31. 32. End Module Le altr e ver sioni del costr utto, invece, sono le seguenti: 1. Do 2. 'istruzioni 3. Loop While [condizione] Esegue le istr uzioni specificate fintanto che una condizione r imane valida, ma tutte le istr uzioni vengono eseguite almeno una volta, poich While si tr ova dopo Do. Esempio:

01. Module Module1 02. Sub Main() 03. Dim a As Int32 = 0 04. Do 05. 06. a += 1 07. Loop While (a < 2) And (a > 0) 08. Console.WriteLine(a) 09. 10. Console.ReadKey() 11. End Sub 12. End Module Il codice scr iver a scher mo "2". 1. Do While [condizione] 2. 'istruzioni 3. Loop Esegue le istr uzioni specificate fintanto che una condizione r imane valida, ma se la condizione non valida all'inizio, non viene eseguita nessuna istr uzione nel blocco. Esempio: 01. Module Module1 02. Sub Main() Dim a As Int32 = 0 03. 04. Do While (a < 2) And (a > 0) 05. a += 1 06. 07. Loop Console.WriteLine(a) 08. 09. Console.ReadKey() 10. End Sub 11. 12. End Module Il codice scr iver a scher mo "0". Bisogna notar e come le stesse condizioni del caso pr ecedente, spostate da dopo Loop a dopo Do, cambino il r isultato di tutto l'algor itmo. In questo caso, il codice nel ciclo non viene neppur e eseguito per ch la condizione nel While diventa subito falsa (in quanto a = 0, e la pr oposizione "a < 0" r isulta falsa). Nel caso pr ecedente, invece, il blocco veniva eseguito almeno una volta poich la condizione di contr ollo si tr ovava dopo di esso: in quel caso, a er a or mai stato incr ementato di 1 e per ci soddisfaceva la condizione affinch il ciclo continuasse (fino ad ar r ivar e ad a = 2, che er a il r isultato visualizzato). 1. Do 2. 'istruzioni 3. Loop Until [condizione] Esegue le istr uzioni specificate fino a che non viene ver ificata la condizione, ma tutte le istr uzioni vengono eseguite almeno una volta, poich Until si tr ova dopo Do. Esempio: 01. Module Module1 02. Sub Main() 03. Dim a As Int32 = 0 04. 05. Do 06. a += 1 07. Loop Until (a <> 1) 08. Console.WriteLine(a) 09. 10. Console.ReadKey() 11. End Sub 12. End Module A scher mo appar ir "2". 1. Do Until [condizione] 2. 'istruzioni 3.

Loop Esegue le istr uzioni specificate fino a che non viene soddisfatta la condizione, ma se la condizione valida all'inizio, non viene eseguita nessuna istr uzione del blocco. Esempio: 01. Module Module1 02. Sub Main() 03. Dim a As Int32 = 0 04. 05. Do Until (a <> 1) 06. a += 1 07. Loop 08. Console.WriteLine(a) 09. 10. Console.ReadKey() 11. End Sub 12. End Module A scher mo appar ir "0". Un piccolo esempio finale: 01. Module Module1 02. Sub Main() 03. Dim a, b, c As Int32 04. Dim n As Int32 05. 06. Console.WriteLine("-- Successione di Fibonacci --") 07. Console.WriteLine("Inserire un numero oltre il quale terminare:") 08. n = Console.ReadLine 09. 10. If n = 0 Then 11. Console.WriteLine("Nessun numero della successione") 12. Console.ReadKey() 13. Exit Sub 14. End If 15. 16. a = 1 17. b = 1 18. Console.WriteLine(a) 19. Console.WriteLine(b) 20. Do While c < n 21. c = a + b 22. b = a a = c 23. 24. Console.WriteLine(c) 25. Loop 26. Console.ReadKey() 27. 28. End Sub 29. End Module

Suggerimen to Per impostar e il valor e di Default (ossia il valor e pr edefinito) di una var iabile si pu usar e questa sintassi: 1. Dim [nome] As [tipo] = [valore] Funziona solo per una var iabile alla volta. Questo tipo di istr uzione si chiama in izializzazion e in -lin e.

A10. I costrutti iterativi: For


Dopo aver visto costr utti iter ativi che eseguono un ciclo un numer o indeter minato di volte, ar r ivato il momento di analizzar ne uno che, al contr ar io, esegue un deter minato numer o di iter azioni. La sintassi la seguente: 1. Dim I As Int32 2. 3. For I = 0 To [numero] 'istruzioni 4. 5. Next La var iabile I, usata in questo esempio, viene definita co ntator e e, ad ogni step, ossia ogni volta che il blocco di istr uzioni si r ipete, viene automaticamente incr ementata di 1, sicch la si pu usar e all'inter no delle istr uzioni come un ver o e pr opr io indice, per r ender e conto del punto al quale l'iter azione del For ar r ivata. Bisogna far notar e che il tipo usato per la var iabile contator e non deve sempr e esser e Int32, ma pu var iar e, spaziando tr a la vasta gamma di numer i inter i, con segno e senza segno, fino anche ai numer i decimali. Un esempio: 01. Module Module1 02. Sub Main() 03. Dim a As Int32 04. 05. 'Scrive 46 volte (da 0 a 45, 0 compreso, sono 46 numeri) 06. 'a schermo 'ciao' 07. For a = 0 To 45 08. Console.WriteLine("ciao") 09. Next 10. 11. Console.ReadKey() 12. End Sub 13. End Module Ovviamente il valor e di par tenza r imane del tutto ar bitr ar io e pu esser e deciso ed inizializzato ad un qualsiasi valor e: 01. Module Module1 02. Sub Main() 03. Dim a, b As Int32 04. 05. Console.WriteLine("Inserisci un numero pari") 06. b = Console.ReadLine 07. 08. 'Se b non pari, ossia se il resto della divisione 09. 'b/2 diverso da 0 10. If b Mod 2 <> 0 Then 11. 'Lo fa diventare un numero pari, aggiungendo 1 12. b += 1 13. End If 14. 15. 'Scrive tutti i numeri da b a b+20 16. For a = b To b + 20 17. Console.WriteLine(a) 18. Next 19. 20. Console.ReadKey() 21. End Sub 22. End Module Intr oduciamo or a una piccola var iante del pr ogr amma pr ecedente, nella quale si devono scr iver e solo i numer i par i da b a b+20. Esistono due modi per r ealizzar e quanto detto. Il pr imo abbastanza intuitivo, ma meno r affinato, e consiste nel contr ollar e ad ogni iter azione la par it del contator e: 1.

For a = b To b + 20 2. If a Mod 2 = 0 Then 3. Console.WriteLine(a) 4. End If 5. Next Il secondo, invece, pi elegante e usa una ver sione "ar r icchita" della str uttur a iter ativa For , nella quale viene specificato che l'incr emento del contator e non deve pi esser e 1, ma bens 2: 1. For a = b To b + 20 Step 2 2. Console.WriteLine(a) 3. Next Infatti, la par ola r iser vata Step posta dopo il numer o a cui ar r ivar e (in questo caso b+20) indica di quanto deve esser e aumentata la var iabile contator e del ciclo (in questo caso a) ad ogni step. L'incr emento pu esser e un valor e inter o, decimale, positivo o negativo, ma, cosa impor tante, deve sempr e appar tener e al r aggio d'azione del tipo del contator e: ed esempio, non si pu dichiar ar e una var iabile contator e di tipo Byte e un incr emento di -1, poich Byte compr ende solo numer i positivi (invece possibile far lo con SByte, che va da -127 a 128). Allo stesso modo non si dovr ebber o specificar e incr ementi decimali con contator i inter i. Suggerimen to Se non si vuole cr ear e una var iabile apposta per esser e contator e di un ciclo for , si pu inzializzar e dir ettamente una var iabile al suo inter no in questo modo: 1. 2. 3. 4. 5. 6. 7. For [variabile] As [tipo] = [valore] To [numero] 'istruzioni Next 'Che, se volessimo descrivere con un esempio, diverrebbe cos: For H As Int16 = 78 To 108 'istruzioni Next

A11. Gli Array - Parte I


Array a una dimensione
Fino a questo momento abbiamo avuto a che far e con var iabili "singole". Con questo voglio dir e che ogni identificator e dichiar ato puntava ad una cella di memor ia dove er a contenuto un solo valor e, leggibile e modificabile usando il nome specificato nella dichiar azione della var iabile. L'esempio classico che si fa in questo contesto quello della scatola, dove una var iabile viene, appunto, assimilata ad una scatola, il cui contenuto pu esser e pr eso, modificato e r eimmesso senza pr oblemi.

Allo stesso modo, un ar r ay un insieme di scatole, tutte una vicina all'altr a (tanto nell'esempio quando nella posizione fisica all'inter no della memor ia), a for mar e un'unica fila che per comodit si indica con un solo nome. Per distinguer e ogni "scompar to" si fa uso di un numer o inter o (che per convenzione un inter o a 32 bit, ossia Integer ), detto indice. Tutti i linguaggi .NET utilizzano sempr e un indice a base 0: ci significa che si inizia a contar e da 0 anzich da 1:

La sintassi usata per dichiar ar e un ar r ay simile a quella usata per dichiar ar e una singola var iabile: 1. Dim [nome]([numero elementi - 1]) As [tipo] La differ enza tr a le due r isiede nelle par entesi tonde che vengono poste dopo il nome della var iabile. Tr a queste

par entesi pu anche esser e specificato un numer o (sempr e inter o, ovviamente) che indica l'indice massimo a cui si pu ar r ivar e: dato che, come abbiamo visto, gli indici sono sempr e a base 0, il numer o effettivo di elementi pr esenti nella collezione sar di un'unit super ior e r ispetto all'indice massimo. Ad esempio, con questo codice: 1. Dim A(5) As String il pr ogr ammator e indica al pr ogr amma che la var iabile A un ar r ay contenente questi elementi: 1. A(0), A(1), A(2), A(3), A(4), A(5) che sono per la pr ecisione 6 elementi. Ecco un listato che esemplifica i concetti fin'or a chiar iti: 01. Module Module1 02. Sub Main() 03. 'Array che contiene 10 valori decimali, rappresentanti voti 04. Dim Marks(9) As Single 05. 'Questa variabile terr traccia di quanti voti 06. 'l'utente avr immesso da tastiera e permetter di 07. 'calcolarne una media 08. Dim Index As Int32 = 0 09. 10. 'Mark conterr il valore temporaneo immesso 11. 'da tastiera dall'utente 12. Dim Mark As Single 13. Console.WriteLine("Inserisci un altro voto (0 per terminare):") 14. Mark = Console.ReadLine 15. 16. 'Il ciclo finisce quando l'utente immette 0 oppure quando 17. 'si raggiunto l'indice massimo che 18. 'possibile usare per identificare una cella dell'array Do While (Mark > 0) And (Index < 10) 19. 20. 'Se il voto immesso maggiore di 0, lo memorizza 21. 'nell'array e incrementa l'indice di 1, cos da 22. 'poter immagazzinare correttamente il prossimo voto nell'array Marks(Index) = Mark 23. Index += 1 24. 25. Console.WriteLine("Inserisci un altro voto (0 per terminare):") 26. Mark = Console.ReadLine 27. Loop 28. 'Decrementa l'indice di 1, poich anche se l'utente 29. 'ha immesso 0, nel ciclo precedente, l'indice era stato 30. 'incrementato prevedendo un'ulteriore immissione, che, 31. 'invece, non c' stata 32. Index -= 1 33. 34. 'Totale dei voti 35. Dim Total As Single = 0 36. 'Usa un ciclo For per scorrere tutte le celle dell'array 37. 'e sommarne i valori 38. For I As Int32 = 0 To Index 39. Total += Marks(I) 40. Next 41. 42. 'Mostra la media 43. Console.WriteLine("La tua media : " & (Total / (Index + 1))) 44. Console.ReadKey() 45. End Sub 46. 47. End Module Il codice potr ebbe non appar ir e subito chiar o a pr ima vista, ma attr aver so uno sguar do pi attento, tutto si far pi limpido. Di seguito scr itto il flusso di elabor azione del pr ogr amma ammettendo che l'utente immetta due voti: Richiede un voto da tastier a: l'utente immette 5 (Mar k = 5) Mar k maggior e di 0 Inser isce il voto nell'ar r ay: Mar ks(Index ) = Mar ks(0) = 5 Incr ementa Index di 1: Index = 1

Entr ambe le condizioni non sono ver ificate: Mar k <> 0 e Index < 9. Il ciclo continua Richiede un voto da tastier a: l'utente immette 10 (Mar k = 10) Mar k maggior e di 0 Inser isce il voto nell'ar r ay: Mar ks(Index ) = Mar ks(1) = 10 Incr ementa Index di 1: Index = 2 Entr ambe le condizioni non sono ver ificate: Mar k <> 0 e Index < 9. Il ciclo continua Richiede un voto da tastier a: l'utente immette 0 (Mar k = 0) Mar k uguale a 0: il codice dentr o if non viene eseguito Una delle condizioni di ar r esto ver ificata: Mar k = 0. Il ciclo ter mina Decr ementa Index di 1: Index = 1 Somma tutti i valor i in Mar ks da 0 a Index (=1): Total = Mar ks(0) + Mar ks(1) = 5 + 10 Visualizza la media: Total / (Index + 1) = 15 / (1 + 1) = 15 / 2 = 7.5 Attende la pr essione di un tasto per uscir e anche possibile dichiar ar e ed inizializzar e (ossia r iempir e) un ar r ay in una sola r iga di codice. La sintassi usata la seguente: 1. Dim [nome]() As [tipo] = {elementi dell'array separati da virgole} Ad esempio: 1. Dim Parole() As String = {"ciao", "mouse", "penna"} Questa sintassi br eve equivale a questo codice: 1. 2. 3. 4. Dim Parole(2) As String Parole(0) = "ciao" Parole(1) = "mouse" Parole(2) = "penna"

Un'ulter ior e sintassi usata per dichiar ar e un ar r ay la seguente: 1. Dim [nome] As [tipo]() Quest'ultima, come vedr emo, sar par ticolar mente utile nel gestir e il tipo r estituito da una funzione.

Array a pi dimensioni
Gli ar r ay a una dimensione sono contr addistinti da un singolo indice: se volessimo par agonar li ad un ente geometr ico, sar ebber o assimilabili ad una r etta, estesa in una sola dimensione, in cui ogni punto r appr esenta una cella dell'ar r ay. Gli ar r ay a pi dimensioni, invece, sono contr addistinti da pi di un indice: il numer o di indici che identifica univocamente un elemento dell'ar r ay di dice r ang o . Un ar r ay di r ango 2 (a 2 dimensioni) potr , quindi, esser e par agonato a un piano, o ad una gr iglia di scatole estesa in lunghezza e in lar ghezza. La sintassi usata : 1. Dim [nome]( , ) As [tipo] 'array di rango 2 2. Dim [nome]( , , ) As [tipo] 'array di rango 3 Ecco un esempio che consider a un ar r ay di r ango 2 come una matr ice quadr ata: 01. Module Module1 02. Sub Main() 03. 'Dichiara e inizializza un array di rango 2. Dato che 04. 'in questo caso abbiamo due dimensioni, e non una sola, 05. 'non si pu specificare una semplice lista di 06. 'valori, ma una specie di "tabella" a due entrate. 07. 'Nell'esempio che segue, ho creato una semplice 08. 'tabella a due righe e due colonne, in cui ogni cella 09. ' 0. 10.

Dim M(,) As Single = _ 11. {{0, 0}, _ 12. {0, 0}} 13. 'Bisogna notare il particolare uso delle graffe: si 14. 'considera l'array di rango 2 come un array i cui 15. 'elementi sono altri array 16. 17. Console.WriteLine("Inserire gli elementi della matrice:") 18. For I As Int32 = 0 To 1 19. For J As Int32 = 0 To 1 20. Console.Write("Inserire l'elemento (" & I & ", " & J & "): ") 21. M(I, J) = Console.ReadLine 22. Next 23. Next 24. 25. Dim Det As Single 26. Det = M(0, 0) * M(1, 1) - M(0, 1) * M(1, 0) 27. Console.WriteLine("Il determinante della matrice : " & Det) 28. Console.ReadKey() 29. 30. End Sub 31. End Module Rappr esentando gr aficamente l'ar r ay M, potr emmo disegnar lo cos:

Ma il computer lo pu anche veder e in questo modo, come un ar r ay di ar r ay:

Come si vede dal codice di inizializzazione, seppur concettualmente diver si, i due modi di veder e un ar r ay sono compatibili. Tuttavia, bisogna chiar ir e che so lo e so ltanto in questo caso, le due visioni sono conciliabili, poich un ar r ay di r ango 2 e un ar r ay di ar r ay sono, dopo tutto, due entit ben distinte. Infatti, esiste un modo per dichiar ar e ar r ay di ar r ay, come segue: 1. Dim [nome]()() As [tipo] 'array di array E se si pr ova a far e una cosa del gener e: 1. 2. 3. 4. Dim A(,) As Int32 Dim B()() As Int32 '... A = B

Si r iceve un er r or e esplicito da par te del compilator e.

Ridimensionare un array
Pu capitar e di dover modificar e la lunghezza di un ar r ay r ispetto alla dichiar azione iniziale. Per far e questo, si usa la par ola r iser vata ReDim, da non confonder e con la keyw or d Dim: hanno due funzioni totalmente differ enti. Quando si r idimensiona un ar r ay, tutto il suo contenuto viene cancellato: per evitar e questo inconveniente, si deve usar e l'istr uzione ReDim Pres erve, che tuttavia ha pr estazioni molto scar se a causa dell'eccessiva lentezza. Entr ambe le istr uzioni der ivano dal Visual Basic classico e non fanno par te, per tanto, della sintassi .NET, sebbene continuino ad esser e molto usate, sia per comodit, sia per abitudine. Il metodo pi cor r etto da adottar e consiste nell'usar e la pr ocedur a Ar r ay.Resize. Eccone un esempio: 01. Module Module1 02. Sub Main() 03. Dim A() As Int32 04. Dim n As Int32 05. 06. Console.WriteLine("Inserisci un numero") 07. n = Console.ReadLine 08. 09. 'Reimposta la lunghezza di a ad n elementi Array.Resize(A, n) 10. 11. 12. 'Calcola e memorizza i primi n numeri pari (zero compreso) 13. For I As Int16 = 0 To n - 1 14. A(I) = I * 2 15. Next 16. 17. Console.ReadKey() 18. End Sub 19. End Module La r iga Ar r ay.Resize(A, n) equivale, usando ReDim a: 1. ReDim A(n - 1) Per r idimensionar e un ar r ay a pi dimensioni, la faccenda si fa abbastanza complessa. Per pr ima cosa, non si pu utilizzar e Ar r ay.Resize a meno che non si utilizzi un ar r ay di ar r ay, ma anche in quel caso le cose non sono semplici. Infatti, possibile stabilir e la lunghezza di una sola dimensione alla volta. Ad esempio, avendo un ar r ay M di r ango 2 con nove elementi, r aggr uppati in 3 r ighe e 3 colonne, non si pu semplicemente scr iver e: 1. ReDim M(2, 2)

per ch, cos facendo, solo la r iga 2 ver r r idimensionata a 3 elementi, mentr e la 0 e la 1 sar anno vuote. Il codice da usar e, quindi, : 1. ReDim M(0, 2) 2. ReDim M(1, 2) 3. ReDim M(2, 2) In questo modo, ogni "r iga" viene aggiustata alla lunghezza giusta.

A12. Gli Array - Parte II


Il c ostrutto iterativo For Eac h
Questo costr utto iter ativo simile al nor male For , ma, invece di aver e una var iabile contator e numer ica, ha una var iabile contator e di var io tipo. In sostanza, questo ciclo iter a attr aver so una ar r ay o una collezione di altr o gener e, selezionando, di volta in volta, l'elemento che si tr ova alla posizione cor r ente nell'ar r ay. Il suo funzionamento intr inseco tr oppo complesso da spiegar e or a, quindi lo affr onter solamente nei capitoli dedicati alle inter facce, in par ticolar e par lando dell'inter faccia IEnumer able. La sintassi la seguente: 1. Dim A As [tipo] 2. For Each A In [array/collezione] 3. 'istruzioni 4. Next Ovviamente anche in questo caso, come nel nor male For , possibile inizializzar e una var iabile contator e all'inter no del costr utto: 1. For Each A As [tipo] in [array/collezione] ... Esempio: 01. Module Module1 02. Sub Main() 03. Dim Words() As String = {"Questo", "", "un", "array", "di", "stringhe"} 04. 05. For Each Str As String In Words 06. Console.Write(Str & " ") 07. Next 08. 09. 'A schermo apparir la frase: 10. ' "Questo un array di stringhe " 11. 12. Console.ReadKey() 13. End Sub 14. End Module Per aver e un ter mine di par agone, il semplicissimo codice pr oposto equivale, usando un for nor male, a questo: 1. 2. 3. 4. 5. 'Words.Length restituisce il numero di elementi 'presenti nell'array Words For I As Int32 = 0 To Words.Length - 1 Console.Write(Words(I) & " ") Next

Gli array sono un tipo referenc e


Diver samente da come accade in altr i linguaggi, gli ar r ay sono un tipo r efer ence, indipendentemente dal tipo di dati da essi contenuto. Ci significa che si compor tano come ho spiegato nel capitolo "Tipi r efer ence e tipi value": l'ar ea di memor ia ad essi associata non contiene il lor o valor e, ma un puntator e alla lor o posizione nell'heap managed. Questo significa che l'oper ator e = tr a due ar r ay non copia il contenuto di uno nell'altr o, ma li r ende identici, ossia lo stesso oggetto. Per lo stesso motivo, anche lecito distr ugger e logicamente un ar r ay ponendolo uguale a Nothing: questa oper azione pu salvar e un discr eto ammontar e di memor ia, ad esempio quando si usano gr andi ar r ay per la lettur a di file binar i, ed sempr e bene annullar e un ar r ay dopo aver lo usato. 01. Module Module1 02. Sub Main() 03.

'A e B sono due array di interi 04. Dim A() As Int32 = {1, 2, 3} 05. Dim B() As Int32 = {4, 5, 6} 06. 07. 'Ora A e B sono due oggetti diversi e contengono 08. 'numeri diversi. Questa riga stamper sullo 09. 'schermo "False", infatti A Is B = False 10. Console.WriteLine(A Is B) 11. 'Adesso poniamo A uguale a B. Dato che gli array 12. 'sono un tipo reference, da ora in poi, entrambi 13. 'saranno lo stesso oggetto 14. A = B 15. 'Infatti questa istruzione stamper a schermo 16. ''"True", poich A Is B = True 17. Console.WriteLine(A Is B) 18. 'Dato che A e B sono lo stesso oggetto, se modifichiamo 19. 'un valore dell'array riferendoci ad esso con il nome 20. 'B, anche richiamandolo con A, esso mostrer 21. 'che l'ultimo elemento lo stesso 22. B(2) = 90 23. 'Su schermo apparir 90 24. Console.WriteLine(A(2)) 25. 26. Dim C() As Int32 = {7, 8, 9} 27. B = C 'Ora cosa succede? 28. 29. Console.ReadKey() 30. End Sub 31. 32. End Module Ecco come appar e la memor ia dopo l'assegnazione A = B:

Ed ecco come appar e dopo l'assegnazione B = C:

Come si vede, le var iabili contengono solo l'indir izzo degli oggetti effettivi, per ci ogni singola var iabile (A, B o C) pu puntar e allo stesso oggetto ma anche a oggetti diver si: se A = B e B = C, non ver o che A = C, come si vede dal gr afico. L'indir izzo di memor ia contenuto in A non cambia se non si usa esplicitamente un oper ator e di assegnamento. Se state leggendo la guida un capitolo alla volta, potete fer mar vi qui: il pr ossimo par agr afo utile solo per consultazione.

Manipolazione di array
La classe System.Ar r ay contiene molti metodi statici utili per la manipolazione degli ar r ay. I pi usati sono: Clear (A, I, L) : cancella L elementi a par tir e dalla posizione I nell'ar r ay A Clone() : cr ea una coppia esatta dell'ar r ay Constr ainedCopy(A1, I1, A2, I2, L) : copia L elementi dall'ar r ay A1 a par tir e dall'indice I1 nell'ar r ay A2, a par tir e dall'indice I2; se la copia non ha successo, ogni cambiamento sar annullato e l'ar r ay di destinazione non subir alcun danno Copy(A1, A2, L) / CopyTo(A1, A2) : il pr imo metodo copia L elementi da A1 a A2 a par tir e dal pr imo, mentr e il secondo fa una copia totale dell'ar r ay A1 e la deposita in A2 Find / FindLast (A, P(Of T)) As T : cer ca il pr imo elemento dell'ar r ay A per il quale la funzione gener ic Of T assegnata al delegate P r estituisce un valor e Tr ue, e ne r itor na il valor e Find(A, P(Of T)) As T() : cer ca tutti gli elementi dell'ar r ay A per i quali la funzione gener ic Of T assegnata al delegate P r estituisce un valor e Tr ue FindIndex / FindLastIndex (A, P(Of T)) As Int32 : cer ca il pr imo o l'ultimo elemento dell'ar r ay A per il quale la funzione gener ic Of T assegnata al delegate P r estituisce un valor e Tr ue, e ne r itor na l'indice For Each(A(Of T)) : esegue un'azione A deter minata da un delegate Sub per ogni elemento dell'ar r ay GetLength(A) : r estituisce la dimensione dell'ar r ay Index Of(A, T) / LastIndex Of(A, T) : r estituisce il pr imo o l'ultimo indice dell'oggetto T nell'ar r ay A Rever se(A) : inver te l'or dine di tutti gli elementi nell'ar r ay A Sor t(A) : or dina alfabeticamente l'ar r ay A. Esistono 16 ver sioni di questa pr ocedur a, tr a le quali una accetta

come secondo par ametr o un oggetto che implementa un'inter faccia ICompar er che per mette di decider e come or dinar e l'ar r ay Molti di questi metodi, come si visto, compr endono ar gomenti molto avanzati: quando sar ete in gr ado di compr ender e i Gener ics e i Delegate, r itor nate a far e un salto in questo capitolo: scopr ir ete la potenza di questi metodi.

A13. I Metodi - Parte I


Anatomia di un metodo
Il Fr amew or k .NET mette a disposizione dello sviluppator e un enor me numer o di classi contenenti metodi davver o utili, gi scr itti e pr onti all'uso, ma solo in pochi casi questi bastano a cr ear e un'applicazione ben str uttur ata ed elegante. Per questo motivo, possibile cr ear e nuovi metodi - pr ocedur e o funzioni che siano - ed usar li comodamente nel pr ogr amma. Per lo pi, si cr ea un metodo per separ ar e logicamente una cer ta par te di codice dal r esto del sor gente: questo ser ve in pr imis a r ender e il listato pi leggibile, pi consultabile e meno pr olisso, ed inoltr e ha la funzione di r acchiuder e sotto un unico nome (il nome del metodo) una ser ie pi o meno gr ande di istr uzioni. Un metodo costituito essenzialmente da tr e par ti: Nome : un identificator e che si pu usar e in altr e par ti del pr ogr amma per in vocare il metodo, ossia per eseguir e le istr uzioni di cui esso consta; Elenco dei par ametr i : un elenco di var iabili attr aver so i quali il metodo pu scambiar e dati con il pr ogr amma; Cor po : contiene il codice effettivo associato al metodo, quindi tutte le istr uzioni e le oper azioni che esso deve eseguir e Ma or a scendiamo un po' pi nello specifico...

Proc edure senza parametri


Il caso pi semplice di metodo consiste in una pr ocedur a senza par ametr i: essa costituisce, gr osso modo, un sottopr ogr amma a s stante, che pu esser e r ichiamato semplicemente scr ivendone il nome. La sua sintassi molto semplice: 1. Sub [nome]() 2. 'istruzioni 3. End Sub Cr edo che vi sia subito balzato agli occhi che questo esattamente lo stesso modo in cui viene dichiar ata la Sub Main: per tanto, or a posso dir lo, Main un metodo e, nella maggior par te dei casi, una pr ocedur a senza par ametr i (ma si tr atta solo di un caso par ticolar e, come vedr emo fr a poco). Quando il pr ogr amma inizia, Main il pr imo metodo eseguito: al suo inter no, ossia nel suo cor po, r isiede il codice del pr ogr amma. Inoltr e, poich facenti par ti del nover o delle entit pr esenti in una classe, i metodi sono membr i di classe: devono, per ci, esser e dichiar ati a livello di clas s e. Con questa locuzione abbastanza comune nell'ambito della pr ogr ammazione si intende l'atto di dichiar ar e qualcosa all'inter no del cor po di una classe, ma fuor i dal cor po di un qualsiasi suo membr o. Ad esempio, la dichiar azione seguente cor r etta: 01. Module Module1 02. Sub Esempio() 03. 'istruzioni 04. End Sub 05. 06. Sub Main() 07. 'istruzioni 08. End Sub 09. End Module mentr e la pr ossima SBAGLI ATA: 1. Module Module1 2. Sub Main() 3.

Sub Esempio() 4. 'istruzioni 5. End Sub 6. 'istruzioni 7. End Sub 8. End Module Allo stesso modo, i metodi sono l'unica categor ia, oltr e alle pr opr iet e agli oper ator i, a poter contener e delle istr uzioni: sono str umenti "attivi" di pr ogr ammazione e solo lor o possono eseguir e istr uzioni. Quindi astenetevi dallo scr iver e un abominio del gener e: 1. Module Module1 2. Sub Main() 3. 'istruzioni 4. End Sub 5. Console.WriteLine() 6. End Sub E' totalmente e concettualmente sbagliato. Ma or a veniamo al dunque con un esempio: 01. Module Module1 02. 'Dichiarazione di una procedura: il suo nome "FindDay", il 03. 'suo elenco di parametri vuoto, e il suo corpo 04. 'rappresentato da tutto il codice compreso tra "Sub FindDay()" 05. 'ed "End Sub". 06. Sub FindDay() 07. Dim StrDate As String 08. Console.Write("Inserisci giorno (dd/mm/yyyy): ") 09. StrDate = Console.ReadLine 10. 11. Dim D As Date 12. 'La funzione Date.TryParse tenta di convertire la stringa 13. 'StrDate in una variabile di tipo Date (che un tipo 14. 'base). Se ci riesce, ossia non ci sono errori nella 15. 'data digitata, restituisce True e deposita il valore 16. 'ottenuto in D; se, al contrario, non ci riesce, 17. 'restituisce False e D resta vuota. 18. 'Quando una data non viene inizializzata, dato che un 'tipo value, contiene un valore predefinito, il primo 19. 'Gennaio dell'anno 1 d.C. a mezzogiorno in punto. 20. 21. If Date.TryParse(StrDate, D) Then 'D.DayOfWeek contiene il giorno della settimana di D 22. '(luned, marted, eccetera...), ma in un 23. 'formato speciale, l'Enumeratore, che vedremo nei 24. 'prossimi capitoli. 25. 'Il ".ToString()" converte questo valore in una 26. 'stringa, ossia in un testo leggibile: i giorni della 27. 'settimana, per, sono in inglese 28. Console.WriteLine(D.DayOfWeek.ToString()) 29. Else 30. Console.WriteLine(StrDate & " non una data valida!") 31. End If 32. End Sub 33. 34. 'Altra procedura, simile alla prima 35. Sub CalculateDaysDifference() 36. Dim StrDate1, StrDate2 As String 37. Console.Write("Inserisci il primo giorno (dd/mm/yyyy): ") 38. StrDate1 = Console.ReadLine 39. Console.Write("Inserisci il secondo giorno (dd/mm/yyyy): ") 40. StrDate2 = Console.ReadLine 41. 42. Dim Date1, Date2 As Date 43. 44. If Date.TryParse(StrDate1, Date1) And _ 45. Date.TryParse(StrDate2, Date2) Then 46. 'La differenza tra due date restituisce il tempo 47. 'trascorso tra l'una e l'altra. In questo caso noi 48. 'prendiamo solo i giorni 49. Console.WriteLine((Date2 - Date1).Days) 50. 51.

52. 53. 54. 55. 56. 57. 58. 59. 60. 61. 62. 63. 64. 65. 66. 67. 68. 69. 70. 71. 72. 73. 74. 75. 76. 77. 78. 79. 80. 81. 82. 83. 84. 85. 86. 87. 88. 89. 90. 91. 92. 93. 94. 95. End

Console.WriteLine("Inserire due date valide!") End If End Sub Sub Main() 'Command una variabile di tipo char (carattere) che 'conterr una lettera indicante quale compito eseguire Dim Command As Char Do Console.Clear() Console.WriteLine("Qualche operazione con le date:") Console.WriteLine("- Premere F per sapere in che giorno " & _ "della settimana cade una certa data;") Console.WriteLine("- Premere D per calcolare la differenza tra due date;") Console.WriteLine("- Premere E per uscire.") 'Console.ReadKey() la funzione che abbiamo sempre 'usato fin'ora per fermare il programma in attesa della 'pressione di un pulsante. Come vedremo fra breve, non ' necessario usare il valore restituito da una 'funzione, ma in questo caso ci serve. Ci che 'ReadKey restituisce qualcosa che non ho ancora 'trattato. Per ora basti sapere che 'Console.ReadKey().KeyChar contiene l'ultimo carattere 'premuto sulla tastiera Command = Console.ReadKey().KeyChar 'Analizza il valore di Command Select Case Command Case "f" 'Invoca la procedura FindDay() FindDay() Case "d" 'Invoca la procedura CalculateDaysDifference() CalculateDaysDifference() Case "e" 'Esce dal ciclo Exit Do Case Else Console.WriteLine("Comando non riconosciuto!") End Select Console.ReadKey()

Else

Loop End Sub Module

In questo pr imo caso, le due pr ocedur e dichiar ate sono effettivamente sottopr ogr ammi a s stanti: non hanno nulla in comune con il modulo (eccetto il semplice fatto di esser ne membr i), n con Main, ossia non scambiano alcun tipo di infor mazione con essi; sono come degli ingr anaggi sigillati all'inter no di una scatola chiusa. A questo r iguar do, bisogna inser ir e una pr ecisazione sulle var iabili dichiar ate ed usate all'inter no di un metodo, qualsiasi esso sia. Esse si dicono lo cali o tem po r anee, poich esistono solo all'inter no del metodo e vengono distr utte quando il flusso di elabor azione ne r aggiunge la fine. Anche sotto questo aspetto, si pu notar e come le pr ocedur e appena stilate siano par ticolar mente chiuse e r estr ittive. Tuttavia, si pu benissimo far inter agir e un metodo con oggetti ed entit ester ne, e questo appr occio decisamente pi utile che non il semplice impacchettar e ed etichettar e blocchi di istr uzioni in locazioni distinte. Nel pr ossimo esempio, la pr ocedur a attinge dati dal modulo, poich in esso dichiar ata una var iabile a livello di classe. 01. Module Module1 02. 'Questa variabile dichiarata a livello di classe 03. '(o di modulo, in questo caso), perci accessibile 04. 'a tutti i membri del modulo, sempre seguendo il discorso 05. 'dei blocchi di codice fatto in precedenza 06. Dim Total As Single = 0 07. 08. 'Legge un numero da tastiera e lo somma al totale 09. Sub Sum() 10. Dim Number As Single = Console.ReadLine 11.

12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40. 41. 42. 43. 44. 45. 46. 47. 48. 49. 50. 51. 52. 53. 54. 55. 56. 57. 58. 59. End

Total += Number End Sub 'Legge un numero da tastiera e lo sottrae al totale Sub Subtract() Dim Number As Single = Console.ReadLine Total -= Number End Sub 'Legge un numero da tastiera e divide il totale per 'tale numero Sub Divide() Dim Number As Single = Console.ReadLine Total /= Number End Sub 'Legge un numero da tastiera e moltiplica il totale 'per tale numero Sub Multiply() Dim Number As Single = Console.ReadLine Total *= Number End Sub Sub Main() 'Questa variabile conterr il simbolo matematico 'dell'operazione da eseguire Dim Operation As Char Do Console.Clear() Console.WriteLine("Risultato attuale: " & Total) Operation = Console.ReadKey().KeyChar Select Case Operation Case "+" Sum() Case "-" Subtract() Case "*" Multiply() Case "/" Divide() Case "e" Exit Do Case Else Console.WriteLine("Operatore non riconosciuto") Console.ReadKey() End Select Loop End Sub Module

Proc edure c on parametri


Avviandoci ver so l'inter azione sempr e maggior e del metodo con l'ambiente in cui esso esiste, tr oviamo le pr ocedur e con par ametr i. Al contr ar io delle pr ecedenti, esse possono r icever e e scambiar e dati con il chiaman te: con quest'ultimo ter mine ci si r ifer isce alla gener ica entit all'inter no della quale il metodo in questione stato invocato. I par ametr i sono come delle var iabili locali fittizie: esistono solo all'inter no del cor po, ma non sono dichiar ate in esso, bens nell'elenco dei par ametr i. Tale elenco deve esser e specificato dopo il nome del metodo, r acchiuso da una coppia di par entesi tonde, e ogni suo elemento deve esser e separ ato dagli altr i da vir gole. 1. Sub [nome](ByVal [parametro1] As [tipo], ByVal [parametro2] As [tipo], ...) 2. 'istruzioni 3. End Sub Come si vede, anche la dichiar azione abbastanza simile a quella di una var iabile, fatta eccezione per la par ola r iser vata ByVal, di cui tr a poco vedr emo l'utili. Per intr odur r e semplicemente l'ar gomento, facciamo subito un

esempio, r iscr ivendo l'ultimo codice pr oposto nel par agr afo pr ecedente con l'aggiunta dei par ametr i: 01. Module Module1 02. Dim Total As Single = 0 03. Sub Sum(ByVal Number As Single) 04. Total += Number 05. 06. End Sub 07. 08. Sub Subtract(ByVal Number As Single) Total -= Number 09. End Sub 10. 11. 12. Sub Divide(ByVal Number As Single) 13. Total /= Number End Sub 14. 15. 16. Sub Multiply(ByVal Number As Single) 17. Total *= Number 18. End Sub 19. Sub Main() 20. 21. 'Questa variabile conterr il simbolo matematico 'dell'operazione da eseguire 22. Dim Operation As Char 23. Do 24. Console.Clear() 25. Console.WriteLine("Risultato attuale: " & Total) 26. Operation = Console.ReadKey().KeyChar 27. Select Case Operation 28. 'Se si tratta di simboli accettabili 29. Case "+", "-", "*", "/" 30. 'Legge un numero da tastiera 31. Dim N As Single = Console.ReadLine 32. 'E a seconda dell'operazione, utilizza una 33. 'procedura piuttosto che un'altra 34. Select Case Operation 35. Case "+" 36. Sum(N) 37. Case "-" 38. Subtract(N) 39. Case "*" 40. Multiply(N) 41. Case "/" 42. Divide(N) 43. End Select 44. Case "e" 45. Exit Do 46. Case Else 47. Console.WriteLine("Operatore non riconosciuto") 48. Console.ReadKey() 49. End Select 50. Loop 51. End Sub 52. 53. End Module Richiamando, ad esempio, Sum(N) si invoca la pr ocedur a Sum e si assegna al par ametr o Number il valor e di N: quindi, Number viene sommato a Total e il ciclo continua. Number , per ci, un "segnaposto", che r iceve solo dur ante l'esecuzione un valor e pr eciso, che pu anche esser e, come in questo caso, il contenuto di un'altr a var iabile. Nel ger go tecnico, Number - ossia, pi in gener ale, l'identificator e dichiar ato nell'elenco dei par ametr i - si dice par am etr o for m ale, mentr e N - ossia ci che viene concr etamente pas s ato al metodo - si dice par am etr o attuale. Non ho volutamente assegnato al par ametr o attuale lo stesso nome di quello for male, anche se del tutto lecito far lo: ho agito in questo modo per far capir e che non necessar io nessun legame par ticolar e tr a i due; l'unico vincolo che deve sussister e r isiede nel fatto che par ametr o for male ed attuale abbiano lo stesso tipo. Quest'ultima asser zione, del r esto, abbastanza ovvia: se r ichiamassimo Sum("ciao") come far ebbe il pr ogr amma a sommar e una str inga ("ciao") ad un numer o (Total)?

Or a pr oviamo a modificar e il codice pr ecedente r iassumendo tutte le oper azioni in una sola pr ocedur a, a cui, per , vengono passati due par ametr i: il numer o e l'oper ator e da usar e. 01. Module Module1 02. Dim Total As Single = 0 03. Sub DoOperation(ByVal Number As Single, ByVal Op As Char) 04. Select Case Op 05. 06. Case "+" 07. Total += Number 08. Case "-" Total -= Number 09. Case "*" 10. 11. Total *= Number 12. Case "/" 13. Total /= Number End Select 14. 15. End Sub 16. 17. Sub Main() 18. Dim Operation As Char Do 19. Console.Clear() 20. Console.WriteLine("Risultato attuale: " & Total) 21. Operation = Console.ReadKey().KeyChar 22. Select Case Operation 23. Case "+", "-", "*", "/" 24. Dim N As Single = Console.ReadLine 25. 'A questa procedura vengono passati due 26. 'parametri: il primo il numero da 27. 'aggiungere/sottrarre/moltiplicare/dividere 28. 'a Total; il secondo il simbolo 29. 'matematico che rappresenta l'operazione 30. 'da eseguire 31. DoOperation(N, Operation) 32. Case "e" 33. Exit Do 34. Case Else 35. Console.WriteLine("Operatore non riconosciuto") 36. Console.ReadKey() 37. End Select 38. Loop 39. End Sub 40. 41. End Module

Passare parametri al programma da riga di c omando


Come avevo accennato in pr ecedenza, non sempr e ver o che Main una pr ocedur a senza par ametr i. Infatti, possibile dichiar ar e Main in un altr o modo, che le consente di ottener e infor mazioni quando il pr ogr amma viene eseguito da r ig a di co m ando . In quest'ultimo caso, Main viene dichiar ata con un solo ar gomento, un ar r ay di str inghe: 01. Module Module1 02. Sub Main(ByVal Args() As String) 03. If Args.Length = 0 Then 04. 'Se la lunghezza dell'array 0, significa che vuoto 05. 'e quindi non stato passato nessun parametro a riga 06. 'di comando. Scrive a schermo come utilizzare 07. 'il programma 08. Console.WriteLine("Utilizzo: nomeprogramma.exe tuonome") 09. Else 10. 'Args ha almeno un elemento. Potrebbe anche averne di 11. 'pi, ma a noi interessa solo il primo. 12. 'Saluta l'utente con il nome passato da riga di comando 13. Console.WriteLine("Ciao " & Args(0) & "!") 14. End If 15. Console.ReadKey() 16. End Sub 17.

End Module Per pr ovar lo, potete usar e cmd.ex e, il pr ompt dei comandi. Io ho digitato: 1. CD "C:\Users\Totem\Documents\Visual Studio 2008\Projects\ConsoleApplication2\bin\Debug" 2. ConsoleApplication2.exe Totem La pr ima istr uzione per cambiar e la dir ector y di lavor o, la seconda l'invocazione ver a e pr opr ia del pr ogr amma, dove "Totem" l'unico ar gomento passatogli: una volta pr emuto invio, appar ir il messaggio "Ciao Totem!". In alter nativa, possibile specificar e gli ar gomenti passati nella casella di testo "Command line ar guments" pr esente nella scheda Debug delle pr opr iet di pr ogetto. Per acceder e alle pr opr iet di pr ogetto, cliccate col pulsante destr o sul nome del pr ogetto nella finestr a a destr a, quindi scegliete Pr oper ties e r ecatevi alla tabella Debug:

A14. I Metodi - Parte II


By V al e By Ref
Nel capitolo pr ecedente, tutti i par ametr i sono stati dichiar anti anteponendo al lor o nome la keyw or d ByVal. Essa ha il compito di comunicar e al pr ogr amma che al par ametr o for male deve esser e passata una co pia del par ametr o attuale. Questo significa che qualsiasi codice sia scr itto entr o il cor po del metodo, ogni manipolazione e ogni oper azione eseguita su quel par ametr o agisce, di fatto, su un 'altra variabile, tempor anea, e no n sul par ametr o attuale for nito. Ecco un esempio: 01. Module Module1 02. Sub Change(ByVal N As Int32) 03. N = 30 End Sub 04. 05. 06. Sub Main() 07. Dim A As Int32 = 56 Change(A) 08. 09. Console.WriteLine(A) Console.ReadKey() 10. End Sub 11. 12. End Module A scher mo appar ir la scr itta "56": A una var iabile di Main, che viene passata come par ametr o attuale alla pr ocedur a Change. In quest'ultima, N costituisce il par ametr o for male - il segnaposto - a cui, dur ante il passaggio dei par ametr i, viene attr ibuita un copia del valor e di A. In definitiva, per N viene cr eata un'altr a ar ea di memor ia, totalmente distinta, e per questo motivo ogni oper azione eseguita su quest'ultima non cambia il valor e di A. Di fatto, ByVal indica di tr attar e il par ametr o come un tipo value (pas s aggio per valore). Al contr ar io, ByRef indica di tr attar e il par ametr o come un tipo r efer ence (pas s aggio per in dirizzo). Questo significa che, dur ante il passaggio dei par ametr i, al par ametr o for male non viene attr ibuito come valor e una coppia di quello attuale, ma bens viene for zato a puntar e alla sua stessa cella di memor ia. In questa situazione, quindi, anche i tipi value come numer i, date e valor i logici, si compor tano come se fosser o oggetti. Ecco lo stesso esempio di pr ima, con una piccola modifica: 01. Module Module1 02. Sub Change(ByRef N As Int32) 03. N = 30 04. End Sub 05. 06. Sub Main() 07. Dim A As Int32 = 56 08. Change(A) 09. Console.WriteLine(A) 10. Console.ReadKey() 11. End Sub 12. End Module Nel codice, la sola differ enza consiste nella keyw or d ByRef, la quale, tuttavia, cambia r adicalmente il r isultato. Infatti, a scher mo appar ir "30" e non "56". Dato che stata applicata la clausola ByRef, N punta alla stessa ar ea di memor ia di A, quindi ogni alter azione per petr ata nel cor po del metodo sul par ametr o for male si r iper cuote su quello attuale. A questo punto molto impor tante sottolinear e che i tipi r efer ence si compor tano SEM PRE allo stesso modo, anche se vengono inser iti nell'elenco dei par ametr i accompagnati da ByVal. Eccone una dimostr azione: 01. Module Module1 02. Dim A As New Object 03. 04. Sub Test(ByVal N As Object) 05. Console.WriteLine(N Is A) 06.

End Sub 07. 08. Sub Main() 09. Test(A) Console.ReadKey() 10. 11. End Sub 12. End Module Se ByVal modificasse il compor tamento degli oggetti, allor a N conter r ebbe una copia di A, ossia un altr o oggetto semplicemente uguale, ma non identico. Invece, a scher mo appar e la scr itta "Tr ue", che significa "Ver o", per ci N e A sono lo stesso oggetto, anche se N er a pr eceduto da ByVal.

Le funzioni
Le funzioni sono simili alle pr ocedur e, ma possiedono qualche car atter istica in pi. La lor o sintassi la seguente: 1. Function [name]([elenco parametri]) As [tipo] 2. '... 3. Return [risultato] 4. End Function La pr ima differ enza che salta all'occhio l'As che segue l'elenco dei par ametr i, come a sugger ir e che la funzione sia di un cer to tipo. Ad esser e pr ecisi, quell'As non indica il tipo della funzione, ma piuttosto quello del suo r isultato. Infatti, le funzioni r estituiscono qualcosa alla fine del lor o ciclo di elabor azione. Per questo motivo, pr ima del ter mine del suo cor po, deve esser e posta almeno un'istr uzione Retur n, seguita da un qualsiasi dato, la quale for nisce al chiamante il ver o r isultato di tutte le oper azioni eseguite. Non un er r or e scr iver e funzioni pr ive dell'istr uzione Retur n, ma non avr ebbe comunque senso: si dovr ebbe usar e una pr ocedur a in quel caso. Ecco un esempio di funzione: 01. Module Module1 02. 'Questa funzione calcola la media di un insieme 03. 'di numeri decimali passati come array 04. Function Average(ByVal Values() As Single) As Single 05. 'Total conterr la somma totale di tutti 06. 'gli elementi di Values 07. Dim Total As Single = 0 08. 'Usa un For Each per ottenere direttamente i valori 09. 'presenti nell'array piuttosto che enumerarli 10. 'attraverso un indice mediante un For normale 11. For Each Value As Single In Values 12. Total += Value 13. Next 14. 'Restituisce la media aritmetica, ossia il rapporto 15. 'tra la somma totale e il numero di elementi 16. Return (Total / Values.Length) 17. End Function 18. 19. Sub Main(ByVal Args() As String) 20. Dim Values() As Single = {1.1, 5.2, 9, 4, 8.34} 21. 'Notare che in questo caso ho usato lo stesso nome 22. 'per il parametro formale e attuale Console.WriteLine("Media: " & Average(Values)) 23. Console.ReadKey() 24. 25. End Sub 26. End Module E un altr o esempio in cui ci sono pi Retur n: 01. Module Module1 02. Function Potenza(ByVal Base As Single, ByVal Esponente As Byte) As Double 03. Dim X As Double = 1 04. 05. If Esponente = 0 Then 06. Return 1 07. Else 08. If Esponente = 1 Then 09.

10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. End

For i As Byte = 1 To Esponente X *= Base Next Return X End If End If End Function Sub Main() Dim F As Double Dim b As Single Dim e As Byte Console.WriteLine("Inserire base ed esponente:") b = Console.ReadLine e = Console.ReadLine F = Potenza(b, e) Console.WriteLine(b & " elevato a " & e & " vale " & F) Console.ReadKey() End Sub Module

Else

Return Base

In quest'ultimo esempio, il cor po della funzione contiene ben tr e Retur n, ma ognuno appar tiene a un path di co dice differ ente. Path significa "per cor so" e la locuzione appena usata indica il flusso di elabor azione seguito dal pr ogr amma per deter minati valor i di Base ed Esponente. Disegnando un diagr amma di flusso della funzione, sar facile capir e come ci siano tr e per cor si differ enti, ossia quando l'esponente vale 0, quando vale 1 e quando maggior e di 1. sintatticamente lecito usar e due Retur n nello stesso path, o addir ittur a uno dopo l'altr o, ma non ha nessun senso logico: Retur n, infatti, non solo r estituisce un r isultato al chiamante, ma ter mina anche l'esecuzione della funzione. A questo pr oposito, bisogna dir e che esiste anche lo statement (=istr uzione) Exit Fun ction , che for za il pr ogr amma ad uscir e immediatamente dal cor po della funzione: inutile dir e che abbastanza per icoloso da usar e, poich si cor r e il r ischio di non r estituir e alcun r isultato al chiamante, il che pu tr adur si in un er r or e in fase di esecuzione. Come ultima postilla vor r ei aggiunger e che, come per le var ibili, non str ettamente necessar io specificar e il tipo del valor e r estituito dalla funzione, anche se for temente consigliato: in questo caso, il pr ogr amma suppor r che si tr atti del tipo Object.

Usi partic olari delle funzioni


Ci sono cer te cir costanze in cui le funzioni possono differ ir e legger mente dal lor o uso e dalla lor o for ma consueti. Di seguito sono elencati alcuni casi: Quando una funzione si tr ova a destr a dell'uguale, in qualsiasi punto di un'espr essione dur ante un assegnamento, ed essa non pr esenta un elenco di par ametr i, la si pu invocar e senza usar e la coppia di par entesi. L'esempio classico la funzione Console.Readline. L'uso pi cor r etto sar ebbe: 1. a = Console.ReadLine() ma possibile scr iver e, come abbiamo fatto fin'or a: 1. a = Console.ReadLine

Non obbligator io usar e il valor e r estituito da una funzione: nei casi in cui esso viene tr alasciato, la si tr atta come se fosse una pr ocedur a. Ne un esempio la funzione Console.ReadKey(). A noi ser ve per fer mar e il pr ogr amma in attesa della pr essione di un pulsante, ma essa non si limita a questo: r estituisce anche infor mazioni dettagliate sulle condizioni di pr essione e sul codice del car atter e inviato dalla tastier a. Tuttavia, a noi non inter essava usar e queste infor mazioni; cos, invece di scr iver e un codice come questo: 1. Dim b = Console.ReadKey() ci siamo limitati a: 1. Console.ReadKey() Questa ver satilit pu, in cer ti casi, cr ear e pr oblemi, poich si usa una funzione convinti che sia una pr ocedur a, mentr e il valor e r estituito impor tante per evitar e l'insor ger e di er r or i. Ne un esempio la funzione IO.File.Cr eate, che vedr emo molto pi in l, nella sezione E della guida.

V ariabili Static
Le var iabili Static sono una par ticolar e eccezione alle var iabili locali/tempor anee. Avevo chiar amente scr itto pochi par agr afi fa che queste ultime esistono solo nel cor po del metodo, vengono cr eate al momento dell'invocazione e distr utte al ter mine dell'esecuzione. Le Static, invece, possiedono soltanto le pr ime due car atter istiche: non vengono distr utte alla fine del cor po, ma il lor o valor e si conser va in memor ia e r imane tale anche quando il flusso entr a una seconda volta nel metodo. Ecco un esempio: 01. Module Module1 02. Sub Test() 03. Static B As Int32 = 0 04. B += 1 05. Console.WriteLine(B) 06. End Sub 07. 08. Sub Main(ByVal Args() As String) 09. For I As Int16 = 1 To 6 Test() 10. 11. Next 12. Console.ReadKey() 13. End Sub 14. End Module Il pr ogr amma stamper a scher mo, in successione, 1, 2, 3, 4, 5 e 6. Come volevasi dimostr ar e, nonostante B sia tempor anea, mantiene il suo valor e tr a una chiamata e la successiva.

A15. I Metodi - Parte III


Parametri opzionali
Come sugger isce il nome stesso, i par ametr i opzionali sono speciali par ametr i che non obbligator io specificar e quando si invoca un metodo. Li si dichiar a facendo pr eceder e la clausola ByVal o ByRef dalla keyw or d Optional: inoltr e, dato che un par ametr o del gener e pu anche esser e omesso, bisogna necessar iamente indicar e un valor e pr edefinito che esso possa assumer e. Tale valor e pr edefinito deve esser e una costante e, per questo motivo, se r icor date il discor so pr ecedentemente fatto sull'assegnamento delle costanti, i par ametr i opzionali possono esser e solo di tipo base. Ecco un esempio: 01. Module Module1 02. 'Disegna una barra "di caricamento" animata con dei trattini 03. 'e dei pipe (|). Length indica la sua lunghezza, ossia quanti 'caratterei debbano essere stampati a schermo. AnimationSpeed 04. 05. ' la velocit dell'animazione, di default 1 06. Sub DrawBar(ByVal Length As Int32, _ 07. Optional ByVal AnimationSpeed As Single = 1) 'La variabile static tiene conto del punto a cui si 08. 'arrivati al caricamento 09. Static Index As Int32 = 1 10. 11. 12. 'Disegna la barra 13. For I As Int32 = 1 To Length If I > Index Then 14. 15. Console.Write("-") Else 16. 17. Console.Write("|") 18. End If Next 19. 20. 'Aumenta l'indice di uno. Notare il particolare 21. 'assegnamento che utilizza l'operatore Mod. Finch 22. 'Index minore di Length, questa espressione equivale 23. 'banalmente a Index + 1, poich a Mod b = a se a < b. 24. 'Quando Index supera il valore di Length, allora l'operatore 25. 'Mod cambia le cose: infatti, se Index = Length + 1, 26. 'l'espressione restituisce 0, che, sommato a 1, d 1. 27. 'Il risultato che otteniamo che Index reinizia 28. 'da capo, da 1 fino a Length. 29. Index = (Index Mod (Length + 1)) + 1 30. 'Il metodo Sleep, che vedremo approfonditamente solo nella 31. 'sezione B, fa attendere al programma un certo numero di 32. 'millisecondi. 33. '1000 / AnimationSpeed provoca una diminuzione del tempo 34. 'di attesa all'aumentare della velocit 35. Threading.Thread.CurrentThread.Sleep(1000 / AnimationSpeed) 36. End Sub 37. 38. Sub Main() 39. 'Disegna la barra con un ciclo infinito. Potete invocare 40. 'DrawBar(20) tralasciando l'ultimo argomento e l'animazione 41. 'sar lenta poich la velocit di default 1 42. Do 43. Console.Clear() 44. DrawBar(20, 5) 45. Loop 46. End Sub 47. 48. End Module

Parametri indefiniti

Questo par ticolar e tipo di par ametr i non r appr esenta un solo elemento, ma bens una collezione di elementi: infatti, si specifica un par ametr o come indefinito quando non si sa a pr ior i quanti par ametr i il metodo r ichieder . A sostegno di questo fatto, i par ametr i indefiniti sono dichiar ati come ar r ay, usando la keyw or d Par amAr r ay inter posta tr a la clausola ByVal o ByRef e il nome del par ametr o. 01. Module Module1 02. 'Somma tutti i valori passati come parametri. 03. Function Sum(ByVal ParamArray Values() As Single) As Single Dim Result As Single = 0 04. 05. 06. For I As Int32 = 0 To Values.Length - 1 07. Result += Values(I) 08. Next 09. Return Result 10. 11. End Function 12. 13. Sub Main() Dim S As Single 14. 15. 16. 'Somma due valori 17. S = Sum(1, 2) 18. 'Somma quattro valori S = Sum(1.1, 5.6, 98.2, 23) 19. 'Somma un array di valori 20. Dim V() As Single = {1, 8, 3.4} 21. S = Sum(V) 22. End Sub 23. 24. End Module Come si vede, mediante Par amAr r ay, la funzione diventa capace si accettar e sia una lista di valor i specificata dal pr ogr ammator e si un ar r ay di valor i, dato che il par ametr o indefinito, in fondo, pur sempr e un ar r ay. N.B.: pu esister e uno e un solo par ametr o dichiar ato con Par amAr r ay per ciascun metodo, ed esso deve sempr e esser e posto alla fine dell'elenco dei par ametr i. Esempio: 01. Module Module1 02. 'Questa funzione calcola un prezzo includendovi anche 'il pagamento di alcune tasse (non sono un esperto di 03. 04. 'economia, perci mi mantengono piuttosto sul vago XD). 'Il primo parametro rappresenta il prezzo originale, mentre 05. 06. 'il secondo un parametro indefinito che 07. 'raggruppa tutte le varie tasse vigenti sul prodotto 'da acquistare che devono essere aggiunte all'importo 08. 09. 'iniziale (espresse come percentuali) 10. Function ApplyTaxes(ByVal OriginalPrice As Single, _ 11. ByVal ParamArray Taxes() As Single) As Single 12. Dim Result As Single = OriginalPrice 13. For Each Tax As Single In Taxes 14. Result += OriginalPrice * Tax / 100 15. Next 16. Return Result 17. End Function 18. 19. Sub Main() Dim Price As Single = 120 20. 21. 22. 'Aggiunge una tassa del 5% a Price Dim Price2 As Single = _ 23. ApplyTaxes(Price, 5) 24. 25. 'Aggiunge una tassa del 5%, una del 12.5% e una 26. 27. 'dell'1% a Price Dim Price3 As Single = _ 28. ApplyTaxes(Price, 5, 12.5, 1) 29. 30. Console.WriteLine("Prezzo originale: " & Price) 31. Console.WriteLine("Presso con tassa 1: " & Price2) 32. Console.WriteLine("Prezzo con tassa 1, 2 e 3: " & Price3) 33. 34.

35. Console.ReadKey() 36. End Sub 37. End Module

Ric orsione
Si ha una situazione di r icor sione quando un metodo invoca se stesso: in questi casi, il metodo viene detto r icor sivo. Tale tecnica possiede pr egi e difetti: il pr egio pr incipale consiste nella r iduzione dr astica del codice scr itto, con un conseguente aumento della leggibilit; il difetto pi r ilevante l'uso spr opositato di memor ia, per evitar e il quale necessar io adottar e alcune tecniche di pr ogr ammazione dinamica. La r icor sione, se male usata, inoltr e, pu facilmente pr ovocar e il cr ash di un'applicazione a causa di un over flow dello stack. Infatti, se un metodo continua indiscr iminatamente a invocar e se stesso, senza alcun contr ollo per poter si fer mar e (o con costr utti di contr ollo contenenti er r or i logici), continua anche a r ichieder e nuova memor ia per il passaggio dei par ametr i e per le var iabili locali, oltr e che per l'invocazione stessa: tutte queste r ichieste finiscono per sovr accar icar e la memor ia tempor anea, che, non r iuscendo pi a soddisfar le, le deve r ifiutar e, pr ovocando il suddetto cr ash. Ma for se sono tr oppo pessimista: non vor r ei che r inunciaste ad usar e la r icor sione per paur a di incor r er e in tutti questi spaur acchi: ci sono cer ti casi in cui davver o utile. Come esempio non posso che pr esentar e il classico calcolo del fatto r iale: 01. Module Module1 02. 'Notare che il parametro di tipo Byte perch il 'fattoriale cresce in modo abnorme e gi a 170! Double non 03. 04. 'basta pi a contenere il risultato Function Factorial(ByVal N As Byte) As Double 05. If N <= 1 Then 06. 07. Return 1 Else 08. Return N * Factorial(N - 1) 09. 10. End If End Function 11. 12. Sub Main() 13. 14. Dim Number As Byte 15. 16. Console.WriteLine("Inserisci un numero (0 <= x < 256):") 17. Number = Console.ReadLine 18. Console.WriteLine(Number & "! = " & Factorial(Number)) 19. Console.ReadKey() 20. End Sub 21. 22. End Module

A16. Gli Enumeratori


Gli enumer ator i sono tipi value par ticolar i, che per mettono di r aggr uppar e sotto un unico nome pi costanti. Essi vengono utilizzati sopr attutto per r appr esentar e opzioni, attr ibuti, car atter istiche o valor i pr edefiniti, o, pi in gener ale, qualsiasi dato che si possa "sceglier e" in un insieme finito di possibilit. Alcuni esempi di enumer ator e potr ebber o esser e lo stato di un computer (acceso, spento, standby, iber nazione, ...) o magar i gli attr ibuti di un file (nascosto, ar chivio, di sistema, sola lettur a, ...): non a caso, per quest'ultimo, il .NET impiega ver amente un enumer ator e. Ma pr ima di andar e oltr e, ecco la sintassi da usar e nella dichiar azione: 1. Enum [Nome] 2. [Nome valore 1] 3. [Nome valore 2] 4. ... 5. End Enum Ad esempio: 01. Module Module1 02. 'A seconda di come sono configurati i suoi caratteri, una 03. 'stringa pu possedere diverse denominazioni, chiamate 04. 'Case. Se costituita solo da caratteri minuscoli 05. '(es.: "stringa di esempio") si dice che in Lower 06. 'Case; al contrario se contiene solo maiuscole (es.: "STRINGA 07. 'DI ESEMPIO") sar Upper Case. Se, invece, ogni 08. 'parola ha l'iniziale maiuscola e tutte le altre lettere 09. 'minuscole si indica con Proper Case (es.: "Stringa Di Esempio"). 10. 'In ultimo, se solo la prima parola ha l'iniziale 11. 'maiuscola e il resto della stringa tutto minuscolo 12. 'e questa termina con un punto, si ha Sentence Case 13. '(es.: "Stringa di esempio."). 14. 'Questo enumeratore indica questi casi 15. Enum StringCase 16. Lower 17. Upper 18. Sentence 19. Proper 20. End Enum 21. 22. 'Questa funzione converte una stringa in uno dei Case 23. 'disponibili, indicati dall'enumeratore. Il secondo parametro 24. ' specificato fra parentesi quadre solamente perch 'Case una keyword, ma noi la vogliamo usare come 25. 26. 'identificatore. Function ToCase(ByVal Str As String, ByVal [Case] As StringCase) As String 27. 'Le funzioni per convertire in Lower e Upper 28. 'case sono gi definite. E' sufficiente 29. 'indicare un punto dopo il nome della variabile 30. 31. 'stringa, seguito a ToLower e ToUpper Select Case [Case] 32. Case StringCase.Lower 33. Return Str.ToLower() 34. Case StringCase.Upper 35. Return Str.ToUpper() 36. Case StringCase.Proper 37. 'Consideriamo la stringa come array di 38. 'caratteri: 39. Dim Chars() As Char = Str.ToLower() 40. 'Iteriamo lungo tutta la lunghezza della 41. 'stringa, dove Str.Length restituisce appunto 42. 'tale lunghezza 43. For I As Int32 = 0 To Str.Length - 1 44. 'Se questo carattere uno spazio oppure 45. ' il primo di tutta la stringa, il 46. 'prossimo indicher l'inizio di una nuova 47. 48.

49. 50. 51. 52. 53. 54. 55. 56. 57. 58. 59. 60. 61. 62. 63. 64. 65. 66. 67. 68. 69. 70. 71. 72. 73. 74. 75. 76. 77. 78. 79. 80. 81. 82. 83. 84. 85. 86. End

Next 'Restituisce l'array modificato (un array di caratteri 'e una stringa sono equivalenti) Return Chars Case StringCase.Sentence 'Riduce tutta la stringa a Lower Case Str = Str.ToLower() 'Imposta il primo carattere come maiuscolo Dim Chars() As Char = Str Chars(0) = Char.ToUpper(Chars(0)) Str = Chars 'La chiude con un punto Str = Str & "." Return Str End Select End Function Sub Main() Dim Str As String = "QuEstA una stRingA DI prova" 'Per usare i valori di un enumeratore bisogna sempre scrivere 'il nome dell'enumeratore seguito dal punto Console.WriteLine(ToCase(Str, StringCase.Lower)) Console.WriteLine(ToCase(Str, StringCase.Upper)) Console.WriteLine(ToCase(Str, StringCase.Proper)) Console.WriteLine(ToCase(Str, StringCase.Sentence)) Console.ReadKey() End Sub Module

'parola e dovr essere maiuscolo. If I = 0 Then Chars(I) = Char.ToUpper(Chars(I)) End If If Chars(I) = " " And I < Str.Length - 1 Then 'Char.ToUpper rende maiuscolo un carattere 'passato come parametro e lo restituisce Chars(I + 1) = Char.ToUpper(Chars(I + 1)) End If

L'enumer ator e Str ingCase offr e quattr o possibilit: Low er , Upper , Pr oper e Sentence. Chi usa la funzione invitato a sceglier e una fr a queste costanti, ed in questo modo non si r ischia di dimenticar e il significato di un codice. Notar e che ho scr itto "invitato", ma non "obbligato", poich l'Enumer ator e soltanto un mezzo attr aver so il quale il pr ogr ammator e d nomi significativi a costanti, che sono pur sempr e dei numer i. A pr ima vista non si dir ebbe, vedendo la dichiar azione, ma ad ogni nome indicato come campo dell'enumer ator e viene associato un numer o (sempr e inter o e di solito a 32 bit). Per saper e quale valor e ciascun identificator e indica, basta scr iver e un codice di pr ova come questo: 1. 2. 3. 4. Console.WriteLine(StringCase.Lower) Console.WriteLine(StringCase.Upper) Console.WriteLine(StringCase.Sentence) Console.WriteLine(StringCase.Proper)

A scher mo appar ir 1. 2. 3. 4. 0 1 2 3

Come si vede, le costanti assegnate par tono da 0 per il pr imo campo e vengono incr ementate di 1 via via che si pr ocede a indicar e nuovi campi. anche possibile deter minar e esplicitamente il valor e di ogni identificator e: 1. Enum StringCase 2. Lower = 5 3. Upper = 10 4. Sentence = 20 5.

Proper = 40 6. End Enum Se ad un nome non viene assegnato valor e, esso assumer il valor e del suo pr ecedente, aumentato di 1: 1. Enum StringCase 2. Lower = 5 3. Upper '= 6 4. Sentence = 20 5. Proper '= 21 6. End Enum Gli enumer ator i possono assumer e solo valor i inter i, e sono, a dir la ver it, dir ettamente der ivati dai tipi numer ici di base. , infatti, per fettamente lecito usar e una costante numer ica al posto di un enumer ator e e vicever sa. Ecco un esempio lampante in cui utilizzo un enumer ator e indicante le note musicali da cui r icavo la fr equenza delle suddette: 01. Module Module1 02. 'Usa i nomi inglesi delle note. L'enumerazione inizia 03. 'da -9 poich il Do centrale si trova 9 semitoni 'sotto il La centrale 04. Enum Note 05. C = -9 06. CSharp 07. D 08. DSharp 09. E 10. F 11. FSharp 12. G 13. GSharp 14. A 15. ASharp 16. B 17. End Enum 18. 19. 'Restituisce la frequenza di una nota. N, in concreto, 20. 'rappresenta la differenza, in semitoni, di quella nota 21. 'dal La centrale. Ecco l'utilitt degli enumeratori, 22. 'che danno un nome reale a ci che un dato indica 23. 'indirettamente 24. Function GetFrequency(ByVal N As Note) As Single 25. Return 440 * 2 ^ (N / 12) 26. End Function 27. 28. 'Per ora prendete per buona questa funzione che restituisce 29. 'il nome della costante di un enumeratore a partire dal 30. 'suo valore. Avremo modo di approfondire nei capitoli 31. 'sulla Reflection 32. Function GetName(ByVal N As Note) As String 33. Return [Enum].GetName(GetType(Note), N) 34. End Function 35. 36. Sub Main() 37. 'Possiamo anche iterare usando gli enumeratori, poich 38. 'si tratta pur sempre di semplici numeri 39. For I As Int32 = Note.C To Note.B 40. Console.WriteLine("La nota " & GetName(I) & _ 41. " risuona a una frequenza di " & GetFrequency(I) & "Hz") 42. Next 43. 44. Console.ReadKey() 45. End Sub 46. 47. End Module anche possibile specificar e il tipo di inter o di un enumer ator e (se Byte, Int16, Int32, Int64 o SByte, UInt16, UInt32, UInt64) apponendo dopo il nome la clausola As seguita dal tipo: 1. Enum StringCase As Byte 2. Lower = 5 3.

Upper = 10 4. Sentence = 20 5. Proper = 40 6. End Enum Questa par ticolar it si r ivela molto utile quando bisogna scr iver e enumer ator i su file in modalit binar ia. In questi casi, essi r appr esentano solitamente un campo detto Flags, di cui mi occuper nel pr ossimo par agr afo.

Campi c odific ati a bit (Flags)


Chi non conosca il codice binar io pu legger e un ar ticolo su di esso nella sezione FFS. I campi codificati a bit sono enumer ator i che per mettono di immagazzinar e numer ose infor mazioni in pochissimo spazio, anche in un solo byte! Di solito, tuttavia, si utilizzano tipi Int32 per ch si ha bisogno di un numer o maggior e di infor mazioni. Il meccanismo molto semplice. Ogni opzione deve poter assumer e due valor i, Ver o o Falso: questi vengono quindi codificati da un solo bit (0 o 1), ad esempio: 1. 00001101 Rappr esenta un inter o senza segno a un byte, ossia il tipo Byte: in esso si possono immagazzinar e 8 campi (uno per ogni bit), ognuno dei quali pu esser e acceso o spento. In questo caso, sono attivi solo il pr imo, il ter zo e il quar to valor e. Per por tar e a ter mine con successo le oper azioni con enumer ator i pr ogettati per codificar e a bit, necessar io che ogni valor e dell'enumer ator e sia una potenza di 2, da 0 fino al numer o che ci inter essa. Il motivo molto semplice: dato che ogni potenza di due occupa un singolo spazio nel byte, non c' per icolo che alcuna opzione si sovr apponga. Per unir e insieme pi opzioni bisogna usar e l'oper ator e logico Or . Un esempio: 01. Module Module1 02. ' convenzione che gli enumeratori che codificano a bit 'abbiano un nome al plurale 03. 04. 'Questo enumeratore definisce alcuni tipi di file 05. Public Enum FileAttributes As Byte 06. '1 = 2 ^ 0 07. 'In binario: 08. '00000001 09. Normal = 1 10. 11. '2 = 2 ^ 1 12. '00000010 13. Hidden = 2 14. 15. '4 = 2 ^ 2 16. '00000100 17. System = 4 18. 19. '8 = 2 ^ 3 20. '00001000 21. Archive = 8 22. End Enum 23. Sub Main() 24. Dim F As FileAttributes 25. 26. 'F all'inizio 0, non contiene niente: '00000000 27. 28. F = FileAttributes.Normal 29. 'Ora F 1, ossia Normal 30. 31. '00000001 32. F = FileAttributes.Hidden Or FileAttributes.System 33. 'La situazione diventa complessa: 34. 'Il primo valore 2: 000000010 35. 'Il secondo valore 4: 000000100 36. 'Abbiamo gi visto l'operatore Or: restituisce True se 37. 'almeno una delle condizioni vera: qui True 38. '1 e False 0: 39. 40.

'000000010 Or 41. '000000100 = 42. '000000110 43. 'Come si vede, ora ci sono due campi attivi: 4 e 2, che 44. 'corrispondono a Hidden e System. Abbiamo fuso insieme due 45. 'attributi con Or 46. 47. F = FileAttributes.Archive Or FileAttributes.System Or _ 48. FileAttributes.Hidden 49. 'La stessa cosa: 50. '00001000 Or 51. '00000100 Or 52. '00000010 = 53. '00001110 54. End Sub 55. End Module Or a sappiamo come immagazzinar e i campi, ma come si fa a legger li? Nel pr ocedimento inver so si una invece un And: 01. Module Module1 02. Sub Main() 03. Dim F As FileAttributes 04. 05. F = FileAttributes.Archive Or FileAttributes.System Or _ 06. FileAttributes.Hidden 07. 'Ora F 00001110 e bisogna eseguire un'operazione di And 08. 'sui bit, confrontando questo valore con Archive, che 8. 09. 'And restituisce Vero solo quando entrambe le condizioni 10. 'sono vere: 11. 12. '00001110 And 13. '00001000 = '00001000, ossia Archive! 14. 15. If F And FileAttributes.Archive = FileAttributes.Archive Then Console.WriteLine("Il file marcato come 'Archive'") 16. 17. End If 18. Console.ReadKey() End Sub 19. 20. End Module In definitiva, per immagazzinar e pi dati in poco spazio occor r e un enumer ator e contenente solo valor i che sono potenze di due; con Or si uniscono pi campi; con And si ver ifica che un campo sia attivo.

A17. Le Strutture
Nel capitolo pr ecedente ci siamo soffer mati ad analizzar e una par ticolar e categor ia di tipi di dato, gli enumer ator i, str umenti capaci di r appr esentar e tr amite costanti numer iche possibilit, scelte, opzioni, flags e in gener e valor i che si possano sceglier e in un insieme finito di elementi. Le str uttur e, invece, appar tengono ad un'altr a categor ia. Anch'esse r appr esentano un tipo di dato der ivato, o complesso, poich non r ientr a fr a i tipi base (di cui ho gi par lato) ma da essi composto. Le str uttur e ci per mettono di cr ear e nuovi tipi di dato che possano adattar si in modo miglior e alla logica dell'applicazione che si sta scr ivendo: in r ealt, quello che per mettono di far e una specie di "collage" di var iabili. Ad esempio, ammettiamo di voler scr iver e una r ubr ica, in gr ado di memor izzar e nome, cognome e numer o di telefono dei nostr i pr incipali amici e conoscenti. Ovviamente, dato che si tr atta di tan te per sone, avr emo bisogno di ar r ay per contener e tutti i dati, ma in che modo li potr emmo immagazzinar e? Per quello che ho illustr ato fino a questo punto, la soluzione pi lampante sar ebbe quella di dichiar ar e tr e ar r ay, uno per i nomi, uno per i cognomi e uno per i numer i telefonici. 1. Dim Names() As String 2. Dim Surnames() As String 3. Dim PhoneNumbers() As String Inutile dir e che seguendo questo appr occio il codice r isulter ebbe molto confusionar io e poco aggior nabile: se si volesse aggiunger e, ad esempio, un altr o dato, "data di nascita", si dovr ebbe dichiar ar e un altr o ar r ay e modificar e pr essoch tutte le par ti del listato. Usando una str uttur a, invece, potr emmo cr ear e un nuovo tipo di dato che contenga al suo inter no tutti i campi necessar i: 1. 2. 3. 4. 5. 6. 7. 8. Structure Contact Dim Name, Surname, PhoneNumber As String End Structure '... 'Un array di conttati, ognuno rappresentato dalla struttura Contact Dim Contacts() As Contact

Come si vede dall'esempio, la sintassi usata per dichiar ar e una str uttur a la seguente: 1. Structure [Nome] 2. Dim [Campo1] As [Tipo] Dim [Campo2] As [Tipo] 3. 4. '... 5. End Structure Una volta dichiar ata la str uttur a e una var iabile di quel tipo, per , come si fa ad acceder e ai campi in essa pr esenti? Si usa l'oper ator e punto ".", posto dopo il nome della var iabile: 01. Module Module1 02. Structure Contact 03. Dim Name, Surname, PhoneNumber As String 04. End Structure 05. 06. Sub Main() 07. Dim A As Contact 08. 09. A.Name = "Mario" 10. A.Surname = "Rossi" 11. A.PhoneNumber = "333 33 33 333" 12. End Sub 13. End Module [Ricor date che le dichiar azioni di nuovi tipi di dato (fino ad or a quelli che abbiamo analizzato sono enumer ator i e

str uttur e, e le classi solo come intr oduzione) possono esser e fatte solo a livello di classe o di namespace, e m ai dentr o ad un metodo.] Una str uttur a, volendo ben veder e, non altr o che un agglomer ato di pi var iabili di tipo base e, cosa molto impor tante, un tipo value, quindi si compor ta esattamente come Integer , Shor t, Date, ecceter a... e viene memor izzata dir ettamente sullo stack, senza uso di puntator i.

Ac robazie c on le strutture
Ma or a veniamo al codice ver o e pr opr io. Vogliamo scr iver e quella r ubr ica di cui avevo par lato pr ima, ecco un inizio: 01. Module Module1 02. Structure Contact 03. Dim Name, Surname, PhoneNumber As String End Structure 04. 05. 06. Sub Main() 07. 'Contacts(-1) inizializza un array vuoto, 08. 'ossia con 0 elementi Dim Contacts(-1) As Contact 09. Dim Command As Char 10. 11. 12. Do 13. Console.Clear() Console.WriteLine("Rubrica -----") 14. Console.WriteLine("Selezionare l'azione desiderata:") 15. Console.WriteLine("N - Nuovo contatto;") 16. Console.WriteLine("T - Trova contatto;") 17. 18. Console.WriteLine("E - Esci.") Command = Char.ToUpper(Console.ReadKey().KeyChar) 19. Console.Clear() 20. 21. Select Case Command 22. Case "N" 23. 'Usa ReDim Preserve per aumentare le dimensioni 24. 'dell'array mentenendo i dati gi presenti. 25. 'L'uso di array e di redim, in questo caso, 26. 'sconsigliato, a favore delle pi versatili 27. 'Liste, che per non ho ancora introdotto. 28. 'Ricordate che il valore specificato tra 29. 'parentesi indica l'indice massimo e non 30. 'il numero di elementi. 31. 'Se, all'inizio, Contacts.Length 0, 32. 'richiamando ReDim Contacts(0), si aumenta 33. 'la lunghezza dell'array a uno, poich 34. 'in questo caso l'indice massimo 0, 35. 'ossia quello che indica il primo e 36. 'l'unico elemento 37. ReDim Preserve Contacts(Contacts.Length) 38. 39. Dim N As Contact 40. Console.Write("Nome: ") 41. N.Name = Console.ReadLine 42. Console.Write("Cognome: ") 43. N.Surname = Console.ReadLine 44. Console.Write("Numero di telefono: ") 45. N.PhoneNumber = Console.ReadLine 46. 47. 'Inserisce nell'ultima cella dell'array 48. 'l'elemento appena creato 49. Contacts(Contacts.Length - 1) = N 50. 51. Case "T" 52. Dim Part As String 53. 54. Console.WriteLine("Inserire nome o cognome del " & _ 55. "contatto da trovare:") 56. Part = Console.ReadLine 57. 58. 59.

For Each C As Contact In Contacts 60. 'Il confronto avviene in modalit 61. 'case-insensitive: sia il nome/cognome 62. 'che la stringa immessa vengono 63. 'ridotti a Lower Case, cos da 64. 'ignorare la differenza tra 65. 'minuscole e maiuscole, qualora presente 66. If (C.Name.ToLower() = Part.ToLower()) Or _ 67. (C.Surname.ToLower() = Part.ToLower()) Then 68. Console.WriteLine("Nome: " & C.Name) 69. Console.WriteLine("Cognome: " & C.Surname) 70. Console.WriteLine("Numero di telefono: " & C.PhoneNumber) 71. Console.WriteLine() 72. End If 73. Next 74. 75. Case "E" 76. Exit Do 77. 78. Case Else 79. Console.WriteLine("Comando sconosciuto!") 80. End Select 81. Console.ReadKey() 82. Loop 83. End Sub 84. End Module Or a ammettiamo di voler modificar e il codice per per metter e l'inser imento di pi numer i di telefono: 01. Module Module1 02. Structure Contact 03. Dim Name, Surname As String 04. 'Importante: NON possibile specificare le dimensioni 05. 'di un array dentro la dichiarazione di una struttura. 06. 'Risulta chiaro il motivo se ci si pensa un attimo. 07. 'Noi stiamo dichiarando quali sono i campi della struttura 08. 'e quale il loro tipo. Quindi specifichiamo che 09. 'PhoneNumbers un array di stringhe, punto. Se scrivessimo 10. 'esplicitamente le sue dimensioni lo staremmo creando 11. 'fisicamente nella memoria, ma questa una 12. 'dichiarazione, come detto prima, e non una 13. 'inizializzazione. Vedremo in seguito che questa 14. 'differenza molto importante per i tipi reference 15. '(ricordate, infatti, che gli array sono tipi reference). 16. Dim PhoneNumbers() As String 17. End Structure 18. 19. Sub Main() 20. Dim Contacts(-1) As Contact Dim Command As Char 21. 22. Do 23. Console.Clear() 24. 25. Console.WriteLine("Rubrica -----") 26. Console.WriteLine("Selezionare l'azione desiderata:") 27. Console.WriteLine("N - Nuovo contatto;") Console.WriteLine("T - Trova contatto;") 28. Console.WriteLine("E - Esci.") 29. 30. Command = Char.ToUpper(Console.ReadKey().KeyChar) Console.Clear() 31. 32. Select Case Command 33. Case "N" 34. ReDim Preserve Contacts(Contacts.Length) 35. 36. Dim N As Contact 37. Console.Write("Nome: ") 38. N.Name = Console.ReadLine 39. Console.Write("Cognome: ") 40. N.Surname = Console.ReadLine 41. 42. 'Ricordate che le dimensioni dell'array non 43. 44.

'sono ancora state impostate: 45. ReDim N.PhoneNumbers(-1) 46. 47. 'Continua a chiedere numeri di telefono finch 48. 'non si introduce pi nulla 49. Do 50. ReDim Preserve N.PhoneNumbers(N.PhoneNumbers.Length) 51. Console.Write("Numero di telefono " & N.PhoneNumbers.Length & ": ") 52. N.PhoneNumbers(N.PhoneNumbers.Length - 1) = Console.ReadLine 53. Loop Until N.PhoneNumbers(N.PhoneNumbers.Length - 1) = "" 54. 'Ora l'ultimo elemento dell'array sicuramente 55. 'vuoto, lo si dovrebbe togliere. 56. 57. Contacts(Contacts.Length - 1) = N 58. 59. Case "T" 60. Dim Part As String 61. 62. Console.WriteLine("Inserire nome o cognome del " & _ 63. "contatto da trovare:") 64. Part = Console.ReadLine 65. 66. For Each C As Contact In Contacts 67. If (C.Name.ToLower() = Part.ToLower()) Or _ 68. (C.Surname.ToLower() = Part.ToLower()) Then Console.WriteLine("Nome: " & C.Name) 69. Console.WriteLine("Cognome: " & C.Surname) 70. Console.WriteLine("Numeri di telefono: ") 71. For Each N As String In C.PhoneNumbers 72. Console.WriteLine(" - " & N) 73. Next 74. Console.WriteLine() 75. End If 76. Next 77. 78. Case "E" 79. Exit Do 80. 81. Case Else 82. Console.WriteLine("Comando sconosciuto!") 83. End Select 84. Console.ReadKey() 85. Loop 86. End Sub 87. 88. End Module In questi esempi ho cer cato di pr opor r e i casi pi comuni di str uttur a, almeno per quanto si visto fino ad adesso: una str uttur a for mata da campi di tipo base e una composta dagli stessi campi, con l'aggiunta di un tipo a sua volta der ivato, l'ar r ay. Fino ad or a, infatti, ho sempr e detto che la str uttur a per mette di r aggr uppar e pi membr i di tipo base, ma sar ebbe r iduttivo r estr inger e il suo ambito di competenza solo a questo. In r ealt pu contener e var iabili di qualsiasi tipo, compr ese altr e str uttur e. Ad esempio, un contatto avr ebbe potuto anche contener e l'indir izzo di r esidenza, il quale avr ebbe potuto esser e stato r appr esentato a sua volta da un'ulter ior e str uttur a: 01. 02. 03. 04. 05. 06. 07. 08. 09. 10. 11. Structure Address Dim State, Town As String Dim Street, CivicNumber As String Dim Cap As String End Structure Structure Contact Dim Name, Surname As String Dim PhoneNumbers() As String Dim Home As Address End Structure

Per acceder e ai campi di Home si sar ebbe utilizzato un ulter ior e punto: 01. Dim A As Contact 02. 03.

04. 05. 06. 07. 08. 09. 10. 11.

A.Name = "Mario" A.Surname = "Rossi" ReDim A.PhoneNumbers(0) A.PhoneNumbers(0) = "124 90 87 111" A.Home.State = "Italy" A.Home.Town = "Pavia" A.Home.Street = "Corso Napoleone" A.Home.CivicNumber = "96/B" A.Home.Cap = "27010"

A18. Le Classi
Bene bene. Eccoci ar r ivati al sugo della questione. Le classi, entit alla base di tutto l'edificio del .NET. Gi nei pr imi capitoli di questa guida ho accennato alle classi, alla lor o sintassi e al modo di dichiar ar le. Per chi non si r icor dasse (o non avesse voglia di lasciar e questa magnifica pagina per r itor nar e indietr o nei capitoli), una classe si dichiar a semplicemente cos: 1. Class [Nome Classe] 2. '... 3. End Class Con l'atto della dichiar azione, la classe inizia ad esister e all'inter no del codice sor gente, cosicch il pr ogr ammator e la pu usar e in altr e par ti del listato per gli scopi a causa dei quali stata cr eata. Or a che ci stiamo avvicinando sempr e pi all'usar e le classi nei pr ossimi pr ogr ammi, tuttavia, dover oso r icor dar e ancor a una volta la sostanziale differ enza tr a dichiar azione e inizializzazione, tr a classe e oggetto, giusto per r infr escar e le memor ie pi fr agili e, lungi dal far vi odiar e questo concetto, per far e in modo che il messaggio penetr i: 01. Module Module1 02. 'Classe che rappresenta un cubo. 03. 'Segue la dichiarazione della classe. Da questo momento 04. 'in poi, potremo usare Cube come tipo per le nostre variabili. 05. 'Notare che una classe si dichiara e basta, non si 06. '"inizializza", perch non qualcosa di concreto, 07. ' un'astrazione, c', esiste in generale. 08. Class Cube 09. 'Variabile che contiene la lunghezza del lato 10. Dim SideLength As Single 11. 'Variabile che contiene la densit del cubo, e quindi 12. 'ci dice di che materiale composto 13. Dim Density As Single 14. 15. 'Questa procedura imposta i valori del lato e 16. 'della densit 17. Sub SetData(ByVal SideLengthValue As Single, ByVal DensityValue As Single) 18. SideLength = SideLengthValue 19. Density = DensityValue 20. End Sub 21. 22. 'Questa funzione restituisce l'area di una faccia 23. Function GetSurfaceArea() As Single Return (SideLength ^ 2) 24. 25. End Function 26. 'Questa funzione restituisce il volume del cubo 27. 28. Function GetVolume() As Single Return (SideLength ^ 3) 29. End Function 30. 31. 'Questa funzione restituisce la massa del cubo 32. Function GetMass() As Single 33. Return (Density * GetVolume()) 34. End Function 35. End Class 36. 37. Sub Main() 38. 'Variabile di tipo Cube, che rappresenta uno specifico cubo 39. 'La riga di codice che segue contiene la dichiarazione 40. 'della variabile A. La dichiarazione di una variabile 41. 'fa sapere al compilatore, ad esempio, di che tipo 42. 'sar, in quale blocco di codice sar 43. 'visibile, ma nulla di pi. 44. 'Non esiste ancora un oggetto Cube collegato ad A, ma 45. 'potrebbe essere creato in un immediato futuro. 46. 47.

'N.B.: quando si dichiara una variabile di tipo reference, 48. 'viene comunque allocata memoria sullo stack; viene 49. 'infatti creato un puntatore, che punta all'oggetto 50. 'Nothing, il cui valore simbolico stato 51. 'spiegato precedentemente. 52. Dim A As Cube 53. 54. 'Ed ecco l'immediato futuro: con la prossima linea di 55. 'codice, creiamo l'oggetto di tipo Cube che verr 56. 'posto nella variabile A. 57. A = New Cube 58. 'Quando New seguito dal nome di una classe, si crea un 59. 'oggetto di quel tipo. Nella fattispecie, in questo momento 60. 'il programma si preoccuper di richiedere della 61. 'memoria sull'heap managed per allocare i dati relativi 62. 'all'oggetto e di creare un puntatore sullo stack che 63. 'punti a tale oggetto. Esso, inoltre, eseguir 64. 'il codice contenuto nel costruttore. New, infatti, 65. ' uno speciale tipo di procedura, detta 'Costruttore, di cui parler approfonditamente 66. 67. 'in seguito 68. 69. 'Come per le strutture, i membri di classe sono accessibili 'tramite l'operatore punto ".". Ora imposto le variabili 70. 'contenute in A per rappresentare un cubo di alluminio 71. '(densit 2700 Kg/m<sup>3</sup>) di 1.5m di lato 72. A.SetData(1.5, 2700) 73. 74. Console.WriteLine("Superficie faccia: " & A.GetSurfaceArea() & " m2") 75. Console.WriteLine("Volume: " & A.GetVolume() & " m3") 76. Console.WriteLine("Massa: " & A.GetMass() & " Kg") 77. 'It's Over 9000!!!! 78. 79. Console.ReadKey() 80. End Sub 81. 82. End Module In questo esempio ho usato una semplice classe che r appr esenta un cubo di una cer ta dimensione e di un cer to mater iale. Tale classe espone quattr o funzioni, che ser vono per ottener e infor mazioni o impostar e valor i. C' un pr eciso motivo per cui non ho usato dir ettamente le due var iabili accedendovi con l'oper ator e punto, e lo spiegher a br eve nella pr ossima lezione. Quindi, tali funzioni sono membr i di classe e, sopr attutto, funzioni di istanza. Questo lemma non dovr ebbe suonar vi nuovo: gli oggetti, infatti, sono istanze (copie mater iali, concr ete) di classi (astr azioni). Anche questo concetto molto impor tante: il fatto che siano "di istanza" significa che possono esser e r ichiamate ed usate solo da un oggetto. Per far vi capir e, non si possono invocar e con questa sintassi: 1. Cube.GetVolume() ma solo passando attr aver so un'istanza: 1. Dim B As New Cube 2. '... 3. B.GetVolume() E questo, tr a l'altr o, abbastanza banale: infatti, come sar ebbe possibile calcolar e ar ea, volume e massa se non si disponesse della misur a della lunghezza del lato e quella della densit? ovvio che ogni cubo ha le sue pr opr ie misur e, e il concetto gener ale di "cubo" non ci dice niente su queste infor mazioni.

Un semplic e c ostruttore
Anche se entr er emo nel dettaglio solo pi in l, necessar io per i pr ossimi esempi che sappiate come funziona un costr uttor e, anche molto semplice. Esso viene dichiar ato come una nor male pr ocedur a, ma si deve sempr e usar e come nome "New ": 1.

Sub New([parametri]) 2. 'codice 3. End Sub Qualor a non si specificasse nessun costr uttor e, il compilator e ne cr eer uno nuovo senza par ametr i, che equivale al seguente: 1. Sub New() 2. End Sub Il codice pr esente nel cor po del costr uttor e viene eseguito in una delle pr ime fasi della cr eazione dell'oggetto, appena dopo che questo statao fisicamente collocato nella memor ia (ma, badate bene, non la pr ima istr uzione ad esser e eseguita dopo la cr eazione). Lo scopo di tale codice consiste nell'inizializzar e var iabili di tipo r efer ence pr ima solo dichiar ate, attr ibuir e valor i alle var iabili value, eseguir e oper azioni di pr epar azione all'uso di r isor se ester ne, ecceter a... Insomma, ser ve a spianar e la str ada all'uso della classe. In questo caso, l'uso che ne far emo molto r idotto e, non vor r ei dir lo, quasi mar ginale, ma l'unico compito possibile e utile in questo contesto: dar emo al costr uttor e il compito di inizializzar e SideLength e Density. 01. Module Module1 02. Class Cube Dim SideLength As Single 03. Dim Density As Single 04. 05. 'Quasi uguale a SetData 06. Sub New(ByVal SideLengthValue As Single, ByVal DensityValue As Single) 07. SideLength = SideLengthValue 08. Density = DensityValue 09. End Sub 10. 11. Function GetSurfaceArea() As Single 12. Return (SideLength ^ 2) 13. End Function 14. 15. Function GetVolume() As Single 16. Return (SideLength ^ 3) 17. 18. End Function 19. Function GetMass() As Single 20. Return (Density * GetVolume()) 21. End Function 22. End Class 23. 24. Sub Main() 25. 'Questa una sintassi pi concisa che equivale a: 26. 'Dim A As Cube 27. 'A = New Cube(2700, 1.5) 28. 'Tra parentesi vanno passati i parametri richiesti dal 29. 'costruttore 30. Dim A As New Cube(2700, 1.5) 31. 32. Console.WriteLine("Superficie faccia: " & A.GetSurfaceArea() & " m<sup>2</sup>") 33. Console.WriteLine("Volume: " & A.GetVolume() & " m<sup>3</sup>") 34. Console.WriteLine("Massa: " & A.GetMass() & " Kg") 35. 36. Console.ReadKey() 37. End Sub 38. 39. End Module

Una nota sulle Strutture


Anche le str uttur e, come le classi, possono espor r e pr ocedur e e funzioni, e questo non str ano. Esse, inoltr e, possono espor r e anche costr uttor i... e questo dovr ebbe appar ir vi str ano. Infatti, ho appena illustr ato l'impor tanza dei costr uttor i nell'istanziar e oggetti, quindi tipi r efer ence, mentr e le str uttur e sono palesemente tipi value. Il conflitto si

r isolve con una soluzione molto semplice: i costr uttor i dichiar ati nelle str uttur e possono esser e usati esattamente come per le classi, ma il lor o compito solo quello di inizializzar e campi e r ichiamar e r isor se, poich una var iabile di tipo str uttur ato viene cr eata sullo stack all'atto della sua dichiar azione. 01. Module Module1 02. Structure Cube 03. Dim SideLength As Single Dim Density As Single 04. 05. 06. Sub New(ByVal SideLengthValue As Single, ByVal DensityValue As Single) 07. SideLength = SideLengthValue 08. Density = DensityValue End Sub 09. 10. 11. Function GetSurfaceArea() As Single 12. Return (SideLength ^ 2) 13. End Function 14. 15. Function GetVolume() As Single Return (SideLength ^ 3) 16. 17. End Function 18. Function GetMass() As Single 19. Return (Density * GetVolume()) 20. End Function 21. End Structure 22. 23. Sub Main() 24. 'Questo codice 25. Dim A As New Cube(2700, 1.5) 26. 27. 'Equivale a questo 28. Dim B As Cube 29. B.SideLength = 1.5 30. B.Density = 2700 31. 32. 'A e B sono uguali 33. 34. Console.ReadKey() 35. End Sub 36. 37. End Module

A19. Le Classi - Specificatori di accesso


Le classi possono posseder e molti membr i, di svar iate categor ie, e ognuno di questi sempr e contr addistinto da un liv ello di accesso . Esso specifica "chi" pu acceder e a quali membr i, e da quale par te del codice. Molto spesso, infatti, necessar io pr ecluder e l'accesso a cer te par ti del codice da par te di fr uitor i ester ni: fate bene attenzione, non sto par lando di pr otezione del codice, di sicur ezza, intendiamoci bene; mi sto r ifer endo, invece, a chi user il nostr o codice (fossimo anche noi stessi). I motivi sono dispar ati, ma molto spesso si vuole evitar e che vengano modificate var iabili che ser vono per calcoli, oper azioni su file, r isor se, ecceter a. Al contr ar io, anche possibile espander e l'accesso ad un membr o a chiunque. Con questi due esempi intr oduttivi, apr iamo la str ada agli specificato r i di accesso , par ole chiave anteposte alla dichiar azione di un membr o che ne deter minano il livello di accesso. Ecco una lista degli specificator i di accesso esistenti, di cui pr ender or a in esame solo i pr imi due: Pr ivate: un membr o pr ivato accessibile solo all'inter no della classe in cui stato dichiar ato; Public: un membr o pubblico accessibile da qualsiasi par te del codice (dalla stessa classe, dalle sottoclassi, da classi ester ne, per fino da pr ogr ammi ester ni); Fr iend Pr otected Pr otected Fr iend (esiste solo in VB.NET)

Un esempio pratic o
Ripr endiamo il codice della classe Cube r ipr oposto nel capitolo pr ecedente. Pr oviamo a scr iver e nella Sub Main questo codice: 1. Sub Main() 2. Dim A As New Cube(2700, 1.5) 3. A.SideLength = 3 4. End Sub La r iga "A.SideLength = 3" ver r sottolineata e appar ir il seguente er r or e nel log degli er r or i: 1. ConsoleApplication2.Module1.Cube.SideLength' is not accessible in this 2. context because it is 'Private'. Questo il motivo per cui ho usato una pr ocedur a per impostar e i valor i: l'accesso al membr o (in questo caso "campo", in quanto si tr atta di una var iabile) SideLength ci pr ecluso se tentiamo di acceder vi da un codice ester no alla classe, poich, di default, nelle classi, Dim equivale a Pr ivate. Dichiar andolo esplicitamente, il codice di Cube sar ebbe stato cos: 01. Module Module1 02. Class Cube 03. 'Quando gli specificatori di accesso sono anteposti alla 04. 'dichiarazione di una variabile, si toglie il "Dim" 05. Private SideLength As Single 06. Private Density As Single 07. 08. Sub New(ByVal SideLengthValue As Single, ByVal DensityValue As Single) 09. SideLength = SideLengthValue 10. Density = DensityValue 11. End Sub 12. 13. Function GetSurfaceArea() As Single 14. Return (SideLength ^ 2) 15. End Function 16.

17. Function GetVolume() As Single 18. Return (SideLength ^ 3) 19. End Function 20. 21. Function GetMass() As Single 22. Return (Density * GetVolume()) 23. End Function 24. End Class 25. '... 26. End Module In questo specifico caso, sar ebbe stato meglio impostar e tali var iabili come Public, poich nel lor o scope (= livello di accesso) attuale non ser vono a molto e, anzi, r ichiedono molto pi codice di gestione. Ma immaginate una classe che compia oper azioni cr ittogr afiche sui dati che gli sono passati in input, usando var iabili d'istanza per i suoi calcoli: se tali var iabili fosser o accessibili al di fuor i della classe, lo sviluppator e che non sapesse esattamente cosa far ci potr ebbe compr ometter e ser iamente il r isultato di tali oper azioni, e quindi danneggiar e i pr otocolli di sicur ezza usati dall'applicazione. Etichettar e un membr o come pr ivate equivar r ebbe scher zosamente a por vi sopr a un gr ande car tello con scr itto "NON TOCCARE". Ma veniamo invece a Public, uno degli scope pi usati nella scr ittur a di una classe. Di solito, tutti i membr i che devono esser e r esi disponibili per altr e par ti del pr ogr amma o anche per altr i pr ogr ammator i (ad esempio, se si sta scr ivendo una libr er ia che sar usata successivamente da altr e per sone) sono dichiar ati come Public, ossia sempr e accessibili, senza nessun per messo di sor ta.

E che dir e, allor a, dei membr i senza specificator e di accesso? Non esistono, a dir la tutta. Anche quelli che nel codice non vengono esplicitamente mar cati dal pr ogr ammator e con una delle keyw or d sopr a elencate hanno uno scope pr edefinito: si tr atta di Fr iend. Esso ha un compito par ticolar e che potr ete capir e meglio quando affr onter emo la scr ittur a di una libr er ia di classi: per or a vi baster saper e che, all'inter no di uno stesso pr ogetto, equivale a Public. Un'altr a cosa impor tante: anche le classi (e i moduli) sono contr addistinte da un livello di accesso, che segue esattamente le stesse r egole sopr a esposte. Ecco un esempio: 01. Public Class Classe1 02. Private Class Classe2 03. '... 04. End Class 05. 06. Class Classe3 07. '... 08. End Class 09. End Class 10. 11. Class Classe4 12. Public Class Classe5 13. Private Class Classe6 14. '... 15.

End Class 16. End Class 17. End Class 18. 19. Module Module1 20. Sub Main() 21. '... 22. End Sub 23. End Module Il codice contenuto in Main pu acceder e a: Classe1, per ch Public Classe3, per ch Fr iend, ed possibile acceder e al suo contenitor e Classe1 Classe4, per ch Fr iend Classe5, per ch Public, ed possibile acceder e al suo contenitor e Classe4 mentr e non pu acceder e a: Classe2, per ch Pr ivate Classe6, per ch Pr ivate d'altr a par te, il codice di Classe2 pu acceder e a tutto tr anne a Classe6 e vicever sa. N.B.: Una classe pu esser e dichiar ata Pr ivate solo quando si tr ova all'inter no di un'altr a classe (altr imenti non sar ebbe mai accessibile, e quindi inutile).

Spec ific atori di ac c esso nelle Strutture


Anche per i membr i di una str uttur a, cos come per quelli di una classe, possibile specificar e tutti gli scope esistenti. C' solo una differ enza: quando si omette lo scope e si lascia una var iabile dichiar ata solo con Dim, essa automaticamente impostata a Public. Per questo motivo ci er a possibile acceder e ai campi della str uttur a Contact, ad esempio: 1. Structure Contact 2. Dim Name, Surname, PhoneNumber As String 3. End Structure che equivale a: 1. Structure Contact 2. Public Name, Surname, PhoneNumber As String 3. End Structure Ovviamente, anche le str uttur e stesse hanno sempr e uno scope, cos come qualsiasi altr a entit del .NET.

Un esempio intelligente
Ecco un esempio di classe scr itta utilizzando gli specificator i di accesso per limitar e l'accesso ai membr i da par te del codice di Main (e quindi da chi usa la classe, poich l'utente finale pu anche esser e un altr o pr ogr ammator e). Oltr e a questo tr over ete anche un esempio di un diffuso e semplice algor itmo di or dinamento, 2 in 1! 001. Module Module1 002. 'Dato che usiamo la classe solo in questo programma, possiamo 003. 'evitare di dichiararla Public, cosa che sarebbe ideale in 004. 'una libreria 005. Class BubbleSorter 006. 'Enumeratore pubblico: sar accessibile da tutti. In questo 007. 'caso impossibile dichiararlo come Private, poich 008. 'uno dei prossimi metodi richiede come parametro una 009.

010. 011. 012. 013. 014. 015. 016. 017. 018. 019. 020. 021. 022. 023. 024. 025. 026. 027. 028. 029. 030. 031. 032. 033. 034. 035. 036. 037. 038. 039. 040. 041. 042. 043. 044. 045. 046. 047. 048. 049. 050. 051. 052. 053. 054. 055. 056. 057. 058. 059. 060. 061. 062. 063. 064. 065. 066. 067. 068. 069. 070. 071. 072. 073. 074. 075. 076. 077. 078. 079. 080. 081.

'variabile di tipo SortOrder e se questo fosse private, 'non si potrebbe usare al di fuori della classe, cosa 'che invece viene richiesta. Public Enum SortOrder Ascending 'Crescente Descending 'Decrescente None 'Nessun ordinamento End Enum 'Mantiene in memoria il senso di ordinamento della lista, 'per evitare di riordinarla nel caso fosse richiesto due 'volte lo stesso Private CurrentOrder As SortOrder = SortOrder.None 'Mantiene in memoria una copia dell'array, che 'accessibile ai soli membri della classe. In 'questo modo, possibile eseguire tutte 'le operazioni di ordinamento usando un solo metodo 'per l'inserimento dell'array Private Buffer() As Double 'Memorizza in Buffer l'array passato come parametro Public Sub PushArray(ByVal Array() As Double) 'Se Buffer diverso da Nothing, lo imposta 'esplicitamente a Nothing (equivale a distruggere 'l'oggetto) If Buffer IsNot Nothing Then Buffer = Nothing End If 'Copia l'array: ricordate come si comportano i tipi 'reference e pensate a quali ripercussioni tale 'comportamento potr avere sul codice Buffer = Array 'Annulla CurrentOrder CurrentOrder = SortOrder.None End Sub 'Procedura che ordina l'array secondo il senso specificato Public Sub Sort(ByVal Order As SortOrder) 'Se il senso None, oppure uguale a quello corrente, ' inutile proseguire, quindi si ferma ed esce If (Order = SortOrder.None) Or (Order = CurrentOrder) Then Exit Sub End If 'Questa variabile tiene conto di tutti gli scambi 'effettuati Dim Occurrences As Int32 = 0 'Il ciclo seguente ordina l'array in senso crescente: 'se l'elemento i maggiore dell'elemento i+1, 'ne inverte il posto, e aumenta il contatore di 1. 'Quando il contatore rimane 0 anche dopo il For, 'significa che non c' stato nessuno scambio 'e quindi l'array ordinato. Do Occurrences = 0 For I As Int32 = 0 To Buffer.Length - 2 If Buffer(I) > Buffer(I + 1) Then Dim Temp As Double = Buffer(I) Buffer(I) = Buffer(I + 1) Buffer(I + 1) = Temp Occurrences += 1 End If Next Loop Until Occurrences = 0 'Se l'ordine era discendente, inverte l'array If Order = SortOrder.Descending Then Array.Reverse(Buffer) End If 'Memorizza l'ordine

082. 083. 084. 085. 086. 087. 088. 089. 090. 091. 092. 093. 094. 095. 096. 097. 098. 099. 100. 101. 102. 103. 104. 105. 106. 107. 108. 109. 110. End

CurrentOrder = Order End Sub 'Restituisce l'array ordinato Public Function PopArray() As Double() Return Buffer End Function End Class Sub Main() 'Crea un Dim a As 'Crea un Dim b As array temporaneo Double() = {1, 6, 2, 9, 3, 4, 8} nuovo oggetto BubbleSorter New BubbleSorter()

'Vi inserisce l'array b.PushArray(a) 'Invoca la procedura di ordinamento b.Sort(BubbleSorter.SortOrder.Descending) 'E per ogni elemento presente nell'array finale '(quello restituito dalla funzione PopArray), ne stampa 'il valore a schermo For Each n As Double In (b.PopArray()) Console.Write(n & " ") Next Console.ReadKey() End Sub Module

Ric apitolando...
Ricapitolando, quindi, davanti a ogni membr o si pu specificar e una keyw or d tr a Pr ivate, Public e Fr iend (per quello che abbiamo visto in questo capitolo), che ne limita l'accesso. Nel caso non si specifichi nulla, lo specificator e pr edefinito var ia a seconda dell'entit a cui stato applicato, secondo questa tabella: Pr ivate per var iabili contenute in una classe Public per var iabili contenute in una str uttur a Fr iend per tutte le altr e entit

A20. Le Propriet - Parte I


Le pr opr iet sono una categor ia di membr i di classe molto impor tante, che user emo molto spesso da qui in avanti. Non possibile definir ne con pr ecisione la natur a: esse sono una via di mezzo tr a metodi (pr ocedur e o funzioni) e campi (var iabili dichiar ate in una classe). In gener e, si dice che le pr opr iet siano "campi intelligenti", poich il lor o r uolo consiste nel mediar e l'inter azione tr a codice ester no alla classe e campo di una classe. Esse si "avvolgono" intor no a un campo (per questo motivo vengono anche chiamate w r apper , dall'inglese w r ap = impacchettar e) e decidono, tr amite codice scr itto dal pr ogr ammator e, quali valor i siano leciti per quel campo e quali no - stile buttafuor i, per intender ci. La sintassi con cui si dichiar a una pr opr iet la seguente: 01. Property [Nome]() As [Tipo] 02. Get 03. '... Return [Valore restituito] 04. End Get 05. Set(ByVal value As [Tipo]) 06. 07. '... End Set 08. 09. End Property Or a, questa sintassi, nel suo insieme, molto diver sa da tutto ci che abbiamo visto fino ad or a. Tuttavia, guar dando bene, possiamo r iconoscer e alcuni blocchi di codice e r icondur li ad una categor ia pr ecedentemente spiegata: La pr ima r iga di codice r icor da la dichiar azione di una var iabile; Il blocco Get r icor da una funzione; il codice ivi contenuto viene eseguito quando viene r ichiesto il valor e della pr opr iet; Il blocco Set r icor da una pr ocedur a a un par ametr o; il codice ivi contenuto viene eseguito quando un codice imposta il valor e della pr opr iet. Da quello che ho appena scr itto sembr a pr opr io che una pr opr iet sia una var iabile pr ogr ammabile, ma allor a da dove si pr ende il valor e che essa assume? Come ho gi r ipetuto, una pr opr iet media l'inter azione tr a codice ester no e campo di una classe: quindi dobbiamo stabilir e un modo per collegar e la pr opr iet al campo che ci inter essa. Ecco un esempio: 01. Module Module1 02. Class Example 03. 'Campo pubblico di tipo Single. 04. Public _Number As Single 05. 06. 'La propriet Number media, in questo caso, l'uso 07. 'del campo _Number. 08. Public Property Number() As Single 09. Get 10. 'Quando viene chiesto il valore di Number, viene 11. 'restituito il valore della variabile _Number. Si 12. 'vede che la propriet non fa altro che manipolare 13. 'una variabile esistente e non contiene alcun 14. 'dato di per s 15. Return _Number 16. End Get 17. Set(ByVal value As Single) 18. 'Quando alla propriet viene assegnato un valore, 19. 'essa modifica il contenuto di _Number impostandolo 'esattamente su quel valore 20. _Number = value 21. 22. End Set End Property 23. End Class 24. 25.

26. Sub Main() 27. Dim A As New Example() 28. 29. 'Il codice di Main sta impostando il valore di A.Number. 30. 'Notare che una propriet si usa esattamente come una 31. 'comunissima variabile di istanza. 32. 'La propriet, quindi, richiama il suo blocco Set come 33. 'una procedura e assegna il valore 20 al campo A._Number 34. A.Number = 20 35. 36. 'Nella prossima riga, invece, viene richiesto il valore 37. 'di Number per poterlo scrivere a schermo. La propriet 38. 'esegue il blocco Get come una funzione e restituisce al 39. 'chiamante (ossia il metodo/oggetto che ha invocato Get, 40. 'in questo caso Console.WriteLine) il valore di A._Number 41. Console.WriteLine(A.Number) 42. 43. 'Per gli scettici, facciamo un controllo per vedere se 44. 'effettivamente il contenuto di A._Number cambiato. 45. 'Potrete constatare che uguale a 20. 46. Console.WriteLine(A._Number) 47. Console.ReadLine() 48. End Sub 49. 50. End Module Per pr ima cosa bisogna subito far e due impor tanti osser vazioni: Il nome della pr opr iet e quello del campo a cui essa sovr intende sono molto simili. Questa similar it viene mentenuta per l'appunto a causa dello str etto legame che lega pr opr iet e campo. una convenzione che il nome di un campo mediato da una pr opr iet inizi con il car atter e under scor e ("_"), oppur e con una di queste combinazioni alfanumer iche: "p_", "m_". Il nome usato per la pr opr iet sar , invece, identico, ma senza l'under scor e iniziale, come in questo esempio. Il tipo definito per la pr opr iet identico a quello usato per il campo. Abbastanza ovvio, d'altr onde: se essa deve mediar e l'uso di una var iabile, allor a anche tutti i valor i r icevuti e r estituiti dovr anno esser e compatibili.

La potenza nasc osta delle propriet


Ar r ivati a questo punto, uno potr ebbe pensar e che, dopotutto, non vale la pena di spr ecar e spazio per scr iver e una pr opr iet quando pu acceder e dir ettamente al campo. Bene, se c' ver amente qualcuno che leggendo quello che ho scr itto ha pensato ver amente a questo, pu anche andar e a compianger si in un angolino buio. XD Scher zi a par te, l'utilit c', ma spesso non si vede. Pr ima di tutto, iniziamo col dir e che se un campo mediato da una pr opr iet, per convenzione (ma anche per buon senso), deve esser e Pr ivate, altr imenti lo si potr ebbe usar e indiscr iminatamente senza limitazioni, il che pr opr io quello che noi vogliamo impedir e. A questo possiamo anche aggiunger e una consider azione: visto che abbiamo la possibilit di far lo, aggiungendo del codice a Get e Set, per ch non far e qualche contr ollo sui valor i inser iti, giusto per evitar e er r or i peggior i in un immediato futur o? Ammettiamo di aver e la nostr a bella classe: 01. Module Module1 02. 'Questa classe rappresenta un semplice sistema inerziale, 03. 'formato da un piano orizzontale scabro (con attrito) e 04. 'una massa libera di muoversi su di esso 05. Class InertialFrame 06. Private _DynamicFrictionCoefficient As Single 07. Private _Mass As Single 08. Private _GravityAcceleration As Single 09. 10. 'Coefficiente di attrito radente (dinamico), 11. Public Property DynamicFrictionCoefficient() As Single 12. Get 13.

14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40. 41. 42. 43. 44. 45. 46. 47. 48. 49. 50. 51. 52. 53. 54. 55. 56. 57. 58. 59. 60. 61. 62. 63. 64. 65. 66. 67. 68. 69. 70. End

Return _DynamicFrictionCoefficient End Get Set(ByVal value As Single) _DynamicFrictionCoefficient = value End Set End Property 'Massa, m Public Property Mass() As Single Get Return _Mass End Get Set(ByVal value As Single) _Mass = value End Set End Property 'Accelerazione di gravit che vale nel sistema, g Public Property GravityAcceleration() As Single Get Return _GravityAcceleration End Get Set(ByVal value As Single) _GravityAcceleration = value End Set End Property 'Calcola e restituisce la forza di attrito che agisce 'quando la massa in moto Public Function CalculateFrictionForce() As Single Return (Mass * GravityAcceleration) * DynamicFrictionCoefficient End Function End Class Sub Main() Dim F As New InertialFrame() Console.WriteLine("Sistema inerziale formato da:") Console.WriteLine(" - Un piano orizzontale e scabro;") Console.WriteLine(" - Una massa variabile.") Console.WriteLine() Console.WriteLine("Inserire i dati:") Console.Write("Coefficiente di attrito dinamico = ") F.DynamicFrictionCoefficient = Console.ReadLine Console.Write("Massa (Kg) = ") F.Mass = Console.ReadLine Console.Write("Accelerazione di gravit (m/s<sup>2</sup>) = ") F.GravityAcceleration = Console.ReadLine Console.WriteLine() Console.Write("Attrito dinamico = ") Console.WriteLine(F.CalculateFrictionForce() & " N") Console.ReadLine() End Sub Module

I calcoli funzionano, le pr opr iet sono scr itte in modo cor r etto, tutto gir a alla per fezione, se non che... qualcuno tr ova il modo di metter e = 2 e m = -7, valor i assur di poich 0 < <= 1 ed m > 0. Modificando il codice delle pr opr iet possiamo impor r e questi vincoli ai valor i inser ibili: 01. Module Module1 02. Class InertialFrame 03. Private _DynamicFrictionCoefficient As Single 04. Private _Mass As Single 05. Private _GravityAcceleration As Single 06. 07. Public Property DynamicFrictionCoefficient() As Single 08. Get 09.

Return _DynamicFrictionCoefficient 10. End Get 11. Set(ByVal value As Single) 12. If (value > 0) And (value <= 1) Then 13. _DynamicFrictionCoefficient = value 14. Else 15. Console.WriteLine(value & " non un valore consentito!") 16. Console.WriteLine("Coefficiente attrito dinamico = 0.1") 17. _DynamicFrictionCoefficient = 0.1 18. End If 19. End Set 20. End Property 21. 22. Public Property Mass() As Single 23. Get 24. Return _Mass 25. End Get 26. Set(ByVal value As Single) 27. If value > 0 Then 28. _Mass = value 29. Else 30. Console.WriteLine(value & " non un valore consentito!") 31. Console.WriteLine("Massa = 1") _Mass = 1 32. End If 33. End Set 34. End Property 35. 36. Public Property GravityAcceleration() As Single 37. Get 38. Return _GravityAcceleration 39. End Get 40. Set(ByVal value As Single) 41. _GravityAcceleration = Math.Abs(value) 42. End Set 43. End Property 44. 45. Public Function CalculateFrictionForce() As Single 46. Return (Mass * GravityAcceleration) * DynamicFrictionCoefficient 47. End Function 48. 49. End Class 50. 51. '... 52. 53. End Module In gener e, ci sono due modi di agir e quando i valor i che la pr opr iet r iceve in input sono er r ati: Modificar e il campo r eimpostandolo su un valor e di default, ossia la str ategia che abbiamo adottato per questo esempio; Lanciar e un'eccezione. La soluzione for malmente pi cor r etta sar ebbe la seconda: il codice chiamante dovr ebbe poi cattur ar e e gestir e tale eccezione, lasciando all'utente la possibilit di decider e cosa far e. Tuttavia, per far vi fr onte, bisogner ebbe intr odur r e ancor a un po' di teor ia e di sintassi, r agion per cui il suo uso stato posto in secondo piano r ispetto alla pr ima. Inoltr e, bisogner ebbe anche evitar e di por r e il codice che comunica all'utente l'er r or e nel cor po della pr opr iet e, pi in gener ale, nella classe stessa, poich questo codice potr ebbe esser e r iutilizzato in un'altr a applicazione che magar i non usa la console (altr a r agione per sceglier e la seconda possibilit). Mettendo da par te tali osser vazioni di cir costanza, comunque, si nota come l'uso delle pr opr iet offr a molta pi gestibilit e flessibilit di un semplice campo. E non ancor a finita...

Curiosit: dietro le quinte di una propriet


N.B.: Potete anche pr oceder e a legger e il pr ossimo capitolo, poich questo par agr afo pur amente illustr ativo.

Come esempio user questa pr opr iet: 01. Property Number() As Single 02. Get 03. Return _Number End Get 04. Set(ByVal value As Single) 05. 06. If (value > 30) And (value < 100) Then 07. _Number = value 08. Else _Number = 31 09. End If 10. End Set 11. 12. End Property Quando una pr opr iet viene dichiar ata, ci sembr a che essa esista come un'entit unica nel codice, ed pi o meno ver o. Tuttavia, una volta che il sor gente passa nelle fauci del compilator e, succede una cosa abbastanza singolar e. La pr opr iet cessa di esister e e viene invece spezzata in due elementi distinti: Una funzione senza par ametr i, di nome "get_[Nome Pr opr iet]", il cui cor po viene cr eato copiando il codice contenuto nel blocco Get. Nel nostr o caso, get_Number : 1. Function get_Number() As Single 2. Return _Number 3. End Function Una pr ocedur a con un par ametr o, di nome "set_[Nome Pr opr iet]", il cui cor po viene cr eato copiando il codice contenuto nel blocco Set. Nel nostr o caso, set_Number : 1. Sub set_Number(ByVal value As Single) 2. If (value > 30) And (value < 100) Then 3. _Number = value 4. Else 5. _Number = 31 6. End If 7. End Sub Entr ambi i metodi hanno come specificator e di accesso lo stesso della pr opr iet. Inoltr e, ogni r iga di codice del tipo 1. [Propriet] = [Valore] oppur e 1. [Valore] = [Propriet] viene sostituita con la cor r ispondente r iga: 1. set_[Nome Propriet]([Valore]) oppur e: 1. [Valore] = get_[Nome Propriet] Ad esempio, il seguente codice: 1. Dim A As New Example 2. A.Number = 20 3. Console.WriteLine(A.Number) viene tr asfor mato, dur ante la compilazione, in: 1. Dim A As New Example 2. A.set_Number(20) 3. Console.WriteLine(A.get_Number())

Questo per dir e che una pr opr iet un costr utto di alto livello, uno str umento usato nella pr ogr ammazione astr atta: esso viene scomposto nelle sue par ti fondamentali quando il pr ogr amma passa al livello medio, ossia quando tr adotto in IL, lo pseudo-linguaggio macchina del Fr amew or k .NET.

A21. Le Propriet - Parte II


Propriet ReadOnly e W riteOnly
Fin'or a abbiamo visto che le pr opr iet sono in gr ado di mediar e l'inter azione tr a codice ester no alla classe e suoi campi, e tale mediazione compr endeva la possibilit di r ifiutar e cer ti valor i e consentir ne altr i. Ma non finita qui: usando delle apposite keyw or ds possibile r ender e una pr opr iet a sola lettur a (ossia possibile legger ne il valor e ma non modificar lo) o a sola scr ittur a (ossia possibile modificar ne il valor e ma non ottener lo). Per quanto r iguar da la pr ima, viene abbastanza natur ale pensar e che ci possano esser e valor i solo esposti ver so cui pr oibita la manipolazione dir etta, magar i per ch par ticolar mente impor tanti o, pi spesso, per ch logicamente immutabili (vedi oltr e per un esempio); spostando l'attenzione per un attimo sulla seconda, per , sar par imenti del tutto lecito domandar si quale sia la lor o utilit. Le var iabili, i campi, e quindi, per estensione, anche le pr opr iet, sono per lor o natur a atti a contener e dati, che ver r anno poi utilizzati in altr e par ti del pr ogr amma: tali dati vengono continuamente letti e/o modificati e, per quanto sia possibile cr eder e che ve ne siano di immodificabili, come costanti e valor i a sola lettur a, appar e invece assur da l'esistenza di campi solo modificabili. Per modificar e qualcosa, infatti, se ne deve conoscer e almeno qualche infor mazione. La r ealt che le pr opr iet Wr iteOnly sono innatur ali per la str agr ande maggior andza dei pr ogr ammator i; piuttosto di usar le meglio definir e pr ocedur e. Mi occuper quindi di tr attar e solo la keyw or d ReadOnly. In br eve, la sintassi di una pr opr iet a sola lettur a questa: 1. ReadOnly Property [Nome]() As [Tipo] 2. Get 3. '... 4. Return [Valore] 5. End Get 6. End Property Notate che il blocco Set assente: ovviamente, si tr atta di codice inutile dato che la pr opr iet non pu esser e modificata. Per continuar e il discor so iniziato pr ima, ci sono pr incipalmente tr e motivi per dichiar ar e un'entit del gener e: I dati a cui essa for nisce accesso sono impor tanti per la vita della classe, ed quindi necessar io lasciar e che la modifica avvenga tr amite altr i metodi della classe stessa. Tuttavia, non c' motivo di nasconder ne il valor e al codice ester no, cosa che pu anche r ivelar si molto utile, sia come dato da elabor ar e, sia come infor mazione di dettaglio; La pr opr iet espr ime un valor e che non si pu modificar e per ch per pr opr ia natur a immutabile. Un classico esempio pu esser e la data di nascita di una per sona: tipicamente la si inser isce come par ametr o del costr uttor e, o la si pr eleva da un database, e viene memor izzata in un campo esposto tr amite pr opr iet ReadOnly. Questo logico, poich non si pu cambiar e la data di nascita; quella e basta. Un caso par ticolar e sar ebbe quello di un er r or e commesso dur ante l'inser imento della data, che costr inger ebbe a cambiar la. In questi casi, la modifica avviene per altr e vie (metodi con autenticazione o modifica del database); La pr opr iet espr ime un valor e che viene calcolato al momento. Questo caso molto speciale, poich va al di l della nor male funzione di w r apper che le pr opr iet svolgono nor malmente. Infatti, si pu anche scr iver e una pr opr iet che non sovr intende ad alcun campo, ma che, anzi, cr ea un campo fittizio: ossia, da fuor i sembr a che ci sia un'infor mazione in pi nella classe, ma questa viene solo desunta o inter polata da altr i dati noti. Esempio: 01. Class Cube 02. Private _SideLength As Single 03. Private _Density As Single 04. 05. Public Property SideLength() As Single 06.

07. 08. 09. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40. 41. 42. 43. 44. 45. 46. 47. 48. End

Return _SideLength End Get Set(ByVal value As Single) If value > 0 Then _SideLength = value Else _SideLength = 1 End If End Set End Property Public Property Density() As Single Get Return _Density End Get Set(ByVal value As Single) If value > 0 Then _Density = value Else _Density = 1 End If End Set End Property Public ReadOnly Property SurfaceArea() As Single Get Return (SideLength ^ 2) End Get End Property Public ReadOnly Property Volume() As Single Get Return (SideLength ^ 3) End Get End Property Public ReadOnly Property Mass() As Single Get Return (Volume * Density) End Get End Property Class

Get

Vedendola dall'ester no, si pu pensar e che la classe Cube contenga come dati concr eti (var iabili) SideLength, Density, Sur faceAr ea, Volume e Mass, e che questi siano esposti tr amite una pr opr iet. In r ealt essa ne contiene solo i pr imi due e in base a questi calcola gli altr i. In questo esempio teor ico, le due pr opr iet esposte sono r eadonly per il pr imo e il secondo motivo: 01. Module Esempio3 02. Class LogFile 03. Private _FileName As String 04. Private _CreationTime As Date 05. 06. 'Niente deve modificare il nome del file, altrimenti 07. 'potrebbero verificarsi errori nella lettura o scrittura 08. 'dello stesso, oppure si potrebbe chiudere un file 09. 'che non esiste ancora 10. Public ReadOnly Property FileName() As String 11. Get 12. Return _FileName 13. End Get 14. End Property 15. 16. 'Allo stesso modo non si pu modificare la data di 17. 'creazione di un file: una volta creato, viene 18. 'prelevata l'ora e il giorno e impostata la 19. 'variabile. Se potesse essere modificata 20. 'non avrebbe pi alcun significato 21.

Public ReadOnly Property CreationTime() As Date 22. Get 23. Return _CreationTime 24. End Get 25. End Property 26. 27. Public Sub New(ByVal Path As String) 28. _FileName = Path 29. _CreationTime = Date.Now 30. End Sub 31. End Class 32. End Module

Una nota sui tipi referenc e


C' ancor a un'ultima, ma impor tante, clausola da far notar e per le pr opr iet ReadOnly. Si gi vista la differ enza tr a i tipi value e i tipi r efer ence: i pr imi contengono un valor e, mentr e i secondi un puntator e all'ar ea di memor ia in cui r isiede l'oggetto voluto. A causa di questa par ticolar e str uttur a, legger e il valor e di un tipo r efer ence da una pr opr iet ReadOnly significa saper ne l'indir izzo, il che equivale ad ottener e il valor e dell'oggetto puntato. Non quindi assolutamente sbagliato scr iver e: 01. 02. 03. 04. 05. 06. 07. 08. 09. 10. 11. 12. 13. 14. 15. 16. Class ASystem Private _Box As Cube Public ReadOnly Property Box() As Cube Get Return _Box End Get End Property '... End Class '... Dim S As New ASystem() S.Box.SideLength = 4

In questo modo, noi staimo effettivamente modificando l'oggetto S.Box , ma indir ettamente: non stiamo, invece, cambiando il valor e del campo S._Box , che effettivamente ci che ci viene impedito di far e. In sostanza, non stiamo as s egn an do un nuovo oggetto alla var iabile S._Box , ma stiamo solo manipolando i dati di un oggetto esistente, e questo consentito. Anzi, molto meglio dichiar ar e pr opr iet di tipo r efer ence come ReadOnly quando non necessar io assegnar e o impostar e nuovi oggetti.

A22. Le Propriet - Parte III


Propriet c on parametri
Nei due capitoli pr ecedenti, ho sempr e scr itto pr opr iet che semplicemente r estituivano il valor e di un campo, ossia il codice del blocco Get non er a nulla di pi di un semplice Retur n. Intr oduciamo or a, invece, la possibilit di ottener e infor mazioni diver se dalla stessa pr opr iet specificando un par ametr o, pr opr io come fosse un metodo. Avr ete notato, infatti, che fin dal pr incipio c'er a una coppia di par entesi tonde vicino al nome della pr opr iet, ossia pr opr io la sintassi che si usa per dichiar ar e metodi senza par ametr i. Ecco un esempio: 01. Module Module1 02. 'Classe che rappresenta un estrattore di numeri 03. 'casuali Class NumberExtractor 04. Private _ExtractedNumbers() As Byte 05. 06. 'Generatore di numeri casuali. Random una classe 07. 'del namespace System Private Rnd As Random 08. 09. 'Questa propriet ha un parametro, Index, che 10. 'specifica a quale posizione dell'array si intende recarsi 11. 12. 'per prelevarne il valore. Nonostante l'array abbia solo 6 13. 'elementi di tipo Byte, l'indice viene comunemente sempre 14. 'indicato come intero a 32 bit. una specie di 15. 'convenzione, forse derivante dalla maggior facilit di 'elaborazione su macchine a 32 bit 16. 17. Public ReadOnly Property ExtractedNumbers(ByVal Index As Int32) As Byte 18. Get If (Index >= 0) And (Index < _ExtractedNumbers.Length) Then 19. Return _ExtractedNumbers(Index) 20. Else 21. Return 0 22. End If 23. End Get 24. End Property 25. 26. Public Sub New() 27. 'Essendo di tipo reference, si deve creare un nuovo 28. 'oggetto Random e assegnarlo a Rnd. La ragione per cui 29. 'Rnd un membro di classe consiste nel fatto 30. 'che se fosse stata variabile temporanea del corpo 31. 'della procedura ExtractNumbers, sarebbero usciti 32. 'gli stessi numeri. Questo perch la sequenza 33. 'pseudocasuale creata dalla classe non cambia se 34. 'non glielo si comunica espressamente usando un altro 35. 'costruttore. Non tratter questo argomento ora 36. Rnd = New Random() 37. ReDim _ExtractedNumbers(5) 38. End Sub 39. 40. Public Sub ExtractNumbers() 41. 'Estrae 6 numeri casuali tra 1 e 90 e li pone nell'array 42. For I As Int32 = 0 To 5 43. _ExtractedNumbers(I) = Rnd.Next(1, 91) 44. Next 45. End Sub 46. End Class 47. 48. Sub Main() 49. Dim E As New NumberExtractor() 50. 51. E.ExtractNumbers() 52. Console.WriteLine("Numeri estratti: ") 53. For I As Int32 = 0 To 5 54. Console.Write(E.ExtractedNumbers(I) & " ") 55. 56.

Next 57. 58. Console.ReadKey() 59. End Sub 60. End Module Notar e che sar ebbe stato logicamente equivalente cr ear e una pr opr iet che r estituisse tutto l'ar r ay, in questo modo: 1. Public ReadOnly Property ExtractedNumbers() As Byte() 2. Get 3. Return _ExtractedNumbers 4. End Get 5. End Property Ma non si sar ebbe avuto alcun contr ollo sull'indice che l'utente avr ebbe potuto usar e: nel pr imo modo, invece, possibile contr ollar e l'indice usato e r estituir e 0 qualor a esso non sia coer ente con i limiti dell'ar r ay. La r estituzione di elementi di una lista, tuttavia, solo una delle possibilit che le pr opr iet par ametr iche offr ono, e non c' limite all'uso che se ne pu far e. Nonostante ci, bene sottolinear e che meglio utilizzar e una funzione o una pr ocedur a (poich le pr opr iet di questo tipo possono anche non esser e r eadonly, questo er a solo un caso) qualor a si debbano eseguir e calcoli o elabor azioni non immediati, diciamo oltr e le 20/30 r ighe di codice, ma anche di meno, a seconda della pesantezza delle oper azioni. Fate conto che le pr opr iet debbano sempr e esser e il pi legger e possibile, computazionalmente par lando: qualche costr utto di contr ollo come If o Select, qualche calcolo sul Retur n, ma nulla di pi.

Propriet di default
Con questo ter mine si indica la pr opr iet pr edefinita di una classe. Per esister e, essa deve soddisfar e questi due r equisiti: Deve posseder e almeno un par ametr o; Deve esser e unica. Anche se solitamente si usa in altr e cir costanze, ecco una pr opr iet di default applicata al pr ecedente esempio: 01. Module Module1 02. Class NumberExtractor 03. Private _ExtractedNumbers() As Byte 04. Private Rnd As Random 05. 06. 'Una propriet di default si dichiara come una 07. 'normalissima propriet, ma anteponendo allo specificatore 08. 'di accesso la keyword Default 09. Default Public ReadOnly Property ExtractedNumbers(ByVal Index As Int32) As Byte 10. Get 11. If (Index >= 0) And (Index < _ExtractedNumbers.Length) Then 12. Return _ExtractedNumbers(Index) 13. Else 14. Return 0 15. End If 16. End Get 17. End Property 18. 19. Public Sub New() 20. Rnd = New Random() 21. ReDim _ExtractedNumbers(5) 22. End Sub 23. Public Sub ExtractNumbers() 24. For I As Int32 = 0 To 5 25. 26. _ExtractedNumbers(I) = Rnd.Next(1, 91) Next 27. End Sub 28. End Class 29. 30. 31. Sub Main() 32.

Dim E As New NumberExtractor() 33. 34. E.ExtractNumbers() 35. Console.WriteLine("Numeri estratti: ") 36. For I As Int32 = 0 To 5 37. 'Ecco l'utilit delle propriet di default: si possono 38. 'richiamare anche omettendone il nome. In questo caso 39. 'E(I) equivalente a scrivere E.ExtractedNumbers(I), 40. 'ma poich ExtractedNumbers di default, 41. 'viene desunta automaticamente 42. Console.Write(E(I) & " ") 43. Next 44. 45. Console.ReadKey() 46. End Sub 47. End Module Dal codice salta subito all'occhio la motivazione dei due pr er equisiti specificati inizialmente: Se la pr opr iet non avesse almeno un par ametr o, sar ebbe impossibile per il compilator e saper e quando il pr ogr ammator e si sta r ifer endo all'oggetto e quando alla sua pr opr iet di default; Se non fosse unica, sar ebbe impossibile per il compilator e decider e quale pr ender e. Le pr opr iet di default sono molto diffuse, specialmente nell'ambito degli oggetti w indow s for m, ma spesso non le si sa r iconoscer e. Anche per quello che abbiamo impar ato fin'or a, per , possiamo scovar e un esempio di pr opr iet di default. Il tipo Str ing espone una pr opr iet par ametr izzata Char s(I), che per mette di saper e quale car atter e si tr ova alla posizione I nella str inga, ad esempio: 1. 2. 3. 4. Dim S As String = "Ciao" Dim C As Char = S.Chars(1) ' > C = "i", poich "i" il carattere alla posizione 1 ' nella stringa S

Ebbene, Char s una pr opr iet di default, ossia possibile scr iver e: 1. Dim S As String = "Ciao" 2. Dim C As Char = S(1) 3. ' > C = "i"

Get e Set c on spec ific atori di ac c esso


Anche se a pr ima vista potr ebbe sembr ar e str ano, s, possibile assegnar e uno specificator e di accesso anche ai singoli blocchi Get e Set all'inter no di una pr opr iet. Questa peculiar e car atter istica viene sfr uttata ver amente poco, ma offr e una gr ande flessibilit e un'altr ettanto gr ande potenzialit di gestione. Limitando l'accesso ai singoli blocchi, possibile r ender e una pr opr iet ReadOnly solo per cer te par ti di codice e/o Wr iteOnly solo per altr e par ti, pur senza usar e dir ettamente tali keyw or ds. Ovviamente, per esser e logicamente applicabili, gli specificator i di accesso dei blocchi inter ni devono esser e pi r estr ittivi di quello usato per contr assegnar e la pr opr iet stessa. Infatti, se una pr opr iet pr ivata, ovviamente non potr aver e un blocco get pubblico. In gener e, la ger ar chia di r estr ittivit segue questa lista, dove Public il meno r estr ittivo e Pr ivate il pi r estr ittivo: Public Pr otected Fr iend Fr iend Pr otected Pr ivate Altr a condizione necessar ia che uno solo tr a Get e Set pu esser e mar cato con uno scope diver so da quello della

pr opr iet. Non avr ebbe senso, infatti, ad esempio, definir e una pr opr iet pubblica con un Get Fr iend e un Set Pr ivate, poich non sar ebbe pi pubblica (in quanto sia get che set non sono pubblici)! Ecco un esempio: 1. Public Property A() As Byte 2. Get 3. '... End Get 4. Private Set(ByVal value As Byte) 5. 6. '... 7. End Set 8. End Property La pr opr iet A sempr e leggibile, ma modificabile solo all'inter no della classe che la espone. In pr atica, come una nor male pr opr iet per il codice inter no alla classe, ma come una ReadOnly per quello ester no. pur ver o che in questo caso, si sar ebbe potuto r ender la dir ettamente ReadOnly e modificar e dir ettamente il campo da essa avvolto invece che espor r e un Set pr ivato, ma sono punti di vista. Ad ogni modo, l'uso di scope diver sificati per mette di far e di tutto e di pi ed solo un caso che non mi sia venuto in mente un esempio pi significativo.

Mettiamo un po' d'ordine sulle key w ord


In questi ultimi capitoli ho spiegato un bel po' di keyw or d diver se, e specialmente nelle pr opr iet pu accader e di dover specificar e molte keyw or d insieme. Ecco l'or dine cor r etto (anche se l'editor del nostr o ambiente di sviluppo le r ior dina per noi nel caso dovessimo sbagliar e): 1. [Default] [ReadOnly/WriteOnly] [Public/Friend/Private/...] Property ... E or a quelle che conoscete sono ancor a poche... pr ovate voi a scr iver e una pr opr iet del gener e: 1. Default Protected Friend Overridable Overloads ReadOnly Property A(ByVal Index As Int32) As Byte 2. Get 3. '... End Get 4. 5. End Property

N.B.: ovviamente, tutto quello che si detto fin'or a sulle pr opr iet nelle classi vale anche per le str uttur e!

A23. Membri Shared


Tutte le categor ie di membr i che abbiamo analizzato nei pr ecedenti capitoli - campi, metodi, pr opr iet, costr uttor i sono sempr e state viste come appar tenenti ad un oggetto, ad un'istanza di classe. Infatti, ci si r ifer isce ad una pr opr iet o a un metodo di un o s pecifico oggetto, dicendo ad esempio "La pr opr iet SideLength dell'oggetto A di tipo Cube vale 3, mentr e quella dell'oggetto B anch'esso di tipo Cube vale 4.". La classe, ossia il tipo di una var iabile r efer ence, ci diceva solo quali membr i un cer to oggetto potesse espor r e: ci for niva, quindi, il "pr ogetto di costr uzione" di un oggetto nella memor ia, in cui si potevano collocar e tali campi, tali metodi, tal'altr e pr opr iet e via dicendo. Or a, un membr o shar ed, o co ndiv iso , o statico (ter mine da usar si pi in C# che non in VB.NET), non appar tiene pi ad un'istanza di classe, ma alla classe stessa. Mi r endo conto che il concetto possa esser e all'inizio difficile da capir e e da inter ior izzar e cor r ettamente. Per questo motivo far un esempio il pi semplice, ma pi significativo possibile. Ripr endiamo la classe Cube, modificata come segue: 01. Module Module1 02. Class Cube 03. Private _SideLength As Single 04. Private _Density As Single 05. 06. 07. 'Questo campo Shared, condiviso. Come vedete, 'per dichiarare un membro come tale, si pone la 08. 09. 'keyword Shared dopo lo specificatore di accesso. Questa 'variabile conterr il numero di cubi creati 10. 11. 'dal nostro programma. 12. 'N.B.: I campi Shared sono di default Private... 13. Private Shared _CubesCount As Int32 = 0 14. 15. Public Property SideLength() As Single Get 16. 17. Return _SideLength 18. End Get Set(ByVal value As Single) 19. If value > 0 Then 20. _SideLength = value 21. Else 22. _SideLength = 1 23. End If 24. End Set 25. End Property 26. 27. Public Property Density() As Single 28. Get 29. Return _Density 30. End Get 31. Set(ByVal value As Single) 32. If value > 0 Then 33. _Density = value 34. Else 35. _Density = 1 36. End If 37. End Set 38. End Property 39. 40. Public ReadOnly Property SurfaceArea() As Single 41. Get 42. Return (SideLength ^ 2) 43. End Get 44. End Property 45. 46. Public ReadOnly Property Volume() As Single 47. Get 48. Return (SideLength ^ 3) 49. 50.

51. 52. 53. 54. 55. 56. 57. 58. 59. 60. 61. 62. 63. 64. 65. 66. 67. 68. 69. 70. 71. 72. 73. 74. 75. 76. 77. 78. 79. 80. 81. 82. 83. 84. 85. 86. 87. 88. 89. 90. 91. 92. 93. 94. 95. End

End Get End Property Public ReadOnly Property Mass() As Single Get Return (Volume * Density) End Get End Property 'Allo stesso modo, la propriet che espone il membro 'shared deve essere anch'essa shared Public Shared ReadOnly Property CubesCount() As Int32 Get Return _CubesCount End Get End Property 'Ogni volta che un nuovo cubo viene creato, _CubesCount 'viene aumentato di uno, per rispecchiare il nuovo numero 'di istanze della classe Cube esistenti in memoria Sub New() _CubesCount += 1 End Sub End Class Sub Main() Dim Cube1 As New Cube() Cube1.SideLength = 1 Cube1.Density = 2700 Dim Cube2 As New Cube() Cube2.SideLength = 0.9 Cube2.Density = 3500 Console.Write("Cubi creati: ") 'Notate come si accede a un membro condiviso: poich 'appartiene alla classe e non alla singola istanza, vi si 'accede specificando prima il nome della classe, poi 'il comune operatore punto, e successivamente il nome 'del membro. Tutti i membri shared funzionano in questo 'modo Console.WriteLine(Cube.CubesCount) Console.ReadKey() End Sub Module

Facendo cor r er e l'applicazione, si vedr appar ir e a scher mo il numer o 2, poich abbiamo cr eato due oggetti di tipo Cube. Come si vede, il campo CubesCount non r iguar da un solo specifico oggetto, ma la totalit di tutti gli oggetti di tipo Cube, poich un dato globale. In gener ale, esso di dominio della classe Cube, ossia della r appr esentazione pi astr atta dell'essenza di ogni oggetto: per saper e quanti cubi sono stati cr eati, non si pu inter pellar e una singola istanza, per ch essa non "ha per cezione" di tutte le altr e istanze esistenti. Per questo motivo CubesCount un membr o condiviso. Anche in questo caso c' una r istr etta gamma di casi in cui oppor tuno sceglier e di definir e un membr o come condiviso: Quando contiene infor mazioni r iguar danti la totalit delle istanze di una classe, come in questo caso; Quando contiene infor mazioni accessibili e necessar ie a tutte le istanze della classe, come illustr er fr a qualche capitolo; Quando si tr atta di un metodo "di libr er ia". I cosiddetti metodi di libr er ia sono metodi sempr e shar ed che svolgono funzioni gener ali e sono utilizzabili da qualsiasi par te del codice. Un esempio potr ebbe esser e la funzione Math.Abs(x ), che r estituisce il valor e assoluto di x . Come si vede, shar ed poich vi si accede usando il nome della classe. Inoltr e, essa sempr e usabile, poich si tr atta di una semplice funzione matematica, che, quindi, for nisce ser vizi di or dine gener ale;

Quando si tr ova in un modulo, come spiegher nel pr ossimo par agr afo.

Classi Shared
Come!?!? Esistono classi shar ed? Ebbene s. Pu sembr ar e assur do, ma ci sono, ed lecito domandar si quale sia la lor o funzione. Se un membr o shar ed appar tiene a una classe... cosa possiamo dir e di una classe shar ed? A dir e il ver o, abbiamo sempr e usato classi shar ed senza saper lo: i moduli, infatti, non sono altr o che classi condivise (o statiche). Tuttavia, il significato della par ola shar ed, se applicato alle classi, cambia r adicalmente. Un modulo, quindi una classe shar ed, r ende implicitamente shar ed tutti i suoi membr i. Quindi, tutte le pr opr iet, i campi e i metodi appar tenenti ad un modulo - ivi compr esa la Sub Main - sono membr i shar ed. Che senso ha questo? I moduli sono consuetamente usati, al di fuor i delle applicazioni console, per r ender e disponibili a tutto il pr ogetto membr i di par ticolar e r ilevanza o utilit, ad esempio funzioni per il salvataggio dei dati, infor mazioni sulle opzioni salvate, ecceter a... Infatti impossibile definir e un membr o shar ed in un modulo, poich ogni membr o del modulo lo gi di per s: 1. Module Module1 2. Shared Sub Hello() 3. End Sub 4. 5. '... 6. 7. End Sub Il codice sopr a r ipor tato pr ovocher il seguente er r or e: 1. Methods in a Module cannot be declared 'Shared'. Inoltr e, anche possibile acceder e a membr i di un modulo senza specificar e il nome del modulo, ad esempio: 01. 02. 03. 04. 05. 06. 07. 08. 09. 10. 11. 12. Module Module2 Sub Hello() Console.WriteLine("Hello!") End Sub End Module Module Module1 Sub Main() Hello() ' = Module2.Hello() Console.ReadKey() End Sub End Module

Questo fenomeno anche noto col nome di Im po r ts statico . A dir la ver it esiste una piccola differ enza tr a classi statiche e moduli. Una classe pu esser e statica anche solo se tutti i suoi membr i lo sono, ma non gode dell'Impor ts Statico. Un modulo, al contr ar io, oltr e ad aver e tutti i membr i shar ed, gode sempr e dell'Impor ts Statico. Per far la br eve: 01. 02. 03. 04. 05. 06. 07. 08. 09. 10. 11. 12. 13. 14. 15. Module Module2 Sub Hello() Console.WriteLine("Hello Module2!") End Sub End Module Class Class2 Shared Sub Hello() Console.WriteLine("Hello Class2!") End Sub End Class Module Module1 Sub Main()

'Per richiamare l'Hello di Class2, sempre 16. 'necessaria questa sintassi: 17. Class2.Hello() 18. 'Per invocare l'Hello di Module2, invece, basta 19. 'questa, a causa dell'Imports Statico 20. Hello() 21. Console.ReadKey() 22. End Sub 23. End Module

A24. ArrayList, HashTable e SortedList


Abbiamo gi ampiamente visto e illustr ato il funzionamento degli ar r ay. Ho anche gi detto pi volte come essi non siano sempr e la soluzione miglior e ai nostr i pr oblemi di immagazzinamento dati. Infatti, difficile decider ne la dimensione quando non si sa a pr ior i quanti dati ver r anno immessi: inoltr e, oner oso in ter mini di tempo e r isor se modificar ne la lunghezza mentr e il pr ogr amma gir a; e nel caso contr ar io, molto limitativo conceder e all'utente un numer o pr efissato massimo di valor i. A questo pr oposito, ci vengono in aiuto delle classi gi pr esenti nelle libr er ie standar d del Fr amew or k .NET che aiutano pr opr io a gestir e insiemi di elementi di lunghezza var iabile. Di seguito ne pr opongo una br eve panor amica.

Array List
Si tr atta di una classe per la gestione di liste di elementi. Essendo un tipo r efer ence, quindi, segue che ogni oggetto dichiar ato come di tipo Ar r ayList debba esser e inizializzato pr ima dell'uso con un adeguato costr uttor e. Una volta cr eata un'istanza, la si pu utilizzar e nor malmente. La differ enza con l'Ar r ay r isiede nel fatto che l'Ar r ayList, all'inizio della sua "vita", non contiene nessun elemento, e, di conseguenza occupa r elativamente meno memor ia. Infatti, quando noi inizializziamo un ar r ay, ad esempio cos: 1. Dim A(100) As Int32 nel momento in cui questo codice viene eseguito, il pr ogr amma r ichiede 101 celle di memor ia della gr andezza di 4 bytes ciascuna da r iser var e per i pr opr i dati: che esse siano impostate o meno (all'inizio sono tutti 0), non ha impor tanza, per ch A occuper sempr e la stessa quantit di memor ia. Al contr ar io l'Ar r ayList non "sa" nulla su quanti dati vor r emmo intr odur r e, quindi, ogni volta che un nuovo elemento viene intr odotto, esso si es pan de allocando dinamicamente nuova memor ia solo se ce n' bisogno. In questo r isiede la potenza delle liste. Per aggiunger e un nuovo elemento all'ar r aylist bisogna usar e il metodo d'istanza Add, passandogli come par ametr o il valor e da aggiunger e. Ecco un esempio: 01. Module Module1 02. Class Cube 03. 04. '... 05. End Class 06. 07. Sub Main() 'Crea un nuovo arraylist 08. 09. Dim Cubes As New ArrayList 10. 11. Console.WriteLine("Inserismento cubi:") 12. Console.WriteLine() 13. Dim Cmd As Char 14. Do 15. Console.WriteLine() 16. Dim C As New Cube 17. 'Scrive il numero del cubo 18. Console.Write((Cubes.Count + 1) & " - ") Console.Write("Lato (m): ") 19. 20. C.SideLength = Console.ReadLine 21. 22. Console.Write(" Densit (kg/m<sup>3</sup>): ") C.Density = Console.ReadLine 23. 24. 25. 'Aggiunge un nuovo cubo alla collezione Cubes.Add(C) 26. 27. Console.WriteLine("Termina inserimento? y/n") 28. 29.

Cmd = Console.ReadKey().KeyChar 30. Loop Until Char.ToLower(Cmd) = "y" 31. 32. 'Calcola la massa totale di tutti i cubi nella lista 33. Dim TotalMass As Single = 0 34. 'Notate che l'ArrayList si pu usare come un 35. 'normale array. L'unica differenza sta nel fatto che 36. 'esso espone la propriet Count al posto di Length. 37. 'In genere, tutte le liste espongono Count, che comunque 38. 'ha sempre lo stesso significato: restituisce il numero 39. 'di elementi nella lista 40. For I As Int32 = 0 To Cubes.Count - 1 41. TotalMass += Cubes(I).Mass 42. Next 43. 44. Console.WriteLine("Massa totale: " & TotalMass) 45. Console.ReadKey() 46. End Sub 47. End Module Allo stesso modo, possibile r imuover e o inser ir e elementi con altr i metodi: Remove(x ) : r imuove l'elemento x dall'ar r aylist RemoveAt(x ) : r imuove l'elemento che si tr ova nella posizione x dell'Ar r ayList Index Of(x ) : r estituisce l'indice dell'elemento x Contains(x ) : r estituisce Tr ue se x contenuto nell'Ar r ayList, altr imenti False Clear : pulisce l'ar r aylist eliminando ogni elemento Clone : r estituisce una copia esatta dell'Ar r ayList. Questo ar gomento ver r discusso pi in l nella guida.

Hashtable
L'Hashtable possiede un meccanismo di allocazione della memor ia simile a quello di un Ar r ayList, ma concettualmente differ ente in ter mini di utilizzo. L'Ar r ayList, infatti, non si discosta molto, par lando di pr atica, da un Ar r ay - e infatti vediamo questa somiglianza nel nome: ogni elemento pur sempr e contr addistinto da un indice, e mediante questo possibile ottener ne o modificar ne il valor e; inoltr e, gli indici sono sempr e su base 0 e sono sempr e numer i inter i, gener almente a 32 bit. Quest'ultima peculiar it ci per mette di dir e che in un Ar r ayList gli elementi sono logicamente or dinati. In un Hashtable, al contr ar io, tutto ci che ho esposto fin'or a non vale. Questa nuova classe si basa sull'associazione di una chiav e (key) con un v alo r e (value). Quando si aggiunge un nuovo elemento all'Hashtable, se ne deve specificar e la chiave, che pu esser e qualsiasi cosa: una str inga, un numer o, una data, un oggetto, ecceter a... Quando si vuole r ipescar e quello stesso elemento bisogna usar e la chiave che gli er a stata associata. Usando numer i inter i come chiavi si pu s imulare il compor tamento di un Ar r ayList, ma il meccanismo intr inseco di questo tipo di collezione r imane pur sempr e molto diver so. Ecco un esempio: 01. 02. 03. 04. 05. 06. 07. 08. 09. 10. 11. 12. 13. 'Hashtabel contenente alcuni materiali e le 'relative densit Dim H As New Hashtable 'Aggiunge un elemento, contraddistinto da una chiave stringa H.Add("Acqua", 1000) H.Add("Alluminio", 2700) H.Add("Argento", 10490) H.Add("Nichel", 8800) '... 'Possiamo usare l'hashtable per associare 'facilmente densit ai nostri cubi: Dim C As New Cube(1, H("Argento"))

Notar e che anche possibile far e il contr ar io, ossia: 1. Dim H As New Hashtable 2.

H.Add(1000, "Acqua") 3. H.Add(2700, "Alluminio") 4. H.Add(10490, "Argento") 5. H.Add(8800, "Nichel") In quest'ultimo esempio, l'Hashtable contiene quattr o chiavi costituite da valor i numer ici: non comunque possibile ciclar le usando un For . Infatti, negli Ar r ayList e negli Ar r ay, abbiamo la gar anzia che se la collezione contiene 8 elementi, ad esempio, ci sar anno sempr e degli indici inter i validi tr a 0 e 7; con gli Hashtable, al contr ar io, non possiamo desumer e n ulla sulle chiavi osser vando il semplice numer o di elementi. In gener e, per iter ar e attr aver so gli elementi di un Hashtable, si usano dei costr utti For Each: 1. For Each V As String In H.Values 2. 'Enumera tutti gli elementi di H 3. ' V = "Acqua", "Alluminio", "Argento", ... 4. Next

1. For Each K As Int32 In H.Keys 2. 'Enumera tutte le chiavi 3. 'K = 1000, 2700, 10490, ... 4. Next Per l'iter azione ci vengono in aiuto le pr opr iet Values e Keys, che contengono r ispettivamente tutti i valor i e tutte le chiavi dell'Hashtable: queste collezioni sono a sola lettur a, ossia non possibile modificar le in alcun modo. D'altr onde, abbastanza ovvio: se aggiungessimo una chiave l'Hashtable non sapr ebbe a quale elemento associar la. L'unico modo per modificar le indir etto e consiste nell'usar e metodi come Add, Remove, ecceter a... che sono poi gli stessi di Ar r ayList.

SortedList
Si compor ta esattamente come un Hashtable, solo che gli elementi vengono mantenuti sempr e in or dine secondo la chiave.

A25. Metodi factory


Si definisce Factor y un metodo che ha come unico scopo quello di cr ear e una nuova istanza di una classe e r estituir e tale istanza al chiamante (dato che si par la di "r estituir e", i metodi Factor y sar anno sempr e funzioni). Or a, ci si potr ebbe chieder e per ch usar e metodi factor y al posto di nor mali costr uttor i. La differ enza tr a questi non da sottovalutar e: i costr uttor i ser vono ad istanziar e un oggetto, ma, una volta avviati, non possono "fer mar si". Con questo voglio dir e che, qualor a venisser o r iscontr ati degli er r or i nei par ametr i di cr eazione dell'istanza (nel caso ce ne siano), il costr uttor e cr eer ebbe comunque un nuovo oggetto, ma molto pr obabilmente quest'ultimo conter r ebbe dati er r onei. Un metodo Factor y, invece, contr olla che tutto sia a posto pr im a di cr ear e il nuovo oggetto: in questo modo, se c' qualcosa che non va, lo pu comunicar e al pr ogr ammator e (o all'utente), ad esempio lanciando un'eccezione o visualizzando un messaggio di er r or e. E' convenzione - ma anche logica - che un metodo Factor y sia definito sempr e all'inter no della stessa classe che cor r isponde al suo tipo di output e che sia Shar ed (altr imenti non si potr ebbe r ichiamar e pr ima della cr eazione dell'oggetto, ovviamente). Un esempio di quanto detto: 01. Module Module1 02. Class Document 03. 'Campo statico che contiene tutti i documenti 04. 'aperi fin'ora 05. Private Shared Documents As New Hashtable 06. 'Identificatore del documento: un paragrafo nel prossimo 07. 'capitolo spiegher in dettaglio i significato e 08. 'l'utilit delle variabili ReadOnly 09. Private ReadOnly _ID As Int16 10. 'Nome del file e testo contenuto in esso 11. Private ReadOnly _FileName, _Text As String 12. 13. Public ReadOnly Property ID() As Int16 14. Get 15. Return _ID 16. End Get 17. End Property 18. 19. Public ReadOnly Property FileName() As String Get 20. Return _FileName 21. 22. End Get End Property 23. 24. Public ReadOnly Property Text() As String 25. 26. Get Return _Text 27. End Get 28. End Property 29. 30. 31. 'Da notare il costruttore Private: nessun client al di 'fuori della classe pu inizializzare il nuovo 32. 'oggetto. Solo il metodo factory lo pu fare 33. Private Sub New(ByVal ID As Int16, ByVal Path As String) 34. Me._ID = ID 35. Me._FileName = Path 36. Me._Text = IO.File.ReadAllText(Path) 37. 'Me fa riferimento alla classe stessa 38. Documents.Add(ID, Me) 39. End Sub 40. 41. 'Il metodo factory crea un documento se non esiste l'ID 42. 'e se il percorso su disco diverso, altrimenti 43. 'restituisce il documento che esiste gi 44. Public Shared Function Create(ByVal ID As Int16, _ 45. ByVal Path As String) As Document 46. If Documents.ContainsKey(ID) Then 47. 'Ottiene il documento gi esistente con questo ID 48. 49.

50. 51. 52. 53. 54. 55. 56. 57. 58. 59. 60. 61. 62. 63. 64. 65. 66. 67. 68. 69. 70. 71. 72. 73. 74. 75. 76. 77. 78. 79. 80. End

Dim D As Document = Documents(ID) 'Se coincidono sia l'ID che il nome del file, 'allora restituisce l'oggetto gi esistente If D.FileName = Path Then Return D Else 'Altrimenti restituisce Nothing, dato che non 'possono esistere due documenti con uguale ID, 'o si farebbe confusione Return Nothing End If End If 'Se non esiste un documento con questo ID, lo crea Return New Document(ID, Path) End Function End Class Sub Main() Dim D As Dim E As Dim F As Dim G As Document Document Document Document = = = = Document.Create(0, Document.Create(0, Document.Create(0, Document.Create(1, "C:\testo.txt") "C:\testo.txt") "C:\file.txt") "C:\file.txt")

'Dimostra che se ID e Path coincidono, i due oggetti 'sono la stessa istanza Console.WriteLine(E Is D) 'Dimostra che se l'ID esiste gi, ma il Path differisce, 'l'oggetto restituito Nothing Console.WriteLine(F Is Nothing) Console.ReadKey() End Sub Module

Il codice sopr a r ipor tato cr ea volutamente tutte le situazioni contemplate all'inter no del metodo factor y statico: E ha gli stessi par ametr i di D, quindi nel metodo factor y usato per cr ear e E viene r estituita l'istanza D gi esistente; F ha lo stesso ID, quindi Nothing. A pr ova di ci, sullo scher mo appar ir il seguente output: 1. True 2. True

Classi fac tory e oggetti immutabili


Una classe contenente solo metodi factor y detta classe factor y. Il pi delle volte, l'uso di una tattica simile a quella sopr a r ipor tata potr ebbe por tar e alcuni dubbi: dato che esistono due var iabili che puntano alla stessa istanza, il modificar ne l'una potr ebbe causar e l'automatica modifica dell'altr a. Tuttavia, spesse volte, gli oggetti che possono esser e cr eati con metodi factor y non espongono alcun altr o metodo per la modifica o l'eliminazione dello stesso oggetto, che quindi non pu esser e cambiato in alcun modo. Oggetti di questo tipo sono detti im m utabili: un esempio di oggetti immutabili sono la str inghe. Al contr ar io di come si potr ebe pensar e, una volta cr eate il lor o valor e non pu esser e cambiato: l'unica cosa che si pu far e assegnar e alla var iabile str inga un nuovo valor e: 1. 2. 3. 4. 5. 'Questa stringa Dim S As String = 'Viene creata una 'e assegnata a S. S = "Buongiorno" immutabile "Ciao" nuova stringa temporanea con valore "Buongiorno" "Ciao" verr distrutta dal Garbage Colletcion

A26. Costruttori
Come si accennato nelle pr ecedenti lezioni, i costr uttor i ser vono a cr ear e un oggetto, un'istanza mater iale della classe. Ogni costr uttor e, poich ce ne pu esser e anche pi di uno, sempr e dichiar ato usando la keyw or d New e non pu esser e altr imenti. Si possono passar e par ametr i al costr uttor e allo stesso modo di come si passano alle nor mali pr ocedur e o funzioni, specificandoli tr a par entesi. Il codice scr itto nel costr uttor e viene eseguito pr ima di ogni altr o metodo nella classe, per ci pu anche modificar e le var iabili r ead-only (in sola lettur a), come vedr emo in seguito. Anche i moduli possono aver e un costr uttor e e questo viene eseguito pr ima della pr ocedur a Main. Una cosa da tener e bene a mente che, nonostante New sia eseguito pr ima di ogni altr a istr uzione, sia le costanti sia i campi con inizializzator e (ad esempio Dim I As Int32 = 50) sono gi stati inizializzati e contengono gi il lor o valor e. Esempio: 01. Module Module1 02. 'Classe 03. Class Esempio 'Costante pubblica 04. Public Const Costante As Byte = 56 05. 'Variabile pubblica che non pu essere modificata 06. 07. Public ReadOnly Nome As String 'Variabile privata 08. Private Variabile As Char 09. 10. 'Costruttore della classe: accetta un parametro 11. 12. Sub New(ByVal Nome As String) 13. Console.WriteLine("Sto inizializzando un oggetto Esempio...") 'Le variabili ReadOnly sono assegnabli solo nel 14. 'costruttore della classe 15. Me.Nome = Nome 16. 17. Me.Variabile = "c" 18. End Sub End Class 19. 20. 'Costruttore del Modulo 21. Sub New() 22. Console.WriteLine("Sto inizializzando il Modulo...") 23. End Sub 24. 25. Sub Main() 26. Dim E As New Esempio("Ciao") 27. E.Nome = "Io" ' Sbagliato: Nome ReadOnly 28. Console.ReadKey() 29. End Sub 30. 31. End Module Quando si fa cor r er e il pr ogr amma si ha questo output: 1. Sto inizializzando il Modulo... 2. Sto inizializzando un oggetto Esempio... L'esempio mostr a l'or dine in cui vengono eseguiti i costr uttor i: pr ima viene inizializzato il modulo, in seguito viene inizializzato l'oggetto E, che occupa la pr ima linea di codice della pr ocedur a Main. evidente che Main viene eseguita dopo New .

V ariabili ReadOnly
Ho par lato pr ima delle var iabili ReadOnly e ho detto che possono solamente esser e lette ma non modificate. La domanda che viene spontaneo por si : non sar ebbe meglio usar e una costante? La differ enza pi mar cata di quanto sembr i: le costanti devono esser e inizializzate con un valor e immutabile, ossia che definisce il pr ogr ammator e mentr e scr ive il codice (ad esempio, 1, 2, "Ciao" ecceter a); la var iabili ReadOnly possono esser e impostate nel costr uttor e, ma,

cosa pi impor tante, possono assumer e il valor e der ivante da un'espr essione o da una funzione. Ad esempio: 1. Public Const Data_Creazione_C As Date = Date.Now 'Sbagliato! 2. Public ReadOnly Data_Creazione_V As Date = Date.Now ' Giusto La pr ima istr uzione gener a un er r or e "Costant ex pr ession is r equir ed!" (" r ichiesta un'espr essione costante!"), der ivante dal fatto che Date.Now una funzione e, come tale, il suo valor e, pur pr eso una sola volta, non costante, ma pu var iar e. Non si pone nessun pr oblema, invece, per le var iabili ReadOnly, poich sono sempr e var iabili.

Costruttori Shared
I costr uttor i Shar ed sono detti co str utto r i statici e vengono eseguiti solamente quando cr eata la pr im a istanza di una data classe: per questo sono detti anche co str utto r i di classe o di tipo poich non appar tengono ad ogni singolo oggetto che da quella classe pr ende la str uttur a, ma piuttosto alla classe stessa (vedi differ enza tr a classe e oggetto). Un esempio di una possibile applicazione pu esser e questo: si sta scr ivendo un pr ogr amma che tiene tr accia di ogni er r or e r ipor tandolo su un file di log, e gli er r or i vengono gestiti da una classe Er r or s. Data la str uttur a dell'applicazione, possono esister e pi oggetti di tipo Er r or s, ma tutti devono condivider e un file comune... Come si fa? Costr uttor e statico! Questo fa in modo che si apr a il file di log solamente una volta, ossia quando viene istanziato il pr imo oggetto Er r or s. Esempio: 01. Module Esempio 02. Class Errors 03. 'Variabile statica che rappresenta un oggetto in grado 04. 'di scrivere su un file 05. Public Shared File As IO.StreamWriter 06. 07. 'Costruttore statico che inizializza l'oggetto StreamWriter 'Da notare che un costruttore statico NON pu avere 08. 09. 'parametri: il motivo semplice. Se li potesse avere 'e ci fossero pi costruttori normali il compilatore 10. 11. 'non saprebbe cosa fare, poich Shared Sub New 12. 'potrebbe avere parametri diversi dagli altri 13. Shared Sub New() 14. Console.WriteLine("Costruttore statico: sto creando il log...") 15. File = New IO.StreamWriter("Errors.log") 16. End Sub 17. 18. 'Questo il costruttore normale Sub New() 19. Console.WriteLine("Costruttore normale: sto creando un oggetto...") 20. 21. End Sub 22. Public Sub WriteLine(ByVal Text As String) 23. 24. File.WriteLine(Text) End Sub 25. End Class 26. 27. Sub Main() 28. 'Qui viene eseguito il costruttore statico e quello normale 29. Dim E1 As New Errors 30. 'Qui solo quello normale 31. Dim E2 As New Errors 32. 33. E1.WriteLine("Nessun errore") 34. 35. Console.ReadKey() 36. End Sub 37. 38. End Module L'ouput : 1. Costruttore statico: sto creando il log... 2. Costruttore normale: sto creando un oggetto... 3. Costruttore normale: sto creando un oggetto...

Questo esempio evidenzia bene come vengano eseguiti i costr uttor i: mentr e si cr ea il pr imo oggetto Er r or s in assoluto viene eseguito quello statico e in pi anche quello nor male, per i successivi, invece, solo quello nor male. Ovviamente non tr over e il file Er r or s.log con la scr itta "Nessun er r or e" poich nell'esempio il file non stato chiuso. Ripr ender emo lo stesso discor so con i distr uttor i.

Costruttori Friend e Private


I costr uttor i possono esser e specificati come Fr iend e Pr ivate pr opr io come ogni altr o membr o di classe. Tuttavia l'uso degli specificator i di accesso sui costr uttor i ha par ticolar i effetti collater ali. Dichiar ar e un costr uttor e Pr ivate, ad esempio, equivale e impor r e che niente possa inizializzar e l'oggetto al di fuor i della classe stessa: questo caso par ticolar e stato analizzato nella lezione pr ecedente con i metodi factor y statici e ser ve a r ender e obbligator io l'uso di questi ultimi. Un costr uttor e Fr iend invece r ende la classe inizializzabile da ogni par te del pr ogetto cor r ente: se un client ester no utilizzasse la classe impor tandola da una libr er ia (vedi oltr e) non potr ebbe usar ne il costr uttor e.

A27. Gli Operatori


Gli oper ator i sono speciali metodi che per mettono di eseguir e, appunto, oper azioni tr a due valor i mediante l'uso di un simbolo (ad esempio, + per la somma, - per la differ enza, ecceter a...). Quando facciamo i calcoli, comunemente usando i tipi base numer ici del Fr amew or k, come Int16 o Double, usiamo pr aticamente sempr e degli oper ator i. Essi non sono nulla di "str aor dinar io", nel senso che anche se non sembr a, data la lor o par ticolar e sintassi, sono pur sempr e definiti all'inter no delle var ie classi come nor mali membr i (statici). Gli oper ator i, come i tipi base, del r esto, non si sottr aggono alla globale astr azione degli linguaggi or ientati agli oggetti: tutto sempr e incasellato al posto giusto in una qualche classe. Ma questo lo vedr emo pi avanti quando par ler della Reflection. Sor volando su questa br eve par entesi idilliaca, tor niamo all'aspetto pi concr eto di questo capitolo. Anche il pr ogr ammator e ha la possibilit di defin ire nuovi oper ator i per i tipi che ha cr eato: ad esempio, pu scr iver e oper ator i che oper ino tr a str uttur e e tr a classi. In gener e, si pr efer isce adottar e gli oper ator i nel caso delle str uttur e poich, essendo tipi value, si pr estano meglio - come idea, pi che altr o - al fatto di subir e oper azioni tr amite simboli. Venendo alla pr atica, la sintassi gener ale di un oper ator e la seguente: 1. Shared Operator [Simbolo]([Parametri]) As [Tipo Restituito] 2. '... 3. Return [Risultato] 4. End Operator Come si vede, la sintassi molto simile a quella usata per dichiar ar e una funzione, ad eccezione della keyw or d e dell'identificator e. Inoltr e, per far s che l'oper ator e sia non solo sintatticamente, ma anche semanticamente valido, devono esser e soddisfatte queste condizioni: L'oper ator e deve SEM PRE esser e dichiar ato come Shar ed, ossia statico. Infatti, l'oper ator e r ientr a nel dominio della classe in s e per s, appar tiene al tipo, e non ad un'istanza in par ticolar e. Infatti, l'oper ator e pu esser e usato per eseguir e oper azioni tr a tutte le istanze possibili della classe. Anche se viene definito in una str uttur a, deve comunque esser e Shar ed. Infatti, sebbene il concetto di str uttur a si pr esti di meno a questa "visione" un po' assiomatica del concetto di istanza, pur sempr e ver o che possono esister e tante var iabili diver se contenenti dati diver si, ma dello stesso tipo str uttur ato. L'oper ator e pu specificar e al m assim o due par ametr i (si dice unar io se ne specifica uno, e binar io se due), e di questi almeno uno DEVE esser e dello stesso tipo in cui l'oper ator e definito - tipicamente il pr imo dei due deve soddisfar e questa seconda condizione. Questo r isulta abbastanza ovvio: se avessimo una str uttur a Fr azione, come fr a poco mostr er , a cosa ser vir ebbe dichiar ar vi all'inter no un oper ator e + definito tr a due numer i inter i? A par te il fatto che esiste gi, logico aspettar si che, dentr o un nuovo tipo, si descr ivano le istr uzioni necessar ie ad oper ar e con quel nuovo tipo, o al massimo ad attuar e calcoli tr a questo e i tipi gi esistenti. Il simbolo che contr addistingue l'oper ator e dev e esser e scelto tr a quelli disponibili, di cui qui r ipor to un elenco con annessa descr izione della funzione che usualmente l'oper ator e r icopr e: + (somma) - (differ enza) * (pr odotto) / (divisione) \ (divisione inter a) ^ (potenza) & (concatenazione) = (uguaglianza) > (maggior e)

< (minor e) >= (maggior e o uguale) <= (minor e o uguale) >> (shift destr o dei bit) << (shift sinistr o dei bit) And (inter sezione logica) Or (unione logica) Not (negazione logica) Xor (aut logico) Mod (r esto della divisione inter a) Like (r icer ca di un patter n: di solito il pr imo ar gomento indica dove cer car e e il secondo cosa cer car e) IsTr ue ( ver o) IsFalse ( falso) CType (conver sione da un tipo ad un altr o) Sintatticamente par lando, nulla vieta di usar e il simbolo And per far e una somma, ma sar ebbe meglio attener si alle nor mali nor me di utilizzo r ipor tate. Ed ecco un esempio: 001. Module Module1 002. 003. Public Structure Fraction 004. 'Numeratore e denominatore 005. Private _Numerator, _Denumerator As Int32 006. 007. Public Property Numerator() As Int32 008. Get 009. Return _Numerator 010. End Get 011. Set(ByVal value As Int32) 012. _Numerator = value 013. End Set 014. End Property 015. 016. Public Property Denumerator() As Int32 017. Get 018. Return _Denumerator 019. End Get Set(ByVal value As Int32) 020. 021. If value <> 0 Then 022. _Denumerator = value 023. Else 'Il denominatore non pu mai essere 0 024. 'Dovremmo lanciare un'eccezione, ma vedremo pi 025. 026. 'avanti come si fa. Per ora lo impostiamo a uno _Denumerator = 1 027. End If 028. End Set 029. End Property 030. 031. 'Costruttore con due parametri, che inizializza numeratore 032. 'e denominatore 033. Sub New(ByVal N As Int32, ByVal D As Int32) 034. Me.Numerator = N 035. Me.Denumerator = D 036. End Sub 037. 038. 'Restituisce la Fraction sottoforma di stringa 039. Function Show() As String 040. Return Me.Numerator & " / " & Me.Denumerator 041. End Function 042. 043. 'Semplifica la Fraction 044. Sub Semplify() 045. 046.

047. 048. 049. 050. 051. 052. 053. 054. 055. 056. 057. 058. 059. 060. 061. 062. 063. 064. 065. 066. 067. 068. 069. 070. 071. 072. 073. 074. 075. 076. 077. 078. 079. 080. 081. 082. 083. 084. 085. 086. 087. 088. 089. 090. 091. 092. 093. 094. 095. 096. 097. 098. 099. 100. 101. 102. 103. 104. 105. 106. 107. 108. 109. 110. 111. 112. 113. 114. 115. 116. 117. 118.

Dim X As Int32 'Prende X come il valore meno alto in modulo 'e lo inserisce in X. X servir per un 'calcolo spicciolo del massimo comune divisore X = Math.Min(Math.Abs(Me.Numerator), Math.Abs(Me.Denumerator)) 'Prima di iniziare, per evitare errori, controlla 'se numeratore e denominatore sono entrambi negativi: 'in questo caso li divide per -1 If (Me.Numerator < 0) And (Me.Denumerator < 0) Then Me.Numerator /= -1 Me.Denumerator /= -1 End If 'E con un ciclo scova il valore pi alto di X 'per cui sono divisibili sia numeratore che denominatore '(massimo comune divisore) e li divide per quel numero. 'Continua a decrementare X finch non trova un 'valore per cui siano divisibili sia numeratore che 'denominatore: dato che era partito dall'alto, questo 'sar indubbiamente il MCD Do Until ((Me.Numerator Mod X = 0) And (Me.Denumerator Mod X = 0)) X -= 1 Loop 'Divide numeratore e denominatore per l'MCD Me.Numerator /= X Me.Denumerator /= X End Sub 'Somma due frazioni e restituisce la somma Shared Operator +(ByVal F1 As Fraction, ByVal F2 As Fraction) _ As Fraction Dim F3 As Fraction 'Se i denumeratori sono uguali, si limita a sommare 'i numeratori If F1.Denumerator = F2.Denumerator Then F3.Denumerator = F1.Denumerator F3.Numerator = F1.Numerator + F2.Numerator Else 'Altrimenti esegue tutta l'operazione 'x a x*b + a*y '- + - = --------'y b y*b F3.Denumerator = F1.Denumerator * F2.Denumerator F3.Numerator = F1.Numerator * F2.Denumerator + F2.Numerator * F1.Denumerator End If 'Semplifica la Fraction F3.Semplify() Return F3 End Operator 'Sottrae due Fraction e restituisce la differenza Shared Operator -(ByVal F1 As Fraction, ByVal F2 As Fraction) _ As Fraction 'Somma l'opposto del secondo membro F2.Numerator = -F2.Numerator Return F1 + F2 End Operator 'Moltiplica due frazioni e restituisce il prodotto Shared Operator *(ByVal F1 As Fraction, ByVal F2 As Fraction) _ As Fraction 'Inizializza F3 con il numeratore pari al prodotto 'dei numeratori e il denominatore pari al prodotto dei 'denominatori Dim F3 As Fraction = New Fraction(F1.Numerator * F2.Numerator, _ F1.Denumerator * F2.Denumerator)

119. 120. 121. 122. 123. 124. 125. 126. 127. 128. 129. 130. 131. 132. 133. 134. 135. 136. 137. 138. 139. 140. 141. 142. 143. 144. 145. 146. 147. 148. 149. 150. End

F3.Semplify() Return F3 End Operator 'Divide due frazioni e restituisce il quoziente Shared Operator /(ByVal F1 As Fraction, ByVal F2 As Fraction) _ As Fraction 'Inizializza F3 eseguendo l'operazione: 'a x a y '- / - = - * 'b y b x Dim F3 As Fraction = New Fraction(F1.Numerator * F2.Denumerator, _ F1.Denumerator * F2.Numerator) F3.Semplify() Return F3 End Operator End Structure Sub Main() Dim A As New Fraction(8, 112) Dim B As New Fraction(3, 15) A.Semplify() B.Semplify() Console.WriteLine(A.Show()) Console.WriteLine(B.Show()) Dim C As Fraction = A + B Console.WriteLine("A + B = " & C.Show()) Console.ReadKey() End Sub Module

CTy pe
CType un par ticolar e oper ator e che ser ve per conver tir e da un tipo di dato ad un altr o. Non ancor a stato intr odotto nei pr ecedenti capitoli, ma ne par ler pi ampiamente in uno dei successivi. Scr ivo comunque un par agr afo a questo r iguar do per amor di completezza e utilit di consultazione. Come noto, CType pu eseguir e conver sioni da e ver so tipi conosciuti: la sua sintassi, tuttavia, potr ebbe sviar e dalla cor r etta dichiar azione. Infatti, nonostante CType accetti due par ametr i, la sua dichiar azione ne implica uno solo, ossia il tipo che si desider a conver tir e, in questo caso Fr action. Il secondo par ametr o implicitamente indicato dal tipo di r itor no: se scr ivessimo "CType(ByVal F As Fr action) As Double", questa istr uzione gener er ebbe un CType in gr ado di conver tir e dal tipo Fr action al tipo Double nella manier a consueta in cui siamo abituati: 1. Dim F As Fraction 2. '... 3. Dim D As Double = CType(F, Double) La dichiar azione di una conver sione ver so Double gener a automaticamente anche l'oper ator e CDbl, che si pu usar e tr anquillamente al posto della ver sione completa di CType. Or a conviene por r e l'accento sul come CType viene dichiar ato: la sua sintassi non speciale solo per ch pu esser e confuso da unar io a binar io, ma anche per ch deve dichiar ar e sem pr e se una conver sione W idening (di espansione, ossia senza per dita di dati) o Nar r o w ing (di r iduzione, con possibile per dita di dati). Per questo motivo si deve specificar e una delle suddette keyw or d tr a Shar ed e Oper ator . Ad esempio: Fr action r appr esenta un numer o r azionale e, sebbene Double non r appr esenti tutte le cifr e di un possibile numer o per iodico, possiamo consider ar e che nel passaggio ver so i Double non ci sia per dita di dati n di pr ecisione in modo r ilevante. Possiamo quindi definir e la conver sione Widening: 1. Shared Widening Operator CType(ByVal F As Fraction) As Double 2. Return F.Numerator / F.Denumerator 3. End Operator

Invece, la conver sione ver so un numer o inter o implica non solo una per dita di pr ecisione r ilevante ma anche di dati, quindi la definir emo Nar r ow ing: 1. Shared Narrowing Operator CType(ByVal F As Fraction) As Int32 2. 'Notare l'operatore \ di divisione intera (per maggiori 3. 'informazioni sulla divisione intera, vedere capitolo A6) Return F.Numerator \ F.Denumerator 4. 5. End Operator

Operatori di c onfronto
Gli oper ator i di confr onto godono anch'essi di una car atter istica par ticolar e: devono sempr e esser e definiti in coppia, < con >, = con <>, <= con >=. Non pu infatti esister e un modo per ver ificar e se una var iabile minor e di un altr a e non se maggior e. Se manca uno degli oper ator i complementar i, il compilator e visualizzer un messaggio di er r or e. Ovviamente, il tipo r estituito dagli oper ator i di confr onto sar sempr e Boolean, poich una condizione pu esser e solo o ver a o falsa. 01. 02. 03. 04. 05. 06. 07. 08. 09. 10. 11. 12. 13. 14. 15. 16. 17. 18. Shared Operator <(ByVal F1 As Fraction, ByVal F2 As Fraction) As Boolean 'Converte le frazioni in double e confronta questi valori Return (CType(F1, Double) < CType(F2, Double)) End Operator Shared Operator >(ByVal F1 As Fraction, ByVal F2 As Fraction) As Boolean Return (CDbl(F1) > CDbl(F2)) End Operator Shared Operator =(ByVal F1 As Fraction, ByVal F2 As Fraction) As Boolean Return (CDbl(F1) = CDbl(F2)) End Operator Shared Operator <>(ByVal F1 As Fraction, ByVal F2 As Fraction) As Boolean 'L'operatore "diverso" restituisce sempre un valore opposto 'all'operatore "uguale" Return Not (F1 = F2) End Operator

da notar e che le espr essioni come (a=b) o (a-c>b) r estituiscano un valor e booleano. Possono anche esser e usate nelle espr essioni, ma sconsigliabile, in quanto il valor e di Tr ue spesse volte confuso: in VB.NET -1, ma a r untime 1, mentr e negli altr i linguaggi sempr e 1. Queste espr essioni possono tuttavia esser e assegnate con sicur ezza ad altr i valor i booleani: 1. 2. 3. 4. 5. '... a = 10 b = 20 Console.WriteLine("a maggiore di b: " & (a > b)) 'A schermo compare: "a maggiore di b: False"

A28. Differenze tra classi e strutture


Nel cor so dei pr ecedenti capitoli ho pi volte detto che le classi ser vono per cr ear e nuovi tipi e aggiunger e a questi nuove funzionalit, cos da estender e le nor mali capacit del Fr amew or k, ma ho detto la stessa cosa delle str uttur e, magar i enfatizzandone di meno l'impor tanza. Le classi possono espor r e campi, e le str uttur e anche; le classi possono espor r e pr opr iet, e le stuttur e anche; le classi possono espor r e metodi, e le str uttur e anche; le classi possono espor r e costr uttor i, e le str uttur e anche; le classi e i membr i di classe possono aver e specificator i di accesso, e le str uttur e e i lor o membr i anche. Insomma... a dir la tutta sembr er ebbe che classi e str uttur e siano concetti un po' r idondanti, cr eati solo per aver e un tipo r efer ence e un tipo value, ma in definitiva molto simili. Ovviamente non avr ei scr itto questo capitolo se le cose fosser o state r ealmente cos. Le classi sono infinitamente pi potenti delle str uttur e e fr a pochissimo capir ete il per ch.

Memorizzazione
Iniziamo col chiar ir e un aspetto gi noto. Le str uttur e sono tipi value, mentr e le classi sono tipi r efer ence. Ripetendo concetti gi spiegati pr ecedentemente, le pr ime vengono collocate dir ettamente sullo stack, ossia sulla memor ia pr incipale, nello spazio r iser vato alle var iabili del pr ogr amma, mentr e le seconde vengono collocate in un'altr a par te della memor ia (heap managed) e pongono sullo stack solo un puntator e alla lor o ver a locazione. Questo significa pr incipalmente due cose: L'accesso a una str uttur a e ai suoi membr i pi r apido di un accesso ad una classe; La classe occupa pi memor ia, a par it di membr i (almeno 6 bytes in pi). Inoltr e, una str uttur a si pr esta meglio alla memor izzazione "linear e", ed infatti gr andemente pr efer ita quando si esegue il mar shalling dei dati (ossia la lor o tr asfor mazione da entit alla pur a r appr esentazione in memor ia, costituita da una semplice ser ie di bits). In questo modo, per pr ima cosa molto pi facile legger e e scr iver e str uttur e in memor ia se si devono attuar e oper azioni di basso livello, ed anche possibile r ispar miar e spazio usando un'oppor tuna disposizione delle var iabili. Le classi, al contr ar io, non sono cos or dinate, ed meno facile manipolar le. Non mi addentr er oltr e in questo ambito, ma, per chi volesse, ci sono delle mie dispense che spiegano come funziona la memor izzazione delle str uttur e.

Identit
Un'altr a conseguenza del fatto che le classi siano tipi r efer ence consiste in questo: due oggetti, a par it di campi, sono sem pr e diver si, poich si tr atta di due istanze distinte, seppur contenti gli stessi dati. Due var iabili di tipo str uttur ato che contengono gli stessi dati, invece, sono uguali, per ch non esiste il concetto di istanza per i tipi value. I tipi value sono, per l'appunto, v alo r i, ossia semplici dati, infor mazione pur a, ammasso di bits, n pi n meno. Per questo motivo, ad esempio, impossibile modificar e una pr opr iet di una str uttur a tr amite l'oper ator e punto, poich sar ebbe come tentar e di modificar e la par te decimale di 1.23: 1.23 sempr e 1.23, si tr atta di un valor e e non lo si pu modificar e, ma al massimo si pu assegnar e un altr o valor e alla var iabile che lo contiene. Al contr ar io, gli oggetti sono entit pi complesse: non si tr atta di "infor mazione pur a" come i tipi str uttur ati. Un oggetto contiene molteplici campi e pr opr iet sempr e modificabili, per ch indicano solo un aspetto dell'oggetto: ad esempio, il color e di una par ete sempr e modificabile: basta tinteggiar e la par ete con un nuovo color e. Come dir e che "la par ete" non come un numer o, che sempr e quello e basta: essa un qualcosa di concr eto con diver se pr opr iet. Sono concetti molto astr atti e per cer ti ver si molto ar dui da capir e di pr imo acchito... io ho tentato di far e esempi convinceti, ma sper o che con il tempo impar er ete da soli a inter ior izzar e queste differ enze - differ enze che, pur

essendo impor tanti, non sono le pi impor tanti.

Paradigma di programmazione ad oggetti


Ed eccoci ar r ivati al punto caldo della discussione. La sostanziale differ enza che separ a nettamente str uttur e da classi l'ader enza ai dettami del par adigma di pr ogr ammazione ad oggetti, in par ticolar e ad er editar iet e polimor fismo. Le classi possono er editar e cer ti membr i da altr e classi e modificar ne il funzionamento. Le str uttur e non possono far e questo. Inoltr e, le classi possono implementar e inter facce, ossia sistemar e i pr opr i membr i per ader ir e a scheletr i di base: le str uttur e non per mettono di far e neppur e questo. Queste tr e car ater istiche (ma le pr ime due in par ticolar e) sono potenti str umenti a disposizione del pr ogr ammator e, e nei pr ossimi capitoli le analizzer emo nel dettaglio.

A29. L'Ereditariet
Eccoci ar r ivati a par lar e degli aspetti peculiar i di un linguaggio ad oggetti! Iniziamo con l'Eder editar iet.

L'ereditariet la possibilit di un linguaggio ad oggetti di far der ivar e una classe da un'altr a: in questo caso, la pr ima assume il nome di classe der iv ata, mentr e la seconda quello di classe base. La classe der ivata acquisisce tutti i membr i della classe base, ma pu r idefinir li o aggiunger ne di nuovi. Questa car atter istica di ogni linguaggio Object Or iented par ticolar mente efficace nello schematizzar e una r elazione "is-a" (ossia " un"). Per esempio, potr emmo definir e una classe Vegetale, quindi una nuova classe Fior e, che er edita Vegetale. Fior e un Vegetale, come mostr a la str uttur a ger ar chica dell'er editar iet. Se definissimo un'altr a classe Pr imula, der ivata da Fior e, dir emmo che Pr imula un Fior e, che a sua volta un Vegetale. Quest'ultimo tipo di r elazione, che cr ea classi der ivate che sar anno basi per er editar e altr e classi, si chiama er editar iet indir e tta. Passiamo or a a veder e come si dichiar a una classe der ivata: 1. Class [Nome] 2. Inherits [Classe base] 3. 'Membri della classe 4. End Class La keyw or d Inher its specifica quale classe base er editar e: si pu aver e solo UNA dir ettiva Inher its per classe, ossia non possibile er editar e pi classi base. In questo fr angente, si pu scopr ir e come le pr opr iet siano utili e flessibili: se una classe base definisce una var iabile pubblica, questa diver r par te anche della classe der ivata e su tale var iabile ver r anno basate tutte le oper azioni che la coinvolgono. Siccome possibile che la classe der ivata voglia r idefinir e tali oper azioni e molto pr obabilmente anche l'utilizzo della var iabile, sempr e consigliabile dichiar ar e campi Pr ivate avvolti da una pr opr iet, poich non c' mai alcun per icolo nel modificar e una pr opr iet in classi der ivate, ma non possibile modificar e i campi nella stessa classe. Un semplice esempio di er editar iet: 01. 02. 03. 04. 05. 06. 07. 08. 09. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. Class Person 'Per velocizzare la scrittura del codice, assumiamo che 'questi campi pubblici siano propriet Public FirstName, LastName As String Public ReadOnly Property CompleteName() As String Get Return FirstName & " " & LastName End Get End Property End Class 'Lo studente, ovviamente, una persona Class Student 'Student eredita da Person Inherits Person 'In pi, definisce anche questi campi pubblici 'La scuola frequentata Public School As String 'E l'anno di corso Public Grade As Byte End Class

In seguito, si pu utilizzar e la classe der ivata come si sempr e fatto con ogni altr a classe. Nel far ne uso, tuttavia, necessar io consider ar e che una classe der ivata possiede non solo i membr i che il pr ogr ammator e ha esplicitamente definito nel suo cor po, ma anche tutti quei membr i pr esenti nella classe base che si sono implicitamente acquisiti nell'atto stesso di scr iver e "Inher its". Se vogliamo, possiamo assimilar e una classe ad un insieme, i cui elementi sono i

suoi membr i: una classe base sottoinsieme della cor r ispondente classe der ivata. Di solito, l'ambiente di sviluppo aiuta molto in questo, poich, nei sugger imenti pr oposti dur ante la scr ittur a del codice, vengono automaticamente inser ite anche le voci er editate da altr e classi. Ci che abbiamo appena visto vale anche per er editar iet indir etta: se A er edita da B e B er edita da C, A dispor r dei membr i di B, alcuni dei quali sono anche membr i di C (semplice pr opr iet tr ansitiva). Or a, per , bisogna por r e un bel Nota Bene alla questione. Infatti, non tutto semplice come sembr a. For se nessuno si chiesto che fine fanno gli specificator i di accesso quando un membr o viene er editato da una classe der ivata. Ebbene, esistono delle pr ecise r egole che indicano come gli scope vengono tr attati quando si er edita: Un membr o Public o Fr iend della classe base diventa un membr o Public o Fr iend della classe der ivata (in pr atica, non cambia nulla; viene er editato esattamente com'); Un membr o Pr iv ate della classe base non accessibile dalla classe der ivata, poich il suo ambito di visibilit impedisce a ogni chiamante ester no alla classe base di far vi r ifer imento, come gi visto nelle lezioni pr ecedenti; Un membr o Pr o tected della classe base diventa un membr o Pr otected della classe der ivata, ma si compor ta come un membr o Pr ivate. Ed ecco che abbiamo intr odotto uno degli specificator i che ci er avamo lasciati indietr o. I membr i Pr otected sono par ticolar mente utili e costituiscono una sor ta di "scappatoia" al fatto che quelli pr ivati non subiscono l'er editar iet. Infatti, un memebr o Pr otected si compor ta esattamente come uno Pr ivate, con un'unica eccezione: er editabile, ed in questo caso diventa un membr o Pr otected della classe der ivata. Lo stesso discor so vale anche per Pr otected Fr iend. Ecco uno schema che esemplifica il compor tamento dei pr incipali Scope:

Esempio: 001. Module Esempio 002. Class Person 'Due campi protected 003. 004. Protected _FirstName, _LastName As String 005. 'Un campo private readonly: non c' ragione di rendere 'questo campo Protected poich la data di nascita non 006. 'cambia ed sempre accessibile tramite la propriet 007. 008. 'pubblica BirthDay 009. Private ReadOnly _BirthDay As Date 010. Public Property FirstName() As String 011. 012. Get 013. Return _FirstName 014. End Get 015. Set(ByVal Value As String) 016. If Value <> "" Then 017. _FirstName = Value 018. End If 019. End Set 020. End Property 021. Public Property LastName() As String 022. 023. Get Return _LastName 024. End Get 025. Set(ByVal Value As String) 026. If Value <> "" Then 027. 028.

029. 030. 031. 032. 033. 034. 035. 036. 037. 038. 039. 040. 041. 042. 043. 044. 045. 046. 047. 048. 049. 050. 051. 052. 053. 054. 055. 056. 057. 058. 059. 060. 061. 062. 063. 064. 065. 066. 067. 068. 069. 070. 071. 072. 073. 074. 075. 076. 077. 078. 079. 080. 081. 082. 083. 084. 085. 086. 087. 088. 089. 090. 091. 092. 093. 094. 095. 096. 097. 098. 099. 100.

_LastName = Value End If End Set End Property Public ReadOnly Property BirthDay() As Date Get Return _BirthDay End Get End Property Public ReadOnly Property CompleteName() As String Get Return _FirstName & " " & _LastName End Get End Property 'Costruttore che accetta tra parametri obbligatori Sub New(ByVal FirstName As String, ByVal LastName As String, _ ByVal BirthDay As Date) Me.FirstName = FirstName Me.LastName = LastName Me._BirthDay = BirthDay End Sub End Class 'Lo studente, ovviamente, una persona Class Student 'Student eredita da Person Inherits Person 'La scuola frequentata Private _School As String 'E l'anno di corso Private _Grade As Byte Public Property School() As String Get Return _School End Get Set(ByVal Value As String) If Value <> "" Then _School = Value End If End Set End Property Public Property Grade() As Byte Get Return _Grade End Get Set(ByVal Value As Byte) If Value > 0 Then _Grade = Value End If End Set End Property 'Questa nuova propriet si serve anche dei campi FirstName 'e LastName nel modo corretto, poich sono Protected anche 'nella classe derivata e fornisce un profilo completo 'dello studente Public ReadOnly Property Profile() As String Get 'Da notare l'accesso a BirthDay tramite la propriet 'Public: non possibile accedere al campo _BirthDay 'perch privato nella classe base Return _FirstName & " " & _LastName & ", nato il " & _ BirthDay.ToShortDateString & " frequenta l'anno " & _ _Grade & " alla scuola " & _School End Get End Property

101. 102. 103. 104. 105. 106. 107. 108. 109. 110. 111. 112. 113. 114. 115. 116. 117. 118. 119. 120. 121. 122. 123. 124. 125. 126. 127. 128. 129. 130. 131. 132. 133. 134. 135. End L'output: 1. 2. 3. 4. 5.

'Altra clausola importante: il costruttore della classe 'derivata deve sempre richiamare il costruttore della 'classe base Sub New(ByVal FirstName As String, ByVal LastName As String, _ ByVal BirthDay As Date, ByVal School As String, _ ByVal Grade As Byte) MyBase.New(FirstName, LastName, BirthDay) Me.School = School Me.Grade = Grade End Sub End Class Sub Main() Dim P As New Person("Pinco", "Pallino", Date.Parse("06/07/90")) Dim S As New Student("Tizio", "Caio", Date.Parse("23/05/92"), _ "Liceo Classico Ugo Foscolo", 2) Console.WriteLine(P.CompleteName) 'Come si vede, la classe derivata gode degli stessi membri 'di quella base, acquisiti secondo le regole 'dell'ereditariet appena spiegate Console.WriteLine(S.CompleteName) 'E in pi ha anche i suoi nuovi membri Console.WriteLine(S.Profile) 'Altra cosa interessante: dato che Student derivata da 'Person ed espone tutti i membri di Person, pi altri, 'non sbagliato assegnare un oggetto Student a una 'variabile Person P = S Console.WriteLine(P.CompleteName) Console.ReadKey() End Sub Module

Pinco Pallino Tizio Caio Tizio Caio, nato il 23/5/1992 frequenta l'anno 2 alla scuola Liceo Classico Ugo Foscolo Tizio Caio

(Per maggior i infor mazioni sulle oper azioni con le date, veder e il capitolo B13) Anche se il sor gente ampiamente commentato mi soffer mer ei su alcuni punti caldi. Il costr uttor e della classe der ivata deve sem pr e r ichiamar e il costr uttor e della classe base, e questo avviene tr amite la keyw or d MyBase che, usata in una classe der ivata, fa r ifer imento alla classe base cor r ente: attr aver so questa par ola r iser vata possibile anche r aggiunger e i membr i pr ivati della classe base, ma si fa r ar amente, poich il suo impiego pi fr equente quello di r ipr ender e le vecchie ver sioni di metodi modificati. Il secondo punto r iguar da la conver sione di classi: passar e da Student a Per son non , come potr ebbe sembr ar e, una conver sione di r iduzione, poich dur ante il pr ocesso, nulla va per duto nel ver o senso della par ola. Cer to, si per dono le infor mazioni supplementar i, ma alla classe base queste non ser vono: la sicur ezza di eseguir e la conver sione r isiede nel fatto che la classe der ivata gode degli stessi membr i di quella base e quindi non si cor r e il r ischio che ci sia r ifer imento a un membr o inesistente. Questo invece si ver ifica nel caso opposto: se una var iabile di tipo Student assumesse il valor e di un oggetto Per son, School e Gr ade sar ebber o pr ivi di valor e e ci gener ebbe un er r or e. Per eseguir e questo tipo di passaggi necessar io l'oper ator e Dir ectCast.

A30. Polimorfismo
Il polimor fismo la capacit di un linguaggio ad oggetti di r idefinir e i membr i della classe base in modo tale che si compor tino in manier a differ ente all'inter no delle classi der ivate. Questa possibilit quindi str ettamente legata all'er editar iet. Le keyw or ds che per mettono di attuar ne il funzionamento sono due: Over r idable e Over r ides. La pr ima deve mar car e il membr o della classe base che si dovr r idefinir e, mentr e la seconda contr assegna il membr o della classe der ivata che ne costituisce la nuova ver sione. da notar e che solo membr i della stessa categor ia con no m e ug uale e sig natur e identica (ossia con lo stesso numer o e lo stesso tipo di par ametr i) possono subir e questo pr ocesso: ad esempio non si pu r idefinir e la pr ocedur a Show Tex t() con la pr opr iet Tex t, per ch hanno nome differ ente e sono di diver sa categor ia (una una pr ocedur a e l'altr a una pr opr iet). La sintassi semplice: 1. 2. 3. 4. 5. 6. 7. 8. Class [Classe base] Overridable [Membro] End Class Class [Classe derivata] Inherits [Classe base] Overrides [Membro] End Class

Questo esempio pr ende come base la classe Per son definita nel capitolo pr ecedente e sviluppa da questa la classe Teacher (insegnante), modificandone le pr opr iet LastName e CompleteName: 001. Module Module1 002. Class Person 003. Protected _FirstName, _LastName As String 004. Private ReadOnly _BirthDay As Date 005. 006. Public Property FirstName() As String 007. Get 008. Return _FirstName 009. End Get 010. Set(ByVal Value As String) 011. If Value <> "" Then 012. _FirstName = Value 013. End If 014. End Set 015. End Property 016. 017. 'Questa propriet sar ridefinita nella classe Teacher 018. Public Overridable Property LastName() As String 019. Get 020. Return _LastName 021. End Get 022. Set(ByVal Value As String) 023. If Value <> "" Then 024. _LastName = Value 025. End If End Set 026. End Property 027. 028. Public ReadOnly Property BirthDay() As Date 029. Get 030. Return _BirthDay 031. End Get 032. End Property 033. 034. 'Questa propriet sar ridefinita nella classe Teacher 035. Public Overridable ReadOnly Property CompleteName() As String 036. Get 037. Return _FirstName & " " & _LastName 038. End Get 039. 040.

041. 042. 043. 044. 045. 046. 047. 048. 049. 050. 051. 052. 053. 054. 055. 056. 057. 058. 059. 060. 061. 062. 063. 064. 065. 066. 067. 068. 069. 070. 071. 072. 073. 074. 075. 076. 077. 078. 079. 080. 081. 082. 083. 084. 085. 086. 087. 088. 089. 090. 091. 092. 093. 094. 095. 096. 097. 098. 099. 100. 101. 102. 103. 104. 105. 106. 107. 108. 109. 110. 111. 112.

End Property 'Costruttore che accetta tra parametri obbligatori Sub New(ByVal FirstName As String, ByVal LastName As String, _ ByVal BirthDay As Date) Me.FirstName = FirstName Me.LastName = LastName Me._BirthDay = BirthDay End Sub End Class Class Teacher Inherits Person Private _Subject As String Public Property Subject() As String Get Return _Subject End Get Set(ByVal Value As String) If Value <> "" Then _Subject = Value End If End Set End Property 'Ridefinisce la propriet LastName in modo da aggiungere 'anche il titolo di Professore al cognome Public Overrides Property LastName() As String Get Return "Prof. " & _LastName End Get Set(ByVal Value As String) 'Da notare l'uso di MyBase e LastName: in questo 'modo si richiama la vecchia versione della 'propriet LastName e se ne imposta il 'valore. Viene quindi richiamato il blocco Set 'vecchio: si risparmiano due righe di codice 'poich non si deve eseguire il controllo 'If su Value MyBase.LastName = Value End Set End Property 'Ridefinisce la propriet CompleteName in modo da 'aggiungere anche la materia insegnata e il titolo di 'Professore Public Overrides ReadOnly Property CompleteName() As String Get 'Anche qui viene richiamata la vecchia versione di 'CompleteName, che restituisce semplicemente il 'nome completo Return "Prof. " & MyBase.CompleteName & _ ", dottore in " & Subject End Get End Property Sub New(ByVal FirstName As String, ByVal LastName As String, _ ByVal BirthDay As Date, ByVal Subject As String) MyBase.New(FirstName, LastName, BirthDay) Me.Subject = Subject End Sub End Class Sub Main() Dim T As New Teacher("Mario", "Rossi", Date.Parse("01/01/1950"), _ "Letteratura italiana") 'Usiamo le nuove propriet, ridefinite nella classe 'derivata Console.WriteLine(T.LastName) '> "Prof. Rossi"

Console.WriteLine(T.CompleteName) 113. '> "Prof. Mario Rossi, dottore in Letteratura italiana" 114. 115. Console.ReadKey() 116. End Sub 117. End Module In questo modo si visto come r idefinir e le pr opr iet. Ma pr ima di pr oseguir e vor r ei far notar e un compor tamento par ticolar e: 1. Dim P As Person = T 2. Console.WriteLine(P.LastName) 3. Console.WriteLine(P.CompleteName) In questo caso ci si aspetter ebbe che le pr opr iet r ichiamate da P agiscano come specificato nella classe base (ossia senza includer e altr e infor mazioni se non il nome ed il cognome), poich P di quel tipo. Questo, invece, non accade. Infatti, P e T, dato che abbiamo usato l'oper ator e =, puntano or a allo stesso oggetto in memor ia, solo che P lo vede come di tipo Per son e T come di tipo Teacher . Tuttavia, l'oggetto r eale di tipo Teacher e per ci i suoi metodi sono a tutti gli effetti quelli r idefiniti nella classe der ivata. Quando P tenta di r ichiamar e le pr opr iet in questione, ar r iva all'indir izzo di memor ia dove sono conser vate le istr uzioni da eseguir e, solo che queste si tr ovano all'inter no di un oggetto Teacher e il lor o codice , di conseguenza, diver so da quello della classe base. Questo compor tamento, al contr ar io di quanto potr ebbe sembr ar e, utilissimo: ci per mette, ad esempio, di memor izzar e in un ar r ay di per sone sia studenti che insegnanti, e ci per mette di scr iver e a scher mo i lor o nomi differ entemente senza eseguir e una conver sione. Ecco un esempio: 01. 02. 03. 04. 05. 06. 07. 08. 09. 10. Dim Ps(2) As Person Ps(0) = New Person("Luigi", "Ciferri", Date.Parse("7/7/1982")) Ps(1) = New Student("Mario", "Bianchi", Date.Parse("19/10/1991"), _ "Liceo Scientifico Tecnologico Cardano", 5) Ps(2) = New Teacher("Ubaldo", "Nicola", Date.Parse("11/2/1980"), "Filosofia") For Each P As Person In Ps Console.WriteLine(P.CompleteName) Next

lecito assegnar e oggetti Student e Teacher a una cella di un ar r ay di Per son in quanto classi der ivate da Per son. I metodi r idefiniti, tuttavia, r imangono e modificano il compor tamento di ogni oggetto anche se r ichiamato da una "mascher a" di classe base. Pr oviamo or a con un piccolo esempio sul polimor fismo dei metodi: 01. 02. 03. 04. 05. 06. 07. 08. 09. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. Class A Public Overridable Sub ShowText() Console.WriteLine("A: Testo di prova") End Sub End Class Class B Inherits A 'Come si vede il metodo ha: '- lo stesso nome: ShowText '- lo stesso tipo: una procedura '- gli stessi parametri: senza parametri 'Qualunque tentativo di cambiare una di queste caratteristiche 'produrr un errore del compilatore, che comunica di non poter 'ridefinire il metodo perch non ne esistono di uguali nella 'classe base Public Overrides Sub ShowText() Console.WriteLine("B: Testo di prova") End Sub End Class

Ultime due pr ecisazioni: le var iabili non possono subir e polimor fismo, cos come i membr i statici.

Shadow ing
Se il polimor fismo per mette di r idefinir e accur atamente membr i che pr esentano le stesse car atter istiche, ed quindi pi pr eciso, lo shadow ing per mette letter almente di oscur ar e qualsiasi membr o che abbia lo stesso nome, indipendentemente dalla categor ia, dalla signatur e e dalla qauntit di ver sioni alter native pr esenti. La keyw or d da usar e Shadow s, e si applica solo sul membr o della classe der ivata che intendiamo r idefinir e, oscur ando l'omonimo nella classe base. Ad esempio: 01. Module Esempio 02. Class Base 03. Friend Control As Byte End Class 04. 05. 06. Class Deriv 07. Inherits Base 08. Public Shadows Sub Control(ByVal Msg As String) Console.WriteLine("Control, seconda versione: " & Msg) 09. End Sub 10. End Class 11. 12. 13. Sub Main() Dim B As New Base 14. 15. Dim D As New Deriv 16. 17. 'Entrambe le classe hanno lo stesso membro di nome 18. '"Control", ma nella prima un campo friend, 'mentre nella seconda una procedura pubblica 19. Console.WriteLine(B.Control) 20. D.Control("Ciao") 21. 22. Console.ReadKey() 23. End Sub 24. 25. End Module Come si vede, la sintassi come quella di Over r ides: Shadow s viene specificato tr a lo specificator e di accesso (se c'e') e la tipologia del membr o (in questo caso Sub, pr ocedur a). Entr ambe le classi pr esentano Contr ol, ma la seconda ne fa un uso totalmente diver so. Ad ogni modo l'uso dello shadow ing in casi come questo for tememente sconsigliabile: pi che altr o lo si usa per assicur ar si che, se mai dovesse uscir e una nuova ver sione della classe base con dei nuovi metodi che pr esentano lo stesso nome di quelli della classe der ivata da noi definita, non ci siano pr oblemi di compatibilit. Se una var iabile dichiar ata Shadow s, viene omessa la keyw or d Dim.

A31. Conversioni di dati


Il Fr amew or k .NET in gr ado di eseguir e conver sioni automatiche a r untime ver so tipi di ampiezza maggior e, per esempio in gr ado di conver tir e Int16 in Int32, Char in Str ing, Single in Double e via dicendo. Queste oper azioni di conver sione vengono dette w idening (dall'inglese w ide = lar go), ossia che avvengono senza la per dita di dati, poich tr aspor tano un valor e che contiene una data infor mazione in un tipo che pu contener e pi infor mazioni. Gli oper ator i di conver sione ser vono per eseguir e conver sioni che vanno nella dir ezione opposta, e che sono quindi, nar r o w ing (dall'inglese nar r ow = str etto). Queste ultime possono compor tar e la per dita di dati e per ci gener ano un er r or e se implicite.

CTy pe
CType l'oper ator e di conver sione univer sale e per mette la conver sione di qualsiasi tipo in qualsiasi altr o tipo, almeno quando questa possibile. La sintassi molto semplice: [Variabile] = CType([Valore da convertire], [Tipo in cui convertire]) Ad esempio: 1. Dim I As Int32 = 50 2. 'Converte I in un valore Byte 3. Dim B As Byte = CType(I, Byte) Questa lista r ipor ta alcuni casi in cui bene usar e esplicitamente l'oper ator e di conver sione CType: Per conver tir e un valor e inter o o decimale in un valor e booleano; Per conver tir e un valor e Single o Double in Decimal; Per conver tir e un valor e inter o con segno in uno senza segno; Per conver tir e un valor e inter o senza segno in uno con segno della stessa ampiezza (ad esempio da UInt32 a Int32). Oltr e a CType, esistono moltissime ver sioni pi cor te di quest'ultimo che conver tono in un solo tipo: CInt conver te sempr e in Int32, CBool sempr e in booleano, CByte in byte, CShor t Int16, CLong, CUShor t, CULong, CUInt, CSng, CDbl, CDec, CStr , CDate, CObj. inoppor tuno utilizzar e CStr poich ci si pu sevir e della funzione ToStr ing er editata da ogni classe da System.Object; allo stesso modo, meglio evitar e CDate, a favor e di Date.Par se, come si vedr nella lezione "DateTimePicker : Lavor ar e con le date". CType pu comunque esser e usato per qualsiasi altr a conver sione contemplabile, anche e sopr attutto con i tipi Refer ence.

Direc tCast
Dir ectCast lavor a in un modo legger mente di diver so: CType tenta sempr e di conver tir e l'ar gomento di or gine nel tipo specificato, mentr e Dir ectCast lo fa solo se tale valor e pu esser e sottoposto al casting (al "passaggio" da un tipo all'altr o, piuttosto che alla conver sione) ver so il tipo indicato. Per ci non , ad esempio, in gr ado di conver tir e una str inga in inter o, e neanche un valor e shor t in un integer , sebbene questa sia una conver sione di espansione. Questi ultimi esempi non sono validi anche per ch questo par ticolar e oper ator e pu accettar e come ar gomenti solo oggetti, e quindi tipi Refer ence. In gener ale, quindi, dato il legger o r ispar mio di tempo di Dir ectCast in confr onto a CType, conveniente usar e Dir ectCast:

Per eseguir e l'unbox ing di tipi value; Per eseguir e il casting di una classe base in una classe der ivata (vedi "Er editar ieta'"); Per eseguir e il casting di un oggetto in qualsiasi altr o tipo r efer ence; Per eseguir e il casting di un oggetto in un'inter faccia. N.B.: notar e che tutti i casi sopr a menzionati hanno come tipo di par tenza un oggetto, pr opr io come detto pr ecedentemente.

Try Cast
Tr yCast ha la stessa sintassi di Dir ectCast, e quindi anche di CType, ma nasconde un piccolo pr egio. Spesso, quando si esegue una conver sione si deve pr ima contr ollar e che la var iabile in questione sia di un deter minato tipo base o implementi una deter minata inter faccia e solo successivamente si esegue la conver sione ver a e pr opr ia. Con ci si contr olla due volte la stessa var iabile, pr ima con l'If e poi con Dir ectCast. Tr yCast, invece, per mette di eseguir e il tutto in un unico passaggio e r estituisce semplicemente Nothing se il cast fallisce. Questo appr occio r ende tale oper ator e cir ca 0,2 volte pi veloce di Dir ectCast.

Convert
Esiste, poi, una classe statica definita del namespace System - il namespace pi impor tante di tutto il Fr amew or k. Questa classe, essendo statica (e qui facciamo un po' di r ipasso), espone solo metodi statici e non pu esser e istanziata (non espone costr uttor i e comunque sar ebbe inutile far lo). Essa contiene molte funzioni per eseguir e la conver sione ver so i tipi di base ed espone anche un impor tante valor e che vedr emo molto pi in l par lando dei database. Essenzialmente, tutti i suoi metodi hanno un nome del tipo "ToXXXX", dove XXXX uno qualsiasi tr a i tipi base: ad esempio, c', ToInt32, ToDouble, ToByte, ToStr ing, ecceter a... Un esempio: 01. 02. 03. 04. 05. 06. 07. 08. 09. Dim Dim ' D Dim ' S Dim ' N Dim Dim I D = S = N = K A As Int32 As Double 34.0 As String "34" As Single 34.0 As String As Date = = 34 = Convert.ToDouble(I) = Convert.ToString(D) = Convert.ToSingle(S) = "31/12/2008" Convert.ToDate(K)

All'inter no di Conver t sono definiti anche alcuni metodi per conver tir e una str inga da e ver so il for mato Base64, una par ticolar e codifica che utilizza solo 64 car atter i, al contr ar io dell'ASCII standar d che ne utilizza 128 o di quello esteso che ne utilizza 256. Tale codifica viene usata ad esempio nell'invio delle e-mail e pr oduce output un ter zo pi voluminosi degli input, ma in compenso tutti i car atter i contemplati sono sempr e leggibili (non ci sono, quindi, car atter i "speciali"). Per appr ofondir e l'ar gomento, cliccate su w ik ipedia. Per r ipr ender e il discor so conver sioni, sar ebbe lecito pensar e che la definizione di una classe del gener e, quando esistono gi altr i oper ator i come CType e Dir ectCast - altr ettanto qualificati e per for manti - sia abbastanza r idondante. Pi o meno cos. Utilizzar e la classe Conver t al posto degli altr i oper ator i di casting non gar antisce alcun vantaggio di sor ta, e pu anche esser e r icondotta ad una questione di gusti (io per sonalmente pr efer isco CType). Ad ogni modo, c' da dir e un'altr a cosa al r iguar do: i metodi di Conver t sono piuttosto r igor osi e for niscono dei ser vizi molto mir ati. Per questo motivo, in casi molto vantaggiosi, ossia quando il cast pu esser e ottimizzato, essi eseguono pur sempr e le stesse istr uzioni: al contr ar io, CType pu "ingegnar si" e for nir e una conver sione pi efficiente. Quest'ultimo, quindi, legger mente pi elastico ed adattabile alle situazioni.

Parse
Un'oper azione di par sing legge una str inga, la elabor a, e la conver te in un valor e di altr o tipo. Abbiamo gi visto un utilizzo di Par se nell'uso delle date, poich il tipo Date espone il metodo Par se, che ci per mette di conver tir e la r appr esentazione testuale di una data in un valor e date appr opr iato. Quasi tutti i tipi base del Fr amew or k espongono un metodo Par se, che per mette di passar e da una str inga a quel tipo: possiamo dir e che Par se l'inver sa di ToStr ing. Ad esempio: 01. 02. 03. 04. 05. 06. 07. 08. 09. 10. Dim I As Int32 I = Int32.Parse("27") ' I = 27 I = Int32.Parse("78.000") ' Errore di conversione! I = Int32.Parse("123,67") ' Errore di conversione!

Come vedete, Par se ha pur sempr e dei limiti: ad esempio non contempla i punti e le vir gole, sebbene la conver sione, vista da noi "umani", sia del tutto lecita (78.000 settantottomila con il separ ator e delle migliaia e 123,67 un numer o decimale, quindi conver tibile in inter o con un ar r otondamento). Inoltr e, Par se viene anche automaticamente chiamato dai metodi di Conver t quando il valor e passato una str inga. Ad esempio, Conver t.ToInt32("27") r ichiama a sua volta Int32.Par se("27"). Per far vi veder e in che modo CType pi flessibile, r ipetiamo l'esper imento di pr ima usando appunto CType: 01. 02. 03. 04. 05. 06. 07. 08. 09. 10. Dim I As Int32 I = CType("27", Int32) ' I = 27 I = CType("78.000", Int32) ' I = 78000 I = CType("123,67", Int32) ' I = 124

Per fetto: niente er r or i di conver sione e tutto come ci si aspettava!

Try Parse
Una var iante di Par se Tr yPar se, anch'essa definita da molti tipi base. La sostanziale differ enza r isiede nel fatto che, mentr e la pr ima pu gener ar e er r or i nel caso la str inga non possa esser e conver tita, la seconda non lo fa, ma non r estituisce neppur e il r isultato. Infatti, Tr yPar se accetta due ar gomenti, come nella seguente signatur e: 1. TryParse(ByVal s As String, ByRef result As [Tipo]) As Boolean Dove [Tipo] dipende da quale tipo base la stiamo r ichiamando: Int32.Tr yPar se avr il secondo ar gomento di tipo Int32, Date.Tr yPar se ce l'avr di tipo Date, e cos via. In sostanza Tr yPar se tenta di eseguir e la funzione Par se sulla str inga s: se ci r iesce, r estituisce Tr ue e pone il r isultato in r esult (notar e che il par ametr o passato per indir izzo); se non ci r iesce, r estituisce False. Ecco un esempio: 01. 02. 03. 04. 05. 06. 07. 08. 09. Dim S As String = "56/0/1000" 'S contiene una data non valida Dim D As Date If Date.TryParse(S, D) Then Console.WriteLine(D.ToLongDateString()) Else Console.WriteLine("Data non valida!") End If

Ty peOf
TypeOf ser ve per contr ollar e se una var iabile di un cer to tipo, der iva da un cer to tipo o implementa una cer ta inter faccia, ad esempio: 1. Dim I As Int32 2. If TypeOf I Is Int32 Then 3. 'Questo blocco viene eseguito poich I di tipo Int32 4. End If Oppur e: 1. Dim T As Student 2. If TypeOf T Is Person Then 3. 'Questo blocco viene eseguito perch T, essendo Student, 4. 'anche di tipo Person, in quanto Student una sua classe 5. 'derivata 6. End If Ed infine un esempio sulle inter facce, che potr ete tor nar e a guar dar e da qui a qualche capitolo: 1. Dim K(9) As Int32 2. If TypeOf Is IEnumerable Then 3. 'Questo blocco viene eseguito poich gli array implementano 4. 'sempre l'interfaccia IEnumerable 5. End If

A31. L'Overloading
L'Over loading la capacit di un linguaggio ad oggetti di poter definir e, nella stessa classe, pi var ianti dello stesso metodo. Per poter eseguir e cor r ettamente l'over loading, che ogni var iante del metodo abbia queste car atter istiche: Sia della stessa categor ia (pr ocedur a O funzione, anzi, per dir la in modo pi esplicito: pr ocedur a Xor funzione); Abbia lo stesso nome; Abbia signatur e diver sa da tutte le altr e var ianti. Per color o che non se lo r icor dasser o, la signatur e di un metodo indica il tipo e la quantit dei suoi par ametr i. Questo il tr atto essenziale che per mette di differ enziar e concr etamente una var iante dall'altr a. Per far e un esempio, il metodo Console.Wr iteLine espone ben 18 ver sioni diver se, che ci consentono di stampar e pr essoch ogni dato sullo scher mo. Fr a quelle che non abbiamo mai usato, ce n' una in par ticolar e che vale la pena di intr odur r e or a, poich molto utile e flessibile. Essa pr evede un pr imo par ametr o di tipo str inga e un secondo Par amAr r ay di oggetti: 1. Console.WriteLine("stringa", arg0, arg1, arg2, arg3, ...) Il pr imo par ametr o pr ende il nome di str ing a di fo r m ato , poich specifica il for mato in cui i dati costituiti dagli ar gomenti addizionali dovr anno esser e visualizzati. All'inter no di questa str inga, si possono specificar e, oltr e ai nor mali car atter i, dei codici speciali, nella for ma "{I}", dove I un numer o compr eso tr a 0 e il numer o di par amtr i meno uno: "{I}" viene detto segnaposto e ver r sostituito dal par ametr o I nella str inga. Ad esempio: 1. 2. 3. 4. A = 1 B = 3 Console.WriteLine("La somma di {0} e {1} {2}.", A, B, A + B) '> "La somma di 1 e 3 4."

Ulter ior i infor mazioni sulle str inghe di for mato sono disponibili nel capitolo "Magie con le str inghe". Ma or a passiamo alla dichiar azione dei metodi in over load. La par ola chiave da usar e, ovviamente, Over loads, specificata poco dopo lo scope, e dopo gli eventuali Over r idable od Over r ides. Le entit che possono esser e sottoposte ad over load, oltr e ai metodi, sono: Metodi statici Oper ator i Pr opr iet Costr uttor i Distr uttor i Anche se gli ultimi due sono sempr e metodi - per or a tr alasciamo i distr uttor i, che non abbiamo ancor a analizzato - bene specificar e con pr ecisione, per ch a compiti speciali spesso cor r ispondono compor tamenti altr ettanto speciali. Ecco un semplicissimo esempio di over load: 01. Module Module1 02. 03. 'Restituisce il numero di secondi passati dalla data D a oggi 04. Private Function GetElapsed(ByVal D As Date) As Single 05. Return (Date.Now - D).TotalSeconds 06. End Function 07. 08. 'Come sopra, ma il parametro di tipo intero e indica 09. 'un anno qualsiasi 10. Private Function GetElapsed(ByVal Year As Int32) As Single 11. 'Utilizza Year per costruire un nuovo valore Date 12.

13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. End

'e usa la precedente variante del metodo per 'ottenere il risultato Return GetElapsed(New Date(Year, 1, 1)) End Function 'Come le due sopra, ma il parametro di tipo stringa 'e indica la data Private Function GetElapsed(ByVal D As String) As Single Return GetElapsed(Date.Parse(D)) End Function Sub Main() 'GetElapsed viene 'diversi, ma sono Dim El1 As Single Dim El2 As Single Dim El3 As Single Console.ReadKey() End Sub Module chiamata con tre tipi di parametri tutti leciti = GetElapsed(New Date(1987, 12, 4)) = GetElapsed(1879) = GetElapsed("12/12/1991")

Come avr ete notato, nell'esempio pr ecedente non ho usato la keyw or d Over loads: anche se le r egole dicono che i membr i in over load vanno segnati, non sempr e necessar io far lo. Anzi, molte volte si evita di dichiar ar e esplicitamente i membr i di cui esistono var ianti come Over loads. Ci sono var ie r agioni per questa pr atica: l'over load scontato se i metodi pr esentano lo stesso nome, e il compilator e r iesce comunque a distinguer e tutto nitidamente; inoltr e, capita spesso di definir e var ianti e per r ender e il codice pi leggibile e meno pesante (anche se i sor genti in VB tendono ad esser e un poco pr olissi), si omette Over loads. Tuttavia, esistono casi in cui assolutamente necessar io usar e la keyw or d; eccone un esempio: 01. Module Module1 02. Class Person 03. Protected _FirstName, _LastName As String 04. Private ReadOnly _BirthDay As Date 05. 06. Public Property FirstName() As String 07. Get 08. Return _FirstName 09. End Get Set(ByVal Value As String) 10. 11. If Value <> "" Then 12. _FirstName = Value 13. End If 14. End Set 15. End Property 16. 17. Public Overridable Property LastName() As String 18. Get 19. Return _LastName 20. End Get 21. Set(ByVal Value As String) If Value <> "" Then 22. _LastName = Value 23. 24. End If End Set 25. End Property 26. 27. Public ReadOnly Property BirthDay() As Date 28. Get 29. Return _BirthDay 30. End Get 31. End Property 32. 33. Public Overridable ReadOnly Property CompleteName() As String 34. Get 35. Return _FirstName & " " & _LastName 36. End Get 37. End Property 38. 39.

40. 41. 42. 43. 44. 45. 46. 47. 48. 49. 50. 51. 52. 53. 54. 55. 56. 57. 58. 59. 60. 61. 62. 63. 64. 65. 66. 67. 68. 69. 70. 71. 72. 73. 74. 75. 76. 77. 78. 79. 80. 81. 82. 83. 84. 85. 86. 87. 88. 89. 90. 91. 92. 93. End

'ToString una funzione definita nella classe 'System.Object e poich ogni cosa in .NET 'deriva da questa classe, &egrae; sempre possibile 'ridefinire tramite polimorfismo il metodo ToString. 'In questo caso ne scriveremo non una, ma due versioni, 'quindi deve essere dichiarato sia Overrides, perch 'sovrascrive System.Object.ToString, sia Overloads, 'perch una versione alternativa di 'quella che andremo a scrivere tra poco Public Overloads Overrides Function ToString() As String Return CompleteName End Function 'Questa versione accetta un parametro stringa che assume 'la funzione di stringa di formato: il metodo restituir 'la frase immessa, sostituendo {F} con FirstName e {L} con 'LastName. In questa versione sufficiente 'Overloads, dato che non esiste un metodo ToString che 'accetti un parametro stringa in System.Object e perci 'non lo potremmo modificare Public Overloads Function ToString(ByVal FormatString As String) _ As String Dim Temp As String = FormatString 'Sostituisce {F} con FirstName Temp = Temp.Replace("{F}", _FirstName) 'Sostituisce {L} con LastName Temp = Temp.Replace("{L}", _LastName) Return Temp End Function Sub New(ByVal FirstName As String, ByVal LastName As String, _ ByVal BirthDay As Date) Me.FirstName = FirstName Me.LastName = LastName Me._BirthDay = BirthDay End Sub End Class Sub Main() Dim P As New Person("Mario", "Rossi", Date.Parse("17/07/67")) Console.WriteLine(P.ToString) '> Mario Rossi 'vbCrLf una costante che rappresenta il carattere '"a capo" Console.WriteLine(P.ToString("Nome: {F}" & vbCrLf & "Cognome: {L}")) '> Nome: Mario '> Cognome: Rossi Console.ReadKey() End Sub Module

Come mostr ato dall'esempio, quando il membr o di cui si vogliono definir e var ianti sottoposto anche a polimor fismo, necessar io specificar e la keyw or d Over loads, poich, in caso contr ar io, il compilator e r intr accer ebbe quello stesso membr o come diver so e, non potendo esister e membr i con lo stesso nome, pr odur r ebbe un er r or e.

A32. Gestione degli errori


Fino ad or a, nello scr iver e il codice degli esempi, ho sempr e (o quasi sempr e) supposto che l'utente inser isse dati coer enti e cor r etti. Al massimo, ho inser ito qualche costr utto di contr ollo per ver ificar e che tutto andasse bene. Infatti, per quello che abbiamo visto fino ad or a, c'er ano solo due modi per evitar e che il pr ogr amma andasse in cr ash o pr oducesse output pr ivi di senso: scr iver e del codice a pr ova di bomba (e questo, gar antisco, non sempr e possibile) o contr ollar e, pr ima di eseguir e le oper azioni, che tutti i dati fosser o per fettamente coer enti con il pr oblema da affr ontar e. Ahim, non sempr e possibile agir e in questo modo: ci sono cer ti casi in cui n l'uno n l'altr o metodo sono efficaci. E di questo posso for nir e subito un esempio lampante: ammettiamo di aver scr itto un pr ogr amma che esegua la divisione tr a due numer i. Molto banale come codice. Chiediamo all'utente i suddetti dati con Console.ReadLine, contr olliamo che il secondo sia diver so da 0 (pr opr io per evitar e un er r or e a r untime) e in questo caso stampiamo il r isultato. Ma... se l'utente inser isse, ad esempio, una letter a anzich un numer o, o per sbaglio o per pur o sadismo? Beh, qualcuno potr pensar e "Usiamo Tr yCast", tuttavia Tr yCast, essendo una r iedizione di Dir ectCast, agisce solo ver so tipi r efer ence e Int32 un tipo base. Qualcun altr o, invece, potr ebbe pr opor r e di usar e Tr yPar se, ma abbiamo gi r ilevato come la funzione Par se sia di vedute r istr ette. In definitiva, non abbiamo alcun modo di contr ollar e pr ima se il dato immesso o no sia r ealmente coer ente con ci che stiamo chiedendo all'utente. Possiamo saper e se il dato non coer ente solo quando si ver ifica l'er r or e, ma in questo caso non possiamo per metter ci che il pr ogr amma vada in cr ash per una semplice distr azione. Dovr emo, quindi, ges tire l'er r or e. In .NET quelli che finor a ho chiamato "er r or i" si dicono, pi pr opr iamente, Eccezio ni e sono anch'esse r appr esentate da una classe: la classe base di tutte le eccezioni System.Ex ception, da cui der ivano tutte le var ianti per le specifiche eccezioni (ad esempio divisione per zer o, file inesistente, for mato non valido, ecceter a...). Accanto a queste, esiste anche uno specifico costr utto che ser ve per gestir le, e pr ende il nome di Tr y. Ecco la sua sintassi: 1. Try 2. 'Codice che potrebbe generare l'eccezione 3. Catch [Variabile] As [Tipo Eccezione] 4. 'Gestisce l'eccezione [Tipo Eccezione] 5. End Try Tr a Tr y e Catch viene scr itto il codice incr iminato, che potr ebbe eventualmente gener ar e l'er r or e che noi stiamo tentando di r intr acciar e e gestir e. Nello specifico, quando accade un avvenimento del gener e, si dice che il codice "lancia" un'eccezione, poich la par ola chiave usata per gener ar la, come vedr emo, pr opr io Thr ow (= lanciar e). Or a, passatemi il par agone che sto per far e, for se un po' fantasioso: il metodo in questione come una fionda che scaglia un sassolino - un pacchetto di infor mazioni che ci dice tutto sul per ch e sul per come stato gener ato quello specifico er r or e. Or a, se questo sassolino viene inter cettato da qualcosa (dal blocco catch), possiamo evitar e danni collater ali, ma se niente blocca la sua cor sa, ahim, dovr emmo r ipagar e i vetr i r otti a qualcuno. Ecco un esempio: 01. Module Module1 02. Sub Main() 03. Dim a, b As Single 04. 'ok controlla se a e b sono coerenti 05. Dim ok As Boolean = False 06. 07. Do 08. 'Tenta di leggere i numeri da tastiera 09. Try 10. Console.WriteLine("Inserire due numeri non nulli: ") 11. a = Console.ReadLine 12. b = Console.ReadLine 13. 'Se il codice arriva fino a questo punto, significa 14. 'che non si sono verificate eccezioni 15. ok = True 16.

Catch Ex As InvalidCastException 17. 'Se, invece, il programma arriva in questo blocco, 18. 'vuol dire che abbiamo "preso" (catch) un'eccezione 19. 'di tipo InvalidCastException, che stata 20. '"lanciata" dal codice precedente. Tutti i dati 21. 'relativi a quella eccezione sono ora conservati 22. 'nella variabile Ex. 23. 'Possiamo accedervi oppure no, come in questo caso, 24. 'ma sono in ogni caso informazioni utili, come 25. 'vedremo fra poco 26. Console.WriteLine("I dati inseriti non sono numeri!") 27. 'I dati non sono coerenti, quindi ok = False 28. ok = False 29. End Try 30. 'Richiede gli stessi dati fino a che non si tratta 31. 'di due numeri 32. Loop Until ok 33. 34. 'Esegue il controllo su b e poi effettua la divisione 35. If b <> 0 Then 36. Console.WriteLine("{0} / {1} = {2}", a, b, a / b) 37. Else 38. Console.WriteLine("Divisione impossibile!") 39. End If 40. Console.ReadKey() 41. End Sub 42. 43. End Module Or a potr este anche chieder vi "Come faccio a saper e quale classe r appr esenta quale eccezione?". Beh, in gener e, si mette un blocco Tr y dopo aver notato il ver ificar si dell'er r or e e quindi dopo aver letto il messaggio di er r or e che contiene anche il nome dell'eccezione. In alter nativa si pu specificar e come tipo semplicemente Ex ception, ed in quel caso ver r anno cattur ate tutte le eccezioni gener ate, di qualsiasi tipo. Ecco una var iante dell'esempio pr ecedente: 01. Module Module1 02. Sub Main() 03. 'a e b sono interi short, ossia possono assumere 04. 'valori da -32768 a +32767 05. Dim a, b As Int16 06. Dim ok As Boolean = False 07. 08. Do 09. Try 10. Console.WriteLine("Inserire due numeri non nulli: ") 11. a = Console.ReadLine 12. b = Console.ReadLine 13. ok = True 14. Catch Ex As Exception 15. 'Catturiamo una qualsiasi eccezione e stampiamo il 16. 'messaggio 17. 'ad essa relativo. Il messaggio contenuto nella 18. 'propriet Message dell'oggetto Ex. 19. Console.WriteLine(Ex.Message) 20. ok = False 21. End Try 22. Loop Until ok 23. 24. If b <> 0 Then 25. Console.WriteLine("{0} / {1} = {2}", a, b, a / b) Else 26. Console.WriteLine("Divisione impossibile!") 27. 28. End If 29. Console.ReadKey() 30. End Sub 31. 32. End Module Pr ovando ad inser ir e un numer o tr oppo gr ande o tr oppo piccolo si otter r "Over flow di un'oper azione ar itmetica."; inser endo una str inga non conver tibile in numer o si otter r "Cast non valido dalla str inga [str inga] al tipo 'Shor t'".

Questa ver sione, quindi, cattur a e gestisce ogni possibile eccezione. Ricor date che possibile usar e anche pi clausole Catch in un unico blocco Tr y, ad esempio una per ogni eccezione diver sa.

Clausola Finally
Il costr utto Tr y costituito da un blocco Tr y e da una o pi clausole Catch. Tuttavia, opzionalmente, possibile specificar e anche un'ulter ior e clausola, che deve esser e posta dopo tutti i Catch: Finally. Finally d inizio ad un altr o blocco di codice che viene s empre eseguito, sia che si gener i un'eccezione, sia che non se ne gener i alcuna. Il codice ivi contenuto viene eseguito comunque dopo il tr y e il catch. Ad esempio, assumiamo di aver e questo blocco di codice, con alcune istr uzioni di cui non ci inter essa la natur a: mar chiamo le istr uzioni con delle letter e e ipotizziamo che la D gener i un'eccezione: 01. 02. 03. 04. 05. 06. 07. 08. 09. 10. 11. 12. 13. 14. Try A B C D E F Catch Ex As Exception G H Finally I L End Try

Le istr uzioni eseguite sar anno: 01. 02. 03. 04. 05. 06. 07. 08. 09. A B C 'Eccezione: salta nel blocco Catch G H 'Alla fine esegue comunque il Finally I L

Lanc iare un'ec c ezione e c reare ec c ezioni personalizzate


Ammettiamo or a di aver bisogno di un'eccezione che r appr esenti una par ticolar e cir costanza che si ver ifica solo nle nostr o pr ogr amma, e di cui non esiste un cor r ispettivo tr a le eccezioni pr edefinite del Fr amew or k. Dovr emo scr iver e una nuova eccezione. Per far ci, bisogna semplicemente dichiar ar e una nuova classe che er editi dalla classe Ex eption: 01. Module Module1 02. 'Questa classe rappresenta l'errore lanciato quando una 03. 'password imessa sbagliata. Per convenzione, tutte le 04. 'classi che rappresentano un'eccezione devono terminare 05. 'con la parola "Exception" 06. Class IncorrectPasswordException 07. Inherits System.Exception 'Eredita da Exception 08. 09. 'Queste propriet ridefiniscono quelle della classe 10. 'Exception tramite polimorfismo, perci sono 11. 'dichiarate Overrides 12. 13. 'Sovrascrive il messaggio di errore 14. Public Overrides ReadOnly Property Message() As String 15. Get 16.

17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40. 41. 42. 43. 44. 45. 46. 47. 48. 49. 50. 51. 52. 53. 54. 55. 56. 57. End

Return "La password inserita sbagliata!" End Get End Property 'Modifica il link di aiuto Public Overrides Property HelpLink() As String Get Return "http://totem.altervista.org" End Get Set(ByVal Value As String) MyBase.HelpLink = value End Set End Property 'Il resto dei membri di Exception sono molto importanti 'e vengono inizializzati con dati prelevati tramite 'Reflection (ultimo argomento di questa sezione), perci ' conveniente non modificare altro. Potete 'semmai aggiungere qualche membro End Class Sub Main() Dim Pass As String = "b7dha90" Dim NewPass As String Console.WriteLine("Inserire la password:") NewPass = Console.ReadLine If NewPass <> Pass Then 'Lancia l'eccezione usando la keyword Throw Throw New IncorrectPasswordException End If Catch IPE As IncorrectPasswordException 'Visualizza il messaggio Console.WriteLine(IPE.Message) 'E il link d'aiuto Console.WriteLine("Help: " & IPE.HelpLink) End Try Console.ReadKey() End Sub Module Try

Come si visto nell'esempio, lanciar e un'eccezione molto semplice: basta scr iver e Thr ow , seguito da un oggetto Ex ception valido. In questo caso abbiamo cr eato l'oggetto Incor r ectPassw or dEx ception nello stessa linea di codice in cui l'abbiamo lanciato.

A33. Distruttori
Avver tenza: questo un capitolo molto tecnico. For se vi sar pi utile in futur o.

Gli oggetti COM (Component Object Model) utilizzati dal vecchio VB6 possedevano una car atter istica peculiar e che per metteva di deter minar e quando non vi fosse pi bisogno di lor o e la memor ia associata potesse esser e r ilasciata: er ano dotati di un r efer ence counter , ossia di un "contator e di r ifer imenti". Ogni volta che una var iabile veniva impostata su un oggetto COM, il contator e veniva aumentato di 1, mentr e quando quella var iabile veniva distr utta o se ne cambiava il valor e, il contator e scendeva di un'unit. Quando tale valor e r aggiungeva lo zer o, gli oggetti venivano distr utti. Er ano pr esenti alcuni pr oblemi di cor r uzione della memor ia, per : ad esempio se due oggetti si puntavano vicendevolmente ma non er ano utilizzati dall'applicazione, essi non venivano distr utti (r ifer imento cir colar e). Il meccanismo di gestione della memor ia con il .NET Fr amew or k molto diver so, e or a vediamo come oper a.

Garbage Collec tion


Questo il nome del pr ocesso sul quale si basa la gestione della memor ia del Fr amew or k. Quando l'applicazione tenta di cr ear e un nuovo oggetto e lo spazio disponibile nell'heap managed scar seggia, viene messo in moto questo meccanismo, attr aver so l'attivazione del Gar bage Collector . Per pr ima cosa vengono visitati tutti gli oggetti pr esenti nello heap: se ce n' uno che non r aggiungibile dall'applicazione, questo viene distr utto. Il pr ocesso molto sofisticato, in quanto in gr ado di r ilevar e anche dipendenze indir ette, come classi non r aggiungibili dir ettamente, r efer enziate da altr e classi che sono r aggiungibili dir ettamente; r iesce anche a r isolver e il pr oblema opposto, quello del r ifer imento cir colar e. Se uno o pi oggetti non vengono distr utti per ch sono necessar i al pr ogr amma per funzionar e, si dice che essi sono sopr avvissuti a una Gar bage Collection e appar tengono alla gener azione 1, mentr e quelli inizializzati che non hanno subito ancor a nessun pr ocesso di r accolta della memor ia sono di gener azione 0. L'indice gener azionale viene incr ementato di uno fino ad un massimo di 2. Questi ultimi oggetti sono sopr avvissuti a molti contr olli, il che significa che continuano a esser e utilizzati nello stesso modo: per ci il Gar bage Collector li sposta in una posizione iniziale dell'heap managed, in modo che si dovr anno eseguir e meno oper azioni di spostamento della memor ia in seguito. La stessa cosa vale per le gener azioni successive. Questo sistema assicur a che ci sia sempr e spazio liber o, ma non gar antisce che ogni oggetto logicamente distr utto lo sia anche fisicamente: se per quegli oggetti che allocano solo memor ia il pr oblema r elativo, per altr i che utilizzano file e r isor se ester ne, invece, diventa pi complicato. Il compito di r ilasciar e le r isor se spetta quindi al pr ogr ammator e, che dovr ebbe, in una classe ideale, pr eoccupar si che quando l'oggetto venga distr utto lo siano cor r ettamente anche le r isor se ad esso associate. Bisogna quindi far e eseguir e del codice appena pr ima della distr uzione: come? lo vediamo or a.

Finalize
Il metodo Finalize di un oggetto speciale, poich viene r ichiamato dal Gar bage Collector "in per sona" dur ante la r accolta della memor ia. Come gi detto, non possibile saper e quando un oggetto logicamente distr utto lo sar anche fisicamente, quindi Finalize potr ebbe esser e eseguito anche diver si secondi, o minuti, o addir ittur a or e, dopo che sia stato annullato ogni r ifer imento all'oggetto. Come seconda clausola impor tante, necessar io non acceder e m ai ad oggetti ester ni in una pr ocedur a Finalize: dato che il GC (acr onimo di gar bage collector ) pu distr ugger e gli oggetti in qualsiasi or dine, non si pu esser e sicur i che l'oggetto a cui si sta facendo r ifer imento esista ancor a o sia gi stato distr utto. Questo vale anche per oggetti singleton come Console o Application, o addir ittur a per i tipi Str ing, Byte, Date e tutti gli altr i (dato che, essendo anch'essi istanze di System.Type, che definisce le car atter istiche di ciascun tipo,

sono soggetti alla GC alla fine del pr ogr amma). Per saper e se il pr ocesso di distr uzione stato avviato dalla chiusur a del pr ogr amma si pu r ichiamar e una semplice pr opr iet booleana, Envir onment.HasShutdow nStar ted. Per esemplificar e i concetti, in questo par agr afo far uso dell'oggetto singleton GC, che r appr esenta il Gar bage Collector , per mettendo di avviar e for zatamente la r accolta della memor ia e altr e cose: questo no n deve mai esser e fatto in un'applicazione r eale, poich potr ebbe compr ometter ne le pr estazioni. 01. Module Module1 02. Class Oggetto 03. Sub New() Console.WriteLine("Un oggetto sta per essere creato.") 04. End Sub 05. 06. 'La procedura Finalize definita in System.Object, quindi, 07. 'per ridefinirla dobbiamo usare il polimorfismo. Inoltre 08. 'deve essere dichiarata Protected, poich non pu 09. 'essere richiamata da altro ente se non dal GC e allo 10. 'stesso tempo ereditabile 11. Protected Overrides Sub Finalize() 12. Console.WriteLine("Un oggetto sta per essere distrutto.") 13. 'Blocca il programma per 4 secondi circa, consentendoci 'di vedere cosa viene scritto a schermo 14. 15. System.Threading.Thread.CurrentThread.Sleep(4000) 16. End Sub 17. End Class 18. Sub Main() 19. Dim O As New Oggetto 20. Console.WriteLine("Oggetto = Nothing") 21. Console.WriteLine("L'applicazione sta per terminare.") 22. End Sub 23. 24. End Module L'output sar : 1. 2. 3. 4. Un oggetto sta per essere creato. Oggetto = Nothing L'applicazione sta per terminare. Un oggetto sta per essere distrutto.

Come si vede, l'oggetto viene distr utto do po il ter mine dell'applicazione (siamo for tunati che Console ancor a "in vita" pr ima della distr uzione): questo significa che c'er a abbastanza spazio disponibile da non avviar e la GC, che quindi stata r imandata fino alla fine del pr ogr amma. Ripr oviamo invece in questo modo: 01. Sub Main() 02. Dim O As New Oggetto 03. O = Nothing 04. Console.WriteLine("Oggetto = Nothing") 05. 06. 'NON PROVATECI A CASA! 07. 'Forza una garbage collection 08. GC.Collect() 09. 'Attende che tutti i metodi Finalize siano stati eseguiti 10. GC.WaitForPendingFinalizers() 11. 12. Console.WriteLine("L'applicazione sta per terminare.") 13. Console.ReadKey() 14. End Sub Ci che appar ir sullo scher mo : 1. 2. 3. 4. Un oggetto sta per essere creato. Oggetto = Nothing Un oggetto sta per essere distrutto. L'applicazione sta per terminare.

Si vede che l'or dine delle ultime due azioni stato cambiato a causa delle GC avviata anzi tempo pr ima del ter mine del pr ogr amma.

Anche se ci siamo diver titi con Finalize, questo metodo deve esser e definito solo se str ettamente necessar io, per alcune r agioni. La pr ima che il GC impiega non uno, ma due cicli per finalizzar e un oggetto in cui stata definita Finalize dal pr ogr ammator e. Il motivo consiste nella possibilit che venga usata la cosiddetta r esur r ezio ne dell'o g g etto : in questa tecnica, ad una var iabile globale viene assegnato il r ifer imento alla classe stessa usando Me; dato che in questo modo c' ancor a un r ifer imento valido all'oggetto, questo non deve venir e distr utto. Tuttavia, per r ilevar e questo fenomeno, il GC impiega due cicli e si r ischia di occupar e memor ia inutile. Inoltr e, sempr e per questa causa, si impiega pi tempo macchina che potr ebbe esser e speso in altr o modo.

Dispose
Si potr ebbe definir e Dispose come un Finalize manuale: esso per metto di r ilasciar e qualsiasi r isor sa che non sia la memor ia (ossia connessioni a database, files, immagini, pennelli, oggetti di sistema, ecceter a...) manualmente, appena pr ima di impostar e il r ifer imento a Nothing. In questo modo non si dovr aspettar e una successiva GC affinch sia r ilasciato tutto cor r ettamente. Dispose non un metodo definito da tutti gli oggetti, e per ci ogni classe che intende definir lo deve implementar e l'inter faccia IDisposable (per ulter ior i infor mazioni sulle inter facce, veder e capitolo 36): per or a pr endete per buono il codice che for nisco, vedr emo in seguito pi appr ofonditamente l'agor mento delle inter facce. 01. Class Oggetto 02. 'Implementa l'interfaccia IDisposable 03. Implements IDisposable 04. 'File da scrivere: 05. Dim W As IO.StreamWriter 06. 07. Sub New() 08. 'Inizializza l'oggetto 09. W = New IO.StreamWriter("C:\test.txt") 10. End Sub 11. 12. Public Sub Dispose() Implements IDisposable.Dispose 13. 'Chiude il file 14. W.Close() 15. End Sub 16. End Class Invocando il metodo Dispose di Oggetto, possibile chiuder e il file ed evitar e che venga lasciato aper to. Il Vb.NET for nisce un costr utto, valido per tutti gli oggetti che implementano l'inter faccia IDisposable, che si assicur a di r ichiamar e il metodo Dispose e impostar e il r ifer imento a Nothing automaticamente dopo l'uso. La sintassi questa: 1. 2. 3. 4. 5. 6. 7. 8. Using [Oggetto] 'Codice da eseguire End Using 'Che corrisponde a scrivere: 'Codice da eseguire [Oggetto].Dispose() [Oggetto] = Nothing

Per convenzione, se una classe implementa un'inter faccia IDisposable e contiene altr e classi nidificate o altr i oggetti, il suo metodo Dispose deve r ichiamar e il Dispose di tutti gli oggetti inter ni, almeno per quelli che ce l'hanno. Altr a convenzione che se viene r ichiamata Dispose da un oggetto gi distr utto logicamente, deve gener ar si l'eccezione ObjectDisposedEx ception.

Usare Dispose e Finalize


Ci sono alcune cir costanze che r ichiedono l'uso di una sola delle due, altr e che non le r ichiedono e altr e ancor a che dovr ebber o r cihieder le entr ambe. Segue una piccola lista di sugger imenti su come metter e in pr atica questi

meccanismi: N Dispose, n Finalize: la classe impiega solo la memor ia come unica r isor sa o, se ne impiegate altr e, le r ilascia pr ima di ter minar e le pr opr ie oper azioni. Solo Dispose: la classe impiega r isor se facendo r ifer imento ad altr i oggetti .NET e si vuole for nir e al chiamante la possibilit di r ilasciar e tali r isor se il pr ima possibile. Dispose e Finalize: la classe impiega dir ettamente una r isor sa, ad esempio invocando un metodo di una libr er ia unmanaged, che r ichiede un r ilascio esplicito; in pi si vuole for nir e al client la possibilit di deallocar e manualmente gli oggetti. Solo Finalize: si deve eseguir e un cer to codice pr ima della distr uzione. A questo punto ci si deve pr eoccupar e di due pr oblemi che possono pr esentar si: Finalize pu esser e chiamato anche dopo che l'oggetto stato distr utto e le sue r isor se deallocate con Dispose, quindi potr ebbe tantar e di distr ugger e un oggetto inesistente; il codice che viene eseguito in Finalize potr ebbe far r ifer imento a oggetti inesistenti. Le convenzioni per mettono di aggir ar e il pr oblema facendo uso di ver sioni in over load di Dispose e di una var iabile pr ivata a livello di classe. La var iabile booleana Disposed ha il compito di memor izzar e se l'oggetto stato distr utto: in questo modo eviter emo di r ipeter e il codice in Finalize. Il metodo in over load di Dispose accetta un par ametr o di tipo booleano, di solito chiamato Disposing, che indica se l'oggetto sta subendo un pr ocesso di distr uzione manuale o di finalizzazione: pr ocedendo con questo metodo si cer ti di r ichiamar e eventuali altr i oggetti nel caso non ci sia finalizzazione. Il codice seguente implementa una semplicissima classe FileWr iter e, tr amite messaggi a scher mo, visualizza quando e come l'oggetto viene r imosso dalla memor ia: 001. Module Module1 002. Class FileWriter 003. Implements IDisposable 004. 005. Private Writer As IO.StreamWriter 006. 'Indica se l'oggetto gi stato distrutto con Dispose 007. Private Disposed As Boolean 008. 'Indica se il file aperto 009. Private Opened As Boolean 010. 011. Sub New() 012. Disposed = False 013. Opened = False 014. Console.WriteLine("FileWriter sta per essere creato.") 015. 'Questa procedura comunica al GC di non richiamare pi 016. 'il metodo Finalize per questo oggetto. Scriviamo ci 017. 'perch se file non viene esplicitamente aperto con 018. 'Open non c' alcun bisogno di chiuderlo 019. GC.SuppressFinalize(Me) 020. End Sub 021. 022. 'Apre il file 023. Public Sub Open(ByVal FileName As String) Writer = New IO.StreamWriter(FileName) 024. Opened = True 025. 026. Console.WriteLine("FileWriter sta per essere aperto.") 'Registra l'oggetto per eseguire Finalize: ora il file 027. ' aperto e pu quindi essere chiuso 028. GC.ReRegisterForFinalize(Me) 029. End Sub 030. 031. 'Scrive del testo nel file 032. Public Sub Write(ByVal Text As String) 033. If Opened Then 034. Writer.Write(Text) 035. End If 036. End Sub 037. 038. 'Una procedura analoga a Open aiuta a impostare meglio 039. 'l'oggetto e non fa altro che richiamare Dispose: 040. 'pi una questione di completezza 041. 042.

043. 044. 045. 046. 047. 048. 049. 050. 051. 052. 053. 054. 055. 056. 057. 058. 059. 060. 061. 062. 063. 064. 065. 066. 067. 068. 069. 070. 071. 072. 073. 074. 075. 076. 077. 078. 079. 080. 081. 082. 083. 084. 085. 086. 087. 088. 089. 090. 091. 092. 093. 094. 095. 096. 097. 098. 099. 100. 101. 102. 103. 104. 105. 106. 107. 108. 109. End L'output:

Public Sub Close() Dispose() End Sub 'Questa versione in overload perch l'altra viene 'chiamata solo dall'utente ( Public), mentre questa 'implementa tutto il codice che necessario eseguire 'per rilasciare le risorse. 'Il parametro Disposing indica se l'oggetto sta per 'essere distrutto, quindi manualmente, o finalizzato, 'quindi nel processo di GC: nel secondo caso altri oggetti 'che questa classe utilizza potrebbero non esistere pi, 'perci si deve controllare se possibile 'invocarli correttamente Protected Overridable Overloads Sub Dispose(ByVal Disposing _ As Boolean) 'Esegue il codice solo se l'oggetto esiste ancora If Disposed Then 'Se distrutto, esce dalla procedura Exit Sub End If If Disposing Then 'Qui possiamo chiamare altri oggetti con la 'sicurezza che esistano ancora Console.WriteLine("FileWriter sta per essere distrutto.") Else Console.WriteLine("FileWriter sta per essere finalizzato.") End If 'Chiude il file Writer.Close() Disposed = True Opened = False End Sub Public Overloads Sub Dispose() Implements IDisposable.Dispose 'L'oggetto stato distrutto Dispose(True) 'Quindi non deve pi essere finalizzato GC.SuppressFinalize(Me) End Sub Protected Overrides Sub Finalize() 'Processo di finalizzazione: Dispose(False) End Sub End Class Sub Main() Dim F As New FileWriter 'Questo blocco mostra l'esecuzione di Dispose F.Open("C:\test.txt") F.Write("Ciao") F.Close() 'Questo mostra l'esecuzione di Finalize F = New FileWriter F.Open("C:\test2.txt") F = Nothing GC.Collect() GC.WaitForPendingFinalizers() Console.ReadKey() End Sub Module

1. FileWriter sta per essere creato. 2.

3. 4. 5. 6.

FileWriter FileWriter FileWriter FileWriter FileWriter

sta sta sta sta sta

per per per per per

essere essere essere essere essere

aperto. distrutto. creato. aperto. finalizzato.

A34. I Delegate
Con il ter mine Deleg ate si indica un par ticolar e tipo di dato che in gr ado di "contener e" un metodo, ossia una pr ocedur a o una funzione. Ho messo di pr oposito le vir golette sul ver bo "contener e", poich non pr opr iamente esatto, ma ser ve per r ender e pi incisiva la definizione. Come esistono tipi di dato per gli inter i, i decimali, le date, le str inghe, gli oggetti, ne esistono anche per i metodi, anche se pu sembr ar e un po' str ano. Per chi avesse studiato altr i linguaggi pr ima di appr occiar si al VB.NET, possiamo assimilar e i Delegate ai tipi pr ocedur ali del Pascal o ai puntator i a funzione del C. Ad ogni modo, i delegate sono legger mente diver si da questi ultimi e pr esentano alcuni tr atti par ticolar i: Un delegate non pu contener e quals ias i metodo, ma he dei limiti. Infatti, in gr ado di contener e solo metodi con la stessa signatur e specificata nella definizione del tipo. Fr a br eve vedr emo in cosa consiste questo punto; Un delegate pu contener e sia metodi di istanza sia metodi statici, a patto che questi r ispettino la r egole di cui al punto sopr a; Un delegate un tipo r efer ence, quindi si compor ta come un comunissimo oggetto, seguendo quelle r egole che mi sembr a di aver gi r ipetuto fino alla noia; Un oggetto di tipo delegate un oggetto immutabile, ossia, una volta cr eato, non pu esser e modificato. Per questo motivo, non espone alcuna pr opr iet (tr anne due in sola lettur a). D'altr a par te, questo compor tamento er a pr evedibile fin dalla definizione: infatti, se un delegate contiene un r ifer imento ad un metodo - e quindi un metodo gi esistente e magar i definito in un'altr a par te del codice - come si far ebbe a modificar lo? Non si potr ebbe modificar e la signatur e per ch questo andr ebbe in conflitto con la sua natur a, e non si potr ebbe modificar ne il cor po per ch si tr atta di codice gi scr itto (r icor date che gli oggetti esistono solo a r un-time, per ch vengono cr eati solo dopo l'avvio del pr ogr amma, e tutto il codice gi stato compilato e tr asfor mato in linguaggio macchina inter medio); Un delegate un tipo s afe, ossia non pu mai contener e r ifer imenti ad indir izzi di memor ia che non indichino espr essamente un metodo (al contr ar io dei per icolosi puntator i del C). Mi r endo conto che questa intr oduzione pu appar ir e un po' tr oppo teor ica e fumosa, ma ser ve per compr ender e il compor tamento dei delegate.

Dic hiarazione di un delegate


Un nuovo tipo delegate viene dichiar ato con questa sintassi: 1. Delegate [Sub/Function] [Nome]([Elenco parametri]) Appar e subito chiar o il legame con i metodi data la for tissima somiglianza della sintassi con quella usata per definir e, appunto, un metodo. Notate che in questo caso si specifica solo la signatur e (tipo e quantit dei par ametr i) e la categor ia (pr ocedur a o funzione) del delegate, mentr e il [Nome] indica il nome del nuovo tipo cr eato (cos come il nome di una nuova classe o una nuova str uttur a), ma non vi tr accia del "cor po" del delegate. Un delegate, infatti, non ha cor po, per ch, se invocato da un oggetto, esegue i metodi che esso stesso contiene, e quindi esegue il codice contenuto nei lor o cor pi. Da questo momento in poi, potr emo usar e nel codice questo nuovo tipo per immagazzinar e inter i metodi con le stesse car atter istiche appena definite. Dato che si tr atta di un tipo r efer ence, per , bisogna anche inizializzar e l'oggetto con un costr uttor e... Qui dovr ebbe sor ger e spontaneamente un dubbio: dove e come si dichiar a il costr uttor e di un delegate? Fino ad or a, infatti, gli unici tipi r efer ence che abbiamo impar ato a dichiar ar e sono le classi, e nelle classi lecito scr iver e un nuovo costr uttor e New nel lor o cor po. Qui, invece, non c' nessun cor po in cui por r e un ipotetico costr uttor e. La r ealt che si usa sem pr e il costr uttor e di default, ossia quello pr edefinito, che

viene automaticamente cr eato all'atto stesso della dichiar azione, anche se noi non r iusciamo a veder lo. Questo costr uttor e accetta sempr e e solo un par ametr o: un oggetto di tipo indeter minato r estituito da uno speciale oper ator e, Addr essOf. Questo un oper ator e unar io che accetta come oper ando il metodo di cui ottener e l'"indir izzo": 1. AddressOf [NomeMetodo] Ci che Addr essOf r estituisce non molto chiar o: la sua descr izione dice espr essamente che viene r estituito un oggetto delegate (il che gi abbastanza str ano di per s, dato che per cr ear e un delegate ci vuole un altr o delegate). Tuttavia, se si utilizza come par ametr o del costr uttor e un oggetto System.Delegate viene r estituito un er r or e. Ma lasciamo queste disquisizioni a chi ha tempo da per der e e pr ocediamo con le cose impor tanti. N.B.: Dalla ver sione 2008, i costr uttor i degli oggetti delegate accettano anche espr essioni lambda! Una volta dichiar ata ed inizializzata una var iabile di tipo delegate, possibile usar la esattamente come se fosse un metodo con la signatur e specificata. Ecco un esempio: 01. Module Module1 02. 'Dichiarazione di un tipo delegate Sub che accetta un parametro 03. 'di tipo stringa. Delegate Sub Display(ByVal Message As String) 04. 05. 06. 'Una procedura dimostrativa 07. Sub Write1(ByVal S As String) Console.WriteLine("1: " & S) 08. 09. End Sub 10. 11. 'Un'altra procedura dimostrativa 12. Sub Write2(ByVal S As String) 13. Console.WriteLine("2: " & S) 14. End Sub 15. 16. Sub Main() 17. 'Variabile D di tipo Display, ossia il nuovo tipo 18. 'delegate appena definito all'inizio del modulo Dim D As Display 19. 20. 21. 'Inizializa D con un nuovo oggetto delegate contenente 'un riferimento al metodo Console.WriteLine 22. D = New Display(AddressOf Console.WriteLine) 23. 24. 'Invoca il metodo referenziato da D: in questo caso 25. 'equivarrebbe a scrivere Console.WriteLine("Ciao") 26. D("Ciao") 27. 28. 'Reinizializza D, assegnandogli l'indirizzo di Write1 29. D = New Display(AddressOf Write1) 30. ' come chiamare Write1("Ciao") 31. D("Ciao") 32. 33. 'Modo alternativo per inizializzare un delegate: si omette 34. 'New e si usa solo AddressOf. Questo genera una conversione 35. 'implicita che d errore di cast nel caso in cui Write1 36. 'non sia compatibile con la signature del delegate 37. D = AddressOf Write2 38. D("Ciao") 39. 40. 'Notare che D pu contenere metodi di istanza 41. '(come Console.WriteLine) e metodi statici (come Write1 42. 'e Write2) 43. 44. Console.ReadKey() 45. End Sub 46. 47. End Module La signatur e di un delegate no n pu contener e par ametr i indefiniti (Par amAr r ay) od opzionali (Optional), tuttavia i metodi memor izzati in un oggetto di tipo delegate possono aver e par ametr i di questo tipo. Eccone un esempio: 001. Module Module1 002.

003. 004. 005. 006. 007. 008. 009. 010. 011. 012. 013. 014. 015. 016. 017. 018. 019. 020. 021. 022. 023. 024. 025. 026. 027. 028. 029. 030. 031. 032. 033. 034. 035. 036. 037. 038. 039. 040. 041. 042. 043. 044. 045. 046. 047. 048. 049. 050. 051. 052. 053. 054. 055. 056. 057. 058. 059. 060. 061. 062. 063. 064. 065. 066. 067. 068. 069. 070. 071. 072. 073. 074.

'Tipo delegate che pu contenere riferimenti a funzioni Single 'che accettino un parametro di tipo array di Single Delegate Function ProcessData(ByVal Data() As Single) As Single 'Tipo delegate che pu contenere riferimenti a procedure 'che accettino due parametri, un array di Single e un Boolean Delegate Sub PrintData(ByVal Data() As Single, ByVal ReverseOrder As Boolean) 'Funzione che calcola la media di alcuni valori. Notare che 'l'unico parametro indefinito, in quanto 'dichiarato come ParamArray Function CalculateAverage(ByVal ParamArray Data() As Single) As Single Dim Total As Single = 0 For I As Int32 = 0 To Data.Length - 1 Total += Data(I) Next Return (Total / Data.Length) End Function 'Funzione che calcola la varianza di alcuni valori. Notare che 'anche in questo caso il parametro indefinito Function CalculateVariance(ByVal ParamArray Data() As Single) As Single Dim Average As Single = CalculateAverage(Data) Dim Result As Single = 0 For I As Int32 = 0 To Data.Length - 1 Result += (Data(I) - Average) ^ 2 Next Return (Result / Data.Length) End Function 'Procedura che stampa i valori di un array in ordine normale 'o inverso. Notare che il secondo parametro opzionale Sub PrintNormal(ByVal Data() As Single, _ Optional ByVal ReverseOrder As Boolean = False) If ReverseOrder Then For I As Int32 = Data.Length - 1 To 0 Step -1 Console.WriteLine(Data(I)) Next Else For I As Int32 = 0 To Data.Length - 1 Console.WriteLine(Data(I)) Next End If End Sub 'Procedura che stampa i valori di un array nella forma: '"I+1) Data(I)" 'Notare che anche in questo caso il secondo parametro ' opzionale Sub PrintIndexed(ByVal Data() As Single, _ Optional ByVal ReverseOrder As Boolean = False) If ReverseOrder Then For I As Int32 = Data.Length - 1 To 0 Step -1 Console.WriteLine("{0}) {1}", Data.Length - I, Data(I)) Next Else For I As Int32 = 0 To Data.Length - 1 Console.WriteLine("{0}) {1}", (I + 1), Data(I)) Next End If End Sub Sub Main() Dim Process As ProcessData Dim Print As PrintData Dim Data() As Single Dim Len As Int32 Dim Cmd As Char

Console.WriteLine("Quanti valori inserire?") 075. Len = Console.ReadLine 076. 077. ReDim Data(Len - 1) 078. For I As Int32 = 1 To Len 079. Console.Write("Inserire il valore " & I & ": ") 080. Data(I - 1) = Console.ReadLine 081. Next 082. 083. Console.Clear() 084. 085. Console.WriteLine("Scegliere l'operazione da eseguire: ") 086. Console.WriteLine("m - Calcola la media dei valori;") 087. Console.WriteLine("v - Calcola la varianza dei valori;") 088. Cmd = Console.ReadKey().KeyChar 089. Select Case Cmd 090. Case "m" 091. Process = New ProcessData(AddressOf CalculateAverage) 092. Case "v" 093. Process = New ProcessData(AddressOf CalculateVariance) 094. Case Else 095. Console.WriteLine("Comando non valido!") 096. Exit Sub End Select 097. Console.WriteLine() 098. Console.WriteLine("Scegliere il metodo di stampa: ") 099. Console.WriteLine("s - Stampa i valori;") 100. Console.WriteLine("i - Stampa i valori con il numero ordinale a fianco.") 101. Cmd = Console.ReadKey().KeyChar 102. Select Case Cmd 103. Case "s" 104. Print = New PrintData(AddressOf PrintNormal) 105. Case "i" 106. Print = New PrintData(AddressOf PrintIndexed) 107. Case Else 108. Console.WriteLine("Comando non valido!") 109. Exit Sub 110. End Select 111. 112. Console.Clear() 113. 114. Console.WriteLine("Valori:") 115. 'Eccoci arrivati al punto. Come detto prima, i delegate 116. 'non possono definire una signature che comprenda parametri 117. 'opzionali o indefiniti, ma si 118. 'pu aggirare questa limitazione semplicemente dichiarando 119. 'un array di valori al posto del ParamArray (in quanto si 120. 'tratta comunque di due vettori) e lo stesso parametro 121. 'non opzionale al posto del parametro opzionale. 122. 'L'inconveniente, in questo ultimo caso, che il 123. 'parametro, pur essendo opzionale va sempre specificato 124. 'quando il metodo viene richiamato attraverso un oggetto 125. 'delegate. Questo escamotage permette di aumentare la 126. 'portata dei delegate, includendo anche metodi che 127. 'possono essere stati scritti tempo prima in un'altra 128. 'parte inaccessibile del codice: cos 129. 'non necessario riscriverli! 130. Print(Data, False) 131. Console.WriteLine("Risultato:") 132. Console.WriteLine(Process(Data)) 133. 134. Console.ReadKey() 135. End Sub 136. 137. 138. End Module

Un esempio pi signific ativo


I delegate sono par ticolar mente utili per r ispar miar e spazio nel codice. Tr amite i delegate, infatti, possiamo usar e lo

stesso metodo per eseguir e pi compiti differ enti. Dato che una var iabile delegate contiene un r ifr iento ad un metodo qualsiasi, semplicemente cambiando questo r ifer imento possiamo eseguir e codici diver si r ichiamando la stessa var iabile. E' come se potessimo "innestar e" del codice sempr e diver so su un substr ato costante. Ecco un esempio piccolo, ma significativo: 01. Module Module2 02. 'Nome del file da cercare 03. Dim File As String 04. 'Questo delegate referenzia una funzione che accetta un 05. 'parametro stringa e restituisce un valore booleano 06. 07. Delegate Function IsMyFile(ByVal FileName As String) As Boolean 08. 'Funzione 1, stampa il contenuto del file a schermo 09. Function PrintFile(ByVal FileName As String) As Boolean 10. 11. 'Io.Path.GetFileName(F) restituisce solo il nome del 12. 'singolo file F, togliendo il percorso delle cartelle 13. If IO.Path.GetFileName(FileName) = File Then 'IO.File.ReadAllText(F) restituisce il testo contenuto 14. 15. 'nel file F in una sola operazione 16. Console.WriteLine(IO.File.ReadAllText(FileName)) 17. Return True 18. End If Return False 19. End Function 20. 21. 'Funzione 2, copia il file sul desktop 22. Function CopyFile(ByVal FileName As String) As Boolean 23. If IO.Path.GetFileName(FileName) = File Then 24. 'IO.File.Copy(S, D) copia il file S nel file D: 25. 'se D non esiste viene creato, se esiste viene 26. 'sovrascritto 27. IO.File.Copy(FileName, _ 28. My.Computer.FileSystem.SpecialDirectories.Desktop & _ 29. "\" & File) 30. Return True 31. End If 32. Return False 33. End Function 34. 35. 'Procedura ricorsiva che cerca il file 36. Function SearchFile(ByVal Dir As String, ByVal IsOK As IsMyFile) _ 37. As Boolean 38. 'Ottiene tutte le sottodirectory 39. Dim Dirs() As String = IO.Directory.GetDirectories(Dir) 40. 'Ottiene tutti i files 41. Dim Files() As String = IO.Directory.GetFiles(Dir) 42. 43. 'Analizza ogni file per vedere se quello cercato 44. For Each F As String In Files 45. ' il file cercato, basta cercare 46. If IsOK(F) Then 47. 'Termina la funzione e restituisce Vero, cosicch 48. 'anche nel for sulle cartelle si termini 49. 'la ricerca 50. Return True 51. End If 52. Next 53. 54. 'Analizza tutte le sottocartelle 55. For Each D As String In Dirs 56. If SearchFile(D, IsOK) Then 57. 'Termina ricorsivamente la ricerca 58. Return True 59. End If 60. Next 61. End Function 62. 63. Sub Main() 64. Dim Dir As String 65. 66.

67. Console.WriteLine("Inserire il nome file da cercare:") 68. File = Console.ReadLine 69. 70. Console.WriteLine("Inserire la cartella in cui cercare:") 71. Dir = Console.ReadLine 72. 73. 'Cerca il file e lo scrive a schermo 74. SearchFile(Dir, AddressOf PrintFile) 75. 76. 'Cerca il file e lo copia sul desktop 77. SearchFile(Dir, AddressOf CopyFile) 78. 79. Console.ReadKey() 80. End Sub 81. End Module Nel sor gente si vede che si usano pochissime r ighe per far compier e due oper azioni molto differ enti alla stessa pr ocedur a. In altr e condizioni, un aspir ante pr ogr ammator e che non conoscesse i delegate avr ebbe scr itto due pr ocedur e inter e, spr ecando pi spazio, e condannandosi, inoltr e, a r iscr iver e la stessa cosa per ogni futur a var iante.

A35. I Delegate Multicast


Al contr ar io di un delegate semplice, un delegate multicast pu contener e r ifer imenti a pi metodi insieme, pur ch della stessa categor ia e con la stessa signatur e. Dato che il costr uttor e sempr e lo stesso e accetta un solo par ametr o, non possibile cr ear e delegate multicast in fase di inizializzazione. L'unico modo per far lo r ichiamar e il metodo statico Combine della classe System.Delegate (ossia la classe base di tutti i delegate). Combine espone anche un over load che per mette di unir e molti delegate alla volta, specificandoli tr amite un Par amAr r ay. Dato che un delegate multicast contiene pi r ifer imenti a metodi distinti, si par la di inv o catio n list (lista di invocazione) quando ci si r ifer isce all'insieme di tutti i metodi memor izzati in un delegate multicast. Ecco un semplice esempio: 01. Module Module2 02. 'Vedi esempio precedente 03. Sub Main() Dim Dir As String 04. Dim D As IsMyFile 05. 06. Console.WriteLine("Inserire il nome file da cercare:") 07. File = Console.ReadLine 08. 09. Console.WriteLine("Inserire la cartella in cui cercare:") 10. Dir = Console.ReadLine 11. 12. 13. 'Crea un delegate multicast, unendo PrintFile e CopyFile. 'Da notare che in questa espressione necessario usare 14. 15. 'delle vere e proprie variabili delegate, poich 'l'operatore AddressOf da solo non valido in questo caso 16. 17. D = System.Delegate.Combine(New IsMyFile(AddressOf PrintFile), _ 18. New IsMyFile(AddressOf CopyFile)) 'Per la cronaca, Combine un metodo factory 19. 20. 'Ora il file trovato viene sia visualizzato che copiato 21. 'sul desktop 22. SearchFile(Dir, D) 23. 24. 'Se si vuole rimuovere uno o pi riferimenti a metodi del 25. 'delegate multicast si deve utilizzare il metodo statico Remove: 26. D = System.Delegate.Remove(D, New IsMyFile(AddressOf CopyFile)) 27. 'Ora D far visualizzare solamente il file trovato 28. 29. Console.ReadKey() 30. End Sub 31. 32. End Module La funzione Combine, tuttavia, nasconde molte insidie. Infatti, essendo un metodo factor y della classe System.Delegate, come abbiamo detto nel capitolo r elativo ai metodi factor y, r estituisce un oggetto di tipo System.Delegate. Nell'esempio, noi abbiamo potuto assegnar e il valor e r estituito da Combine a D, che di tipo IsMyFile, per ch solitamente le opzioni di compilazione per mettono di eseguir e conver sioni implicite di questo tipo - ossia Option Str ict solitamente impostato su Off (per ulter ior i infor mazioni, veder e il capitolo sulle opzioni di compilazione). Come abbiamo detto nel capitolo sulle conver sioni, assegnar e il valor e di una classe der ivata a una classe base lecito, poich nel passaggio da una all'altr a non si per de alcun dato, ma si gener elizza soltanto il valor e r appr esentato; eseguir e il passaggio inver so, invece, ossia assegnar e una classe base a una der ivata, pu r isultar e in qualche str ano er r or e per ch i membr i in pi della classe der ivata sono vuoti. Nel caso dei delegate, che sono oggetti immutabili, e che quindi non espongono pr opr iet modificabili, questo non un pr oblema, ma il compilator e questo non lo sa. Per esser e sicur i, meglio utilizzar e un oper ator e di cast come Dir ectCast: 1. DirectCast(System.Delegate.Combine(A, B), IsMyFile) N.B.: Quando un delegate multicast contiene delle funzioni e viene r ichiamato, il valor e r estituito quello della pr ima

funzione memor izzata. Ecco or a un altr o esempio molto ar ticolato sui delegate multicast: 001. 002. 003. 004. 005. 006. 007. 008. 009. 010. 011. 012. 013. 014. 015. 016. 017. 018. 019. 020. 021. 022. 023. 024. 025. 026. 027. 028. 029. 030. 031. 032. 033. 034. 035. 036. 037. 038. 039. 040. 041. 042. 043. 044. 045. 046. 047. 048. 049. 050. 051. 052. 053. 054. 055. 056. 057. 058. 059. 060. 061. 062. 063. 064. 065. 066. 067. 068. 069. 'Questo esempio si basa completamente sulla manipolazione 'di file e cartelle, argomento non ancora affrontato. Se volete, 'potete dare uno sguardo ai capitoli relativi nelle parti 'successive della guida, oppure potete anche limitarvi a leggere 'i commenti, che spiegano tutto ci che accade. Module Module1 'In questo esempio eseguiremo delle operazioni su file con i delegate. 'Nel men sar possibile scegliere quali operazioni 'eseguire (una o tutte insieme) e sotto quali condizioni modificare 'un file. 'Il delegate FileFilter rappresenta una funzione che restituisce 'True se la condizione soddisfatta. Le condizioni 'sono racchiuse in un delegate multicast che contiene pi 'funzioni di questo tipo Delegate Function FileFilter(ByVal FileName As String) As Boolean 'Il prossimo delegate rappresenta un'operazione su un file Delegate Sub MassFileOperation(ByVal FileName As String) 'AskForData un delegate del tipo pi semplice. 'Servir per reperire le informazioni necessarie ad 'eseguire le operazioni (ad esempio, se si sceglie di copiare 'tutti i file di una cartella, si dovr anche scegliere 'dove copiare questi file). Delegate Sub AskForData() 'Queste variabili globali rappresentano le informazioni necesarie 'per lo svolgimento delle operazioni o la verifica delle condizioni. 'Stringa di formato per rinominare i file Dim RenameFormat As String 'Posizione di un file nella cartella Dim FileIndex As Int32 'Directory in cui copiare i file Dim CopyDirectory As String 'File in cui scrivere. Il tipo StreamWriter permette di scrivere 'facilmente stringhe su un file usando WriteLine come in Console Dim LogFile As IO.StreamWriter 'Limitazioni sulla data di creazione del file Dim CreationDateFrom, CreationDateTo As Date 'Limitazioni sulla data di ultimo accesso al file Dim LastAccessDateFrom, LastAccessDateTo As Date 'Limitazioni sulla dimensione Dim SizeFrom, SizeTo As Int64 'Rinomina un file Sub Rename(ByVal Path As String) 'Ne prende il nome semplice, senza estensione Dim Name As String = IO.Path.GetFileNameWithoutExtension(Path) 'Apre un oggetto contenente le informazioni sul file 'di percorso Path Dim Info As New IO.FileInfo(Path) 'Formatta il nome secondo la stringa di formato RenameFormat Name = String.Format(RenameFormat, _ Name, FileIndex, Info.Length, Info.LastAccessTime, Info.CreationTime) 'E aggiunge ancora l'estensione al nome modificato Name &= IO.Path.GetExtension(Path) 'Copia il vecchio file nella stessa cartella, ma con il nuovo nome IO.File.Copy(Path, IO.Path.GetDirectoryName(Path) & "\" & Name) 'Elimina il vecchio file IO.File.Delete(Path) 'Aumenta l'indice di uno FileIndex += 1 End Sub 'Funzione che richiede i dati necessari per far funzionare 'il metodo Rename Sub InputRenameFormat()

070. 071. 072. 073. 074. 075. 076. 077. 078. 079. 080. 081. 082. 083. 084. 085. 086. 087. 088. 089. 090. 091. 092. 093. 094. 095. 096. 097. 098. 099. 100. 101. 102. 103. 104. 105. 106. 107. 108. 109. 110. 111. 112. 113. 114. 115. 116. 117. 118. 119. 120. 121. 122. 123. 124. 125. 126. 127. 128. 129. 130. 131. 132. 133. 134. 135. 136. 137. 138. 139.

Console.WriteLine("Immettere una stringa di formato valida per rinominare i file.") Console.WriteLine("I parametri sono:") Console.WriteLine("0 = Nome originale del file;") Console.WriteLine("1 = Posizione del file nella cartella, in base 0;") Console.WriteLine("2 = Dimensione del file, in bytes;") Console.WriteLine("3 = Data dell'ultimo accesso;") Console.WriteLine("4 = Data di creazione.") RenameFormat = Console.ReadLine End Sub 'Elimina un file di percorso Path Sub Delete(ByVal Path As String) IO.File.Delete(Path) End Sub 'Copia il file da Path alla nuova cartella Sub Copy(ByVal Path As String) IO.File.Copy(Path, CopyDirectory & "\" & IO.Path.GetFileName(Path)) End Sub 'Richiede una cartella valida in cui copiare i file. Se non esiste, la crea Sub InputCopyDirectory() Console.WriteLine("Inserire una cartella valida in cui copiare i file:") CopyDirectory = Console.ReadLine If Not IO.Directory.Exists(CopyDirectory) Then IO.Directory.CreateDirectory(CopyDirectory) End If End Sub 'Scrive il nome del file sul file aperto Sub Archive(ByVal Path As String) LogFile.WriteLine(IO.Path.GetFileName(Path)) End Sub 'Chiede il nome di un file su cui scrivere tutte le informazioni Sub InputLogFile() Console.WriteLine("Inserire il percorso del file su cui scrivere:") LogFile = New IO.StreamWriter(Console.ReadLine) End Sub 'Verifica che la data di creazione del file cada tra i limiti fissati Function IsCreationDateValid(ByVal Path As String) As Boolean Dim Info As New IO.FileInfo(Path) Return (Info.CreationTime >= CreationDateFrom) And (Info.CreationTime >= CreationDateTo) End Function 'Richiede di immettere una limitazione temporale per considerare 'solo certi file Sub InputCreationDates() Console.WriteLine("Verranno considerati solo i file con data di creazione:") Console.Write("Da: ") CreationDateFrom = Date.Parse(Console.ReadLine) Console.Write("A: ") CreationDateTo = Date.Parse(Console.ReadLine) End Sub 'Verifica che la data di ultimo accesso al file cada tra i limiti fissati Function IsLastAccessDateValid(ByVal Path As String) As Boolean Dim Info As New IO.FileInfo(Path) Return (Info.LastAccessTime >= LastAccessDateFrom) And (Info.LastAccessTime >= LastAccessDateTo) End Function 'Richiede di immettere una limitazione temporale per considerare 'solo certi file Sub InputLastAccessDates() Console.WriteLine("Verranno considerati solo i file con data di creazione:") Console.Write("Da: ") LastAccessDateFrom = Date.Parse(Console.ReadLine) Console.Write("A: ")

140. 141. 142. 143. 144. 145. 146. 147. 148. 149. 150. 151. 152. 153. 154. 155. 156. 157. 158. 159. 160. 161. 162. 163. 164. 165. 166. 167. 168. 169. 170. 171. 172. 173. 174. 175. 176. 177. 178. 179. 180. 181. 182. 183. 184. 185. 186. 187. 188. 189. 190. 191. 192. 193. 194. 195. 196. 197. 198. 199. 200. 201. 202. 203. 204. 205. 206. 207. 208. 209. 210. 211.

LastAccessDateTo = Date.Parse(Console.ReadLine) End Sub 'Verifica che la dimensione del file sia coerente coi limiti fissati Function IsSizeValid(ByVal Path As String) As Boolean Dim Info As New IO.FileInfo(Path) Return (Info.Length >= SizeFrom) And (Info.Length >= SizeTo) End Function 'Richiede di specificare dei limiti dimensionali per i file Sub InputSizeLimit() Console.WriteLine("Verranno considerati solo i file con dimensione compresa:") Console.Write("Tra (bytes):") SizeFrom = Console.ReadLine Console.Write("E (bytes):") SizeTo = Console.ReadLine End Sub 'Classe che rappresenta un'operazione eseguibile su file Class Operation Private _Description As String Private _Execute As MassFileOperation Private _RequireData As AskForData Private _Enabled As Boolean 'Descrizione Public Property Description() As String Get Return _Description End Get Set(ByVal value As String) _Description = value End Set End Property 'Variabile che contiene l'oggetto delegate associato 'a questa operazione, ossia un riferimento a una delle Sub 'definite poco sopra Public Property Execute() As MassFileOperation Get Return _Execute End Get Set(ByVal value As MassFileOperation) _Execute = value End Set End Property 'Variabile che contiene l'oggetto delegate che serve 'per reperire informazioni necessarie ad eseguire 'l'operazione, ossia un riferimento a una delle sub 'di Input definite poco sopra. E' Nothing quando 'non serve nessun dato ausiliario (come nel caso 'di Delete) Public Property RequireData() As AskForData Get Return _RequireData End Get Set(ByVal value As AskForData) _RequireData = value End Set End Property 'Determina se l'operazione va eseguita oppure no Public Property Enabled() As Boolean Get Return _Enabled End Get Set(ByVal value As Boolean) _Enabled = value End Set End Property

212. 213. 214. 215. 216. 217. 218. 219. 220. 221. 222. 223. 224. 225. 226. 227. 228. 229. 230. 231. 232. 233. 234. 235. 236. 237. 238. 239. 240. 241. 242. 243. 244. 245. 246. 247. 248. 249. 250. 251. 252. 253. 254. 255. 256. 257. 258. 259. 260. 261. 262. 263. 264. 265. 266. 267. 268. 269. 270. 271. 272. 273. 274. 275. 276. 277. 278. 279. 280. 281. 282. 283.

Sub New(ByVal Description As String, _ ByVal ExecuteMethod As MassFileOperation, _ ByVal RequireDataMethod As AskForData) Me.Description = Description Me.Execute = ExecuteMethod Me.RequireData = RequireDataMethod Me.Enabled = False End Sub End Class 'Classe che rappresenta una condizione a cui sottoporre 'i file nella cartella: verranno elaborati solo quelli che 'soddisfano tutte le condizioni Class Condition Private _Description As String Private _Verify As FileFilter Private _RequireData As AskForData Private _Enabled As Boolean Public Property Description() As String Get Return _Description End Get Set(ByVal value As String) _Description = value End Set End Property 'Contiene un oggetto delegate associato a una delle 'precedenti funzioni Public Property Verify() As FileFilter Get Return _Verify End Get Set(ByVal value As FileFilter) _Verify = value End Set End Property Public Property RequireData() As AskForData Get Return _RequireData End Get Set(ByVal value As AskForData) _RequireData = value End Set End Property Public Property Enabled() As Boolean Get Return _Enabled End Get Set(ByVal value As Boolean) _Enabled = value End Set End Property Sub New(ByVal Description As String, _ ByVal VerifyMethod As FileFilter, _ ByVal RequireDataMethod As AskForData) Me.Description = Description Me.Verify = VerifyMethod Me.RequireData = RequireDataMethod End Sub End Class Sub Main() 'Contiene tutte le operazioni da eseguire: sar, quindi, un 'delegate multicast Dim DoOperations As MassFileOperation 'Contiene tutte le condizioni da verificare Dim VerifyConditions As FileFilter

284. 285. 286. 287. 288. 289. 290. 291. 292. 293. 294. 295. 296. 297. 298. 299. 300. 301. 302. 303. 304. 305. 306. 307. 308. 309. 310. 311. 312. 313. 314. 315. 316. 317. 318. 319. 320. 321. 322. 323. 324. 325. 326. 327. 328. 329. 330. 331. 332. 333. 334. 335. 336. 337. 338. 339. 340. 341. 342. 343. 344. 345. 346. 347. 348. 349. 350.

'Indica la cartella di cui analizzare i file Dim Folder As String 'Hashtable di caratteri-Operation o carattri-Condition. Il 'carattere indica quale tasto necessario 'premere per attivare/disattivare l'operazione/condizione Dim Operations As New Hashtable Dim Conditions As New Hashtable Dim Cmd As Char 'Aggiunge le operazioni esistenti. La 'c' messa dopo la stringa 'indica che la costante digitata un carattere e non una 'stringa. Il sistema non riesce a distinguere tra stringhe di lunghezza 1 e caratteri, al contrario di come accade in C With Operations .Add("r"c, New Operation("Rinomina tutti i file nella cartella;", _ New MassFileOperation(AddressOf Rename), _ New AskForData(AddressOf InputRenameFormat))) .Add("c"c, New Operation("Copia tutti i file nella cartella in un'altra cartella;", _ New MassFileOperation(AddressOf Copy), _ New AskForData(AddressOf InputCopyDirectory))) .Add("a"c, New Operation("Scrive il nome di tutti i file nella cartella su un file;", _ New MassFileOperation(AddressOf Archive), _ New AskForData(AddressOf InputLogFile))) .Add("d"c, New Operation("Cancella tutti i file nella cartella;", _ New MassFileOperation(AddressOf Delete), _ Nothing)) End With 'Aggiunge le condizioni esistenti With Conditions .Add("r"c, New Condition("Seleziona i file da elaborare in base alla data di creazione;", _ New FileFilter(AddressOf IsCreationDateValid), _ New AskForData(AddressOf InputCreationDates))) .Add("l"c, New Condition("Seleziona i file da elaborare in base all'ultimo accesso;", _ New FileFilter(AddressOf IsLastAccessDateValid), _ New AskForData(AddressOf InputLastAccessDates))) .Add("s"c, New Condition("Seleziona i file da elaborare in base alla dimensione;", _ New FileFilter(AddressOf IsSizeValid), _ New AskForData(AddressOf InputSizeLimit))) End With Console.WriteLine("Modifica in massa di file ---") Console.WriteLine() Do Console.WriteLine("Immetti il percorso della cartella su cui operare:") Folder = Console.ReadLine Loop Until IO.Directory.Exists(Folder) Do Console.Clear() Console.WriteLine("Premere la lettera corrispondente per selezionare la voce.") Console.WriteLine("Premere 'e' per procedere.") Console.WriteLine() For Each Key As Char In Operations.Keys 'Disegna sullo schermo una casella di spunta, piena: ' [X] 'se l'operazione attivata, altrimenti vuota: ' [ ] Console.Write("[") If Operations(Key).Enabled = True Then Console.Write("X") Else Console.Write(" ") End If Console.Write("] ") 'Scrive quindi il carattere da premere e vi associa la descrizione

351. 352. 353. 354. 355. 356. 357. 358. 359. 360. 361. 362. 363. 364. 365. 366. 367. 368. 369. 370. 371. 372. 373. 374. 375. 376. 377. 378. 379. 380. 381. 382. 383. 384. 385. 386. 387. 388. 389. 390. 391. 392. 393. 394. 395. 396. 397. 398. 399. 400. 401. 402. 403. 404. 405. 406. 407. 408. 409. 410. 411. 412. 413. 414. 415. 416. 417. 418. 419. 420. 421. 422.

Next Cmd = Console.ReadKey().KeyChar If Operations.ContainsKey(Cmd) Then Operations(Cmd).Enabled = Not Operations(Cmd).Enabled End If Loop Until Cmd = "e"c Console.Clear() Console.WriteLine("Premere la lettera corrispondente per selezionare la voce.") Console.WriteLine("Premere 'e' per procedere.") Console.WriteLine() For Each Key As Char In Conditions.Keys Console.Write("[") If Conditions(Key).Enabled = True Then Console.Write("X") Else Console.Write(" ") End If Console.Write("] ") Console.Write(Key) Console.Write(" - ") Console.WriteLine(Conditions(Key).Description) Next Cmd = Console.ReadKey().KeyChar If Conditions.ContainsKey(Cmd) Then Conditions(Cmd).Enabled = Not Conditions(Cmd).Enabled End If Loop Until Cmd = "e"c Console.Clear() Console.WriteLine("Acquisizione informazioni") Console.WriteLine() 'Cicla su tutte le operazioni presenti nell'Hashtable. For Each Op As Operation In Operations.Values 'Se l'operazione attivata... If (Op.Enabled) Then 'Se richiede dati ausiliari, invoca il delegate memorizzato 'nella propriet RequireData. Invoke un metodo 'di istanza che invoca i metodi contenuti nel delegate. 'Si pu anche scrivere: ' Op.RequireData()() 'Dove la prima coppia di parentesi indica che la propriet 'non indicizzata e la seconda, in questo caso, specifica 'che il metodo sotteso dal delegate non richiede parametri. ' pi comprensibile la prima forma If Op.RequireData IsNot Nothing Then Op.RequireData.Invoke() End If 'Se DoOperations non contiene ancora nulla, vi inserisce Op.Execute If DoOperations Is Nothing Then DoOperations = Op.Execute Else 'Altrimenti, combina gli oggetti delegate gi memorizzati 'con il nuovo DoOperations = System.Delegate.Combine(DoOperations, Op.Execute) End If End If Next For Each C As Condition In Conditions.Values If C.Enabled Then If C.RequireData IsNot Nothing Then C.RequireData.Invoke() End If If VerifyConditions Is Nothing Then VerifyConditions = C.Verify Else Do

Console.Write(Key) Console.Write(" - ") Console.WriteLine(Operations(Key).Description)

VerifyConditions = System.Delegate.Combine(VerifyConditions, C.Verify) 423. End If 424. End If 425. Next 426. 427. FileIndex = 0 428. For Each File As String In IO.Directory.GetFiles(Folder) 429. 'Ok indica se il file ha passato le condizioni 430. Dim Ok As Boolean = True 431. 'Se ci sono condizioni da applicare, le verifica 432. If VerifyConditions IsNot Nothing Then 433. 'Dato che nel caso di delegate multicast contenenti 434. 'rifermenti a funzione, il valore restituito 435. 'solo quello della prima funzione e a noi interessano 436. '<b>tutti</b> i valori restituiti, dobbiamo enumerare 437. 'ogni singolo oggetto delegate presente nel 438. 'delegate multicast e invocarlo singolarmente. 439. 'Ci viene in aiuto il metodo di istanza GetInvocationList, 440. 'che restituisce un array di delegate singoli. 441. For Each C As FileFilter In VerifyConditions.GetInvocationList() 442. 'Tutte le condizioni attive devono essere verificate, 443. 'quindi bisogna usare un And 444. Ok = Ok And C(File) 445. Next 446. End If 'Se le condizioni sono verificate, esegue le operazioni 447. If Ok Then 448. Try 449. DoOperations(File) 450. Catch Ex As Exception 451. Console.WriteLine("Impossibile eseguire l'operazione: " & Ex.Message) 452. End Try 453. End If 454. Next 455. 'Chiude il file di log se era aperto 456. If LogFile IsNot Nothing Then 457. LogFile.Close() 458. End If 459. 460. Console.WriteLine("Operazioni eseguite con successo!") 461. Console.ReadKey() 462. End Sub 463. 464. 465. End Module Questo esempio molto ar tificioso solo un assaggio delle potenzialit dei delegate (noter ete che ci sono anche molti conflitti, ad esempio se si seleziona sia copia che elimina, i file potr ebber o esser e cancellati pr ima della copia a seconda dell'or dine di invocazione). Vedr emo fr a poco come utilizzar e alcuni delegate piuttosto comuni messi a disposizione dal Fr amew or k, e scopr ir emo nella sezione B che i delegate sono il meccanismo fondamentale alla base di tutto il sistema degli ev enti.

Alc uni membri importanti per i delegate multic ast


La classe System.Delegate espone alcuni metodi statici pubblici, molti dei quali sono davver o utili quando si tr atta di delegate multicast. Eccone una br eve lista: Combine(A, B) o Combine(A, B, C, ...) : fonde insieme pi delegate per cr ear e un unico delegate multicast invocando il quale vengono invocati tutti i metodi in esso contenuti; GetInvocationList() : funzione d'istanza che r estituisce un ar r ay di oggetti di tipo System.Delegate, i quali r appr esentano i singoli delegate che sono stati memor izzati nell'unica var iabile Remove(A, B) : r imuove l'oggetto delegate B dalla invocation list di A (ossia dalla lista di tutti i singoli delegate memor izzati in A). Si suppone che A sia multicast. Se anche B multicast, solo l'ultimo elemento dell'invocation list di B viene r imosso da quella di A

RemoveAll(A, B) : r imuove tutte le occor r enze degli elementi pr esenti nell'invocation list di B da quella di A. Si suppone che sia A che B siano multicast

A36. Classi Astratte, Sigillate e Parziali


Classi Astratte
Le classi astr atte sono speciali classi che esistono con il solo scopo di esser e er editate da altr e classi: non possono esser e usate da sole, non espongono costr uttor i e alcuni lor o metodi sono pr ivi di un cor po. Queste sono car atter istiche molto peculiar i, e anche abbastanza str ane, che, tuttavia, nascondono un potenziale segr eto. Se qualcuno dei miei venticinque lettor i avesse avuto l'occasione di osser var e qualcuno dei miei sor genti, avr ebbe notato che in pi di un occasione ho fatto uso di classi mar cate con la keyw or d MustInher it. Questa la par ola r iser vata che si usa per r ender e as tratta una classe. L'utilizzo pr incipale delle classi astr atte quello di for nir e un o s cheletro o un a bas e di as trazion e per altr e classi. Pr endiamo come esempio uno dei miei pr ogr ammi, che potete tr ovar e nella sezione dow nload, Totem Char ting: ci r ifer ir emo al file Char t.vb. In questo sor gente, la pr ima classe che incontr ate definita come segue: 1. <Serializable()> _ 2. Public MustInherit Class Chart Per or a lasciamo per der e ci che viene compr eso tr a le par entesi angolar i e focalizziamoci sulla dichiar azione nuda e cr uda. Quella che avete visto pr opr io la dichiar azione di una classe astr atta, dove MustInher it significa appunto "deve er editar e", come r ipor tato nella definizione poco sopr a. Char t r appr esenta un gr afico: espone delle pr opr iet (Pr oper ties, Type, Sur face, Plane, ...) e un paio di metodi Pr otected. Sar ete d'accor do con me nell'asser ir e che ogni gr afico pu aver e una legenda e pu contemplar e un insieme di dati limitato per cui esista un massimo: ne concludiamo che i due metodi in questione ser vono a tutti i gr afici ed cor r etto che siano stati definiti all'inter no del cor po di Char t. Ma or a andiamo un po' pi in su e tr oviamo questa singolar e dichiar azione di metodo: 1. Public MustOverride Sub Draw() Non c' il cor po del metodo! Aiuto! L'hanno r ubato! No... Si d il caso che nelle classi astr atte possano esister e anche metodi astr atti, ossia che devono esser e per for za r idefiniti tr amite polimor fismo nelle classi der ivate. E questo abbastanza semplice da capir e: un gr afico deve poter esser e disegnato, quindi ogni oggetto gr afico deve espor r e il metodo Dr aw , ma c' un piccolo inconveniente. Dato che non esiste un solo tipo di gr afico - ce ne sono molti, e nel codice di Totem Char ting vengono contemplati solo gli istogr ammi, gli ar eaogr ammi e i gr afici a disper sione - non possiamo saper e a pr ior i che codice dovr emmo usar e per effettuar e il r ender ing (ossia per disegnar e ci che ser ve). Sappiamo, per , che dovr emo disegnar e qualcosa: allor a lasciamo il compito di definir e un codice adeguato alle classi der ivate (nella fattispecie, Histogr am, PieChar t, LinesChar t, Disper sionChar t). Questo pr opr io l'utilizzo delle classi astr atte: definir e un ar chetipo, uno schema, sulla base del quale le classi che lo er editer anno dovr anno modellar e il pr opr io compor tamento. Altr a osser vazione: le classi astr atte, come dice il nome stesso, sono utilizzate per r appr esentar e concetti astr atti, che non possono concr etamente esser e istanziati: ad esempio, non ha senso un oggetto di tipo Char t, per ch non esiste un gr afico gener ico pr ivo di qualsiasi car atter istica, ma esiste solo declinato in una delle altr e for me sopr a r ipor tate. Natur almente, valgono ancor a tutte le r egole r elative agli specificator i di accesso e all'er editar iet e sono utilizzabili tutti i meccanismi gi illustr ati, compr eso l'over loading; infatti, ho dichiar ato due metodi Pr otected per ch ser vir anno alle classi der ivate. Inoltr e, una classe astr atta pu anche er editar e da un'altr a classe astr atta: in questo caso, tutti i metodi mar cati con MustOver r ide dovr anno subir e una di queste sor ti: Esser e modificati tr amite polimor fismo, definendone, quindi, il cor po; Esser e r idichiar ati MustOver r ide, r imandandone ancor a la definizione. Nel secondo caso, si r imanda ancor a la definizione di un cor po valido alla "discendenza", ma c' un piccolo ar tifizio da

adottar e: eccone una dimostr azione nel pr ossimo esempio: 001. Module Module1 002. 003. 'Classe astratta che rappresenta un risolutore di equazioni. 'Dato che di equazioni ce ne possono essere molte tipologie 004. 'differenti, non ha senso rendere questa classe istanziabile. 005. 006. 'Provando a scrivere qualcosa come: 007. ' Dim Eq As New EquationSolver() 008. 'Vi verr comunicato un errore, in quanto le classi 'astratte sono per loro natura non istanziabili 009. MustInherit Class EquationSolver 010. 011. 'Per lo stesso discorso fatto prima, se non conosciamo come 012. ' fatta l'equazione che questo tipo contiene non 013. 'possiamo neppure tentare di risolverla. Perci 014. 'ci limitiamo a dichiarare una funzione Solve come MustOverride. 015. 'Notate che il tipo restituito un array di Single, 016. 'in quanto le soluzioni saranno spesso pi di una. 017. Public MustOverride Function Solve() As Single() 018. End Class 019. 'La prossima classe rappresenta un risolutore di equazioni 020. 'polinomiali. Dato che la tipologia ben definita, 021. 'avremmo potuto anche <i>non</i> rendere astratta la classe 022. 'e, nella funzione Solve, utilizzare un Select Case per 023. 'controllare il grado dell'equazione. Ad ogni modo, 024. 'utile vedere come si comporta l'erediteriet attraverso 025. 'pi classi astratte. 026. 'Inoltre, ci ritorner molto utile in seguito disporre 027. 'di questa classe astratta intermedia 028. MustInherit Class PolynomialEquationSolver 029. Inherits EquationSolver 030. 031. Private _Coefficients() As Single 032. 033. 'Array di Single che contiene i coefficienti dei 034. 'termini di i-esimo grado all'interno dell'equazione. 035. 'L'elemento 0 dell'array indica il coefficiente del 036. 'termine a grado massimo. 037. Public Property Coefficients() As Single() 038. Get 039. Return _Coefficients 040. End Get 041. Set(ByVal value As Single()) 042. _Coefficients = value 043. End Set 044. End Property 045. 046. 'Ecco quello a cui volevo arrivare. Se un metodo astratto 047. 'lo si vuole mantenere tale anche nella classe derivata, 048. 'non basta scrivere: 049. ' MustOverride Function Solve() As Single() 050. 'Perc in questo caso verrebbe interpretato come 051. 'un membro che non c'entra niente con MyBase.Solve, 052. 'e si genererebbe un errore in quanto stiamo tentando 053. 'di dichiarare un nuovo membro con lo stesso nome 054. 'di un membro della classe base. 055. 'Per questo motivo, dobbiamo comunque usare il polimorfismo 056. 'come se si trattasse di un normale metodo e dichiararlo 057. 'Overrides. In aggiunta a questo, deve anche essere 058. 'astratto, e perci aggiungiamo MustOverride: 059. Public MustOverride Overrides Function Solve() As Single() 060. 061. 'Anche in questo caso usiamo il polimorfismo, ma ci riferiamo 062. 'alla semplice funzione ToString, derivata dalla classe base 063. 'di tutte le entit esistenti, System.Object. 064. 'Questa si limita a restituire una stringa che rappresenta 065. 'l'equazione a partire dai suoi coefficienti. Ad esempio: 066. ' 3x^2 + 2x^1 + 4x^0 = 0 067. 'Potete modificare il codice per eliminare le forme ridondanti 068. 069. 'x^1 e x^0. 070. Public Overrides Function ToString() As String 071.

072. 073. 074. 075. 076. 077. 078. 079. 080. 081. 082. 083. 084. 085. 086. 087. 088. 089. 090. 091. 092. 093. 094. 095. 096. 097. 098. 099. 100. 101. 102. 103. 104. 105. 106. 107. 108. 109. 110. 111. 112. 113. 114. 115. 116. 117. 118. 119. 120. 121. 122. 123. 124. 125. 126. 127. 128. 129. 130. 131. 132. 133. 134. 135. 136. 137. 138. 139. 140. 141. 142. 143.

Dim Result As String = "" For I As Int16 = 0 To Me.Coefficients.Length - 1 If I > 0 Then Result &= " + " End If Result &= String.Format("{0}x^{1}", _ Me.Coefficients(I), Me.Coefficients.Length - 1 - I) Next Result &= " = 0" Return Result End Function End Class 'Rappresenta un risolutore di equazioni non polinomiali. 'La classe non astratta, ma non presenta alcun codice. 'Per risolvere questo tipo di equazioni, necessario 'sapere qualche cosa in pi rispetto al punto in cui siamo 'arrivati, perci mi limiter a lasciare in bianco Class NonPolynomialEquationSolver Inherits EquationSolver Public Overrides Function Solve() As Single() Return Nothing End Function End Class 'Rappresenta un risolutore di equazioni di primo grado. Eredita 'da PolynomialEquationSolver poich, ovviamente, si 'tratta di equazioni polinomiali. In pi, definisce 'le propriet a e b che sono utili per inserire i 'coefficienti. Infatti, l'equazione standard : ' ax + b = 0 Class LinearEquationSolver Inherits PolynomialEquationSolver Public Property a() As Single Get Return Me.Coefficients(0) End Get Set(ByVal value As Single) Me.Coefficients(0) = value End Set End Property Public Property b() As Single Get Return Me.Coefficients(1) End Get Set(ByVal value As Single) Me.Coefficients(1) = value End Set End Property 'Sappiamo gi quanti sono i coefficienti, dato 'che si tratta di equazioni lineari, quindi ridimensioniamo 'l'array il prima possibile. Sub New() ReDim Me.Coefficients(1) End Sub 'Funzione Overrides che sovrascrive il metodo astratto della 'classe base. Avrete notato che quando scrivete: ' Inherits PolynomialEquationSolver 'e premete invio, questa funzione viene aggiunta automaticamente 'al codice. Questa un'utile feature dell'ambiente 'di sviluppo Public Overrides Function Solve() As Single() If a <> 0 Then Return New Single() {-b / a}

144. 145. 146. 147. 148. 149. 150. 151. 152. 153. 154. 155. 156. 157. 158. 159. 160. 161. 162. 163. 164. 165. 166. 167. 168. 169. 170. 171. 172. 173. 174. 175. 176. 177. 178. 179. 180. 181. 182. 183. 184. 185. 186. 187. 188. 189. 190. 191. 192. 193. 194. 195. 196. 197. 198. 199. 200. 201. 202. 203. 204. 205. 206. 207. 208. 209. 210. 211. 212. 213. 214. 215.

Return Nothing End If End Function End Class 'Risolutore di equazioni di secondo grado: ' ax<sup>2</sup> + bx + c = 0 Class QuadraticEquationSolver Inherits LinearEquationSolver Public Property c() As Single Get Return Me.Coefficients(2) End Get Set(ByVal value As Single) Me.Coefficients(2) = value End Set End Property Sub New() ReDim Me.Coefficients(2) End Sub Public Overrides Function Solve() As Single() If b ^ 2 - 4 * a * c >= 0 Then Return New Single() { _ (-b - Math.Sqrt(b ^ 2 - 4 * a * c)) / 2, _ (-b + Math.Sqrt(b ^ 2 - 4 * a * c)) / 2} Else Return Nothing End If End Function End Class 'Risolutore di equazioni di grado superiore al secondo. So 'che avrei potuto inserire anche una classe relativa 'alle cubiche, ma dato che si tratta di un esempio, vediamo 'di accorciare il codice... 'Comunque, dato che non esiste formula risolutiva per 'le equazioni di grado superiore al quarto (e gi, 'ci mancava un'altra classe!), usiamo in questo caso 'un semplice ed intuitivo metodo di approssimazione degli 'zeri, il metodo dicotomico o di bisezione (che vi pu 'essere utile per risolvere un esercizio dell'eserciziario) Class HighDegreeEquationSolver Inherits PolynomialEquationSolver Private _Epsilon As Single Private _IntervalLowerBound, _IntervalUpperBound As Single 'Errore desiderato: l'algoritmo si fermer una volta 'raggiunta una precisione inferiore a Epsilon Public Property Epsilon() As Single Get Return _Epsilon End Get Set(ByVal value As Single) _Epsilon = value End Set End Property 'Limite inferiore dell'intervallo in cui cercare la soluzione Public Property IntervalLowerBound() As Single Get Return _IntervalLowerBound End Get Set(ByVal value As Single) _IntervalLowerBound = value End Set End Property

Else

216. 217. 218. 219. 220. 221. 222. 223. 224. 225. 226. 227. 228. 229. 230. 231. 232. 233. 234. 235. 236. 237. 238. 239. 240. 241. 242. 243. 244. 245. 246. 247. 248. 249. 250. 251. 252. 253. 254. 255. 256. 257. 258. 259. 260. 261. 262. 263. 264. 265. 266. 267. 268. 269. 270. 271. 272. 273. 274. 275. 276. 277. 278. 279. 280. 281. 282. 283. 284. 285. 286. 287.

'Limite superiore dell'intervallo in cui cercare la soluzione Public Property IntervalUpperBound() As Single Get Return _IntervalUpperBound End Get Set(ByVal value As Single) _IntervalUpperBound = value End Set End Property 'Valuta la funzione polinomiale. Dati i coefficienti immessi, 'noi disponiamo del polinomio p(x), quindi possiamo calcolare 'i valori che esso assume per ogni x Private Function EvaluateFunction(ByVal x As Single) As Single Dim Result As Single = 0 For I As Int16 = 0 To Me.Coefficients.Length - 1 Result += Me.Coefficients(I) * x ^ (Me.Coefficients.Length - 1 - I) Next Return Result End Function Public Overrides Function Solve() As Single() Dim a, b, c As Single Dim fa, fb, fc As Single Dim Interval As Single = 100 Dim I As Int16 = 0 Dim Result As Single a = IntervalLowerBound b = IntervalUpperBound 'Non esiste uno zero tra a e b se f(a) e f(b) hanno 'lo stesso segno If EvaluateFunction(a) * EvaluateFunction(b) > 0 Then Return Nothing End If Do 'c il punto medio tra a e b c = (a + b) / 2 'Calcola f(a), f(b) ed f(c) fa = EvaluateFunction(a) fb = EvaluateFunction(b) fc = EvaluateFunction(c) 'Se uno tra f(a), f(b) e f(c) vale zero, allora abbiamo 'trovato una soluzione perfetta, senza errori, ed 'usciamo direttamente dal ciclo If fa = 0 Then c = a Exit Do End If If fb = 0 Then c = b Exit Do End If If fc = 0 Then Exit Do End If 'Altrimenti, controlliamo quale coppia di valori scelti 'tra f(a), f(b) ed f(c) ha segni discorsi: lo zero si trover 'tra le ascisse di questi If fa * fc < 0 Then b = c Else a = c End If Loop Until Math.Abs(a - b) < Me.Epsilon

288. 289. 290. 291. 292. 293. 294. 295. 296. 297. 298. 299. 300. 301. 302. 303. 304. 305. 306. 307. 308. 309. 310. 311. 312. 313. 314. 315. 316. 317. 318. 319. 320. 321. 322. 323. 324. 325. 326. 327. 328. 329. 330. 331. 332. 333. 334. 335. 336. 337. 338. 339. 340. 341. 342. 343. 344. 345. 346. 347. 348. 349. 350. 351. 352. 353. 354. 355. 356. 357. 358.

'Cicla finch l'ampiezza dell'intervallo non 'sufficientemente piccola, quindi assume come zero pi 'probabile il punto medio tra a e b: Result = c Return New Single() {Result} End Function End Class Sub Main() 'Contiene un generico risolutore di equazioni. Non sappiamo ancora 'quale tipologia di equazione dovremo risolvere, ma sappiamo per 'certo che lo dovremo fare, ed EquationSolver la classe 'base di tutti i risolutori che espone il metodo Solve. Dim Eq As EquationSolver Dim x() As Single Dim Cmd As Char Console.WriteLine("Scegli una tipologia di equazione: ") Console.WriteLine(" l - lineare;") Console.WriteLine(" q - quadratica;") Console.WriteLine(" h - di grado superiore al secondo;") Console.WriteLine(" e - non polinomiale;") Cmd = Console.ReadKey().KeyChar Console.Clear() If Cmd <> "e" Then 'Ancora, sappiamo che si tratta di un'equazione polinomiale 'ma non di quale grado Dim Poly As PolynomialEquationSolver 'Ottiene i dati relativi a ciascuna equazione Select Case Cmd Case "l" Dim Linear As New LinearEquationSolver() Poly = Linear Case "q" Dim Quadratic As New QuadraticEquationSolver() Poly = Quadratic Case "h" Dim High As New HighDegreeEquationSolver() Dim CoefNumber As Int16 Console.WriteLine("Inserire il numero di coefficienti: ") CoefNumber = Console.ReadLine ReDim High.Coefficients(CoefNumber - 1) Console.WriteLine("Inserire i limti dell'intervallo in cui cercare gli zeri:") High.IntervalLowerBound = Console.ReadLine High.IntervalUpperBound = Console.ReadLine Console.WriteLine("Inserire la precisione (epsilon):") High.Epsilon = Console.ReadLine Poly = High End Select 'A questo punto la variabile Poly contiene sicuramente un oggetto '(LinearEquationSolver, QuadraticEquationSolver oppure 'HighDegreeEquationSolver), anche se non sappiamo quale. Tuttavia, 'tutti questi sono pur sempre polinomiali e perci tutti 'hanno bisogno di sapere i coefficienti del polinomio. 'Ecco che allora possiamo usare Poly con sicurezza perc 'sicuramente contiene un oggetto e la propriet Coefficients ' stata definita proprio nella classe PolynomialEquationSolver. '<b>N.B.: ricordate tutto quello che abbiamo detto sull'assegnamento ' di un oggetto di classe derivata a uno di classe base!</b> Console.WriteLine("Inserire i coefficienti: ") For I As Int16 = 1 To Poly.Coefficients.Length - 1 Console.Write("a{0} = ", Poly.Coefficients.Length - I) Poly.Coefficients(I - 1) = Console.ReadLine Next 'Assegnamo Poly a Eq. Osservate che siamo andati via via dal

'caso pi particolare al pi generale: 359. ' - Abbiamo creato un oggetto specifico per un certo grado 360. ' di un'equazione polinomiale (Linear, Quadratic, High); 361. ' - Abbiamo messo quell'oggetto in uno che si riferisce 362. ' genericamente a tutti i polinomi; 363. ' - Infine, abbiamo posto quest'ultimo in uno ancora pi 364. ' generale che si riferisce a tutte le equazioni; 365. 'Questo percorso porta da oggetto molto specifici e ricchi di membri 366. '(tante propriet e tanti metodi), a tipi molto generali 367. 'e poveri di membri (nel caso di Eq, un solo metodo). 368. Eq = Poly 369. Else 370. 'Inseriamo in Eq un nuovo oggetto per risolvere equazioni non 371. 'polinomiali, anche se il codice al momento vuoto 372. Eq = New NonPolynomialEquationSolver 373. Console.WriteLine("Non implementato") 374. End If 375. 376. 'Risolviamo l'equazione. Richiamare la funzione Solve da un oggetto 377. 'EquationSolver potrebbe non dirvi nulla, ma ricordate che dentro Eq 378. ' memorizzato un oggetto pi specifico in cui 379. ' stata definita la funzione Solve(). Per questo motivo, 380. 'anche se Eq di tipo classe base, purtuttavia contiene 381. 'al proprio interno un oggetto di tipo classe derivata, ed 382. ' questo che conta: viene usato il metodo Solve della classe 383. 'derivata. 'Se ci pensate bene, vi verr pi spontaneo capire, 384. 385. 'poich noi, ora, stiamo guardando ATTRAVERSO il tipo 'EquationSolver un oggetto di altro tipo. come osservare 386. 'attraverso filtri via via sempre pi fitti (cfr 387. 'immagine seguente) 388. x = Eq.Solve() 389. 390. If x IsNot Nothing Then 391. Console.WriteLine("Soluzioni trovate: ") 392. For Each s As Single In x 393. Console.WriteLine(s) 394. Next 395. Else 396. Console.WriteLine("Nessuna soluzione") 397. End If 398. 399. Console.ReadKey() 400. End Sub 401. 402. End Module Eccovi un'immagine dell'ultimo commento:

Il piano r osso l'oggetto che r ealmente c' in memor ia (ad esempio, Linear EquationSolver ); il piano blu con tr e aper tur e ci che r iusciamo a veder e quando l'oggetto viene memor izzato in una classe astr atta PolynomialEquationSolver ; il piano blu iniziale, invece, ci a cui possiamo acceder e attr aver so un EquationSolver : il fascio di luce indica le nostr e possibilit di accesso. pr opr io il caso di dir e che c' molto di pi di ci che si vede!

Classi Sigillate
Le classi sigillate sono esattamente l'opposto di quelle astr atte, ossia non possono mai esser e er editate. Si dichiar ano con la keyw or d NotInher itable: 1. NotInheritable Class Example 2. '... 3. End Class Allo stesso modo, penser ete voi, i membr i che non possono subir e over loading sar anno mar cati con qualcosa tipo NotOver r idable... In par te esatto, ma in par te er r ato. La keyw or d NotOver r idable si pu applicar e solo e soltanto a metodi gi modificati tr amite polimor fismo, ossia Over r ides. 01. 02. 03. 04. 05. 06. 07. 08. 09. 10. 11. 12. 13. 14. 15. 16. Class A Sub DoSomething() '... End Sub End Class Class B Inherits A 'Questa procedura sovrascrive la precedente versione 'di DoSomething dichiarata in A, ma preclude a tutte le 'classi derivate da B la possibilit di fare lo stesso NotOverridable Overrides Sub DoSomething() '... End Sub End Class

Inoltr e, le classi sigillate no n possono mai espor r e membr i sigillati, anche per ch tutti i lor o membr i lo sono implicitamente (se una classe non pu esser e er editata, ovviamente non si potr anno r idefinir e i membr i con polimor fismo).

Classi Parziali
Una classe si dice par ziale quando il suo cor po suddiviso su pi files. Si tr atta solamento di un'utilit pr atica che ha poco a che veder e con la pr ogr ammazione ad oggetti. Mi sembr ava, per , or dinato espor r e tutte le keyw or d associate alle classi in un solo capitolo. Semplicemente, una classe par ziale si dichiar a in questo modo: 1. Partial Class [Nome] 2. '... 3. End Class sufficiente dichiar ar e una classe come par ziale per ch il compilator e associ, in fase di assemblaggio, tutte le classi con lo stesso nome in file diver si a quella definizione. Ad esempio: 01. 'Nel file Codice1.vb : 02. Partial Class A 03. Sub One() 04.

05. 06. 07. 08. 09. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36.

'... End Sub End Class 'Nel file Codice2.vb Class A Sub Two() '... End Sub End Class 'Nel file Codice3.vb Class A Sub Three() '... End Sub End Class 'Tutte le classi A vengono compilate come un'unica classe 'perch una possiede la keyword Partial: Class A Sub One() '... End Sub Sub Two() '... End Sub Sub Three() '... End Sub End Class

A37. Le Interfacce
Sc opo delle Interfac c e
Le inter facce sono un'entit davver o singolar e all'inter no del .NET Fr amew or k. La lor o funzione assimilabile a quella delle classi astr atte, ma il modo con cui esse la svolgono molto diver so da ci che abbiamo visto nel capitolo pr ecedente. Il pr incipale scopo di un'inter faccia definir e lo scheletr o di una classe; potr ebbe esser e scher zosamente assimilata alla r icetta con cui si pr epar a un dolce. Quello che l'inter faccia X fa, ad esempio, consiste nel dir e che per costr uir e una classe Y che r ispetti "la r icetta" descr itta in X ser vono una pr opr iet Id di tipo Integer , una funzione GetSomething senza par ametr i che r estituisce una str inga e una pr ocedur a DoSomething con un singolo par ametr o Double. Tutte le classi che avr anno intenzione di seguir e i pr ecetti di X (in ger go im plem entar e X) dovr anno definir e, allo stesso modo, quella pr opr iet di quel tipo e quei metodi con quelle specifiche signatur e (il nome ha impor tanza r elativa). Faccio subito un esempio. Fino ad or a, abbiamo visto essenzialmente due tipi di collezione: gli Ar r ay e gli Ar r ayList. Sia per l'uno che per l'altr o, ho detto che possibile eseguir e un'iter azione con il costr utto For Each: 01. 02. 03. 04. 05. 06. 07. 08. 09. 10. 11. 12. 13. 14. 15. Dim Ar() As Int32 = {1, 2, 3, 4, 5, 6} Dim Al As New ArrayList For I As Int32 = 1 To 40 Al.Add(I) Next 'Stampa i valori di Ar: For Each K As Int32 In Ar Console.WriteLine(K) Next 'Stampa i valori di Al For Each K As Int32 In Al Console.WriteLine(K) Next

Ma il sistema come fa a saper e che Ar e Al sono degli insiemi di valor i? Dopotutto, il lor o nome significativo solo per noi pr ogr ammator i, mentr e per il calcolator e non altr o che una sequenza di car atter i. Allo stesso modo, il codice di Ar r ay e Ar r ayList, definito dai pr ogr ammator i che hanno scr itto il Fr amew or k, intelligibile solo agli uomini, per ch al computer non comunica nulla sullo scopo per il quale stato scr itto. Allor a, siamo al punto di par tenza: nelle classi Ar r ay e Ar r ayList non c' nulla che possa far "capir e" al pr ogr amma che quelli sono a tutti gli effetti delle collezioni e che, quindi, sono iter abili; e, anche se in qualche str ano modo l'elabor ator e lo potesse capir e, non "sapr ebbe" (in quanto entit non senziente) come far per estr ar r e singoli dati e dar celi uno in fila all'altr o. Ecco che entr ano in scena le inter facce: tutte le classi che r appr esentano un insieme o una collezione di elementi implemen tan o l'inter faccia IEnumer able, la quale, se potesse par lar e, dir ebbe "Guar da che questa classe una collezione, tr attala di conseguenza!". Questa inter faccia obbliga le classi dalle quali implementata a definir e alcuni metodi che ser vono per l'enumer azione (Cur r ent, MoveNex t e Reset) e che vedr emo nei pr ossimi capitoli. In conclusione, quindi, il For Each pr ima di tutto contr olla che l'oggetto posto dopo la clausola "In" implementi l'inter faccia IEnumer able. Quindi r ichiama il metodo Reset per por si sul pr imo elemento, poi deposita in K il valor e esposto dalla pr opr iet Cur r ent, esegue il codice contenuto nel pr opr io cor po e, una volta ar r ivato a Nex t, esegue il metodo MoveNex t per avanzar e al pr ossimo elemento. Il For Each " sicur o" dell'esistenza di questi membr i per ch l'inter faccia IEnumer able ne impone la definizione. Riassumendo, le inter facce hanno il compito di infor mar e il sistema su quali siano le car atter istiche e i compiti di una classe. Per questo motivo, il lor o nomi ter minano spesso in "-able", come ad esempio IEnumer able, IEquatable, ICompr able, che ci dicono "- enumer abile", "- eguagliabile", "- compar abile", " ... qualcosa".

Dic hiarazione e implementazione


La sintassi usata per dichiar ar e un'inter faccia la seguente: 1. Interface [Nome] 2. 'Membri 3. End Interface I membr i delle inter facce, tuttavia, sono un po' diver si dai membr i di una classe, e nello scr iver li bisogna r ispettar e queste r egole: Nel caso di metodi, pr opr iet od eventi, il cor po non va specificato; Non si possono mai usar e gli specificator i di accesso; Si possono comunque usar e dei modificator i come Shar ed, ReadOnly e Wr iteOnly. Il pr imo ed il secondo punto sar anno ben compr esi se ci si soffer ma a pensar e che l'inter faccia ha il solo scopo di definir e quali membr i una classe debba implementar e: per questo motivo, non se ne pu scr iver e il cor po, dato che spetta espr essamente alle classi implementanti, e non ci si pr eoccupa dello specificator e di accesso, dato che si sta specificando solo il "cosa" e non il "come". Ecco alcuni semplici esempi di dichiar azioni: 01. 02. 03. 04. 05. 06. 07. 08. 09. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 'Questa interfaccia dal nome improbabile indica che 'la classe che la implementa rappresenta qualcosa di '"identificabile" e per questo espone una propriet Integer Id 'e una funzione ToString. Id e ToString, infatti, sono gli 'elementi pi utili per identificare qualcosa, prima in 'base a un codice univoco e poi grazie ad una rappresentazione 'comprensibile dall'uomo Interface IIdentifiable ReadOnly Property Id() As Int32 Function ToString() As String End Interface 'La prossima interfaccia, invece, indica qualcosa di resettabile 'e obbliga le classi implementanti a esporre il metodo Reset 'e la propriet DefaultValue, che dovrebbe rappresentare 'il valore di default dell'oggetto. Dato che non sappiamo ora 'quali classi implementeranno questa interfaccia, dobbiamo 'per forza usare un tipo generico come Object per rappresentare 'un valore reference. Vedremo come aggirare questo ostacolo 'fra un po', con i Generics Interface IResettable Property DefaultValue() As Object Sub Reset() End Interface 'Come avete visto, i nomi di interfaccia iniziano per convenzione 'con la lettera I maiuscola

Or a che sappiamo come dichiar ar e un'inter faccia, dobbiamo scopr ir e come usar la. Per implementar e un'inter faccia in una classe, si usa questa sintassi: 1. Class Example 2. Implements [Nome Interfaccia] 3. 4. [Membro] Implements [Nome Interfaccia].[Membro] 5. End Class Si capisce meglio con un esempio: 01. Module Module1 02. Interface IIdentifiable 03. ReadOnly Property Id() As Int32 04. Function ToString() As String 05. End Interface 06. 07.

08. 09. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40. 41. 42. 43. 44. 45. 46. 47. 48. 49. 50. 51. 52. 53. 54. 55. 56. 57. 58. 59. 60. 61. 62. 63. 64. 65. 66. 67. 68. 69. 70. 71. 72. 73. 74. 75. 76. 77. 78. 79.

'Rappresenta un pacco da spedire Class Pack 'Implementa l'interfaccia IIdentifiable, in quanto un pacco 'dovrebbe poter essere ben identificato Implements IIdentifiable 'Notate bene che l'interfaccia ci obbliga a definire una 'propriet, ma non ci obbliga a definire un campo 'ad essa associato Private _Id As Int32 Private _Destination As String Private _Dimensions(2) As Single 'La classe definisce una propriet id di tipo Integer 'e la associa all'omonima presente nell'interfaccia in 'questione. Il legame tra questa propriet Id e quella 'presenta nell'interfaccia dato solamente dalla 'clausola (si chiama cos in gergo) "Implements", 'la quale avvisa il sistema che il vincolo imposto ' stato soddisfatto. 'N.B.: il fatto che il nome di questa propriet sia uguale 'a quella definita in IIdentifiable non significa nulla. 'Avremmo potuto benissimo chiamarla "Pippo" e associarla 'a Id tramite il codice "Implements IIdentifiable.Id", ma 'ovviamente sarebbe stata una palese idiozia XD Public ReadOnly Property Id() As Integer Implements IIdentifiable.Id Get Return _Id End Get End Property 'Destinazione del pacco. 'Il fatto che l'interfaccia ci obblighi a definire quei due 'membri non significa che non possiamo definirne altri Public Property Destination() As String Get Return _Destination End Get Set(ByVal value As String) _Destination = value End Set End Property 'Piccolo ripasso delle propriet indicizzate e 'della gestione degli errori Public Property Dimensions(ByVal Index As Int32) As Single Get If (Index >= 0) And (Index < 3) Then Return _Dimensions(Index) Else Throw New IndexOutOfRangeException() End If End Get Set(ByVal value As Single) If (Index >= 0) And (Index < 3) Then _Dimensions(Index) = value Else Throw New IndexOutOfRangeException() End If End Set End Property Public Overrides Function ToString() As String Implements IIdentifiable.ToString Return String.Format("{0}: Pacco {1}x{2}x{3}, Destinazione: {4}", _ Me.Id, Me.Dimensions(0), Me.Dimensions(1), _ Me.Dimensions(2), Me.Destination) End Function End Class Sub Main() '... End Sub

End Module Or a che abbiamo implementato l'inter faccia nella classe Pack, tuttavia, non sappiamo che far cene. Siamo a conoscenza del fatto che gli oggetti Pack sar anno sicur amente identificabili, ma nulla di pi. Ritor niamo, allor a, all'esempio del pr imo par agr afo: cos' che r ende ver amente utile IEnumer able, al di l del fatto di r ender e funzionante il For Each? Si applica a qualsiasi collezione o insieme, non impor ta di quale natur a o per quali scopi, non impor ta nemmeno il codice che sottende all'enumer azione: l'impor tante che una vastissima gamma di oggetti possano esser e r icondotti ad un solo ar chetipo (io ne ho nominati solo due, ma ce ne sono a iosa). Allo stesso modo, potr emo usar e IIdentifiable per manipolar e una gr an quantit di dati di natur a differ ente. Ad esempio, il codice di sopr a potr ebbe esser e sviluppato per cr ear e un sistema di gestione di un ufficio postale. Eccone un esempio: 001. Module Module1 002. 003. Interface IIdentifiable 004. ReadOnly Property Id() As Int32 005. Function ToString() As String 006. End Interface 007. 008. Class Pack 009. Implements IIdentifiable 010. Private _Id As Int32 011. 012. Private _Destination As String 013. Private _Dimensions(2) As Single 014. 015. Public ReadOnly Property Id() As Integer Implements IIdentifiable.Id Get 016. Return _Id 017. 018. End Get End Property 019. 020. Public Property Destination() As String 021. Get 022. Return _Destination 023. End Get 024. Set(ByVal value As String) 025. _Destination = value 026. End Set 027. End Property 028. 029. Public Property Dimensions(ByVal Index As Int32) As Single 030. Get 031. If (Index >= 0) And (Index < 3) Then 032. Return _Dimensions(Index) 033. Else 034. Throw New IndexOutOfRangeException() 035. End If 036. End Get 037. Set(ByVal value As Single) 038. If (Index >= 0) And (Index < 3) Then 039. _Dimensions(Index) = value 040. Else 041. Throw New IndexOutOfRangeException() 042. End If 043. End Set 044. End Property 045. 046. Sub New(ByVal Id As Int32) 047. _Id = Id 048. End Sub 049. 050. Public Overrides Function ToString() As String Implements IIdentifiable.ToString 051. Return String.Format("{0:0000}: Pacco {1}x{2}x{3}, Destinazione: {4}", _ 052. Me.Id, Me.Dimensions(0), Me.Dimensions(1), _ 053. Me.Dimensions(2), Me.Destination) 054. End Function 055. End Class 056. 057. 058.

059. 060. 061. 062. 063. 064. 065. 066. 067. 068. 069. 070. 071. 072. 073. 074. 075. 076. 077. 078. 079. 080. 081. 082. 083. 084. 085. 086. 087. 088. 089. 090. 091. 092. 093. 094. 095. 096. 097. 098. 099. 100. 101. 102. 103. 104. 105. 106. 107. 108. 109. 110. 111. 112. 113. 114. 115. 116. 117. 118. 119. 120. 121. 122. 123. 124. 125. 126. 127. 128. 129. 130.

Class Telegram Implements IIdentifiable Private _Id As Int32 Private _Recipient As String Private _Message As String Public ReadOnly Property Id() As Integer Implements IIdentifiable.Id Get Return _Id End Get End Property Public Property Recipient() As String Get Return _Recipient End Get Set(ByVal value As String) _Recipient = value End Set End Property Public Property Message() As String Get Return _Message End Get Set(ByVal value As String) _Message = value End Set End Property Sub New(ByVal Id As Int32) _Id = Id End Sub Public Overrides Function ToString() As String Implements IIdentifiable.ToString Return String.Format("{0:0000}: Telegramma per {1} ; Messaggio = {2}", _ Me.Id, Me.Recipient, Me.Message) End Function End Class Class MoneyOrder Implements IIdentifiable Private _Id As Int32 Private _Recipient As String Private _Money As Single Public ReadOnly Property Id() As Integer Implements IIdentifiable.Id Get Return _Id End Get End Property Public Property Recipient() As String Get Return _Recipient End Get Set(ByVal value As String) _Recipient = value End Set End Property Public Property Money() As Single Get Return _Money End Get Set(ByVal value As Single) _Money = value End Set End Property

131. 132. 133. 134. 135. 136. 137. 138. 139. 140. 141. 142. 143. 144. 145. 146. 147. 148. 149. 150. 151. 152. 153. 154. 155. 156. 157. 158. 159. 160. 161. 162. 163. 164. 165. 166. 167. 168. 169. 170. 171. 172. 173. 174. 175. 176. 177. 178. 179. 180. 181. 182. 183. 184. 185. 186. 187. 188. 189. 190. 191. 192. 193. 194. 195. 196. 197. 198. 199. 200. 201. 202.

Sub New(ByVal Id As Int32) _Id = Id End Sub Public Overrides Function ToString() As String Implements IIdentifiable.ToString Return String.Format("{0:0000}: Vaglia postale per {1} ; Ammontare = {2}", _ Me.Id, Me.Recipient, Me.Money) End Function End Class 'Classe che elabora dati di tipo IIdentifiable, ossia qualsiasi 'oggetto che implementi tale interfaccia Class PostalProcessor 'Tanto per tenersi allenati coi delegate, ecco una 'funzione delegate che funge da filtro per i vari id Public Delegate Function IdSelector(ByVal Id As Int32) As Boolean Private _StorageCapacity As Int32 Private _NextId As Int32 = 0 'Un array di interfacce. Quando una variabile viene 'dichiarata come di tipo interfaccia, ci 'che pu contenere qualsiasi oggetto 'che implementi quell'interfaccia. Per lo stesso 'discorso fatto nel capitolo precedente, noi 'possiamo vedere <i>attraverso</i> l'interfaccia 'solo quei membri che essa espone direttamente, anche 'se il contenuto vero e proprio qualcosa 'di pi Private Storage() As IIdentifiable 'Capacit del magazzino. Assumeremo che tutti 'gli oggetti rappresentati dalle classi Pack, Telegram 'e MoneyOrder vadano in un magazzino immaginario che, 'improbabilmente, riserva un solo posto per ogni 'singolo elemento Public Property StorageCapacity() As Int32 Get Return _StorageCapacity End Get Set(ByVal value As Int32) _StorageCapacity = value ReDim Preserve Storage(value) End Set End Property 'Modifica od ottiene un riferimento all'Index-esimo 'oggetto nell'array Storage Public Property Item(ByVal Index As Int32) As IIdentifiable Get If (Index >= 0) And (Index < Storage.Length) Then Return Me.Storage(Index) Else Throw New IndexOutOfRangeException() End If End Get Set(ByVal value As IIdentifiable) If (Index >= 0) And (Index < Storage.Length) Then Me.Storage(Index) = value Else Throw New IndexOutOfRangeException() End If End Set End Property 'Restituisce la prima posizione libera nell'array 'Storage. Anche se in questo esempio non l'abbiamo 'contemplato, gli elementi possono anche essere rimossi 'e quindi lasciare un posto libero nell'array Public ReadOnly Property FirstPlaceAvailable() As Int32 Get For I As Int32 = 0 To Me.Storage.Length - 1 If Me.Storage(I) Is Nothing Then

203. 204. 205. 206. 207. 208. 209. 210. 211. 212. 213. 214. 215. 216. 217. 218. 219. 220. 221. 222. 223. 224. 225. 226. 227. 228. 229. 230. 231. 232. 233. 234. 235. 236. 237. 238. 239. 240. 241. 242. 243. 244. 245. 246. 247. 248. 249. 250. 251. 252. 253. 254. 255. 256. 257. 258. 259. 260. 261. 262. 263. 264. 265. 266. 267. 268. 269. 270. 271. 272. 273. 274.

Next Return (-1) End Get End Property

Return I End If

'Tutti gli oggetti che inizializzeremo avranno bisogno 'di un id: ce lo fornisce la stessa classe Processor 'tramite questa propriet che si autoincrementa Public ReadOnly Property NextId() As Int32 Get _NextId += 1 Return _NextId End Get End Property 'Due possibili costruttori: uno che accetta un insieme 'gi formato di elementi... Public Sub New(ByVal Items() As IIdentifiable) Me.Storage = Items _SorageCapacity = Items.Length End Sub '... e uno che accetta solo la capacit del magazzino Public Sub New(ByVal Capacity As Int32) Me.StorageCapacity = Capacity End Sub 'Stampa a schermo tutti gli elementi che la funzione 'contenuta nel parametro Selector di tipo delegate 'considera validi (ossia tutti quelli per cui 'Selector.Invoke restituisce True) Public Sub PrintByFilter(ByVal Selector As IdSelector) For Each K As IIdentifiable In Storage If K Is Nothing Then Continue For End If If Selector.Invoke(K.Id) Then Console.WriteLine(K.ToString()) End If Next End Sub 'Stampa l'oggetto con Id specificato Public Sub PrintById(ByVal Id As Int32) For Each K As IIdentifiable In Storage If K Is Nothing Then Continue For End If If K.Id = Id Then Console.WriteLine(K.ToString()) Exit For End If Next End Sub 'Cerca tutti gli elementi che contemplano all'interno 'della propria descrizione la stringa Str e li 'restituisce come array di Id Public Function SearchItems(ByVal Str As String) As Int32() Dim Temp As New ArrayList For Each K As IIdentifiable In Storage If K Is Nothing Then Continue For End If If K.ToString().Contains(Str) Then Temp.Add(K.Id) End If Next

275. 276. 277. 278. 279. 280. 281. 282. 283. 284. 285. 286. 287. 288. 289. 290. 291. 292. 293. 294. 295. 296. 297. 298. 299. 300. 301. 302. 303. 304. 305. 306. 307. 308. 309. 310. 311. 312. 313. 314. 315. 316. 317. 318. 319. 320. 321. 322. 323. 324. 325. 326. 327. 328. 329. 330. 331. 332. 333. 334. 335. 336. 337. 338. 339. 340. 341. 342. 343. 344. 345. 346.

Dim Result(Temp.Count - 1) As Int32 For I As Int32 = 0 To Temp.Count - 1 Result(I) = Temp(I) Next Temp.Clear() Temp = Nothing Return Result End Function End Class Private Processor As New PostalProcessor(10) Private Cmd As Char Private IdFrom, IdTo As Int32 Function SelectId(ByVal Id As Int32) As Boolean Return (Id >= IdFrom) And (Id <= IdTo) End Function Sub InsertItems(ByVal Place As Int32) Console.WriteLine("Scegliere la tipologia di oggetto:") Console.WriteLine(" p - pacco;") Console.WriteLine(" t - telegramma;") Console.WriteLine(" v - vaglia postale;") Cmd = Console.ReadKey().KeyChar Console.Clear() Select Case Cmd Case "p" Dim P As New Pack(Processor.NextId) Console.WriteLine("Pacco - Id:{0:0000}", P.Id) Console.Write("Destinazione: ") P.Destination = Console.ReadLine Console.Write("Larghezza: ") P.Dimensions(0) = Console.ReadLine Console.Write("Lunghezza: ") P.Dimensions(1) = Console.ReadLine Console.Write("Altezza: ") P.Dimensions(2) = Console.ReadLine Processor.Item(Place) = P Case "t" Dim T As New Telegram(Processor.NextId) Console.WriteLine("Telegramma - Id:{0:0000}", T.Id) Console.Write("Destinatario: ") T.Recipient = Console.ReadLine Console.Write("Messaggio: ") T.Message = Console.ReadLine Processor.Item(Place) = T Case "v" Dim M As New MoneyOrder(Processor.NextId) Console.WriteLine("Vaglia - Id:{0:0000}", M.Id) Console.Write("Beneficiario: ") M.Recipient = Console.ReadLine Console.Write("Somma: ") M.Money = Console.ReadLine Processor.Item(Place) = M Case Else Console.WriteLine("Comando non riconosciuto.") Console.ReadKey() Exit Sub End Select Console.WriteLine("Inserimento eseguito!") Console.ReadKey() End Sub Sub ProcessData() Console.WriteLine("Selezionare l'operazione:") Console.WriteLine(" c - cerca;") Console.WriteLine(" v - visualizza;")

347. 348. 349. 350. 351. 352. 353. 354. 355. 356. 357. 358. 359. 360. 361. 362. 363. 364. 365. 366. 367. 368. 369. 370. 371. 372. 373. 374. 375. 376. 377. 378. 379. 380. 381. 382. 383. 384. 385. 386. 387. 388. 389. 390. 391. 392. 393. 394. 395. 396. 397. 398. 399. 400. 401. 402. 403. 404. 405. 406. 407. 408. 409. 410. 411. 412. 413. 414. 415. 416. End

Cmd = Console.ReadKey().KeyChar Console.Clear() Select Case Cmd Case "c" Dim Str As String Console.WriteLine("Inserire la parola da cercare:") Str = Console.ReadLine Dim Ids() As Int32 = Processor.SearchItems(Str) Console.WriteLine("Trovati {0} elementi. Visualizzare? (y/n)", Ids.Length) Cmd = Console.ReadKey().KeyChar Console.WriteLine() If Cmd = "y" Then For Each Id As Int32 In Ids Processor.PrintById(Id) Next End If Case "v" Console.WriteLine("Visualizzare gli elementi") Console.Write("Da Id: ") IdFrom = Console.ReadLine Console.Write("A Id: ") IdTo = Console.ReadLine Processor.PrintByFilter(AddressOf SelectId) Case Else Console.WriteLine("Comando sconosciuto.") End Select Console.ReadKey() End Sub Sub Main() Do Console.WriteLine("Gestione ufficio") Console.WriteLine() Console.WriteLine("Selezionare l'operazione da effettuare:") Console.WriteLine(" i - inserimento oggetti;") Console.WriteLine(" m - modifica capacit magazzino;") Console.WriteLine(" p - processa i dati;") Console.WriteLine(" e - esci.") Cmd = Console.ReadKey().KeyChar Console.Clear() Select Case Cmd Case "i" Dim Index As Int32 = Processor.FirstPlaceAvailable Console.WriteLine("Inserimento oggetti in magazzino") Console.WriteLine() If Index > -1 Then InsertItems(Index) Else Console.WriteLine("Non c' pi spazio in magazzino!") Console.ReadKey() End If Case "m" Console.WriteLine("Attuale capacit: " & Processor.StorageCapacity) Console.WriteLine("Inserire una nuova dimensione: ") Processor.StorageCapacity = Console.ReadLine Console.WriteLine("Operazione effettuata.") Console.ReadKey() Case "p" ProcessData() End Select Console.Clear() Loop Until Cmd = "e" End Sub Module

Avevo in mente di definir e anche un'altr a inter faccia, IPayable, per calcolar e anche il costo di spedizione di ogni pezzo: volevo far notar e come, sebbene il costo vada calcolato in manier a diver sa per i tr e tipi di oggetto (in base alle dimensioni per il pacco, in base al numer o di par ole per il telegr amma e in base all'ammontar e inviato per il vaglia), bastasse r ichiamar e una funzione attr aver so l'inter faccia per ottener e il r isultato. Poi ho consider ato che un esempio di 400 r ighe er a gi abbastanza. Ad ogni modo, user adesso quel'idea in uno spezzone tr atto dal pr ogr amma appena scr itto per mostr ar e l'uso di inter facce multiple: 01. Module Module1 02. '... 03. Interface IPayable 04. Function CalculateSendCost() As Single 05. End Interface 06. 07. 08. Class Telegram 'Nel caso di pi interfacce, le si separa con la virgola 09. Implements IIdentifiable, IPayable 10. 11. 12. '... 13. Public Function CalculateSendCost() As Single Implements IPayable.CalculateSendCost 14. 15. 'Come vedremo nel capitolo dedicato alle stringhe, 'la funzione Split(c) spezza la stringa in tante 16. 17. 'parti, divise dal carattere c, e le restituisce 18. 'sottoforma di array. In questo caso, tutte le sottostringhe 'separate da uno spazio sono all'incirca tante 19. 'quanto il numero di parole nella frase 20. Select Case Me.Message.Split(" ").Length 21. Case Is <= 20 22. Return 4.39 23. Case Is <= 50 24. Return 6.7 25. Case Is <= 100 26. Return 10.3 27. Case Is <= 200 28. Return 19.6 29. Case Is <= 500 30. Return 39.75 31. End Select 32. End Function 33. End Class 34. 35. '... 36. 37. End Class

Definizione di tipi in un'interfac c ia


Cos come possibile dichiar ar e una nuova classe all'inter no di un'altr a, o una str uttur a in una classe, o un'inter faccia in una classe, o una str uttur a in una str uttur a, o tutte le altr e possibili combinazioni, anche possibile dichiar ar e un nuovo tipo in un'inter faccia. In questo caso, solo le classi che implementer anno quell'inter faccia sar anno in gr ado di usar e quel tipo. Ad esempio: 01. Interface ISaveable 02. Structure FileInfo 03. 'Assumiamo per brevit che queste variabili Public 04. 'siano in realt propriet 05. Public Path As String 06. 'FileAttribues un enumeratore su bit che contiene 07. 'informazioni sugli attributi di un file (nascosto, a sola 08. 'lettura, archivio, compresso, eccetera...) 09. Public Attributes As FileAttributes 10. End Structure 11. Property SaveInfo() As FileInfo 12. Sub Save() 13.

14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27.

End Interface Class A Private _SaveInfo As ISaveable.FileInfo 'SBAGLIATO! '... End Class Class B Implements ISaveable Private _SaveInfo As ISaveable.FileInfo 'GIUSTO '... End Class

Ereditariet, polimorfismo e overloading per le interfac c e


Anche le inter facce possono er editar e da un'altr a inter faccia base. In questo caso, dato che in un'inter faccia non si possono usar e specificator i di accesso, la classe der ivata acquisisce tutti i membr i di quella base: 1. 2. 3. 4. 5. 6. 7. 8. Interface A Property PropA() As Int32 End Interface Interface B Inherits A Sub SubB() End Interface

Non si pu usar e il polimor fismo per ch non c' nulla da r idefinir e, in quanto i metodi non hanno un cor po. Si pu, invece, usar e l'over loading come si fa di consueto: non ci sono differ enze significative in questo ambito.

Perc h preferire un'interfac c ia a una c lasse astratta


La differ enza sostanziale tr a una classe astr atta e un'inter faccia che la pr ima definisce l'es s en za di un oggetto (che cosa ), mentr e la seconda ne indica il comportamen to (che cosa fa). Inoltr e una classe astr atta in gr ado di definir e membr i che ver r anno acquisiti dalla classe der ivata, e quindi dichiar a delle funzionalit di base er editabili da tutti i discendenti; l'inter faccia, al contr ar io, "or dina" a chi la implementa di definir e un cer to membr o con una cer ta funzione, ma non for nisce alcun codice di base, n alcuna dir ettiva su come un dato compito debba esser e svolto. Ecco che, sulla base di queste osser vazioni, possiamo individuar e alcune casistiche in cui sia meglio l'una o l'altr a: Quando esistono compor tamenti comuni : inter facce Quando esistono classi non r iconducibili ad alcun ar chetipo o classe base: inter facce Quando tutte le classi hanno fondamentalmente la stessa essenza : classe astr atta Quando tutte le classi, assimilabili ad un unico ar chetipo, hanno bisogno di implementar e la stessa funzionalit o gli stessi membr i : classe astr atta

A38. Utilizzo delle Interfacce - Parte I


L'aspetto pi inter essante e sicur amente pi utile delle inter facce che il lor o utilizzo fondamentale per l'uso di alcuni costr utti par ticolar i, quali il For Each e l'Using, e per molte altr e funzioni e pr ocedur e che inter vengono nella gestione delle collezioni. Impar ar e a manipolar e con facilit questo str umento per metter di scr iver e non solo meno codice, pi efficace e r iusabile, ma anche di impostar e l'applicazione in una manier a solida e r obusta.

IComparable e IComparer
Un oggetto che implementa ICompar able comunica implicitamente al .NET Fr amew or k che pu esser e confr ontato con altr i oggetti, stabilendo se uno di essi maggior e, minor e o uguale all'altr o e abilitando in questo modo l'or dinamento automatico attr aver so il metodo Sor t di una collection. Infatti, tale metodo confr onta uno ad uno ogni elemento di una collezione o di un ar r ay e tr amite la funzione Compar eTo che ogni inter faccia ICompar able espone e li or dina in or dine cr escente o decr escente. Compar eTo una funzione di istanza che implementa ICompar able.Compar eTo e ha dei r isultati pr edefiniti: r estituisce 1 se l'oggetto passato come par ametr o minor e dell'oggetto dalla quale viene r ichiamata, 0 se uguale e -1 se maggior e. Ad esempio, questo semplice pr ogr amma illustr a il funzionamento di Compar eTo e Sor t: 01. Module Module1 02. Sub Main() 03. Dim A As Int32 04. 05. Console.WriteLine("Inserisci un numero intero:") 06. A = Console.ReadLine 07. 08. 'Tutti i tipi di base espongono il metodo CompareTo, poich 09. 'tutti implementano l'interfaccia IComparable: 10. If A.CompareTo(10) = 1 Then 11. Console.WriteLine(A & " maggiore di 10") 12. ElseIf A.CompareTo(10) = 0 Then 13. Console.WriteLine(A & " uguale a 10") 14. Else 15. Console.WriteLine(A & " minore di 10") 16. End If 17. 18. 'Il fatto che i tipi di base siano confrontabili implica 19. 'che si possano ordinare tramite il metodo Sort di una 20. 'qualsiasi collezione o array di elementi 21. Dim B() As Int32 = {1, 5, 2, 8, 10, 56} 22. 'Ordina l'array 23. Array.Sort(B) 'E visualizza i numeri in ordine crescente 24. For I As Int16 = 0 To UBound(B) 25. 26. Console.WriteLine(B(I)) Next 27. 28. 'Anche String espone questo metodo, quindi si pu ordinare 29. 'alfabeticamente un insieme di stringhe: 30. 31. Dim C As New ArrayList C.Add("Banana") 32. C.Add("Zanzara") 33. C.Add("Anello") 34. C.Add("Computer") 35. 'Ordina l'insieme 36. C.Sort() 37. For I As Int16 = 0 To C.Count - 1 38. Console.WriteLine(C(I)) 39. Next 40. 41. Console.ReadKey() 42. 43.

End Sub 44. End Module Dopo aver immesso un input, ad esempio 8, avr emo la seguente scher mata: Inserire un numero intero: 8 8 minore di 10 1 2 5 8 10 56 Anello Banana Computer Zanzara Come si osser va, tutti gli elementi sono stati or dinati cor r ettamente. Or a che abbiamo visto la potenza di ICompar able, vediamo di capir e come implementar la. L'esempio che pr ender come r ifer imento or a pone una semplice classe Per son, di cui si gi par lato addietr o, e or dina un Ar r ayList di questi oggetti pr endendo come r ifer imento il nome completo: 01. Module Module1 02. Class Person 03. Implements IComparable 04. Private _FirstName, _LastName As String Private ReadOnly _BirthDay As Date 05. 06. 07. Public Property FirstName() As String Get 08. 09. Return _FirstName End Get 10. Set(ByVal Value As String) 11. 12. If Value <> "" Then 13. _FirstName = Value 14. End If 15. End Set End Property 16. 17. 18. Public Property LastName() As String 19. Get 20. Return _LastName 21. End Get Set(ByVal Value As String) 22. If Value <> "" Then 23. 24. _LastName = Value End If 25. End Set 26. End Property 27. 28. Public ReadOnly Property BirthDay() As Date 29. Get 30. Return _BirthDay 31. End Get 32. End Property 33. 34. Public ReadOnly Property CompleteName() As String 35. Get 36. Return _FirstName & " " & _LastName 37. End Get 38. End Property 39. 40. 'Per definizione, purtroppo, CompareTo deve sempre usare 41. 'un parametro di tipo Object: risolveremo questo problema 42. 43.

44. 45. 46. 47. 48. 49. 50. 51. 52. 53. 54. 55. 56. 57. 58. 59. 60. 61. 62. 63. 64. 65. 66. 67. 68. 69. 70. 71. 72. 73. 74. 75. 76. 77. 78. 79. 80. 81. 82. 83. End

'pi in l utilizzando i Generics Public Function CompareTo(ByVal obj As Object) As Integer _ Implements IComparable.CompareTo 'Un oggetto non-nothing (questo) sempre maggiore di 'un oggetto Nothing (ossia obj) If obj Is Nothing Then Return 1 End If 'Tenta di convertire obj in Person Dim P As Person = DirectCast(obj, Person) 'E restituisce il risultato dell'operazione di 'comparazione tra stringhe dei rispettivi nomi Return String.Compare(Me.CompleteName, P.CompleteName) End Function Sub New(ByVal FirstName As String, ByVal LastName As String, _ ByVal BirthDay As Date) Me.FirstName = FirstName Me.LastName = LastName Me._BirthDay = BirthDay End Sub End Class Sub Main() 'Crea un array di oggetti Person Dim Persons() As Person = _ {New Person("Marcello", "Rossi", Date.Parse("10/10/1992")), _ New Person("Guido", "Bianchi", Date.Parse("01/12/1980")), _ New Person("Bianca", "Brega", Date.Parse("23/06/1960")), _ New Person("Antonio", "Felice", Date.Parse("16/01/1930"))} 'E li ordina, avvalendosi di IComparable.CompareTo Array.Sort(Persons) For I As Int16 = 0 To UBound(Persons) Console.WriteLine(Persons(I).CompleteName) Next Console.ReadKey() End Sub Module

Dato che il nome viene pr ima del congnome, la lista sar : Antonio, Bianca, Guido, Mar cello. E se si volesse or dinar e la lista di per sone in base alla data di nascita? Non possibile definir e due ver sioni di Compar eTo, poich devono aver e la stessa signatur e, e cr ear e due metodi che or dinino l'ar r ay sar ebbe scomodo: qui che entr a in gioco l'inter faccia ICompar er . Essa r appr esenta un oggetto che deve eseguir e la compar azione tr a due altr i oggetti, facendo quindi da tramite nell'or dinamento. Dato che Sor t accetta in una delle sue ver sioni un oggetto ICompar er , possibile or dinar e una lista di elementi con qualsiasi cr iter io si voglia semplicemente cambiando il par ametr o. Ad esempio, in questo sor gente scr ivo una classe Bir thDayCompar er che per mette di or dinar e oggetti Per son in base all'anno di nascita: 01. Module Module2 02. 'Questa classe fornisce un metodo per comparare oggetti Person 03. 'utilizzando la propriet BirthDay. 04. 'Per convenzione, classi che implementano IComparer dovrebbero 05. 'avere un suffisso "Comparer" nel nome. 06. 'Altra osservazione: se ci sono molte interfacce il cui nome 07. 'termina in "-able", definendo una caratteristica dell'oggetto 08. 'che le implementa (ad es.: un oggetto enumerabile, 09. 'comparabile, distruggibile, ecc...), ce ne sono altrettante 10. 'che terminano in "-er", indicando, invece, un oggetto 11. 'che "fa" qualcosa di specifico. 12. 'Nel nostro esempio, oggetti di tipo BirthDayComparer 13. 'hanno il solo scopo di comparare altre oggetti 14. Class BirthDayComparer 15. 'Implementa l'interfaccia 16. Implements IComparer 17. 18.

19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40. 41. 42. 43. 44. 45. 46. 47. 48. 49. 50. 51. 52. 53. 54. 55. 56. 57. End

'Anche questa funzione deve usare parametri object Public Function Compare(ByVal x As Object, ByVal y As Object) _ As Integer Implements System.Collections.IComparer.Compare 'Se entrambi gli oggetti sono Nothing, allora sono 'uguali If x Is Nothing And y Is Nothing Then Return 0 ElseIf x Is Nothing Then 'Se x Nothing, y maggiore Return -1 ElseIf y Is Nothing Then 'Se y Nothing, x maggiore Return 1 Else Dim P1 As Person = DirectCast(x, Person) Dim P2 As Person = DirectCast(y, Person) 'Compara le date Return Date.Compare(P1.BirthDay, P2.BirthDay) End If End Function End Class Sub Main() Dim Persons() As Person = _ {New Person("Marcello", "Rossi", Date.Parse("10/10/1992")), _ New Person("Guido", "Bianchi", Date.Parse("01/12/1980")), _ New Person("Bianca", "Brega", Date.Parse("23/06/1960")), _ New Person("Antonio", "Felice", Date.Parse("16/01/1930"))} 'Ordina gli elementi utilizzando il nuovo oggetto 'inizializato in linea BirthDayComparer Array.Sort(Persons, New BirthDayComparer()) For I As Int16 = 0 To UBound(Persons) Console.WriteLine(Persons(I).CompleteName) Next Console.ReadKey() End Sub Module

Usando questo meccanismo possibile or dinar e qualsiasi tipo di lista o collezione fin'or a analizzata (tr anne Sor tedList, che si or dina automaticamente), in modo semplice e veloce, par ticolar mente utile nell'ambito delle liste visuali a colonne, come vedr emo nei capitoli sulle ListView .

IDisposable
Nel capitolo sui distr uttor i si visto come sia possibile utilizzar e il costr utto Using per gestir e un oggetto e poi distr ugger lo in poche r ighe di codice. Ogni classe che espone il metodo Dispose deve obbligator iamente implementar e anche l'inter faccia IDisposable, la quale comunica implicitamente che essa ha questa car atter istica. Dato che gi molti esempi sono stati fatti sull'ar gomento distr uttor i, eviter di tr attar e nuovamente Dispose in questo capitolo.

A39. Utilizzo delle Interfacce - Parte II


IEnumerable e IEnumerator
Una classe che implementa IEnumer able diventa enum er abile agli occhi del .NET Fr amew or k: ci significa che si pu usar e su di essa un costr utto For Each per scor r er ne tutti gli elementi. Di solito questo tipo di classe r appr esenta una collezione di elementi e per questo motivo il suo nome, secondo le convenzioni, dovr ebbe ter minar e in "Collection". Un motivo per costr uir e una nuova collezione al posto di usar e le classiche liste pu consister e nel voler definir e nuovi metodi o pr opr iet per modificar la. Ad esempio, si potr ebbe scr iver e una nuova classe Per sonCollection che per mette di r aggr uppar e ed enumer ar e le per sone ivi contenute e magar i calcolar e anche l'et media. 01. Module Module1 02. Class PersonCollection 03. Implements IEnumerable 'La lista delle persone 04. Private _Persons As New ArrayList 05. 06. 07. 'Teoricamente, si dovrebbero ridefinire tutti i metodi 'di una collection comune, ma per mancanza di spazio, 08. 09. 'accontentiamoci Public ReadOnly Property Persons() As ArrayList 10. Get 11. Return _Persons 12. 13. End Get End Property 14. 15. 'Restituisce l'et media. TimeSpan una struttura che si 16. 'ottiene sottraendo fra loro due oggetti date e indica un 17. 18. 'intervallo di tempo Public ReadOnly Property AverageAge() As String 19. Get 20. 'Variabile temporanea 21. Dim Temp As TimeSpan 22. 'Somma tutte le et 23. For Each P As Person In _Persons 24. Temp = Temp.Add(Date.Now - P.BirthDay) 25. Next 26. 'Divide per il numero di persone 27. Temp = TimeSpan.FromSeconds(Temp.TotalSeconds / _Persons.Count) 28. 29. 'Dato che TimeSpan pu contenere al massimo 30. 'giorni e non mesi o anni, dobbiamo fare qualche 31. 'calcolo 32. Dim Years As Int32 33. 'Gli anni, ossia il numero dei giorni fratto 365 34. 'Divisione intera 35. Years = Temp.TotalDays \ 365 36. 'Sottrae gli anni: da notare che 37. '(Temp.TotalDays \ 365) * 365) non un passaggio 38. 'inutile. Infatti, per determinare il numero di 39. 'giorni che rimangono, bisogna prendere la 40. 'differenza tra il numero totale di giorni e 41. 'il multiplo pi vicino di 365 42. Temp = _ 43. Temp.Subtract(TimeSpan.FromDays((Temp.TotalDays \ 365) * 365)) 44. 45. Return Years & " anni e " & CInt(Temp.TotalDays) & " giorni" 46. End Get 47. End Property 48. 49. 'La funzione GetEnumerator restituisce un oggetto di tipo 50. 'IEnumerator che vedremo fra breve: esso permette di 51. 'scorrere ogni elemento ordinatamente, dall'inizio 52. 'alla fine. In questo caso, poich non abbiamo ancora 53. 'analizzato questa interfaccia, ci limitiamo a restituisce 54. 55.

56. 57. 58. 59. 60. 61. 62. 63. 64. 65. 66. 67. 68. 69. 70. 71. 72. 73. 74. 75. 76. 77. 78. 79. End

'l'IEnumerator predefinito per un ArrayList Public Function GetEnumerator() As IEnumerator _ Implements IEnumerable.GetEnumerator Return _Persons.GetEnumerator End Function End Class Sub Main() Dim Persons As New PersonCollection With Persons.Persons .Add(New Person("Marcello", "Rossi", Date.Parse("10/10/1992"))) .Add(New Person("Guido", "Bianchi", Date.Parse("01/12/1980"))) .Add(New Person("Bianca", "Brega", Date.Parse("23/06/1960"))) .Add(New Person("Antonio", "Felice", Date.Parse("16/01/1930"))) End With For Each P As Person In Persons Console.WriteLine(P.CompleteName) Next Console.WriteLine("Et media: " & Persons.AverageAge) '> 41 anni e 253 giorni Console.ReadKey() End Sub Module

Come si vede dall'esempio, lecito usar e Per sonCollection nel costr utto For Each: l'iter azione viene svolta dal pr imo elemento inser ito all'ultimo, poich l'IEnumer ator dell'Ar r ayList oper a in questo modo. Tuttavia, cr eando una diver sa classe che implementa IEnumer ator si pu scor r er e la collezione in qualsiasi modo: dal pi giovane al pi vecchio, al pr imo all'ultimo, dall'ultimo al pr imo, a caso, saltandone alcuni, a seconda dell'or a di cr eazione ecceter a. Quindi in questo modo si pu per sonalizzar e la pr opr ia collezione. Ci che occor r e per costr uir e cor r ettamente una classe basata su IEnumer ator sono tr e metodi fondamentali definiti nell'inter faccia: MoveNex t una funzione che r estituisce Tr ue se esiste un elemento successivo nella collezione (e in questo caso lo imposta come elemento cor r ente), altr imenti False; Cur r ent una pr opr iet ReadOnly di tipo Object che r estituisce l'elemento cor r ente; Reset una pr ocedur a senza par ametr i che r esetta il contator e e fa iniziar e il ciclo daccapo. Quest'ultimo metodo non viene mai utilizzato, ma nell'esempio che segue ne scr iver comunque il cor po: 001. Module Module1 002. Class PersonCollection 003. Implements IEnumerable 004. 'La lista delle persone 005. Private _Persons As New ArrayList 006. 007. 'Questa classe ha il compito di scorrere ordinatamente gli 008. 'elementi della lista, dal pi vecchio al pi giovane 009. Private Class PersonAgeEnumerator 010. Implements IEnumerator 011. 012. 'Per enumerare gli elementi, la classe ha bisogno di un 013. 'riferimento ad essi: perci si deve dichiarare ancora 014. 'un nuovo ArrayList di Person. Questo passaggio 015. 'facoltativo nelle classi nidificate come questa, ma 016. 'obbligatorio in tutti gli altri casi 017. Private Persons As New ArrayList 018. 'Per scorrere la collezione, si user un comune indice 019. Private Index As Int32 020. 021. 'Essendo una normalissima classe, lecito definire un 022. 'costruttore, che in questo caso inizializza la 023. 'collezione Sub New(ByVal Persons As ArrayList) 024. 'Ricordate: poich ArrayList deriva da Object, 025. 026. 'un tipo reference. Assegnare Persons a Me.Persons 'equivale ad assegnarne l'indirizzo e quindi ogni 027. 'modifica su questo arraylist privato si rifletter 028. 'su quello passato come parametro. Si pu 029. 'evitare questo problema clonando la lista 030. 031.

032. 033. 034. 035. 036. 037. 038. 039. 040. 041. 042. 043. 044. 045. 046. 047. 048. 049. 050. 051. 052. 053. 054. 055. 056. 057. 058. 059. 060. 061. 062. 063. 064. 065. 066. 067. 068. 069. 070. 071. 072. 073. 074. 075. 076. 077. 078. 079. 080. 081. 082. 083. 084. 085. 086. 087. 088. 089. 090. 091. 092. 093. 094. 095. 096. 097. 098. 099. 100. 101. 102. 103.

Me.Persons = Persons.Clone 'MoveNext viene richiamato prima di usare Current, 'quindi Index verr incrementata subito. 'Per farla diventare 0 al primo ciclo la si 'deve impostare a -1 Index = -1 'Dato che l'enumeratore deve scorrere la lista 'secondo l'anno di nascita, bisogna prima ordinarla Me.Persons.Sort(New BirthDayComparer) End Sub 'Restituisce l'elemento corrente Public ReadOnly Property Current() As Object _ Implements System.Collections.IEnumerator.Current Get Return Persons(Index) End Get End Property 'Restituisce True se esiste l'elemento successivo e lo 'imposta, altrimenti False Public Function MoveNext() As Boolean _ Implements System.Collections.IEnumerator.MoveNext If Index = Persons.Count - 1 Then Return False Else Index += 1 Return True End If End Function 'Resetta il ciclo Public Sub Reset() _ Implements System.Collections.IEnumerator.Reset Index = -1 End Sub End Class Public ReadOnly Property Persons() As ArrayList Get Return _Persons End Get End Property Public ReadOnly Property AverageAge() As String Get Dim Temp As TimeSpan For Each P As Person In _Persons Temp = Temp.Add(Date.Now - P.BirthDay) Next Temp = TimeSpan.FromSeconds(Temp.TotalSeconds / _Persons.Count) Dim Years As Int32 Years = Temp.TotalDays \ 365 Temp = _ Temp.Subtract(TimeSpan.FromDays((Temp.TotalDays \ 365) * 365)) Return Years & " anni e " & CInt(Temp.TotalDays) & " giorni" End Get End Property 'La funzione GetEnumerator restituisce ora un oggetto di 'tipo IEnumerator che abbiamo definito in una classe 'nidificata e il ciclo For Each scorrer quindi 'dal pi vecchio al pi giovane Public Function GetEnumerator() As IEnumerator _ Implements IEnumerable.GetEnumerator Return New PersonAgeEnumerator(_Persons) End Function End Class Sub Main()

Dim Persons As New PersonCollection 104. With Persons.Persons 105. .Add(New Person("Marcello", "Rossi", Date.Parse("10/10/1992"))) 106. .Add(New Person("Guido", "Bianchi", Date.Parse("01/12/1980"))) 107. .Add(New Person("Bianca", "Brega", Date.Parse("23/06/1960"))) 108. .Add(New Person("Antonio", "Felice", Date.Parse("16/01/1930"))) 109. End With 110. 111. 'Enumera ora per data di nascita, ma senza modificare 112. 'l'ordine degli elementi 113. For Each P As Person In Persons 114. Console.WriteLine(P.BirthDay.ToShortDateString & ", " & _ 115. P.CompleteName) 116. Next 117. 118. 'Stampa la prima persona, dimostrando che l'ordine 119. 'della lista intatto 120. Console.WriteLine(Persons.Persons(0).CompleteName) 121. Console.ReadKey() 122. 123. End Sub 124. End Module

ICloneable
Come si visto nell'esempio appena scr itto, si pr esentano alcune difficolt nel manipolar e oggetti di tipo Refer ence, in quanto l'assegnazione di questi cr eer ebbe due istanze che puntano allo stesso oggetto piuttosto che due oggetti distinti. in questo tipo di casi che il metodo Clone e l'inter faccia ICloneable assumono un gr an valor e. Il pr imo per mette di eseguir e una copia dell'oggetto, cr eando un nuo v o og g etto a tutti gli effetti, totalmente disgiunto da quello di par tenza: questo per mette di non intaccar ne accidentalmente l'integr it. Una dimostr azione: 01. Module Esempio 02. Sub Main() 03. 'Il tipo ArrayList espone il metodo Clone 04. Dim S1 As New ArrayList Dim S2 As New ArrayList 05. 06. 07. S2 = S1 08. 09. 'Verifica che S1 e S2 puntano lo stesso oggetto Console.WriteLine(S1 Is S2) 10. 11. '> True 12. 13. 'Clona l'oggetto 14. S2 = S1.Clone 15. 'Verifica che ora S2 referenzia un oggetto differente, 16. 'ma di valore identico a S1 17. Console.WriteLine(S1 Is S2) 18. '> False 19. 20. Console.ReadKey() 21. End Sub 22. End Module L'inter faccia, invece, come accadeva per IEnumer able e ICompar able, indica al .NET Fr amew or k che l'oggetto clonabile. Questo codice mostr a la funzione Close all'oper a: 01. Module Module1 02. Class UnOggetto 03. Implements ICloneable 04. Private _Campo As Int32 05. 06. Public Property Campo() As Int32 07. Get 08. Return _Campo 09. End Get 10.

11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40. 41. 42. 43. 44. 45. 46. 47. 48. 49. 50. 51. 52. End

Set(ByVal Value As Int32) _Campo = Value End Set End Property 'Restituisce una copia dell'oggetto Public Function Clone() As Object Implements ICloneable.Clone 'La funzione Protected MemberwiseClone, ereditata da 'Object, esegue una copia superficiale dell'oggetto, 'come spiegher fra poco: quello che 'serve in questo caso Return Me.MemberwiseClone End Function 'L'operatore = permette di definire de due oggetti hanno un 'valore uguale Shared Operator =(ByVal O1 As UnOggetto, ByVal O2 As UnOggetto) As _ Boolean Return O1.Campo = O2.Campo End Operator Shared Operator <>(ByVal O1 As UnOggetto, ByVal O2 As UnOggetto) As _ Boolean Return Not (O1 = O2) End Operator End Class Sub Main() Dim O1 As New UnOggetto Dim O2 As UnOggetto = O1.Clone 'I due oggetti NON sono lo stesso oggetto: il secondo ' solo una copia, disgiunta da O1 Console.WriteLine(O1 Is O2) '> False 'Tuttavia hanno lo stesso identico valore Console.WriteLine(O1 = O2) '> True Console.ReadKey() End Sub Module

Or a, impor tante distinguer e due tipi di copia: quella Shallo w e quella Deep. La pr ima cr ea una copia super ficiale dell'oggetto, ossia si limita a clonar e tutti i campi. La seconda, invece, in gr ado di eseguir e questa oper azione anche su tutti gli oggetti inter ni e i r ifer imenti ad altr i oggetti: cos, se si ha una classe Per son che al pr opr io inter no contiene il campo Childer n, di tipo ar r ay di Per son, la copia Shallow cr eer un clone della classe in cui Childr en punta sempr e allo stesso oggetto, mentr e una copia Deep cloner anche Childr en. Si nota meglio con un gr afico: le fr ecce ver di indicano oggetti clonati, mentr e la fr eccia ar ancio si r ifer isce allo stesso oggetto.

Non possibile specificar e nella dichiar azione di Clone quale tipo di copia ver r eseguita, quindi tutto viene lasciato all'ar bitr io del pr ogr ammator e. Dal codice sopr a scr itto, si nota che Clone deve r estituir e per for za un tipo Object. In questo caso, il metodo si dice a tipizzazio ne debo le, ossia ser ve un oper ator e di cast per conver tir lo nel tipo desider ato; per cr ear ne una ver sione a tipizzazio ne fo r te necessar io scr iver e una funzione che r estituisca, ad esempio, un tipo Per son. Quest'ultima ver sione avr il nome Clone, mentr e quella che implementa ICloneable.Clone() avr un nome differ ente, come CloneMe().

A40. Le librerie di classi


Cer te volte accade che non si voglia scr iver e un pr ogr amma, ma piuttosto un insieme di utilit per gestir e un cer to tipo di infor mazioni. In questi casi, si scr ive una libr er ia di classi, ossia un insieme, appunto, di namespace, classi e tipi che ser vono ad un deter minato scopo. Potete tr ovar e un esempio tr a i sor genti della sezione Dow nload: mi r ifer isco a Mp3 Deep Analyzer , una libr er ia di classi che for nisce str umenti per legger e e scr iver e tag ID3 nei file mp3 (per ulter ior i infor mazioni sull'ar gomento, consultar e la sezione FFS). Con quel pr ogetto non ho voluto scr iver e un pr ogr amma che svolgesse quei compiti, per ch da solo sar ebe stato poco utile, ma piuttosto metter e a disposizione anche agli altr i pr ogr ammator i un modo semplice per manipolar e quel tipo di infor mazioni. Cos facendo, uno potr ebbe usar e le funzioni di quella libr er ia in un pr opr io pr ogr amma. Le libr er ie, quindi, sono un inventar io di classi scr itto appositamente per esser e r iusato.

Creare una nuova libreria di c lassi


Per cr ear e una libr er ia, cliccate su File > New Pr oject e, invece si selezionar e la solita "Console Application", selezionate "Class Libr ar y". Una volta inizializzato il pr ogetto, vi tr over ete di fr onte a un codice pr eimpostato diver so dal solito: 1. Class Class1 2. 3. End Class Noter ete, inoltr e, che, pr emendo F5, vi ver r comunicato un er r or e: non stiamo scr ivendo un pr ogr amma, infatti, ma solo una libr er ia, che quindi non pu esser e "eseguita" (non avr ebbe senso neanche pensar e di far lo). Per far e un esempio, significativo, r ipr endiamo il codice di esempio del capitolo sulle inter facce e scor por iamolo dal pr ogr amma, estr aendone solo le classi: 001. Namespace PostalManagement 002. 003. Public Interface IIdentifiable 004. ReadOnly Property Id() As Int32 005. Function ToString() As String 006. End Interface 007. 008. Public Class Pack 009. Implements IIdentifiable 010. 011. Private _Id As Int32 012. Private _Destination As String 013. Private _Dimensions(2) As Single 014. 015. Public ReadOnly Property Id() As Integer Implements IIdentifiable.Id 016. Get 017. Return _Id 018. End Get 019. End Property 020. 021. Public Property Destination() As String 022. Get 023. Return _Destination 024. End Get 025. Set(ByVal value As String) _Destination = value 026. End Set 027. 028. End Property 029. Public Property Dimensions(ByVal Index As Int32) As Single 030. Get 031. 032.

033. 034. 035. 036. 037. 038. 039. 040. 041. 042. 043. 044. 045. 046. 047. 048. 049. 050. 051. 052. 053. 054. 055. 056. 057. 058. 059. 060. 061. 062. 063. 064. 065. 066. 067. 068. 069. 070. 071. 072. 073. 074. 075. 076. 077. 078. 079. 080. 081. 082. 083. 084. 085. 086. 087. 088. 089. 090. 091. 092. 093. 094. 095. 096. 097. 098. 099. 100. 101. 102. 103. 104.

If (Index >= 0) And (Index < 3) Then Return _Dimensions(Index) Else Throw New IndexOutOfRangeException() End If End Get Set(ByVal value As Single) If (Index >= 0) And (Index < 3) Then _Dimensions(Index) = value Else Throw New IndexOutOfRangeException() End If End Set End Property Public Sub New(ByVal Id As Int32) _Id = Id End Sub Public Overrides Function ToString() As String Implements IIdentifiable.ToString Return String.Format("{0:0000}: Pacco {1}x{2}x{3}, Destinazione: {4}", _ Me.Id, Me.Dimensions(0), Me.Dimensions(1), _ Me.Dimensions(2), Me.Destination) End Function End Class Public Class Telegram Implements IIdentifiable Private _Id As Int32 Private _Recipient As String Private _Message As String Public ReadOnly Property Id() As Integer Implements IIdentifiable.Id Get Return _Id End Get End Property Public Property Recipient() As String Get Return _Recipient End Get Set(ByVal value As String) _Recipient = value End Set End Property Public Property Message() As String Get Return _Message End Get Set(ByVal value As String) _Message = value End Set End Property Public Sub New(ByVal Id As Int32) _Id = Id End Sub Public Overrides Function ToString() As String Implements IIdentifiable.ToString Return String.Format("{0:0000}: Telegramma per {1} ; Messaggio = {2}", _ Me.Id, Me.Recipient, Me.Message) End Function End Class Public Class MoneyOrder Implements IIdentifiable Private _Id As Int32 Private _Recipient As String

105. 106. 107. 108. 109. 110. 111. 112. 113. 114. 115. 116. 117. 118. 119. 120. 121. 122. 123. 124. 125. 126. 127. 128. 129. 130. 131. 132. 133. 134. 135. 136. 137. 138. 139. 140. 141. 142. 143. 144. 145. 146. 147. 148. 149. 150. 151. 152. 153. 154. 155. 156. 157. 158. 159. 160. 161. 162. 163. 164. 165. 166. 167. 168. 169. 170. 171. 172. 173. 174. 175. 176.

Private _Money As Single Public ReadOnly Property Id() As Integer Implements IIdentifiable.Id Get Return _Id End Get End Property Public Property Recipient() As String Get Return _Recipient End Get Set(ByVal value As String) _Recipient = value End Set End Property Public Property Money() As Single Get Return _Money End Get Set(ByVal value As Single) _Money = value End Set End Property Public Sub New(ByVal Id As Int32) _Id = Id End Sub Public Overrides Function ToString() As String Implements IIdentifiable.ToString Return String.Format("{0:0000}: Vaglia postale per {1} ; Ammontare = {2}", _ Me.Id, Me.Recipient, Me.Money) End Function End Class Public Class PostalProcessor Public Delegate Function IdSelector(ByVal Id As Int32) As Boolean Private _StorageCapacity As Int32 Private _NextId As Int32 = 0 Private Storage() As IIdentifiable Public Property StorageCapacity() As Int32 Get Return _StorageCapacity End Get Set(ByVal value As Int32) _StorageCapacity = value ReDim Preserve Storage(value) End Set End Property Public Property Item(ByVal Index As Int32) As IIdentifiable Get If (Index >= 0) And (Index < Storage.Length) Then Return Me.Storage(Index) Else Throw New IndexOutOfRangeException() End If End Get Set(ByVal value As IIdentifiable) If (Index >= 0) And (Index < Storage.Length) Then Me.Storage(Index) = value Else Throw New IndexOutOfRangeException() End If End Set End Property Public ReadOnly Property FirstPlaceAvailable() As Int32 Get

177. 178. 179. 180. 181. 182. 183. 184. 185. 186. 187. 188. 189. 190. 191. 192. 193. 194. 195. 196. 197. 198. 199. 200. 201. 202. 203. 204. 205. 206. 207. 208. 209. 210. 211. 212. 213. 214. 215. 216. 217. 218. 219. 220. 221. 222. 223. 224. 225. 226. 227. 228. 229. 230. 231. 232. 233. 234. 235. 236. 237. 238. 239. 240. 241. 242. 243. 244. 245. 246. 247. 248.

For I As Int32 = 0 To Me.Storage.Length - 1 If Me.Storage(I) Is Nothing Then Return I End If Next Return (-1) End Get End Property Public ReadOnly Property NextId() As Int32 Get _NextId += 1 Return _NextId End Get End Property Public Sub New(ByVal Items() As IIdentifiable) Me.Storage = Items _StorageCapacity = Items.Length End Sub Public Sub New(ByVal Capacity As Int32) Me.StorageCapacity = Capacity End Sub Public Sub PrintByFilter(ByVal Selector As IdSelector) For Each K As IIdentifiable In Storage If K Is Nothing Then Continue For End If If Selector.Invoke(K.Id) Then Console.WriteLine(K.ToString()) End If Next End Sub Public Sub PrintById(ByVal Id As Int32) For Each K As IIdentifiable In Storage If K Is Nothing Then Continue For End If If K.Id = Id Then Console.WriteLine(K.ToString()) Exit For End If Next End Sub Public Function SearchItems(ByVal Str As String) As Int32() Dim Temp As New ArrayList For Each K As IIdentifiable In Storage If K Is Nothing Then Continue For End If If K.ToString().Contains(Str) Then Temp.Add(K.Id) End If Next Dim Result(Temp.Count - 1) As Int32 For I As Int32 = 0 To Temp.Count - 1 Result(I) = Temp(I) Next Temp.Clear() Temp = Nothing Return Result End Function End Class

249. End Namespace Notate che ho r acchiuso tutto in un namespace e ho anche messo lo scope Public a tutti i membr i non pr ivati. Se non avessi messo Public, infatti, i membr i senza scope sar ebber o stati automaticamente mar cati con Fr iend. Suppongo vi r icor diate che Fr iend r ende accessibile un membr o solo dalle classi appar tenenti allo stesso assembly (in questo caso, allo stesso pr ogetto): questo equivale a dir e che tutti i membr i Fr iend non sar anno accessibili al di fuor i della libr er ia e quindi chi la user non potr acceder vi. Ovviamente, dato che il tutto si basa sull'inter faccia IIdentifiable non potevo pr ecluder ne l'accesso agli utenti della libr er ia, e allo stesso modo i costr uttor i senza Public sar ebber o stati inaccessibili e non si sar ebbe potuto istanziar e alcun oggetto. Ecco che concludiamo la lista di tutti gli specificator i di accesso con Pr otected Fr iend: un membr o dichiar ato Pr otected Fr iend sar accessibile solo ai membr i delle classi der ivate appar tenenti allo stesso assembly. Per r icapitolar vi tutti gli scope, ecco uno schema dove le fr ecce ver di indicano gli unici accessi consentiti:

Importare la libreria in un altro progetto


Una volta compilata la libr er ia, al posto dell'eseguibile, nella sottocar tella bin\Release del vostr o pr ogetto, si tr over un file con estensione *.dll. Per usar e le classi contenute in questa libr er ia (o r ifer im ento , nome tecnico che si confonde spesso con i nomi comuni), bisogna impor tar la nel pr ogetto cor r ente. Per far e questo, nel Solution Ex plor er (la finestr a che mostr a tutti gli elementi del pr ogetto) cliccate col pulsante destr o sul nome del pr ogetto e selezionate "Add Refer ence" ("Aggiungi r ifer imento"):

Quindi r ecatevi fino alla car tella della libr er ia cr eata, selezionate il file e pr emete OK (nell'esempio c' una delle libr er ie che ho scr itto e che potete tr ovar e nella sezione Dow nload):

or a il r ifer imento stato aggiunto al pr ogetto, ma non potete ancor a usar e le classi della libr er ia. Pr ima dovete "dir e" al compilator e che nel codice che sta per esser e letto potr este far e r ifer imento ad esse. Questo si fa "impor tando" il namespace, con il codice: 1. Imports [Nome Libreria].[Nome Namespace] Io ho chiamato la libr er ia con lo stesso nome del namespace, ma potete usar e anche nomi diver si, poich in una libr er ia ci possono esser e tanti namespace differ enti: 1. Imports PostalManagement.PostalManagement Impor ts una "dir ettiva", ossia non costituisce codice eseguibile, ma infor ma il compilator e che alcune classi del sor gente potr ebber o appar tener e a questo namespace (omettendo questa r iga, dovr ete scr iver e ogni volta PostalManagement.Pack, ad esempio, per usar e la classe Pack, per ch altr imenti il compilator e non sar ebbe in gr ado di

tr ovar e il name Pack nel contesto cor r ente). Ecco un esempio: 01. Imports PostalManagement.PostalManagement 02. 03. Module Module1 04. Sub Main() 05. 06. Dim P As New PostalProcessor(10) 07. Dim Pk As New Pack(P.NextId) 08. P.Item(P.FirstPlaceAvailable) = Pk 09. '... 10. 11. End Sub 12. 13. End Module che equivale a: 01. Module Module1 02. 03. Sub Main() 04. Dim P As New PostalManagement.PostalManagement.PostalProcessor(10) 05. Dim Pk As New PostalManagement.PostalManagement.Pack(P.NextId) 06. 07. P.Item(P.FirstPlaceAvailable) = Pk 08. '... 09. End Sub 10. 11. End Module Nella scheda ".NET" che vedete nella seconda immagine di sopr a, ci sono molte libr er ie facenti par te del Fr amew or k che user emo nelle pr ossime sezioni della guida.

A41. I Generics - Parte I


Panoramic a sui Generic s
I Gener ics sono un concetto molto impor tante per quanto r iguar da la pr ogr ammazione ad oggetti, specialmente in .NET e, se fino ad or a non ne conoscevate nemmeno l'esistenza, d'or a in poi non potr ete far ne a meno. Cominciamo col far e un par agone per esemplificar e il concetto di gener ics. Ammettiamo di dichiar ar e una var iabile I di tipo Int32: in questa var iabile potr emo immagazzinar e qualsiasi infor mazione che consista di un numer o inter o r appr esentabile su 32 bit. Possiamo dir e, quindi, che il tipo Int32 costituisce un'astr azione di tutti i numer i inter i esistenti da -2'147'483'648 a +2'147'483'647. Analogamente un tipo g ener ic pu assumer e come valor e un altr o tipo e, quindi, astr ae tutti i possibili tipi usabili in quella classe/metodo/pr opr iet ecceter a. come dir e: definiamo la funzione Somma(A, B), dove A e B sono di un tipo T che non conosciamo. Quando utilizziamo la funzione Somma, oltr e a specificar e i par ametr i r ichiesti, dobbiamo anche "dir e" di quale tipo essi siano (ossia immetter e in T non un valor e ma un tipo): in questo modo, definendo un solo metodo, potr emo eseguir e somme tr a inter i, decimali, str inghe, date, file, classi, ecceter a... In VB.NET, l'oper azione di specificar e un tipo per un entit gener ic si attua con questa sintassi: 1. [NomeEntit](Of [NomeTipo]) Dato i gener ics di possono applicar e ad ogni entit del .NET (metodi, classi, pr opr iet, str uttur e, inter facce, delegate, ecceter a...), ho scr itto solo "NomeEntit" per indicar e il nome del tar get a cui si applicano. Il pr ossimo esempio mostr a come i gener ics, usati sulle liste, possano aumentar e di molto le per for mance di un pr ogr amma. La collezione Ar r ayList, molte volte impiegata negli esempi dei pr ecedeti capitoli, per mette di immagazzinar e qualsiasi tipo di dato, memor izzando, quindi, var iabili di tipo Object. Come gi detto all'inizio del cor so, l'uso di Object compor ta molti r ischi sia a livello di pr estazioni, dovute alle continue oper azioni di box ing e unbox ing (e le gar bage collection che ne conseguono, data la cr eazione di molti oggetti tempor anei), sia a livello di cor r ettezza del codice. Un esempio di questo ultimo caso si ver ifica quando si tenta di scor r er e un Ar r ayList mediante un ciclo For Each e si incontr a un r ecor d che non del tipo specificato, ad esempio: 01. 02. 03. 04. 05. 06. 07. 08. 09. 10. Dim A As New ArrayList A.Add(2) A.Add(3) A.Add("C") 'A run-time, sar lanciata un'eccezione inerente il cast 'poich la stringa "C" non del tipo specificato 'nel blocco For Each For Each V As Int32 In A Console.WriteLine(V) Next

Infatti, se l'applicazione dovesse er r oneamente inser ir e una str inga al posto di un numer o inter o, non ver r ebbe gener ato nessun er r or e, ma si ver ificher ebbe un'eccezione successivamente. Altr a pr oblematica legata all'uso di collezioni a tipizzazione debole (ossia che r egistr ano gener ici oggetti Object, come l'Ar r ayList, l'HashTable o la Sor tedList) dovuta al fatto che sia necessar ia una conver sione esplicita di tipo nell'uso dei suoi elementi, almeno nella maggior anza dei casi. La soluzione adottata da un pr ogr ammator e che non conoscesse i gener ics per r isolver e tali inconvenienti sar ebbe quella di cr ear e una nuova lista, ex novo, er editandola da un tipo base come CollectionBase e r idefinendone tutti i metodi (Add, Remove, Index Of ecc...). L'uso dei Gener ics, invece, r ende molto pi veloce e meno insidiosa la scr ittur a di un codice r obusto e solido nell'ambito non solo delle collezioni, ma di molti altr i ar gomenti. Ecco un esempio di come implementar e una soluzione basata sui Gener ics: 01. 02. 03. 04. 05. 'La lista accetta solo oggetti di tipo Int32: per questo motivo 'si genera un'eccezione quando si tenta di inserirvi elementi di 'tipo diverso e la velocit di elaborazione aumenta! Dim A As New List(Of Int32)

06. 07. 08. 09. 10. 11.

A.Add(1) A.Add(4) A.Add(8) 'A.Add("C") '<- Impossibile For Each V As Int32 In A Console.WriteLine(V) Next

E questa una dimostr azione dell'incr emento delle pr estazioni: 01. Module Module1 02. Sub Main() 03. Dim TipDebole As New ArrayList 04. Dim TipForte As New List(Of Int32) 05. Dim S As New Stopwatch 06. 07. 'Cronometra le operazioni su ArrayList 08. S.Start() 09. For I As Int32 = 1 To 1000000 10. TipDebole.Add(I) 11. Next 12. S.Stop() 13. Console.WriteLine(S.ElapsedMilliseconds & _ 14. " millisecondi per ArrayList!") 15. 16. 'Cronometra le operazioni su List 17. S.Reset() 18. S.Start() For I As Int32 = 1 To 1000000 19. TipForte.Add(I) 20. 21. Next S.Stop() 22. Console.WriteLine(S.ElapsedMilliseconds & _ 23. " millisecondi per List(Of T)!") 24. 25. Console.ReadKey() 26. End Sub 27. 28. End Module Sul mio computer por tatile l'Ar r ayList impiega 197ms, mentr e List 33ms: i Gener ics incr ementano la velocit di 6 volte! Oltr e a List, esistono anche altr e collezioni gener ic, ossia Dictionar y e Sor tedDictionar y: tutti questi sono la ver sione a tipizzazione for te delle nor mali collezioni gi viste. Ma or a vediamo come scr iver e nuove classi e metodi gener ic.

Generic s Standard
Una volta impar ato a dichiar ar e e scr iver e entit gener ics, sar anche altr ettanto semplice usar e quelli esistenti, per ci iniziamo col dar e le pr ime infor mazioni su come scr iver e, ad esempio, una classe gener ics. Una classe gener ics si r ifer isce ad un qualsiasi tipo T che non possiamo conoscer e al momento dela scr ittur a del codice, ma che il pr ogr ammator e specificher all'atto di dichiar azione di un oggetto r appr esentato da questa classe. Il fatto che essa sia di tipo gener ico indica che anche i suoi membr i, molto pr obabilmente, avr anno lo stesso tipo: pi nello specifico, potr ebber o esser ci campi di tipo T e metodi che lavor ano su oggetti di tipo T. Se nessuna di queste due condizioni ver ificata, allor a non ha senso scr iver e una classe gener ics. Ma iniziamo col veder e un semplice esempio: 001. Module Module1 002. 'Collezione generica che contiene un qualsiasi tipo T di 003. 'oggetto. T si dice "tipo generic aperto" 004. Class Collection(Of T) 005. 'Per ora limitiamoci a dichiarare un array interno 006. 'alla classe. 007. 'Vedremo in seguito che possibile ereditare da 008. 'una collezione generics gi esistente. 009. 'Notate che la variabile di tipo T: una volta che 010. 'abbiamo dichiarato la classe come generics su un tipo T, 011.

012. 013. 014. 015. 016. 017. 018. 019. 020. 021. 022. 023. 024. 025. 026. 027. 028. 029. 030. 031. 032. 033. 034. 035. 036. 037. 038. 039. 040. 041. 042. 043. 044. 045. 046. 047. 048. 049. 050. 051. 052. 053. 054. 055. 056. 057. 058. 059. 060. 061. 062. 063. 064. 065. 066. 067. 068. 069. 070. 071. 072. 073. 074. 075. 076. 077. 078. 079. 080. 081. 082. 083.

' come se avessimo "dichiarato" l'esistenza di T 'come tipo fittizio. Private _Values() As T 'Restituisce l'Index-esimo elemento di Values (anch'esso ' di tipo T) Public Property Values(ByVal Index As Int32) As T Get If (Index >= 0) And (Index < _Values.Length) Then Return _Values(Index) Else Throw New IndexOutOfRangeException() End If End Get Set(ByVal value As T) If (Index >= 0) And (Index < _Values.Length) Then _Values(Index) = value Else Throw New IndexOutOfRangeException() End If End Set End Property 'Propriet che restituiscono il primo e l'ultimo 'elemento della collezione Public ReadOnly Property First() As T Get Return _Values(0) End Get End Property Public ReadOnly Property Last() As T Get Return _Values(_Values.Length - 1) End Get End Property 'Stampa tutti i valori presenti nella collezione a schermo. 'Su un tipo generic sempre possibile usare 'l'operatore Is (ed il suo corrispettivo IsNot) e 'confrontarlo con Nothing. Se si tratta di un tipo value 'l'uguaglianza con Nothing sar sempre falsa. Public Sub PrintAll() For Each V As T In _Values If V IsNot Nothing Then Console.WriteLine(V.ToString()) End If Next End Sub 'Inizializza la collezione con Count elementi, tutti del 'valore DefaultValue Sub New(ByVal Count As Int32, ByVal DefaultValue As T) If Count < 1 Then Throw New ArgumentOutOfRangeException() End If ReDim _Values(Count - 1) For I As Int32 = 0 To _Values.Length - 1 _Values(I) = DefaultValue Next End Sub End Class Sub Main() 'Dichiara quattro variabili contenenti quattro nuovi 'oggetti Collection. Ognuno di questi, per, ' specifico per un solo tipo che decidiamo 'noi durante la dichiarazione. String, Int32, Date 'e Person, ossia i tipi che stiamo inserendo nel tipo

'generico T, si dicono "tipi generic collegati", 084. 'poich collegano il tipo fittizio T con un 085. 'reale tipo esistente 086. Dim Strings As New Collection(Of String)(10, "null") 087. Dim Integers As New Collection(Of Int32)(5, 12) 088. Dim Dates As New Collection(Of Date)(7, Date.Now) 089. Dim Persons As New Collection(Of Person)(10, Nothing) 090. 091. Strings.Values(0) = "primo" 092. Integers.Values(3) = 45 093. Dates.Values(6) = New Date(2009, 1, 1) 094. Persons.Values(3) = New Person("Mario", "Rossi", Dates.Last) 095. 096. Strings.PrintAll() 097. Integers.PrintAll() 098. Dates.PrintAll() 099. Persons.PrintAll() 100. 101. Console.ReadKey() 102. End Sub 103. 104. End Module Ognuna della quattr o var iabili del sor gente contiene un oggetto di tipo Collection, ma tali oggetti non sono dello stesso tipo, poich ognuno espone un differ ente tipo gener ics collegato. Quindi, nonostante si tr atti sempr e della stessa classe Collection, Collection(Of Int32) e Collection(Of Str ing) sono a tutti gli effetti due tipi diver si: come se esistesser o due classi in cui T sostituito in una da Int32 e nell'altr a da Str ing. Per dimostr ar e la lor o diver sit, basta scr iver e: 1. Console.WriteLine(Strings.GetType() Is Integers.GetType()) 2. 'Output : False

Metodi Generic s e tipi generic s c ollegati implic iti


Se si decide di scr iver e un solo metodo gener ics, e di focalizzar e su di esso l'attenzione, solo accanto al suo nome appar ir la dichiar azione di un tipo gener ics aper to, con la consueta clausola "(Of T)". Anche se fin'or a ho usato come nome solamente T, nulla vieta di specificar e un altr o identificator e valido (ad esempio Pippo): tuttavia, convenzione che il nome dei tipi gener ics aper ti sia Tn (con n numer o inter o, ad esempio T1, T2, T3, eccetr a...) o, in caso contr ar io, che inizi almeno con la letter a T (ad esempio TSize, TClass, ecceter a...). 1. 2. 3. 4. 5. 6. 7. Sub [NomeProcedura](Of T)([Parametri]) '... End Sub Function [NomeFunzione](Of T)([Parametri]) As [TipoRestituito] '... End Function

Ecco un semplice esempio: 01. Module Module1 02. 03. 'Scambia i valori di due variabili, passate 04. 'per indirizzo 05. Public Sub Swap(Of T)(ByRef Arg1 As T, ByRef Arg2 As T) 06. Dim Temp As T = Arg1 07. Arg1 = Arg2 08. Arg2 = Temp 09. End Sub 10. 11. Sub Main() 12. Dim X, Y As Double 13. Dim Z As Single 14. Dim A, B As String 15. 16. X = 90.0 17.

Y = 67.58 18. Z = 23.01 19. A = "Ciao" 20. B = "Mondo" 21. 22. 'Nelle prossime chiamate, Swap non presenta un 23. 'tipo generics collegato: il tipo viene dedotto dai 24. 'tipi degli argomenti 25. 26. 'X e Y sono Double, quindi richiama il metodo con 27. 'T = Double 28. Swap(X, Y) 29. 'A e B sono String, quindi richiama il metodo con 30. 'T = String 31. Swap(A, B) 32. 33. 'Qui viene generato un errore: nonostante Z sia 34. 'convertibile in Double implicitamente senza perdita 35. 'di dati, il suo tipo non corrisponde a quello di X, 36. 'dato che c' un solo T, che pu assumere 37. 'un solo valore-tipo. Per questo necessario 38. 'utilizzare una scappatoia 39. 'Swap(Z, X) 40. 'Soluzione 1: si esplicita il tipo generic collegato 41. Swap(Of Double)(Z, X) 42. 'Soluzione 2: si converte Z in double esplicitamente 43. Swap(CDbl(Z), X) 44. 45. Console.ReadKey() 46. End Sub 47. 48. End Module

Generic s multipli
Quando, anzich un solo tipo gener ics, se ne specificano due o pi, si par la di genr ics multipli. La dichiar azione avviene allo stesso modo di come abbiamo visto pr ecedentemente e i tipi vengono separ ati da una vir gola: 01. Module Module2 02. 'Una relazione qualsiasi fra due oggetti di tipo indeterminato 03. Public Class Relation(Of T1, T2) 04. Private Obj1 As T1 Private Obj2 As T2 05. 06. 07. Public ReadOnly Property FirstObject() As T1 08. Get 09. Return Obj1 10. End Get 11. End Property 12. 13. Public ReadOnly Property SecondObject() As T2 14. Get 15. Return Obj2 16. End Get 17. End Property 18. 19. Sub New(ByVal Obj1 As T1, ByVal Obj2 As T2) 20. Me.Obj1 = Obj1 21. Me.Obj2 = Obj2 End Sub 22. End Class 23. 24. Sub Main() 25. 'Crea una relazione fra uno studente e un insegnante, 26. 'utilizzando le classi create nei capitoli precedenti 27. Dim R As Relation(Of Student, Teacher) 28. Dim S As New Student("Pinco", "Pallino", Date.Parse("25/06/1990"), _ 29. "Liceo Scientifico N. Copernico", 4) 30. Dim T As New Teacher("Mario", "Rossi", Date.Parse("01/07/1950"), _ 31. 32.

"Matematica") 33. 34. 'Crea una nuova relazione tra lo studente e l'insegnante 35. R = New Relation(Of Student, Teacher)(S, T) 36. Console.WriteLine(R.FirstObject.CompleteName) 37. Console.WriteLine(R.SecondObject.CompleteName) 38. 39. Console.ReadKey() 40. End Sub 41. End Module Notate che anche possibile cr ear e una r elazione tr a due r elazioni (e la cosa diventa complicata): 01. Dim S As New Student("Pinco", "Pallino", Date.Parse("25/06/1990"), "Liceo Scientifico N. Copernico", 4) 02. Dim T As New Teacher("Mario", "Rossi", Date.Parse("01/07/1950"), "Matematica") 03. Dim StudentTeacherRelation As Relation(Of Student, Teacher) 04. Dim StudentClassRelation As Relation(Of Student, String) 05. Dim Relations As Relation(Of Relation(Of Student, Teacher), Relation(Of Student, String)) 06. 07. StudentTeacherRelation = New Relation(Of Student, Teacher)(S, T) 08. StudentClassRelation = New Relation(Of Student, String)(S, "5A") 09. Relations = New Relation(Of Relation(Of Student, Teacher), Relation(Of Student, String)) (StudentTeacherRelation, StudentClassRelation) 10. 11. 'Relations.FirstObject.FirstObject 12. ' > Student "Pinco Pallino" 13. 'Relations.FirstObject.SecondObject 14. ' > Teacher "Mario Rossi" 15. 'Relations.SecondObject.FirstObject 16. ' > Student "Pinco Pallino" 17. 'Relations.SecondObject.SecondObject 18. ' > String "5A"

Alc une regole per l'uso dei Generic s


Si pu sempr e assegnar e Nothing a una var iabile di tipo gener ics. Nel caso il tipo gener ics collegato sia r efer ence, alla var iabile ver r assegnato nor malmente Nothing; in caso contr ar io, essa assumer il valor e di default per il tipo; Non si pu er editar e da un tipo gener ic aper to: 1. Class Example(Of T) 2. Inherits T 3. ' SBAGLIATO 4. End Class Tuttavia si pu er editar e da una classe gener ics specificando come tipo gener ics collegato lo stesso tipo aper to: 1. Class Example(Of T) 2. Inherits List(Of T) 3. ' CORRETTO 4. End Class Allo stesso modo, non si pu implementar e T come se fosse un'inter faccia: 1. Class Example(Of T) 2. Implements T 3. ' SBAGLIATO 4. End Class Ma si pu implementar e un'inter faccia gener ics di tipo T: 1. Class Example(Of T) 2. Implements IEnumerable(Of T) 3. ' CORRETTO 4.

End Class Entit con lo stesso nome ma con gener ics aper ti differ enti sono consider ate in over load. Per tanto, lecito scr iver e: 1. 2. 3. 4. 5. 6. 7. Sub Example(Of T)(ByVal A As T) '... End Sub Sub Example(Of T1, T2)(ByVal A As T1) '... End Sub

A42. I Generics - Parte II


Interfac c e Generic s
Pr oviamo or a a scr iver e qualche inter faccia gener ics per veder ne il compor tamento. Ripr endiamo l'inter faccia ICompar er , che indica qualcosa con il compito di compar ar e oggetti: esiste anche la sua cor r ispettiva gener ics, ossia ICompar er (Of T). Non fa nessun differ enza il compor tamento di quest'ultima: l'unica cosa che cambia il tipo degli oggetti da compar ar e. 01. Module Module1 02. 'Questa classe implementa un comaparatore di oggetti Student 03. 'in base al loro anno di corso Class StudentByGradeComparer 04. Implements IComparer(Of Student) 05. 06. 07. 'Come potete osservare, in questo metodo non viene eseguito 08. 'nessun tipo di cast, poich l'interfaccia IComparer(Of T) 'prevede un metodo Compare a tipizzazione forte. Dato che 09. 'abbiamo specificato come tipo generic collegato Student, 10. 11. 'anche il tipo a cui IComparer si riferisce sar 12. 'Student. Possiamo accedere alle propriet di x e y 13. 'senza nessun late binding (per ulteriori informazioni, 14. 'vedere i capitoli sulla reflection) 15. Public Function Compare(ByVal x As Student, ByVal y As Student) As Integer Implements IComparer(Of Student).Compare 16. Return x.Grade.CompareTo(y.Grade) 17. End Function End Class 18. 19. Sub Main() 20. 'Crea un nuovo array di oggeti Student 21. Dim S(2) As Student 22. 23. 'Inizializza ogni oggetto 24. S(0) = New Student("Mario", "Rossi", New Date(1993, 2, 3), "Liceo Classico Ugo 25. Foscolo", 2) S(1) = New Student("Luigi", "Bianchi", New Date(1991, 6, 27), "Liceo Scientifico 26. Fermi", 4) S(2) = New Student("Carlo", "Verdi", New Date(1992, 5, 12), "ITIS Cardano", 1) 27. 28. 'Ordina l'array con il comparer specificato 29. Array.Sort(S, New StudentByGradeComparer()) 30. 31. 'Stampa il profilo di ogni studente: vedrete che essi sono 32. 'in effetti ordinati in base all'anno di corso 33. For Each St As Student In S 34. Console.WriteLine(St.Profile) 35. Next 36. Console.ReadKey() 37. End Sub 38. 39. 40. End Module

I V inc oli
I tipi gener ics sono molto utili, ma spesso sono un po' tr oppo... "gener ici" XD Faccio un esempio. Ammettiamo di aver e un metodo gener ics (Of T) che accetta due par ametr i A e B. Pr oviamo a scr iver e: 1. If A = B Then '... L'IDE ci comunica subito un er r or e: "Oper ator '=' is not definited for type 'T' and 'T'." In effetti, poich T pu esser e un

qualsiasi tipo, non possiamo neanche saper e se questo tipo implementi l'oper ator e uguale =. In questo caso, vogliamo impor r e come condizione, ossia come v inco lo , che, per usar e il metodo in questione, il tipo gener ic collegato debba obbligator iamente espor r e un modo per saper e se due oggetti di quel tipo sono uguali. Come si r ende in codice? Se fate mente locale sulle inter facce, r icor der ete che una classe r appr esenta un concetto con deter minate car atter istiche se implementa deter minate inter facce. Dovr emo, quindi, tr ovar e un'inter faccia che r appr esenta l'"eguagliabilit": l'inter faccia in questione IEquatable(Of T). Per poter saper e se due oggetti T sono uguali, quindi, T dovr esser e un qualsiasi tipo che implementa IEquatable(Of T). Ecco che dobbiamo impor r e un vincolo al tipo. Esistono cinque categor ie di vincoli: Vincolo di inter faccia; Vincolo di er editar iet; Vincolo di classe; Vincolo di str uttur a; Vincolo New . Iniziamo con l'analizzar e il pr imo di cui abbiamo par lato.

V inc olo di Interfac c ia


Il vincolo di inter faccia indubbiamente uno dei pi utili e usati accanto a quello di er editar iet. Esso impone che il tipo gener ic collegato implementi l'inter faccia specificata. Dato che dopo l'imposizione del vincolo sappiamo per ipotesi che il tipo T espor r sicur amente tutti i membr i di quell'inter faccia, possiamo r ichiamar e tali membr i da tutte le var iabili di tipo T. La sintassi molto semplice: 1. (Of T As [Interfaccia]) Ecco un esempio: 001. Module Module1 002. 'Questa classe rappresenta una collezione di 003. 'elementi che possono essere comparati. Per questo 004. 'motivo, il tipo T espone un vincolo di interfaccia 005. 'che obbliga tutti i tipi generics collegati ad 006. 007. 'implementare tale interfaccia. 'Notate bene che in questo caso particolare ho usato 008. 'un generics doppio, poich il vincolo non 009. 010. 'si riferisce a IComparable, ma a IComparable(Of T). 'D'altra parte, abbastanza ovvio che se 011. 012. 'una collezione contiene un solo tipo di dato, 013. 'baster che la comparazione sia possibile 014. 'solo attraverso oggetti di quel tipo 015. Class ComparableCollection(Of T As IComparable(Of T)) 016. 'Ereditiamo direttamente da List(Of T), acquisendone 017. 'automaticamente tutti i membri base e le caratteristiche. 018. 'In questo modo, godremo di due grandi vantaggi: ' - non dovremo definire tutti i metodi per aggiungere, 019. 020. ' rimuovere o cercare elementi, in quanto vengono tutti 021. ' ereditati dalla classe base List; 022. ' - non dovremo neanche implementare l'interfaccia ' IEnumerable(Of T), poich la classe base la 023. ' implementa di per s. 024. 025. Inherits List(Of T) 026. 'Dato che gli oggetti contenuti in oggetti di 027. 'questo tipo sono per certo comparabili, possiamo 028. 'trovarne il massimo ed il minimo. 029. 030. 'Trova il massimo elemento 031. Public ReadOnly Property Max() As T 032. Get 033. 034.

035. 036. 037. 038. 039. 040. 041. 042. 043. 044. 045. 046. 047. 048. 049. 050. 051. 052. 053. 054. 055. 056. 057. 058. 059. 060. 061. 062. 063. 064. 065. 066. 067. 068. 069. 070. 071. 072. 073. 074. 075. 076. 077. 078. 079. 080. 081. 082. 083. 084. 085. 086. 087. 088. 089. 090. 091. 092. 093. 094. 095. 096. 097. 098. 099. 100. 101. 102. 103. 104. 105.

If Me.Count > 0 Then Dim Result As T = Me(0) For Each Element As T In Me 'Ricordate che A.CompareTo(B) restituisce '1 se A > B If Element.CompareTo(Result) = 1 Then Result = Element End If Next Else Return Result

Return Nothing End If End Get End Property 'Trova il minimo elemento Public ReadOnly Property Min() As T Get If Me.Count > 0 Then Dim Result As T = Me(0) For Each Element As T In Me If Element.CompareTo(Result) = -1 Then Result = Element End If Next Else Return Result

Return Nothing End If End Get End Property 'Trova tutti gli elementi uguali ad A e ne restituisce 'gli indici Public Function FindEquals(ByVal A As T) As Int32() Dim Result As New List(Of Int32) For I As Int32 = 0 To Me.Count - 1 If Me(I).CompareTo(A) = 0 Then Result.Add(I) End If Next 'Converte la lista di interi in un array di interi 'con gli stessi elementi Return Result.ToArray() End Function End Class Sub Main() 'Tre collezioni, una di interi, una di stringhe e 'una di date Dim A As New ComparableCollection(Of Int32) Dim B As New ComparableCollection(Of String) Dim C As New ComparableCollection(Of Date) A.AddRange(New Int32() {4, 19, 6, 90, 57, 46, 4, 56, 4}) B.AddRange(New String() {"acca", "casa", "zen", "rullo", "casa"}) C.AddRange(New Date() {New Date(2008, 1, 1), New Date(1999, 12, 31), New Date(2100, 4, 12)}) Console.WriteLine(A.Min()) ' > 4 Console.WriteLine(A.Max()) ' > 90 Console.WriteLine(B.Min())

' > acca 106. Console.WriteLine(B.Max()) 107. ' > zen 108. Console.WriteLine(C.Min().ToShortDateString) 109. ' > 31/12/1999 110. Console.WriteLine(C.Max().ToShortDateString) 111. ' > 12/4/2100 112. 113. 'Trova la posizione degli elementi uguali a 4 114. Dim AEqs() As Int32 = A.FindEquals(4) 115. ' > 0 6 8 116. Dim BEqs() As Int32 = B.FindEquals("casa") 117. ' > 1 4 118. 119. Console.ReadKey() 120. End Sub 121. 122. End Module

V inc olo di ereditariet


Ha la stessa sintassi del vincolo di inter faccia, con la sola differ enza che al posto dell'inter faccia si specifica la classe dalla quale il tipo gener ics collegato deve er editar e. I vantaggi sono pr aticamente uguali a quelli offer ti dal vincolo di inter faccia: possiamo tr attar e T come se fosse un oggetto di tipo [Classe] (una classe qualsiasi) ed utilizzar ne i membr i, poich tutti i tipi possibili per T sicur amente der ivano da [Classe]. Un esempio anche per questo vincolo mi sembr a abbastanza r idondante, ma c' una caso par ticolar e che mi piacer ebbe sottolinear e. Mi r ifer isco al caso in cui al posto della classe base viene specificato un altr o tipo gener ic (aper to), e di questo, data la non immediatezza di compr ensione, posso dar e un veloce esempio: 1. Class IsARelation(Of T, U As T) 2. Public Base As T 3. Public Derived As U 4. End Class Questa classe r appr esenta una r elazione is-a (" un"), quella famosa r elazione che avevo intr odotto come esempio una quar antina di capitoli fa dur ante i pr imi par agr afi di spiegazione. Questa r elazione r appr esentata par ticolar mente bene, dicevo, se si pr ende una classe base e la sua classe der ivata. I tipi gener ics aper ti non fanno altr o che astr ar r e questo concetto: T un tipo qualsiasi e U un qualsiasi altr o tipo der ivato da T o uguale T (non c' un modo per impor r e che sia solo der ivato e non lo stesso tipo). Ad esempio, potr ebbe esser e valido un oggetto del gener e: 1. 2. 3. 4. Dim P As Person Dim S As Student '... Dim A As New IsARelation(Of Person, Student)(P, S)

V inc oli di c lasse e struttura


Il vincolo di classe impone che il tipo gener ics collegato sia un tipo r efer ence, mentr e il vincolo di str uttur a impone che sia un tipo value. Le sintassi sono le seguenti: 1. (Of T As Class) 2. (Of T As Structure) Questi due vincoli non sono molto usati, a dir e il ver o, e la lor o utilit non cos mar cata e lampante come appar e per i pr imi due vincoli analizzati. Cer to, possiamo evitar e alcuni compor tamenti str ani dovuti ai tipi r efer ence, o sfr uttar e alcune car atter istiche dei tipi value, ma nulla di pi. Ecco un esempio dei possibili vantaggi:

Vincolo di classe: Possiamo assegnar e Nothing con la sicur ezza di distr ugger e l'oggetto e non di cambiar ne semplicemente il valor e in 0 (o in quello di default per un tipo non numer ico); Possiamo usar e con sicur ezza gli oper ator i Is, IsNot, TypeOf e Dir ectCast che funzionano solo con i tipi r efer ence; Vincolo di str uttur a: Possiamo usar e l'oper ator e = per compar ar e due valor i sulla base di quello che contengono e non di quello che "sono"; Possiamo evitar e gli inconvenienti dell'assegnamento dovuti ai tipi r efer ence. User il vincolo di classe in un esempio molto significativo, ma solo quando intr odur r la Reflection, quindi fatevi un aster isco su questo capitolo.

V inc olo New


Questo vincolo impone al tipo gener ic collegato di espor r e almeno un costr uttor e senza par ametr i. Par ticolar mente utile quando si devono inizializzar e dei valor i gener ics: 01. Module Module1 02. 03. 'Con molta fantasia, il vincolo New si dichiara postponendo 04. '"As New" al tipo generic aperto. 05. Function CreateArray(Of T As New)(ByVal Count As Int32) As T() 06. Dim Result(Count - 1) As T 07. 08. For I As Int32 = 0 To Count - 1 09. 'Possiamo usare il costruttore perch il 10. 'vincolo ce lo assicura 11. Result(I) = New T() 12. Next 13. 14. Return Result 15. End Function 16. 17. Sub Main() 18. 'Crea 10 flussi di dati in memoria. Non abbiamo 19. 'mai usato questa classe perch rientra in 20. 'un argomento che tratter pi avanti, ma ' una classe particolarmente utile e versatile 21. 22. 'che trova applicazioni in molte situazioni. 23. 'Avere un bel metodo generics che ne crea 10 in una 'volta una gran comodit. 24. 'Ovviamente possiamo fare la stessa cosa con tutti 25. 26. 'i tipi che espongono almeno un New senza parametri Dim Streams As IO.MemoryStream() = CreateArray(Of IO.MemoryStream)(10) 27. 28. '... 29. End Sub 30. 31. 32. End Module

V inc oli multipli


Un tipo gener ic aper to pu esser e sottoposto a pi di un vincolo, ossia ad un vincolo multiplo, che altr o non se non la combinazione di due o pi vincoli semplici di quelli appena visti. La sintassi di un vincolo multiplo legger mente diver sa e pr evede che tutti i vincoli siano r aggr uppati in una copia di par entesi gr affe e separ ati da vir gole: 1. (Of T As {Vincolo1, Vincolo2, ...})

Ecco un esempio: 01. Module Module1 02. 03. 'Classe che filtra dati di qualsiasi natura Class DataFilter(Of T) 04. Delegate Function FilterData(ByVal Data As T) As Boolean 05. 06. 07. 'La signature chilometrica fatta apposta per 08. 'farvi impazzire XD Vediamo le parti una per una: ' - TSerach: deve essere un tipo uguale a T o derivato 09. 10. ' da T, in quanto stiamo elaborando elementi di tipo T; 11. ' inoltre deve anche essere clonabile, poich 12. ' salveremo solo una copia dei valor trovati. 13. ' Questo implica che TSearch sia un tipo reference, e che ' quindi lo sia anche T: questa complicazione solo 14. 15. ' per mostrare dei vincoli multipli e potete anche 16. ' rimuoverla se vi pare; 17. ' - TList: deve essere un tipo reference, esporre un 18. ' costruttore senza parametri ed implementare ' l'interfaccia IList(Of TSearch), ossia deve 19. ' essere una lista; 20. 21. ' - ResultList: lista in cui riporre i risultati (passata ' per indirizzo); 22. ' - Filter: delegate che punta alla funzione usata per 23. ' selezionare i valori; 24. ' - Data: paramarray contenente i valori da filtrare. 25. Sub Filter(Of TSearch As {ICloneable, T}, TList As {IList(Of TSearch), New, Class}) _ 26. (ByRef ResultList As TList, ByVal Filter As FilterData, ByVal ParamArray Data() As 27. TSearch) 28. 'Se la lista Nothing, la inizializza. 29. 'Notare che non avremmo potuto compararla a Nothing 30. 'senza il vincolo Class, n inizializzarla 31. 'senza il vincolo New 32. If ResultList Is Nothing Then 33. ResultList = New TList() 34. End If 35. 36. 'Itera sugli elementi di data 37. For Each Element As TSearch In Data 38. 'E aggiunge una copia di quelli che 39. 'soddisfano la condizione 40. If Filter.Invoke(Element) Then 41. 'Aggiunge una copia dell'elemento alla lista. 42. 'Anche in questo non avremmo potuto richiamare 43. 'Add senza il vincolo interfaccia su IList, n 44. 'clonare Element senza il vincolo interfaccia ICloneable 45. ResultList.Add(Element.Clone()) 46. End If 47. Next 48. End Sub 49. End Class 50. 51. 'Controlla se la stringa A palindroma 52. Function IsPalindrome(ByVal A As String) As Boolean 53. Dim Result As Boolean = True 54. 55. For I As Int32 = 0 To (A.Length / 2) - 1 56. If A.Chars(I) <> A.Chars(A.Length - 1 - I) Then 57. Result = False 58. Exit For 59. End If 60. Next 61. 62. Return Result 63. End Function 64. 65. Sub Main() 66. Dim DF As New DataFilter(Of String) 67. 68. 'Lista di stringhe: notare che la variabile non 69. 'contiene nessun oggetto perch non abbiamo usato New. 70.

'Serve per mostrare che verr inizializzata 71. 'da DF.Filter. 72. Dim L As List(Of String) 73. 74. 'Analizza le stringhe passate, trova quelle palindrome 75. 'e le pone in L 76. DF.Filter(L, AddressOf IsPalindrome, _ 77. "casa", "pane", "anna", "banana", "tenet", "radar") 78. 79. For Each R As String In L 80. Console.WriteLine(R) 81. Next 82. 83. Console.ReadKey() 84. End Sub 85. 86. End Module

A43. I tipi Nullable


I tipi Nullable costituiscono una utile applicazione dei gener ics alla gestione dei database. Infatti, quando si lavor a con dei database, capita molto spesso di tr ovar e alcune celle vuote, ossia il cui valor e non stato impostato. In questo caso, l'oggetto che media tr a il database e il pr ogr amma - oggetto che analizzer emo solo nella sezione C - pone in tali celle uno speciale valor e che significa "non contiene nulla". Questo valor e par i a DBNull.Value, una costante statica pr eimpostata di tipo DBNull, appunto. Essendo un tipo r efer ence, l'assegnar e il valor e di una cella a una var iabile value pu compor tar e er r or i nel caso tale cella contenga il famiger ato DBNull, poich non si in gr ado di effettuar e una conver sione. Compor tamenti del gener e costr ingono (anzi, costr ingevano) i pr ogr ammator i a scr iver e una quantit eccessiva di costr utti di contr ollo del tipo: 01. If Cell.Value IsNot DBNull.Value Then 02. Variable = Cell.Value 03. Else Variable = 0 04. 'Per impostare il valore di default, bisognava ripetere 05. 'questi If tante volte quanti erano i tipi in gioco, poich 06. 07. 'non c'era modo di assegnare un valore Null a tutti 'in un solo colpo 08. 09. End If Tuttavia, con l'avvento dei gener ics, nella ver sione 2005 del linguaggio, questi pr oblemi sono stati ar ginati, almeno in par te e almeno per chi conosce i tipi nullable. Questi speciali tipi sono str uttur e gener ics che possono anche accettar e valor i r efer ence come Nothing: ovviamente, dato che i pr oblemi insor gono solo quando si tr atta di tipi value, i tipi gener ics collegati che lecito specificar e quando si usa nullable devono esser e tipi value (quindi c' un vincolo di str uttur a). Ci sono due sintassi molto diver se per dichiar ar e tipi nullable, una esplicita e una implicita: 1. 2. 3. 4. 5. 'Dichiarazione esplicita: Dim [Nome] As Nullable(Of [Tipo]) 'Dichiarazione implicita: Dim [Nome] As [Tipo]?

La seconda si attua postponendo un punto inter r ogativo al nome del tipo: una sintassi molto br eve e concisa che tuttavia pu anche sfuggir e facilmente all'occhio. Una volta dichiar ata, una var iabile nullable pu esser e usata come una comunissima var iabile del tipo gener ic collegato specificato. Essa, tuttavia, espone alcuni membr i in pi r ispetto ai nor mali tipi value, nella fattispecie: HasValue : pr opr iet r eadonly che r estituisce Tr ue se l'oggetto contiene un valor e; Value : pr opr iet r eadonly che r estituisce il valor e dell'oggetto, nel caso esista; GetValueOr Default() : funzione che r estituisce Value se l'oggetto contiene un valor e, altr imenti il valor e di default per quel tipo (ad esempio 0 per i tipi numer ici). Ha un over load che accetta un par ametr o GetValur Or Default(X): in questo caso, se l'oggetto non contiene nulla, viene r estituito X al posto del valor e di default. Ecco un esempio: 01. Module Module1 02. 03. Sub Main() 04. 'Tre variabili di tipo value dichiarate come 05. 'nullable nei due modi diversi consentiti 06. Dim Number As Integer? 07. Dim Data As Nullable(Of Date) 08.

Dim Cost As Double? 09. Dim Sent As Nullable(Of Boolean) 10. 11. 'Ammettiamo di star controllando un database: 12. 'questo array di oggetti rappresenta il contenuto 13. 'di una riga 14. Dim RowValues() As Object = {DBNull.Value, New Date(2009, 7, 1), 67.99, DBNull.Value} 15. 16. 'Con un solo ciclo trasforma tutti i DBNull.Value 17. 'in Nothing, poich i nullable supportano solo 18. 'Nothing come valore nullo 19. For I As Int16 = 0 To RowValues.Length - 1 20. If RowValues(I) Is DBNull.Value Then 21. RowValues(I) = Nothing 22. End If 23. Next 24. 25. 'Assegna alle variabili i valori contenuti nell'array: 26. 'non ci sono mai problemi in questo codice, poich, 'trattandosi di tipi nullable, questi oggetti possono 27. 28. 'accettare anche valori Nothing. In questo esempio, 29. 'Number e Sent riceveranno un Nothing come valore: la 30. 'loro propriet HasValue varr False. 31. Number = RowValues(0) 32. Data = RowValues(1) 33. Cost = RowValues(2) Sent = RowValues(3) 34. 35. 'Scrive a schermo il valore di ogni variabile, se ne 36. 'contiene uno, oppure il valore di default se non 37. 'contiene alcun valore. 38. Console.WriteLine("{0} {1} {2} {3}", _ 39. Number.GetValueOrDefault, _ 40. Data.GetValueOrDefault, _ 41. Cost.GetValueOrDefault, _ 42. Sent.GetValueOrDefault) 43. 44. 'Provando a stampare una variabile nullable priva 45. 'di valore senza usare la funzione GetValueOrDefault, 46. 'semplicemente non stamperete niente: 47. ' Console.WriteLine(Number) 48. 'Non stampa niente e va a capo. 49. 50. Console.ReadKey() 51. End Sub 52. 53. 54. End Module

Logic a booleana a tre valori


Un valor e nullable Boolean pu assumer e vir tualmente tr e valor i: ver o (Tr ue), falso (False) e null (senza valor e). Usando una var iabile booleana nullable come oper ando per gli oper ator i logici, si otter r anno r isultati diver si a seconda che essa abbia o non abbia un valor e. Le nuove combinazioni che possono esser e eseguite si vanno ad aggiunger e a quelle gi esistenti per cr ear e un nuovo tipo di logica elementar e, detta, appunto, "logica booleana a tr e valor i". Essa segue questo schema nei casi in cui un oper ando sia null:

Valor e 1 Tr ue False Tr ue False

Oper ator e And And Or Or

Valor e 2 Null Null Null Null

Risultato Null False Tr ue Null

Tr ue False

Xor Xor

Null Null

Null Null

A44. La Reflection - Parte I


Con il ter mine gener ale di r eflection si intendono tutte le classi del Fr amew or k che per mettono di acceder e o manipolar e assembly e moduli.

A ssem bly L'assembly l'unit logica pi piccola su cui si basa il Fr amew or k .NET. Un assembly altr o non che un pr ogr amma o una libr er ia di classi (compilati in .NET). Il Fr amew or k stesso composto da una tr entina di assembly pr incipali che costituiscono le libr er ie di classi pi impor tanti per System.Dr aw ing.dll, System.Cor e.dll, ecceter a...). la pr ogr ammazione .NET (ad esempio System.dll,

Il ter mine Reflection ha un significato molto pr egnante: la sua tr aduzione in italiano alquanto lampante e significa "r iflessione". Dato che viene usata per ispezionar e, analizzar e e contr ollar e il contenuto di assembly, r isulta evidente che mediante r eflection noi scr iviamo del codice che analizza altr o codice, anche se compilato: una specie di our obor os, il ser pente che si mor de la coda; una r iflessione della pr ogr ammazione su se stessa, appunto. Lasciando da par te questo inter cor so filosofico, c' da dir e che la r eflection di gr an lunga una delle tecniche pi utilizzate dall'IDE e dal Fr amew or k stesso, anche se spesso questi meccanismi si svolgono "dietr o le quinte" e vengono mascher ati per non far li appar ir e evidenti. Alcuni esempi sono la ser ializzazione, di cui mi occuper in seguito, ed il late binding.

Late Binding L'azione del legar e (in inglese, appunto, "bind") un identificator e a un valor e viene detta binding: si esegue un binding, ad esempio, quando si assegna un nome a una var iabile. Questo consente un'astr azione fondamentale affinch il pr ogr ammator e possa compr ender e ci che sta scr itto nel codice: nessuno r iuscir ebbe a capir e alcunch se al posto dei nomi di var iabile ci fosser o degli indir izzi di memor ia a otto cifr e. Ebbene, esistono due tipi di binding: quello statico o "ear ly", e quello dinam ico o "late". Il pr imo viene effetuato pr ima che il pr ogr amma sia eseguito, ed quello che per mette al compilator e di tr adur r e in linguaggio inter medio le istr uzioni scr itte in for ma testuale dal pr ogr ammator e. Quando assegnamo un nome ad una var iabile, o r ichiamiamo un metodo da un oggetto stiamo attuando un ear ly binding: sappiamo che quell'identificator e logicamente legato a quel pr eciso valor e di quel pr eciso tipo e che, allo stesso modo, quel nome r ichiamer pr opr io quel metodo da quell'oggetto e, non, magar i, un metodo a caso disper so nella memor ia. Il secondo, al contr ar io, viene por tato a ter mine mentr e il pr ogr amma in esecuzione: ad esempio, r ichiamar e dei metodi d'istanza di una classe Per son da un oggetto Object un esempio di late binding, poich solo a r un-time, il nome del membr o ver r letto, ver ificato, e, in caso di successo, r ichiamato. Tuttavia, non esiste alcun legame tr a una var iabile Object e una di tipo Per son, se non che, a r untime, la pr ima potr contener e un valor e di tipo Per son, ma questo il compilator e non pu saper lo in anticipo (mentr e noi s).

Esiste un unico namespace dedicato inter amente alla r eflection e si chiama, appunto, System.Reflection. Una delle classi pi impor tanti in questo ambito, invece, System.Type. Quest'ultima una classe molto speciale, poich ne esistono molte istanze, ognuna unica, ma non possibile cr ear ne di nuove. Ogni istanza di Type r appr esenta un tipo: ad esempio, c' un oggetto Type per Str ing, uno per Per son, uno per Integer , e via dicendo. Risulta logico che non possiamo cr ear e un oggetto Type, per ch non sar ebbe associato ad alcun tipo e non avr ebbe motivo di esister e: possiamo, al contr ar io, ottener e un oggetto Type gi esistente.

I Contesti
Pr ima di iniziar e a veder e come analizzar e un assembly, dobbiamo fer mar ci un attimo a capir e come funziona il sistema oper ativo a livello un po' pi basso del nor male. Questo ci sar utile per sceglier e una modalit di accesso all'assembly coer ente con le nostr e necessit. Quasi ogni sistema oper ativo composto di pi str ati sovr apposti, ognuno dei quali ha il compito di gestir e una deter minata r isor sa dell'elabor ator e e di for nir e per essa un'astr azione, ossia una visione semplificata ed estesa. Il pr imo str ato il gestor e di pr ocessi (o ker nel), che ha lo scopo di coor dinar e ed isolar e i pr ogr ammi in esecuzione r acchiudendoli in ar ee di memor ia separ ate, i pr ocessi appunto. Un pr ocesso r appr esenta un "pr ogr amma in esecuzione" e non contiene solo il semplice codice eseguibile, ma, oltr e a questo, mantiene tutti i dati iner enti al funzionamento del pr ogr amma, ivi compr esi var iabili, collegamenti a r isor se ester ne, stato della CPU, ecceter a... Oltr e ad assegnar e un dato per iodo di tempo macchina ad ogni pr ocesso, il ker nel separ a le ar ee di memor ia r iser vate a ciascuno, r endendo impossibile per un pr ocesso modificar e i dati di un altr o pr ocesso, causando, in