+ All Categories
Home > Documents > Guida visual basic

Guida visual basic

Date post: 23-Jul-2015
Category:
Upload: bianchetto
View: 1,120 times
Download: 15 times
Share this document with a friend
519
Transcript
Page 1: Guida visual basic
Page 2: Guida visual basic

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 veloce panoramica sulla programmazioneLa pr ogr ammazione è quella disciplina dell'infor matica che si occupa di idear e, costr uir e e mantener e il softwar 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 softwar 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.

Alcuni termini da conoscereCodice sorg ente o sorg 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

Compilatore: il softwar 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 softwar e usato per l'analisi e la r isoluzione degli er r or i (bugs) all'inter no di un pr ogr amma;

Parole r iservate o keywords: 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 sviluppoL'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 icrosoft; 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 downloads), un pr ogr amma gr atis e

molto buono (tr over ete una r ecensione nella sezione Sofwtar 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.

Page 3: Guida visual basic

A2. Classi, Moduli e Namespace

Object Oriented ProgrammingI linguaggi .NET sono orientati 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 paradig ma 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'ereditar ietà, il polimorfismo e l'incapsulamento. 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 ClassiCome 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 og g etti, i quali vengono r appr esentati da classi. Una classe non è altr o che

la rappresentazione - ovv iamente astratta - di qualcosa di concreto, 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

passwor 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

esposte dalla classe: ognuna di esse, inoltr e, è r appr esentata da quello che viene chiamato membro. 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ì:

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 corpo; 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:

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

1.2.3.

Class [NomeClasse]...

End Class

1.2.3.

Class Triangolo...

End Class

Page 4: Guida visual basic

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

I ModuliNonostante 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 NamespacePossiamo definir e classi e moduli come unità funzionali: 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:

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;

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 TriangoliClass Scaleno'...End Class Class Isoscele'...End Class Class Equilatero'...End Class

End Namespace Namespace Quadrilateri

Namespace ParallelogrammiClass Parallelogramma'...End Class Namespace Rombi

Class Rombo'...End Class Class Quadrato'...End Class

End NamespaceEnd Namespace

End Namespace

Page 5: Guida visual basic

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

Page 6: Guida visual basic

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

Framework .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 amewor k si pr esente come str atificata in diver si livelli:

1. Sistema operativoIl Fr amewor k .NET pr esenta una str uttur a str atificata, alla base della quale r isiede il sistema oper ativo, Windows. Più

pr ecisamente, si consider a il sistema oper ativo e l'API (Application Pr ogr amming Inter face) di Windows, che espone

tutti i metodi r esi disponibili al pr ogr ammator e per svolger e un dato compito.

2. Common Language RuntimeUn 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 Managed o Safe ("Gestito" o "Sicur o"), poichè questo str ato del Fr amewor 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 LibraryLo str ato successivo è denominato Base Class Libr ar y (BCL): questa par te contiene tutti i tipi e le classi disponibili nel

Fr amewor 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. XMLSuccessivamente tr oviamo i dati, le r isor se. Per salvar e i dati viene usato quasi sempr e il for mato XML (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 Windows Pr esentation Foundation (WPF), intr odotta nella ver sione 3.5 del Fr amewor 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.

Page 7: Guida visual basic

5. Windows Forms e ASP.NETAl livello super ior e tr oviamo ASP.NET e Windows 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 Windows. La costr uzione di una Windows 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 ificationsIl penultimo stadio della str atificazione del Fr amewor 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 amewor k.

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

Versioni del FrameworkCon il passar e degli anni, a par tir e dal 2002, Micr osoft ha r ilasciato ver sioni successive del Fr amewor 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 amewor 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 amewor k

VB2003: si basa sulla ver sione 1.1 del Fr amewor k

VB2005: si basa sulla ver sione 2.0 del Fr amewor 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 amewor 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 amewor k

Page 8: Guida visual basic

A4. Utilizzo base dell'IDE

IDE? Me lo sono dimenticato a casa...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 softwar e che aiuta il pr ogr ammator e nella stesur a del codice. Il softwar 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 keywor 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 compilazione e linking), 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 compilatore .NETIl 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 amewor 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 amewor 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 ApplicationNei 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 console (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

Page 9: Guida visual basic

Sub Main() End SubEnd 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 keywor 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 finitoUna 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 softwar 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).

Page 10: Guida visual basic

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

Page 11: Guida visual basic

A5. Variabili e costanti

Le variabiliUna 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 identificatore (o, più r ar amente, mnemonico), 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 dichiara 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:

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 keywor ds Dim e As:

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

keywor 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 Short: 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 Long : 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;

Double: decimale da cir ca -1,79e+308 a +1,79e+308, con un inter vallo minimo di cir ca 4,9e-324;

Boolean: 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

01.02.03.04.05.06.07.08.09.

Module Module1Sub Main()

Dim Ciao As Int16Ciao = 78Ciao = Ciao + 2Console.WriteLine(Ciao)Console.Readkey()

End SubEnd Module

1. Dim [nome] As [tipo]

Page 12: Guida visual basic

un testo;

Object: r appr esenta un qualsiasi tipo (ma non è un tipo base).

I tipi base vengono detti anche atomici o pr im itiv i, poiché non possono esser e ulter ior mente scomposti. Esistono,

quindi, anche tipi der ivati, 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 keywor 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:

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 amewor 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.

1. Ciao = Ciao + 2

01.02.03.04.05.06.07.08.09.10.11.12.13.14.15.16.17.18.19.20.

Module Module1Sub Main()

'InteriDim Intero, Ese As Int16'DecimaleDim Decimale As Single'BooleanoDim Vero As Boolean'StringaDim Frase As String

Intero = 90Ese = Intero * 2 / 68Intero = Ese - Intero * InteroDecimale = 90.76Decimale = Ese / InteroVero = TrueFrase = "Ciao."'L'operatore "+" tra stringhe concatena due stringhe. Dopo la

Page 13: Guida visual basic

Esistono poi degli speciali oper ator i di assegnamento, che velocizzano l'assegnazione di valor i, alcuni sono:

Le fr asi poste dopo un apice (') sono dette commenti 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 costantiAbbiamo 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 reco, è 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:

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 keywor d Cons t al posto di Dim; la seconda consiste nell'assegnazione posta subito dopo la dichiar azione. Infatti,

una costante, per esser e tale, deve contener e qualcosa: per questo motivo è obblig ator 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:

21.22.23.24.25.26.27.28.29.

'prossima istruzione, la variabile Frase conterrà:' "Buon giornoCiao"Frase = "Buon giorno" + "Ciao"'L'operatore "&" può concatenare qualsiasi dato e'restituisce una stringa. Dopo la prossima istruzione, la'variabile Frase conterrà:' "Il valore decimale è: -0,0003705076"Frase = "Il valore decimale è: " & Decimale

End SubEnd Module

01.02.03.04.05.06.07.08.09.10.

Module Module1Sub Main()Dim V, B As Int32

V += B 'Equivale a V = V + BB -= V 'Equivale a B = B - VV *= B 'Equivale a V = V * BB /= V 'Equivale a B = B / V

End SubEnd Module

1. Const [nome] As [tipo] = [valore]

01.02.03.04.05.06.07.08.09.10.11.12.13.14.15.16.17.18.19.20.21.

Module Module1Sub Main()Const Pi As Single = 3.1415926535897932Dim Raggio, Area As Double

'Questa istruzione scrive sul monitor il messaggio posto tra'virgolette nelle parentesiConsole.WriteLine("Inserire il raggio di un cerchio:") 'Questa istruzione leggè un valore immesso dalla tastiera e'lo deposita nella variabile RaggioRaggio = Console.ReadLineArea = Raggio * Raggio * Pi

Console.WriteLine("L'Area è: " & Area) 'Questa istruzione ferma il programma in attesa della pressione'di un pulsanteConsole.ReadKey()

End SubEnd Module

Page 14: Guida visual basic

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:

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 è

Le istruzioniTutti 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 procedure o funzioni, in sostanza sottopr ogr ammi già

scr itti. Pr ocedur e e funzioni possono esser e globalmente indicate con la par ola metodo. I metodi accettano dei

parametr 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:

Anche i metodi ver r anno tr attai successivamente in dettaglio.

1. Const Sqrt2 As Single = Math.Sqrt(2)

1. Const Sqrt2 As Single = 1.4142135

01.02.03.04.05.06.07.08.09.10.11.12.

Module Module1Sub Main()

Dim F As Double

'Questa è una funzione che restituisce la radice quadrata di 56F = Math.Sqrt(56)

'Questa è una procedura che scrive a video un messaggioConsole.WriteLine("La radice di 56 è " & F)Console.ReadKey()

End SubEnd Module

Page 15: Guida visual basic

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 ticolo sull'utilizzo della memor ia da par te di un

pr ogr amma.

Differenza tra Classi e OggettiAll'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

og 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 tanziare un oggetto significa cr ear lo.

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 ReferenceOgni cosa nel Fr amew or k è un oggetto e la maggior par te di essi sono tipi r efer ence. Si dicono tipi reference 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 amewor 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 manag 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 arbag e collection ("r accolta dei r ifiuti"). Tuttavia, ciò non avviene subito, poichè il

meccanismo del Fr amewor k fa in modo di avviar e la gar bage collection solo quando è necessar io, quindi quando la

1.2.3.

'New serve per creare fisicamente degli oggetti in memoriaDim O1 As New ObjectDim O2 As New Object

Page 16: Guida visual basic

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 log icamente distrutti, 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

finalizzazione non deterministica (il ter mine "finalizzazione" non è casule: veder e il capitolo sui distr uttor i per

maggior i infor mazioni).

NothingNothing è 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.

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 ValueOgni tipo value 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:

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 sono lo stesso

og 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:

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

1.2.

Dim O As New Object 'L'oggetto viene creatoO = Nothing 'L'oggetto viene logicamente distrutto

1.2.3.

Dim O1, O2 As Object'...O1 = O2

1.2.3.

'Scrive a schermo se è vero oppure no che'O1 e O2 sono lo stesso oggettoConsole.WriteLine(O1 Is O2)

Page 17: Guida visual basic

effettivamente una copia del valor e. Non è possibile utilizzar e Is con i tipi value poichè Is è definito solo per i

r efer ence.

Boxing e UnboxingConsider iamo il seguente codice:

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 boxing . 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 riferimento

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?").

1.2.3.

Dim I As Int32 = 50Dim O As ObjectO = I

Page 18: Guida visual basic

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 costrutto di controllo, 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:

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:

If nidificatiQuando 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 Costrutto If Nidificato.

Questo avviene abbastanza spesso, specie se si ha bisogno di far e contr olli multipli:

01.02.03.04.05.06.07.08.09.10.11.12.13.14.15.16.

Module Module1Sub Main()

Dim Lato As Single

Console.WriteLine("Inserire il lato di un quadrato:")Lato = Console.ReadLine

If Lato < 0 Then 'Se Lato è minore di 0...

Console.WriteLine("Il lato non può avere una misura negativa!")Else 'Altrimenti, se non lo è...

Console.WriteLine("L'area del quadrato è: " & Lato * Lato)End If 'Fine controllo

Console.ReadKey()

End SubEnd Module

1.2.3.4.5.

If [Condizione] Then[istruzioni]

Else[istruzioni alternative]

End If

01.02.03.04.05.

Module Module1Sub Main()

Dim Numero As Int16

Page 19: Guida visual basic

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 connettiv i log ici.

I connettivi logic iI 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 contemporaneamente

If A Or B : la condizione r isulta ver ificata se è ver a almeno una delle due condizioni

If A Xor B: la condizione r isulta ver a se una sola delle due condizioni è ver a

If Not A: la condizione r isulta ver ificata se è falsa

Un esempio pr atico:

Continuare il controllo: ElseIfNei 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

non 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:

06.07.08.09.10.11.12.13.14.15.16.17.18.

Console.WriteLine("Inserisci un numero:")Numero = Console.ReadLine

If Numero > 0 Then

If Numero < 5 ThenConsole.WriteLine("Hai indovnato il numero!")

End IfElse

Console.WriteLine("Numero errato!")End If

Console.ReadKey()

End SubEnd Module

01.02.03.04.05.06.07.08.09.10.11.12.13.14.15.16.17.

Module Module1Sub Main()

Dim a, b As Double

Console.WriteLine("Inserire i lati di un rettangolo:")a = Console.ReadLineb = Console.ReadLine

'Se tutti e due i lati sono maggiori di 0If a > 0 And b > 0 Then

Console.WriteLine("L'area è: " & a * b)Else

Console.WriteLine("Non esistono lati con misure negative!")End If

Console.Readkey()End Sub

End Module

01.02.03.04.05.06.

Module Module1Sub Main()

Dim Voto As Single

Console.WriteLine("Inserisci il tuo voto:")

Page 20: Guida visual basic

E' abbastanza disor dinato... La var iante ElseIf è molto utile per miglior e la leggibilità del codice:

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.

Blocchi di istruzioniFino 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:

07.08.09.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.

Voto = Console.ReadLine

If Voto < 3 ThenConsole.WriteLine("Sei senza speranze!")

ElseIf Voto < 5 Then

Console.WriteLine("Ancora un piccolo sforzo...")Else

If Voto < 7 ThenConsole.WriteLine("Stai andando discretamente")

ElseIf Voto < 9 Then

Console.WriteLine("Molto bene, continua così")Else

Console.WriteLine("Sei praticamente perfetto!")End If

End IfEnd If

End If

Console.ReadKey()End Sub

End Module

01.02.03.04.05.06.07.08.09.10.11.12.13.14.15.16.17.18.19.20.21.22.

Module Module1Sub Main()

Dim Voto As Single

Console.WriteLine("Inserisci il tuo voto:")Voto = Console.ReadLine

If Voto < 3 Then

Console.WriteLine("Sei senza speranze!")ElseIf Voto < 5 Then

Console.WriteLine("Ancora un piccolo sforzo...")ElseIf Voto < 7 Then

Console.WriteLine("Stai andando discretamente")ElseIf Voto < 9 Then

Console.WriteLine("Molto bene, continua così")Else

Console.WriteLine("Sei praticamente perfetto!")End If

Console.ReadKey()

End SubEnd Module

1.2.3.

[Keyword]'Blocco di codice

End [Keyword]

Page 21: Guida visual basic

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 non è 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:

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:

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.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.

Module Module1Sub Main()

'a, b e c fanno parte del blocco delimitato da Sub ...'End SubDim a, b, c As Single

'Semplice esempio di risoluzione di equazione di'secondo gradoConsole.WriteLine("Equazione: ax2 + bx + c = 0")Console.WriteLine("Inserisci, in ordine, a, b e c:")a = Console.ReadLineb = Console.ReadLinec = Console.ReadLine

If a = 0 Then

Console.WriteLine("L'equazione si abbassa di grado")Console.ReadKey()'Con Exit Sub si esce dalla Sub, che in questo caso'coincide con il programma. Equivale a terminare'il programma stessoExit Sub

End If

'Anche delta fa parte del blocco delimitato da Sub ...'End SubDim delta As Single = b ^ 2 - 4 * a * c

'Esistono due soluzioni distinteIf delta > 0 Then

'Queste variabili fanno parte del blocco di If ...'ElseIfDim x1, x2 As Single'È possibile accedere senza problemi alla variabile'delta, poiché questo blocco è a sua volta'all'interno del blocco in cui è dichiarato deltax1 = (-b + Math.Sqrt(delta)) / (2 * a)x2 = (-b - Math.Sqrt(delta)) / (2 * a)Console.WriteLine("Soluzioni: ")Console.WriteLine("x1 = " & x1)Console.WriteLine("x2 = " & x2)

'Esiste una soluzione doppiaElseIf delta = 0 Then

'Questa variabile fa parte del blocco ElseIf ... ElseDim x As Singlex = -b / (2 * a)Console.WriteLine("Soluzione doppia: ")Console.WriteLine("x = " & x)

'Non esistono soluzioni in RElse

Console.WriteLine("Non esistono soluzioni in R")End If

Console.ReadKey()

End SubEnd Module

Page 22: Guida visual basic

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.

Page 23: Guida visual basic

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:

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:

Questo tipo di contr ollo r ende molto più linear e, semplice e veloce il codice sor gente. Un esempio:

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:

01.02.03.04.05.06.07.08.09.10.11.

'...If A = 1 Then'istruzioni

End IfIf A = 2 Then'istruzioni

End IfIf A = 3 Then'istruzioni

End If'eccetera

01.02.03.04.05.06.07.08.09.

'...Select Case [Nome variabile da analizzare]Case [valore1]'istruzioni

Case [valore2]'istruzioni

Case [valore3]'istruzioni

End Select

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.

Module Module 1Sub Main()Dim a, b As Double

Dim C As Byte

Console.WriteLine("Inserire due numeri: ")a = Console.ReadLineb = Console.ReadLineConsole.WriteLine("Inserire 1 per calcolare la somma, 2 per la differenza, 3 per il

prodotto, 4 per il quoziente:")C = Console.ReadLine

Select Case C

Case 1Console.WriteLine(a + b)

Case 2Console.WriteLine(a - b)

Case 3Console.WriteLine(a * b)

Case 4Console.WriteLine(a / b)

End Select

Console.ReadKey()End Sub

End Module

1.

Page 24: Guida visual basic

Varianti del costruttoAnche 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 irg ola

La vir gola per mette di definir e non solo uno, ma molti valor i possibili in un solo Case. Ad esempio:

Il codice sopr a pr oposto con Select equivale ad un If scr itto come segue:

Uso di To

Al contr ar io, la keyw or d To per mette di definir e un range 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.

Questo cor r isponde ad un If scr itto come segue:

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.

2.3.4.5.6.7.8.

Dim S As String'...Select Case S

Case "ciao"'...

Case "buongiorno"'...

End Select

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 solo se A'contiene un valore pari a 1, 2 o 3

Case 4, 6, 9'Questo codice viene eseguito solo se A'contiene un valore pari a 4, 6 o 9

End Select

1.2.3.4.5.

If A = 1 Or A = 2 Or A = 3 Then'...

ElseIf A = 4 Or A = 6 Or A = 9 Then'...

End If

1.2.3.4.5.6.7.8.

Select Case ACase 67 To 90

'Questo codice viene eseguito solo se A'contiene un valore compreso tra 67 e 90 (estremi inclusi)

Case 91 To 191'Questo codice viene eseguito solo se A'contiene un valore compreso tra 91 e 191

End Select

1.2.3.4.5.

If A >= 67 And A <= 90 Then'...

ElseIf A >= 91 And A <= 191 Then'...

End If

01.

Page 25: Guida visual basic

Il suo equivalente 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:

Uso delle precedenti alternative in com 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:

02.03.04.05.06.07.08.09.10.11.

Select Case ACase Is >= 6

'Questo codice viene eseguito solo se A'contiene un valore maggiore o uguale di 6

Case Is > 1'Questo codice viene eseguito solo se A'contiene un valore maggiore di 1 (e minore di 6,'dato che, se si è arrivati a questo Case,'significa che la condizione del Case precedente non'è stata soddisfatta)

End Select

1.2.3.4.5.

If A >= 6 Then'...

ElseIf A > 1 Then'...

End If

01.02.03.04.05.06.07.08.09.10.11.12.

Select Case ACase 1, 4

'Questo codice viene eseguito solo se A'contiene 1 o 4

Case 9 To 12'Questo codice viene eseguito solo se A'contiene un valore compreso tra 9 e 12

Case Else'Questo codice viene eseguito solo se A'contiene un valore minore di 9 o maggiore di 12,'ma diverso da 1 e 4

End Select

1.2.3.4.5.6.7.8.

Select Case ACase 7, 9, 10 To 15, Is >= 90

'Questo codice viene eseguito solo se A'contiene 7 o 9 o un valore compreso tra 10 e 15'oppure un valore maggiore o uguale di 90

Case Else'...

End Select

Page 26: Guida visual basic

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:

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:

Le altr e ver sioni del costr utto, invece, sono le seguenti:

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:

1.2.3.

Do'istruzioni

Loop

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.32.

Module Module1

Sub Main()Dim a, b As Single

Do

'Pulisce lo schermoConsole.Clear()'L'underscore serve per andare a capo nel codiceConsole.WriteLine("Inserire le misure di base e altezza " & _

"di un rettangolo:")a = Console.ReadLineb = Console.ReadLine

'Controlla che a e b non siano nulli. In quel caso, esce'dal ciclo. Se non ci fosse questo If in mezzo al codice,'verrebbe scritto a schermo il messaggio:' "L'area del rettangolo è: 0"'cosa che noi vogliamo evitare. Se si usasse un'altra'variante di Do Loop, questo succederebbe sempre. Ecco'perchè, in questa situazione, è meglio'servirsi del semplice Do LoopIf a = 0 Or b = 0 Then

Exit DoEnd If

Console.WriteLine("L'area del rettangolo è: " & (a * b))Console.ReadKey()

LoopEnd Sub

End Module

1.2.3.

Do'istruzioni

Loop While [condizione]

Page 27: Guida visual basic

Il codice scr iver à a scher mo "2".

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:

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).

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:

A scher mo appar ir à "2".

01.02.03.04.05.06.07.08.09.10.11.12.

Module Module1Sub Main()

Dim a As Int32 = 0

Doa += 1

Loop While (a < 2) And (a > 0)Console.WriteLine(a)

Console.ReadKey()

End SubEnd Module

1.2.3.

Do While [condizione]'istruzioni

Loop

01.02.03.04.05.06.07.08.09.10.11.12.

Module Module1Sub Main()

Dim a As Int32 = 0

Do While (a < 2) And (a > 0)a += 1

LoopConsole.WriteLine(a)

Console.ReadKey()

End SubEnd Module

1.2.3.

Do'istruzioni

Loop Until [condizione]

01.02.03.04.05.06.07.08.09.10.11.12.

Module Module1Sub Main()

Dim a As Int32 = 0

Doa += 1

Loop Until (a <> 1)Console.WriteLine(a)

Console.ReadKey()

End SubEnd Module

1.2.3.

Do Until [condizione]'istruzioni

Page 28: Guida visual basic

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:

A scher mo appar ir à "0".

Un piccolo esempio finale:

Suggerimento

Per impostar e il valor e di Default (ossia il valor e pr edefinito) di una var iabile si può usar e questa sintassi:

Funziona solo per una var iabile alla volta. Questo tipo di istr uzione si chiama in izializzazione in-line.

Loop

01.02.03.04.05.06.07.08.09.10.11.12.

Module Module1Sub Main()

Dim a As Int32 = 0

Do Until (a <> 1)a += 1

LoopConsole.WriteLine(a)

Console.ReadKey()

End SubEnd Module

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.

Module Module1Sub Main()

Dim a, b, c As Int32Dim n As Int32

Console.WriteLine("-- Successione di Fibonacci --")Console.WriteLine("Inserire un numero oltre il quale terminare:")n = Console.ReadLine

If n = 0 Then

Console.WriteLine("Nessun numero della successione")Console.ReadKey()Exit Sub

End If

a = 1b = 1Console.WriteLine(a)Console.WriteLine(b)Do While c < n

c = a + bb = aa = cConsole.WriteLine(c)

Loop

Console.ReadKey()End Sub

End Module

1. Dim [nome] As [tipo] = [valore]

Page 29: Guida visual basic

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:

La var iabile I, usata in questo esempio, viene definita contatore 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:

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:

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.2.3.4.5.

Dim I As Int32 For I = 0 To [numero]'istruzioni

Next

01.02.03.04.05.06.07.08.09.10.11.12.13.

Module Module1Sub Main()

Dim a As Int32

'Scrive 46 volte (da 0 a 45, 0 compreso, sono 46 numeri)'a schermo 'ciao'For a = 0 To 45

Console.WriteLine("ciao")Next Console.ReadKey()

End SubEnd Module

01.02.03.04.05.06.07.08.09.10.11.12.13.14.15.16.17.18.19.20.21.22.

Module Module1Sub Main()

Dim a, b As Int32

Console.WriteLine("Inserisci un numero pari")b = Console.ReadLine

'Se b non è pari, ossia se il resto della divisione'b/2 è diverso da 0If b Mod 2 <> 0 Then

'Lo fa diventare un numero pari, aggiungendo 1b += 1

End If

'Scrive tutti i numeri da b a b+20For a = b To b + 20

Console.WriteLine(a)Next Console.ReadKey()

End SubEnd Module

1.

Page 30: Guida visual basic

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:

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.

Suggerimento

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:

2.3.4.5.

For a = b To b + 20If a Mod 2 = 0 Then

Console.WriteLine(a)End If

Next

1.2.3.

For a = b To b + 20 Step 2Console.WriteLine(a)

Next

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

Page 31: Guida visual basic

A11. Gli Array - Parte I

Array a una dimensioneFino 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 array è 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:

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

1. Dim [nome]([numero elementi - 1]) As [tipo]

Page 32: Guida visual basic

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:

il pr ogr ammator e indica al pr ogr amma che la var iabile A è un ar r ay contenente questi elementi:

che sono per la pr ecisione 6 elementi. Ecco un listato che esemplifica i concetti fin'or a chiar iti:

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

1. Dim A(5) As String

1. A(0), A(1), A(2), A(3), A(4), A(5)

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.32.33.34.35.36.37.38.39.40.41.42.43.44.45.46.47.

Module Module1Sub Main()

'Array che contiene 10 valori decimali, rappresentanti votiDim Marks(9) As Single'Questa variabile terrà traccia di quanti voti'l'utente avrà immesso da tastiera e permetterà di'calcolarne una mediaDim Index As Int32 = 0

'Mark conterrà il valore temporaneo immesso'da tastiera dall'utenteDim Mark As SingleConsole.WriteLine("Inserisci un altro voto (0 per terminare):")Mark = Console.ReadLine 'Il ciclo finisce quando l'utente immette 0 oppure quando'si è raggiunto l'indice massimo che è'possibile usare per identificare una cella dell'arrayDo While (Mark > 0) And (Index < 10)

'Se il voto immesso è maggiore di 0, lo memorizza'nell'array e incrementa l'indice di 1, così da'poter immagazzinare correttamente il prossimo voto nell'arrayMarks(Index) = MarkIndex += 1 Console.WriteLine("Inserisci un altro voto (0 per terminare):")Mark = Console.ReadLine

Loop'Decrementa l'indice di 1, poiché anche se l'utente'ha immesso 0, nel ciclo precedente, l'indice era stato'incrementato prevedendo un'ulteriore immissione, che,'invece, non c'è stataIndex -= 1

'Totale dei votiDim Total As Single = 0'Usa un ciclo For per scorrere tutte le celle dell'array'e sommarne i valoriFor I As Int32 = 0 To Index

Total += Marks(I)Next

'Mostra la mediaConsole.WriteLine("La tua media è: " & (Total / (Index + 1)))Console.ReadKey()

End SubEnd Module

Page 33: Guida visual basic

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:

Ad esempio:

Questa sintassi br eve equivale a questo codice:

Un'ulter ior e sintassi usata per dichiar ar e un ar r ay è la seguente:

Quest'ultima, come vedr emo, sar à par ticolar mente utile nel gestir e il tipo r estituito da una funzione.

Array a più dimensioniGli 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 rang 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 è:

Ecco un esempio che consider a un ar r ay di r ango 2 come una matr ice quadr ata:

1. Dim [nome]() As [tipo] = {elementi dell'array separati da virgole}

1. Dim Parole() As String = {"ciao", "mouse", "penna"}

1.2.3.4.

Dim Parole(2) As StringParole(0) = "ciao"Parole(1) = "mouse"Parole(2) = "penna"

1. Dim [nome] As [tipo]()

1.2.

Dim [nome]( , ) As [tipo] 'array di rango 2Dim [nome]( , , ) As [tipo] 'array di rango 3

01.02.03.04.05.06.07.08.09.10.

Module Module1Sub Main()

'Dichiara e inizializza un array di rango 2. Dato che'in questo caso abbiamo due dimensioni, e non una sola,'non si può specificare una semplice lista di'valori, ma una specie di "tabella" a due entrate.'Nell'esempio che segue, ho creato una semplice'tabella a due righe e due colonne, in cui ogni cella'è 0.

Page 34: Guida visual basic

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:

11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.

Dim M(,) As Single = _{{0, 0}, _{0, 0}}

'Bisogna notare il particolare uso delle graffe: si'considera l'array di rango 2 come un array i cui'elementi sono altri array

Console.WriteLine("Inserire gli elementi della matrice:")For I As Int32 = 0 To 1

For J As Int32 = 0 To 1Console.Write("Inserire l'elemento (" & I & ", " & J & "): ")M(I, J) = Console.ReadLine

NextNext

Dim Det As SingleDet = M(0, 0) * M(1, 1) - M(0, 1) * M(1, 0)Console.WriteLine("Il determinante della matrice è: " & Det)

Console.ReadKey()

End SubEnd Module

Page 35: Guida visual basic

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 solo e soltanto 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:

E se si pr ova a far e una cosa del gener e:

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

Ridimensionare un arrayPuò 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 keywor 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:

La r iga Ar r ay.Resize(A, n) equivale, usando ReDim a:

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. Dim [nome]()() As [tipo] 'array di array

1.2.3.4.

Dim A(,) As Int32Dim B()() As Int32'...A = B

01.02.03.04.05.06.07.08.09.10.11.12.13.14.15.16.17.18.19.

Module Module1Sub Main()

Dim A() As Int32Dim n As Int32

Console.WriteLine("Inserisci un numero")n = Console.ReadLine

'Reimposta la lunghezza di a ad n elementiArray.Resize(A, n)

'Calcola e memorizza i primi n numeri pari (zero compreso)For I As Int16 = 0 To n - 1

A(I) = I * 2Next

Console.ReadKey()

End SubEnd Module

1. ReDim A(n - 1)

1. ReDim M(2, 2)

Page 36: Guida visual basic

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, è:

In questo modo, ogni "r iga" viene aggiustata alla lunghezza giusta.

1.2.3.

ReDim M(0, 2)ReDim M(1, 2)ReDim M(2, 2)

Page 37: Guida visual basic

A12. Gli Array - Parte II

Il costrutto iterativo For EachQuesto 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:

Ovviamente anche in questo caso, come nel nor male For , è possibile inizializzar e una var iabile contator e all'inter no del

costr utto:

Esempio:

Per aver e un ter mine di par agone, il semplicissimo codice pr oposto equivale, usando un for nor male, a questo:

Gli array sono un tipo referenceDiver 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.

1.2.3.4.

Dim A As [tipo]For Each A In [array/collezione]

'istruzioniNext

1. For Each A As [tipo] in [array/collezione] ...

01.02.03.04.05.06.07.08.09.10.11.12.13.14.

Module Module1Sub Main()

Dim Words() As String = {"Questo", "è", "un", "array", "di", "stringhe"}

For Each Str As String In WordsConsole.Write(Str & " ")

Next 'A schermo apparirà la frase:' "Questo è un array di stringhe "

Console.ReadKey()

End SubEnd Module

1.2.3.4.5.

'Words.Length restituisce il numero di elementi'presenti nell'array WordsFor I As Int32 = 0 To Words.Length - 1

Console.Write(Words(I) & " ")Next

01.02.03.

Module Module1Sub Main()

Page 38: Guida visual basic

Ecco come appar e la memor ia dopo l'assegnazione A = B:

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

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.

'A e B sono due array di interiDim A() As Int32 = {1, 2, 3}Dim B() As Int32 = {4, 5, 6}

'Ora A e B sono due oggetti diversi e contengono'numeri diversi. Questa riga stamperà sullo'schermo "False", infatti A Is B = FalseConsole.WriteLine(A Is B)'Adesso poniamo A uguale a B. Dato che gli array'sono un tipo reference, da ora in poi, entrambi'saranno lo stesso oggettoA = B'Infatti questa istruzione stamperà a schermo''"True", poiché A Is B = TrueConsole.WriteLine(A Is B)'Dato che A e B sono lo stesso oggetto, se modifichiamo'un valore dell'array riferendoci ad esso con il nome'B, anche richiamandolo con A, esso mostrerà'che l'ultimo elemento è lo stessoB(2) = 90'Su schermo apparirà 90Console.WriteLine(A(2))

Dim C() As Int32 = {7, 8, 9}B = C'Ora cosa succede?

Console.ReadKey()

End SubEnd Module

Page 39: Guida visual basic

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 arrayLa 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

Page 40: Guida visual basic

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.

Page 41: Guida visual basic

A13. I Metodi - Parte I

Anatomia di un metodoIl Fr amewor 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 invocare 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...

Procedure senza parametriIl 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:

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:

mentr e la pr ossima è SBAGLI ATA:

1.2.3.

Sub [nome]()'istruzioni

End Sub

01.02.03.04.05.06.07.08.09.

Module Module1Sub Esempio()

'istruzioniEnd Sub Sub Main()

'istruzioniEnd Sub

End Module

1.2.3.

Module Module1Sub Main()

Page 42: Guida visual basic

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:

E' totalmente e concettualmente sbagliato. Ma or a veniamo al dunque con un esempio:

4.5.6.7.8.

Sub Esempio()'istruzioni

End Sub'istruzioni

End SubEnd Module

1.2.3.4.5.6.

Module Module1Sub Main()

'istruzioniEnd SubConsole.WriteLine()

End Sub

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.32.33.34.35.36.37.38.39.40.41.42.43.44.45.46.47.48.49.50.51.

Module Module1'Dichiarazione di una procedura: il suo nome è "FindDay", il'suo elenco di parametri è vuoto, e il suo corpo è'rappresentato da tutto il codice compreso tra "Sub FindDay()"'ed "End Sub".Sub FindDay()

Dim StrDate As StringConsole.Write("Inserisci giorno (dd/mm/yyyy): ")StrDate = Console.ReadLine

Dim D As Date'La funzione Date.TryParse tenta di convertire la stringa'StrDate in una variabile di tipo Date (che è un tipo'base). Se ci riesce, ossia non ci sono errori nella'data digitata, restituisce True e deposita il valore'ottenuto in D; se, al contrario, non ci riesce,'restituisce False e D resta vuota.'Quando una data non viene inizializzata, dato che è un'tipo value, contiene un valore predefinito, il primo'Gennaio dell'anno 1 d.C. a mezzogiorno in punto.If Date.TryParse(StrDate, D) Then

'D.DayOfWeek contiene il giorno della settimana di D'(lunedì, martedì, eccetera...), ma in un'formato speciale, l'Enumeratore, che vedremo nei'prossimi capitoli.'Il ".ToString()" converte questo valore in una'stringa, ossia in un testo leggibile: i giorni della'settimana, però, sono in ingleseConsole.WriteLine(D.DayOfWeek.ToString())

ElseConsole.WriteLine(StrDate & " non è una data valida!")

End IfEnd Sub

'Altra procedura, simile alla primaSub CalculateDaysDifference()

Dim StrDate1, StrDate2 As StringConsole.Write("Inserisci il primo giorno (dd/mm/yyyy): ")StrDate1 = Console.ReadLineConsole.Write("Inserisci il secondo giorno (dd/mm/yyyy): ")StrDate2 = Console.ReadLine

Dim Date1, Date2 As Date

If Date.TryParse(StrDate1, Date1) And _

Date.TryParse(StrDate2, Date2) Then'La differenza tra due date restituisce il tempo'trascorso tra l'una e l'altra. In questo caso noi'prendiamo solo i giorniConsole.WriteLine((Date2 - Date1).Days)

Page 43: Guida visual basic

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

locali o temporanee, 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.

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.

ElseConsole.WriteLine("Inserire due date valide!")

End IfEnd Sub

Sub Main()

'Command è una variabile di tipo char (carattere) che'conterrà una lettera indicante quale compito eseguireDim 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 tastieraCommand = Console.ReadKey().KeyChar'Analizza il valore di CommandSelect Case Command

Case "f"'Invoca la procedura FindDay()FindDay()

Case "d"'Invoca la procedura CalculateDaysDifference()CalculateDaysDifference()

Case "e"'Esce dal cicloExit Do

Case ElseConsole.WriteLine("Comando non riconosciuto!")

End SelectConsole.ReadKey()

LoopEnd Sub

End Module

01.02.03.04.05.06.07.08.09.10.11.

Module Module1'Questa variabile è dichiarata a livello di classe'(o di modulo, in questo caso), perciò è accessibile'a tutti i membri del modulo, sempre seguendo il discorso'dei blocchi di codice fatto in precedenzaDim Total As Single = 0

'Legge un numero da tastiera e lo somma al totaleSub Sum()

Dim Number As Single = Console.ReadLine

Page 44: Guida visual basic

Procedure con parametriAvviandoci 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 chiamante: 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.

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

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.

Total += NumberEnd Sub

'Legge un numero da tastiera e lo sottrae al totaleSub Subtract()

Dim Number As Single = Console.ReadLineTotal -= Number

End Sub

'Legge un numero da tastiera e divide il totale per'tale numeroSub Divide()

Dim Number As Single = Console.ReadLineTotal /= Number

End Sub

'Legge un numero da tastiera e moltiplica il totale'per tale numeroSub Multiply()

Dim Number As Single = Console.ReadLineTotal *= Number

End Sub

Sub Main()'Questa variabile conterrà il simbolo matematico'dell'operazione da eseguireDim Operation As CharDo

Console.Clear()Console.WriteLine("Risultato attuale: " & Total)Operation = Console.ReadKey().KeyCharSelect Case Operation

Case "+"Sum()

Case "-"Subtract()

Case "*"Multiply()

Case "/"Divide()

Case "e"Exit Do

Case ElseConsole.WriteLine("Operatore non riconosciuto")Console.ReadKey()

End SelectLoop

End SubEnd Module

1.2.3.

Sub [nome](ByVal [parametro1] As [tipo], ByVal [parametro2] As [tipo], ...)'istruzioni

End Sub

Page 45: Guida visual basic

esempio, r iscr ivendo l'ultimo codice pr oposto nel par agr afo pr ecedente con l'aggiunta dei par ametr i:

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 parametro

formale, mentr e N - ossia ciò che viene concr etamente pas s ato al metodo - si dice parametro 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)?

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.32.33.34.35.36.37.38.39.40.41.42.43.44.45.46.47.48.49.50.51.52.53.

Module Module1Dim Total As Single = 0

Sub Sum(ByVal Number As Single)

Total += NumberEnd Sub

Sub Subtract(ByVal Number As Single)

Total -= NumberEnd Sub

Sub Divide(ByVal Number As Single)

Total /= NumberEnd Sub

Sub Multiply(ByVal Number As Single)

Total *= NumberEnd Sub

Sub Main()

'Questa variabile conterrà il simbolo matematico'dell'operazione da eseguireDim Operation As CharDo

Console.Clear()Console.WriteLine("Risultato attuale: " & Total)Operation = Console.ReadKey().KeyCharSelect Case Operation

'Se si tratta di simboli accettabiliCase "+", "-", "*", "/"

'Legge un numero da tastieraDim N As Single = Console.ReadLine'E a seconda dell'operazione, utilizza una'procedura piuttosto che un'altraSelect Case Operation

Case "+"Sum(N)

Case "-"Subtract(N)

Case "*"Multiply(N)

Case "/"Divide(N)

End SelectCase "e"

Exit DoCase Else

Console.WriteLine("Operatore non riconosciuto")Console.ReadKey()

End SelectLoop

End SubEnd Module

Page 46: Guida visual basic

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.

Passare parametri al programma da riga di comandoCome 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 comando. In quest'ultimo caso, Main viene dichiar ata con un solo ar gomento, un ar r ay di str inghe:

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.32.33.34.35.36.37.38.39.40.41.

Module Module1Dim Total As Single = 0

Sub DoOperation(ByVal Number As Single, ByVal Op As Char)

Select Case OpCase "+"

Total += NumberCase "-"

Total -= NumberCase "*"

Total *= NumberCase "/"

Total /= NumberEnd Select

End Sub

Sub Main()Dim Operation As CharDo

Console.Clear()Console.WriteLine("Risultato attuale: " & Total)Operation = Console.ReadKey().KeyCharSelect Case Operation

Case "+", "-", "*", "/"Dim N As Single = Console.ReadLine'A questa procedura vengono passati due'parametri: il primo è il numero da'aggiungere/sottrarre/moltiplicare/dividere'a Total; il secondo è il simbolo'matematico che rappresenta l'operazione'da eseguireDoOperation(N, Operation)

Case "e"Exit Do

Case ElseConsole.WriteLine("Operatore non riconosciuto")Console.ReadKey()

End SelectLoop

End SubEnd Module

01.02.03.04.05.06.07.08.09.10.11.12.13.14.15.16.17.

Module Module1Sub Main(ByVal Args() As String)

If Args.Length = 0 Then'Se la lunghezza dell'array è 0, significa che è vuoto'e quindi non è stato passato nessun parametro a riga'di comando. Scrive a schermo come utilizzare'il programmaConsole.WriteLine("Utilizzo: nomeprogramma.exe tuonome")

Else'Args ha almeno un elemento. Potrebbe anche averne di'più, ma a noi interessa solo il primo.'Saluta l'utente con il nome passato da riga di comandoConsole.WriteLine("Ciao " & Args(0) & "!")

End IfConsole.ReadKey()

End Sub

Page 47: Guida visual basic

Per pr ovar lo, potete usar e cmd.ex e, il pr ompt dei comandi. Io ho digitato:

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:

End Module

1.2.

CD "C:\Users\Totem\Documents\Visual Studio 2008\Projects\ConsoleApplication2\bin\Debug"ConsoleApplication2.exe Totem

Page 48: Guida visual basic

A14. I Metodi - Parte II

ByVal e ByRefNel capitolo pr ecedente, tutti i par ametr i sono stati dichiar anti anteponendo al lor o nome la keywor 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 copia 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 non sul par ametr o attuale for nito.

Ecco un esempio:

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 indirizzo). 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:

Nel codice, la sola differ enza consiste nella keywor 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 SEMPRE allo stesso modo, anche se

vengono inser iti nell'elenco dei par ametr i accompagnati da ByVal. Eccone una dimostr azione:

01.02.03.04.05.06.07.08.09.10.11.12.

Module Module1Sub Change(ByVal N As Int32)

N = 30End Sub

Sub Main()

Dim A As Int32 = 56Change(A)Console.WriteLine(A)Console.ReadKey()

End SubEnd Module

01.02.03.04.05.06.07.08.09.10.11.12.

Module Module1Sub Change(ByRef N As Int32)

N = 30End Sub

Sub Main()

Dim A As Int32 = 56Change(A)Console.WriteLine(A)Console.ReadKey()

End SubEnd Module

01.02.03.04.05.06.

Module Module1Dim A As New Object

Sub Test(ByVal N As Object)

Console.WriteLine(N Is A)

Page 49: Guida visual basic

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 funzioniLe funzioni sono simili alle pr ocedur e, ma possiedono qualche car atter istica in più. La lor o sintassi è la seguente:

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:

E un altr o esempio in cui ci sono più Retur n:

07.08.09.10.11.12.

End Sub

Sub Main()Test(A)Console.ReadKey()

End SubEnd Module

1.2.3.4.

Function [name]([elenco parametri]) As [tipo]'...Return [risultato]

End Function

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.

Module Module1'Questa funzione calcola la media di un insieme'di numeri decimali passati come arrayFunction Average(ByVal Values() As Single) As Single

'Total conterrà la somma totale di tutti'gli elementi di ValuesDim Total As Single = 0'Usa un For Each per ottenere direttamente i valori'presenti nell'array piuttosto che enumerarli'attraverso un indice mediante un For normaleFor Each Value As Single In Values

Total += ValueNext'Restituisce la media aritmetica, ossia il rapporto'tra la somma totale e il numero di elementiReturn (Total / Values.Length)

End Function

Sub Main(ByVal Args() As String)Dim Values() As Single = {1.1, 5.2, 9, 4, 8.34}'Notare che in questo caso ho usato lo stesso nome'per il parametro formale e attualeConsole.WriteLine("Media: " & Average(Values))Console.ReadKey()

End SubEnd Module

01.02.03.04.05.06.07.08.09.

Module Module1Function Potenza(ByVal Base As Single, ByVal Esponente As Byte) As Double

Dim X As Double = 1

If Esponente = 0 ThenReturn 1

ElseIf Esponente = 1 Then

Page 50: Guida visual basic

In quest'ultimo esempio, il cor po della funzione contiene ben tr e Retur n, ma ognuno appar tiene a un path di codice

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 Function , 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 particolari delle funzioniCi 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:

ma è possibile scr iver e, come abbiamo fatto fin'or a:

10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.

Return BaseElse

For i As Byte = 1 To EsponenteX *= Base

NextReturn X

End IfEnd If

End Function Sub Main()

Dim F As DoubleDim b As SingleDim e As Byte

Console.WriteLine("Inserire base ed esponente:")b = Console.ReadLinee = Console.ReadLineF = Potenza(b, e)Console.WriteLine(b & " elevato a " & e & " vale " & F)Console.ReadKey()

End SubEnd Module

1. a = Console.ReadLine()

1. a = Console.ReadLine

Page 51: Guida visual basic

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:

ci siamo limitati a:

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.

Variabili StaticLe 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:

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.

1. Dim b = Console.ReadKey()

1. Console.ReadKey()

01.02.03.04.05.06.07.08.09.10.11.12.13.14.

Module Module1Sub Test()

Static B As Int32 = 0B += 1Console.WriteLine(B)

End Sub

Sub Main(ByVal Args() As String)For I As Int16 = 1 To 6

Test()NextConsole.ReadKey()

End SubEnd Module

Page 52: Guida visual basic

A15. I Metodi - Parte III

Parametri opzionaliCome 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 keywor 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:

Parametri indefiniti

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.32.33.34.35.36.37.38.39.40.41.42.43.44.45.46.47.48.

Module Module1'Disegna una barra "di caricamento" animata con dei trattini'e dei pipe (|). Length indica la sua lunghezza, ossia quanti'caratterei debbano essere stampati a schermo. AnimationSpeed'è la velocità dell'animazione, di default 1Sub DrawBar(ByVal Length As Int32, _

Optional ByVal AnimationSpeed As Single = 1)'La variabile static tiene conto del punto a cui si è'arrivati al caricamentoStatic Index As Int32 = 1

'Disegna la barraFor I As Int32 = 1 To Length

If I > Index ThenConsole.Write("-")

ElseConsole.Write("|")

End IfNext

'Aumenta l'indice di uno. Notare il particolare'assegnamento che utilizza l'operatore Mod. Finché'Index è minore di Length, questa espressione equivale'banalmente a Index + 1, poiché a Mod b = a se a < b.'Quando Index supera il valore di Length, allora l'operatore'Mod cambia le cose: infatti, se Index = Length + 1,'l'espressione restituisce 0, che, sommato a 1, dà 1.'Il risultato che otteniamo è che Index reinizia'da capo, da 1 fino a Length.Index = (Index Mod (Length + 1)) + 1'Il metodo Sleep, che vedremo approfonditamente solo nella'sezione B, fa attendere al programma un certo numero di'millisecondi.'1000 / AnimationSpeed provoca una diminuzione del tempo'di attesa all'aumentare della velocitàThreading.Thread.CurrentThread.Sleep(1000 / AnimationSpeed)

End Sub

Sub Main()'Disegna la barra con un ciclo infinito. Potete invocare'DrawBar(20) tralasciando l'ultimo argomento e l'animazione'sarà lenta poiché la velocità di default è 1Do

Console.Clear()DrawBar(20, 5)

LoopEnd Sub

End Module

Page 53: Guida visual basic

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 keywor d Par amAr r ay inter posta tr a la

clausola ByVal o ByRef e il nome del par ametr o.

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.02.03.04.05.06.07.08.09.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.

Module Module1'Somma tutti i valori passati come parametri.Function Sum(ByVal ParamArray Values() As Single) As Single

Dim Result As Single = 0

For I As Int32 = 0 To Values.Length - 1Result += Values(I)

Next

Return ResultEnd Function

Sub Main()

Dim S As Single

'Somma due valoriS = Sum(1, 2)'Somma quattro valoriS = Sum(1.1, 5.6, 98.2, 23)'Somma un array di valoriDim V() As Single = {1, 8, 3.4}S = Sum(V)

End SubEnd Module

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.32.33.34.

Module Module1'Questa funzione calcola un prezzo includendovi anche'il pagamento di alcune tasse (non sono un esperto di'economia, perciò mi mantengono piuttosto sul vago XD).'Il primo parametro rappresenta il prezzo originale, mentre'il secondo è un parametro indefinito che'raggruppa tutte le varie tasse vigenti sul prodotto'da acquistare che devono essere aggiunte all'importo'iniziale (espresse come percentuali)Function ApplyTaxes(ByVal OriginalPrice As Single, _

ByVal ParamArray Taxes() As Single) As SingleDim Result As Single = OriginalPriceFor Each Tax As Single In Taxes

Result += OriginalPrice * Tax / 100NextReturn Result

End Function

Sub Main()Dim Price As Single = 120

'Aggiunge una tassa del 5% a PriceDim Price2 As Single = _

ApplyTaxes(Price, 5)

'Aggiunge una tassa del 5%, una del 12.5% e una'dell'1% a PriceDim Price3 As Single = _

ApplyTaxes(Price, 5, 12.5, 1)

Console.WriteLine("Prezzo originale: " & Price)Console.WriteLine("Presso con tassa 1: " & Price2)Console.WriteLine("Prezzo con tassa 1, 2 e 3: " & Price3)

Page 54: Guida visual basic

RicorsioneSi 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 fattor iale:

35.36.37.

Console.ReadKey()

End SubEnd Module

01.02.03.04.05.06.07.08.09.10.11.12.13.14.15.16.17.18.19.20.21.22.

Module Module1'Notare che il parametro è di tipo Byte perchè il'fattoriale cresce in modo abnorme e già a 170! Double non'basta più a contenere il risultatoFunction Factorial(ByVal N As Byte) As Double

If N <= 1 ThenReturn 1

ElseReturn N * Factorial(N - 1)

End IfEnd Function

Sub Main()

Dim Number As Byte

Console.WriteLine("Inserisci un numero (0 <= x < 256):")Number = Console.ReadLineConsole.WriteLine(Number & "! = " & Factorial(Number))

Console.ReadKey()

End SubEnd Module

Page 55: Guida visual basic

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:

Ad esempio:

1.2.3.4.5.

Enum [Nome][Nome valore 1][Nome valore 2]...

End Enum

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.32.33.34.35.36.37.38.39.40.41.42.43.44.45.46.47.48.

Module Module1'A seconda di come sono configurati i suoi caratteri, una'stringa può possedere diverse denominazioni, chiamate'Case. Se è costituita solo da caratteri minuscoli'(es.: "stringa di esempio") si dice che è in Lower'Case; al contrario se contiene solo maiuscole (es.: "STRINGA'DI ESEMPIO") sarà Upper Case. Se, invece, ogni'parola ha l'iniziale maiuscola e tutte le altre lettere'minuscole si indica con Proper Case (es.: "Stringa Di Esempio").'In ultimo, se solo la prima parola ha l'iniziale'maiuscola e il resto della stringa è tutto minuscolo'e questa termina con un punto, si ha Sentence Case'(es.: "Stringa di esempio.").'Questo enumeratore indica questi casiEnum StringCase

LowerUpperSentenceProper

End Enum

'Questa funzione converte una stringa in uno dei Case'disponibili, indicati dall'enumeratore. Il secondo parametro'è specificato fra parentesi quadre solamente perchè'Case è una keyword, ma noi la vogliamo usare come'identificatore.Function ToCase(ByVal Str As String, ByVal [Case] As StringCase) As String

'Le funzioni per convertire in Lower e Upper'case sono già definite. E' sufficiente'indicare un punto dopo il nome della variabile'stringa, seguito a ToLower e ToUpperSelect Case [Case]

Case StringCase.LowerReturn Str.ToLower()

Case StringCase.UpperReturn Str.ToUpper()

Case StringCase.Proper'Consideriamo la stringa come array di'caratteri:Dim Chars() As Char = Str.ToLower()'Iteriamo lungo tutta la lunghezza della'stringa, dove Str.Length restituisce appunto'tale lunghezzaFor I As Int32 = 0 To Str.Length - 1

'Se questo carattere è uno spazio oppure'è il primo di tutta la stringa, il'prossimo indicherà l'inizio di una nuova

Page 56: Guida visual basic

L'enumer ator e Str ingCase offr e quattr o possibilità: Lower , 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:

A scher mo appar ir à

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:

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.

'parola e dovrà essere maiuscolo.If I = 0 Then

Chars(I) = Char.ToUpper(Chars(I))End IfIf Chars(I) = " " And I < Str.Length - 1 Then

'Char.ToUpper rende maiuscolo un carattere'passato come parametro e lo restituisceChars(I + 1) = Char.ToUpper(Chars(I + 1))

End IfNext'Restituisce l'array modificato (un array di caratteri'e una stringa sono equivalenti)Return Chars

Case StringCase.Sentence'Riduce tutta la stringa a Lower CaseStr = Str.ToLower()'Imposta il primo carattere come maiuscoloDim Chars() As Char = StrChars(0) = Char.ToUpper(Chars(0))Str = Chars'La chiude con un puntoStr = Str & "."Return Str

End SelectEnd 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 puntoConsole.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 SubEnd Module

1.2.3.4.

Console.WriteLine(StringCase.Lower)Console.WriteLine(StringCase.Upper)Console.WriteLine(StringCase.Sentence)Console.WriteLine(StringCase.Proper)

1.2.3.4.

0123

1.2.3.4.5.

Enum StringCaseLower = 5Upper = 10Sentence = 20

Page 57: Guida visual basic

Se ad un nome non viene assegnato valor e, esso assumer à il valor e del suo pr ecedente, aumentato di 1:

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:

È 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:

6.Proper = 40

End Enum

1.2.3.4.5.6.

Enum StringCaseLower = 5Upper '= 6Sentence = 20Proper '= 21

End Enum

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.32.33.34.35.36.37.38.39.40.41.42.43.44.45.46.47.

Module Module1'Usa i nomi inglesi delle note. L'enumerazione inizia'da -9 poiché il Do centrale si trova 9 semitoni'sotto il La centraleEnum Note

C = -9CSharpDDSharpEFFSharpGGSharpAASharpB

End Enum

'Restituisce la frequenza di una nota. N, in concreto,'rappresenta la differenza, in semitoni, di quella nota'dal La centrale. Ecco l'utilittà degli enumeratori,'che danno un nome reale a ciò che un dato indica'indirettamenteFunction GetFrequency(ByVal N As Note) As Single

Return 440 * 2 ^ (N / 12)End Function

'Per ora prendete per buona questa funzione che restituisce'il nome della costante di un enumeratore a partire dal'suo valore. Avremo modo di approfondire nei capitoli'sulla ReflectionFunction GetName(ByVal N As Note) As String

Return [Enum].GetName(GetType(Note), N)End Function

Sub Main()

'Possiamo anche iterare usando gli enumeratori, poiché'si tratta pur sempre di semplici numeriFor I As Int32 = Note.C To Note.B

Console.WriteLine("La nota " & GetName(I) & _" risuona a una frequenza di " & GetFrequency(I) & "Hz")

Next

Console.ReadKey()End Sub

End Module

1.2.3.

Enum StringCase As ByteLower = 5

Page 58: Guida visual basic

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 codificati 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:

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:

4.5.6.

Upper = 10Sentence = 20Proper = 40

End Enum

1. 00001101

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.32.33.34.35.36.37.38.39.40.

Module Module1'È convenzione che gli enumeratori che codificano a bit'abbiano un nome al plurale'Questo enumeratore definisce alcuni tipi di filePublic Enum FileAttributes As Byte

'1 = 2 ^ 0'In binario:'00000001Normal = 1

'2 = 2 ^ 1'00000010Hidden = 2

'4 = 2 ^ 2'00000100System = 4

'8 = 2 ^ 3'00001000Archive = 8

End Enum

Sub Main()Dim F As FileAttributes'F all'inizio è 0, non contiene niente:'00000000

F = FileAttributes.Normal'Ora F è 1, ossia Normal'00000001

F = FileAttributes.Hidden Or FileAttributes.System'La situazione diventa complessa:'Il primo valore è 2: 000000010'Il secondo valore è 4: 000000100'Abbiamo già visto l'operatore Or: restituisce True se'almeno una delle condizioni è vera: qui True è'1 e False è 0:

Page 59: Guida visual basic

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:

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.

41.42.43.44.45.46.47.48.49.50.51.52.53.54.55.

'000000010 Or'000000100 ='000000110'Come si vede, ora ci sono due campi attivi: 4 e 2, che'corrispondono a Hidden e System. Abbiamo fuso insieme due'attributi con Or

F = FileAttributes.Archive Or FileAttributes.System Or _

FileAttributes.Hidden'La stessa cosa:'00001000 Or'00000100 Or'00000010 ='00001110

End SubEnd Module

01.02.03.04.05.06.07.08.09.10.11.12.13.14.15.16.17.18.19.20.

Module Module1Sub Main()

Dim F As FileAttributes

F = FileAttributes.Archive Or FileAttributes.System Or _FileAttributes.Hidden

'Ora F è 00001110 e bisogna eseguire un'operazione di And'sui bit, confrontando questo valore con Archive, che è 8.'And restituisce Vero solo quando entrambe le condizioni'sono vere:'00001110 And'00001000 ='00001000, ossia Archive!If F And FileAttributes.Archive = FileAttributes.Archive Then

Console.WriteLine("Il file è marcato come 'Archive'")End IfConsole.ReadKey()

End SubEnd Module

Page 60: Guida visual basic

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 strutture, 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 tante 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.

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:

Come si vede dall'esempio, la sintassi usata per dichiar ar e una str uttur a è la seguente:

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:

[Ricor date che le dichiar azioni di nuovi tipi di dato (fino ad or a quelli che abbiamo analizzato sono enumer ator i e

1.2.3.

Dim Names() As StringDim Surnames() As StringDim PhoneNumbers() As String

1.2.3.4.5.6.7.8.

Structure ContactDim Name, Surname, PhoneNumber As String

End Structure '... 'Un array di conttati, ognuno rappresentato dalla struttura ContactDim Contacts() As Contact

1.2.3.4.5.

Structure [Nome]Dim [Campo1] As [Tipo]Dim [Campo2] As [Tipo]'...

End Structure

01.02.03.04.05.06.07.08.09.10.11.12.13.

Module Module1Structure Contact

Dim Name, Surname, PhoneNumber As StringEnd Structure

Sub Main()

Dim A As Contact

A.Name = "Mario"A.Surname = "Rossi"A.PhoneNumber = "333 33 33 333"

End SubEnd Module

Page 61: Guida visual basic

str uttur e, e le classi solo come intr oduzione) possono esser e fatte solo a livello di classe o di namespace, e mai 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.

Acrobazie con le struttureMa 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.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.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.

Module Module1Structure Contact

Dim Name, Surname, PhoneNumber As StringEnd Structure

Sub Main()

'Contacts(-1) inizializza un array vuoto,'ossia con 0 elementiDim Contacts(-1) As ContactDim Command As Char

Do

Console.Clear()Console.WriteLine("Rubrica -----")Console.WriteLine("Selezionare l'azione desiderata:")Console.WriteLine("N - Nuovo contatto;")Console.WriteLine("T - Trova contatto;")Console.WriteLine("E - Esci.")Command = Char.ToUpper(Console.ReadKey().KeyChar)Console.Clear()

Select Case Command

Case "N"'Usa ReDim Preserve per aumentare le dimensioni'dell'array mentenendo i dati già presenti.'L'uso di array e di redim, in questo caso, è'sconsigliato, a favore delle più versatili'Liste, che però non ho ancora introdotto.'Ricordate che il valore specificato tra'parentesi indica l'indice massimo e non'il numero di elementi.'Se, all'inizio, Contacts.Length è 0,'richiamando ReDim Contacts(0), si aumenta'la lunghezza dell'array a uno, poiché'in questo caso l'indice massimo è 0,'ossia quello che indica il primo e'l'unico elementoReDim Preserve Contacts(Contacts.Length)

Dim N As ContactConsole.Write("Nome: ")N.Name = Console.ReadLineConsole.Write("Cognome: ")N.Surname = Console.ReadLineConsole.Write("Numero di telefono: ")N.PhoneNumber = Console.ReadLine

'Inserisce nell'ultima cella dell'array'l'elemento appena creatoContacts(Contacts.Length - 1) = N

Case "T"

Dim Part As String

Console.WriteLine("Inserire nome o cognome del " & _"contatto da trovare:")

Part = Console.ReadLine

Page 62: Guida visual basic

Or a ammettiamo di voler modificar e il codice per per metter e l'inser imento di più numer i di telefono:

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.

For Each C As Contact In Contacts'Il confronto avviene in modalità'case-insensitive: sia il nome/cognome'che la stringa immessa vengono'ridotti a Lower Case, così da'ignorare la differenza tra'minuscole e maiuscole, qualora presenteIf (C.Name.ToLower() = Part.ToLower()) Or _

(C.Surname.ToLower() = Part.ToLower()) ThenConsole.WriteLine("Nome: " & C.Name)Console.WriteLine("Cognome: " & C.Surname)Console.WriteLine("Numero di telefono: " & C.PhoneNumber)Console.WriteLine()

End IfNext

Case "E"

Exit Do

Case ElseConsole.WriteLine("Comando sconosciuto!")

End SelectConsole.ReadKey()

LoopEnd Sub

End Module

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.32.33.34.35.36.37.38.39.40.41.42.43.44.

Module Module1Structure Contact

Dim Name, Surname As String'Importante: NON è possibile specificare le dimensioni'di un array dentro la dichiarazione di una struttura.'Risulta chiaro il motivo se ci si pensa un attimo.'Noi stiamo dichiarando quali sono i campi della struttura'e quale è il loro tipo. Quindi specifichiamo che'PhoneNumbers è un array di stringhe, punto. Se scrivessimo'esplicitamente le sue dimensioni lo staremmo creando'fisicamente nella memoria, ma questa è una'dichiarazione, come detto prima, e non una'inizializzazione. Vedremo in seguito che questa'differenza è molto importante per i tipi reference'(ricordate, infatti, che gli array sono tipi reference).Dim PhoneNumbers() As String

End Structure

Sub Main()Dim Contacts(-1) As ContactDim Command As Char

Do

Console.Clear()Console.WriteLine("Rubrica -----")Console.WriteLine("Selezionare l'azione desiderata:")Console.WriteLine("N - Nuovo contatto;")Console.WriteLine("T - Trova contatto;")Console.WriteLine("E - Esci.")Command = Char.ToUpper(Console.ReadKey().KeyChar)Console.Clear()

Select Case Command

Case "N"ReDim Preserve Contacts(Contacts.Length)

Dim N As ContactConsole.Write("Nome: ")N.Name = Console.ReadLineConsole.Write("Cognome: ")N.Surname = Console.ReadLine

'Ricordate che le dimensioni dell'array non

Page 63: Guida visual basic

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:

Per acceder e ai campi di Home si sar ebbe utilizzato un ulter ior e punto:

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.

'sono ancora state impostate:ReDim N.PhoneNumbers(-1)

'Continua a chiedere numeri di telefono finché'non si introduce più nullaDo

ReDim Preserve N.PhoneNumbers(N.PhoneNumbers.Length)Console.Write("Numero di telefono " & N.PhoneNumbers.Length & ": ")N.PhoneNumbers(N.PhoneNumbers.Length - 1) = Console.ReadLine

Loop Until N.PhoneNumbers(N.PhoneNumbers.Length - 1) = ""'Ora l'ultimo elemento dell'array è sicuramente'vuoto, lo si dovrebbe togliere.

Contacts(Contacts.Length - 1) = N

Case "T"

Dim Part As String

Console.WriteLine("Inserire nome o cognome del " & _"contatto da trovare:")

Part = Console.ReadLine

For Each C As Contact In ContactsIf (C.Name.ToLower() = Part.ToLower()) Or _

(C.Surname.ToLower() = Part.ToLower()) ThenConsole.WriteLine("Nome: " & C.Name)Console.WriteLine("Cognome: " & C.Surname)Console.WriteLine("Numeri di telefono: ")For Each N As String In C.PhoneNumbers

Console.WriteLine(" - " & N)NextConsole.WriteLine()

End IfNext

Case "E"

Exit Do

Case ElseConsole.WriteLine("Comando sconosciuto!")

End SelectConsole.ReadKey()

LoopEnd Sub

End Module

01.02.03.04.05.06.07.08.09.10.11.

Structure AddressDim State, Town As StringDim Street, CivicNumber As StringDim Cap As String

End Structure Structure Contact

Dim Name, Surname As StringDim PhoneNumbers() As StringDim Home As Address

End Structure

01.02.03.

Dim A As Contact

Page 64: Guida visual basic

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"

Page 65: Guida visual basic

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ì:

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:

1.2.3.

Class [Nome Classe]'...

End Class

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.32.33.34.35.36.37.38.39.40.41.42.43.44.45.46.47.

Module Module1'Classe che rappresenta un cubo.'Segue la dichiarazione della classe. Da questo momento'in poi, potremo usare Cube come tipo per le nostre variabili.'Notare che una classe si dichiara e basta, non si'"inizializza", perchè non è qualcosa di concreto,'è un'astrazione, c'è, esiste in generale.Class Cube

'Variabile che contiene la lunghezza del latoDim SideLength As Single'Variabile che contiene la densità del cubo, e quindi'ci dice di che materiale è compostoDim Density As Single

'Questa procedura imposta i valori del lato e'della densitàSub SetData(ByVal SideLengthValue As Single, ByVal DensityValue As Single)

SideLength = SideLengthValueDensity = DensityValue

End Sub

'Questa funzione restituisce l'area di una facciaFunction GetSurfaceArea() As Single

Return (SideLength ^ 2)End Function

'Questa funzione restituisce il volume del cuboFunction GetVolume() As Single

Return (SideLength ^ 3)End Function

'Questa funzione restituisce la massa del cuboFunction GetMass() As Single

Return (Density * GetVolume())End Function

End Class

Sub Main()'Variabile di tipo Cube, che rappresenta uno specifico cubo'La riga di codice che segue contiene la dichiarazione'della variabile A. La dichiarazione di una variabile'fa sapere al compilatore, ad esempio, di che tipo'sarà, in quale blocco di codice sarà'visibile, ma nulla di più.'Non esiste ancora un oggetto Cube collegato ad A, ma'potrebbe essere creato in un immediato futuro.

Page 66: Guida visual basic

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:

ma solo passando attr aver so un'istanza:

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 semplice costruttoreAnche 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 ":

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.

'N.B.: quando si dichiara una variabile di tipo reference,'viene comunque allocata memoria sullo stack; viene'infatti creato un puntatore, che punta all'oggetto'Nothing, il cui valore simbolico è stato'spiegato precedentemente.Dim A As Cube

'Ed ecco l'immediato futuro: con la prossima linea di'codice, creiamo l'oggetto di tipo Cube che verrà'posto nella variabile A.A = New Cube'Quando New è seguito dal nome di una classe, si crea un'oggetto di quel tipo. Nella fattispecie, in questo momento'il programma si preoccuperà di richiedere della'memoria sull'heap managed per allocare i dati relativi'all'oggetto e di creare un puntatore sullo stack che'punti a tale oggetto. Esso, inoltre, eseguirà'il codice contenuto nel costruttore. New, infatti,'è uno speciale tipo di procedura, detta'Costruttore, di cui parlerò approfonditamente'in seguito

'Come per le strutture, i membri di classe sono accessibili'tramite l'operatore punto ".". Ora imposto le variabili'contenute in A per rappresentare un cubo di alluminio'(densità 2700 Kg/m<sup>3</sup>) di 1.5m di latoA.SetData(1.5, 2700)

Console.WriteLine("Superficie faccia: " & A.GetSurfaceArea() & " m2")Console.WriteLine("Volume: " & A.GetVolume() & " m3")Console.WriteLine("Massa: " & A.GetMass() & " Kg")'It's Over 9000!!!!

Console.ReadKey()

End SubEnd Module

1. Cube.GetVolume()

1.2.3.

Dim B As New Cube'...B.GetVolume()

1.

Page 67: Guida visual basic

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:

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.

Una nota sulle StruttureAnche 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

2.3.

Sub New([parametri])'codice

End Sub

1.2.

Sub New()End Sub

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.32.33.34.35.36.37.38.39.

Module Module1Class Cube

Dim SideLength As SingleDim Density As Single

'Quasi uguale a SetDataSub New(ByVal SideLengthValue As Single, ByVal DensityValue As Single)

SideLength = SideLengthValueDensity = DensityValue

End Sub

Function GetSurfaceArea() As SingleReturn (SideLength ^ 2)

End Function

Function GetVolume() As SingleReturn (SideLength ^ 3)

End Function

Function GetMass() As SingleReturn (Density * GetVolume())

End FunctionEnd Class

Sub Main()

'Questa è una sintassi più concisa che equivale a:'Dim A As Cube'A = New Cube(2700, 1.5)'Tra parentesi vanno passati i parametri richiesti dal'costruttoreDim A As New Cube(2700, 1.5)

Console.WriteLine("Superficie faccia: " & A.GetSurfaceArea() & " m<sup>2</sup>")Console.WriteLine("Volume: " & A.GetVolume() & " m<sup>3</sup>")Console.WriteLine("Massa: " & A.GetMass() & " Kg") Console.ReadKey()

End SubEnd Module

Page 68: Guida visual basic

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.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.32.33.34.35.36.37.

Module Module1Structure Cube

Dim SideLength As SingleDim Density As Single

Sub New(ByVal SideLengthValue As Single, ByVal DensityValue As Single)

SideLength = SideLengthValueDensity = DensityValue

End Sub

Function GetSurfaceArea() As SingleReturn (SideLength ^ 2)

End Function

Function GetVolume() As SingleReturn (SideLength ^ 3)

End Function

Function GetMass() As SingleReturn (Density * GetVolume())

End FunctionEnd Structure

Sub Main()

'Questo codiceDim A As New Cube(2700, 1.5)

'Equivale a questoDim B As CubeB.SideLength = 1.5B.Density = 2700

'A e B sono uguali

Console.ReadKey()

End SubEnd Module

Page 69: Guida visual basic

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

livello 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 specificator 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 praticoRipr endiamo il codice della classe Cube r ipr oposto nel capitolo pr ecedente. Pr oviamo a scr iver e nella Sub Main questo

codice:

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:

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ì:

1.2.3.4.

Sub Main()Dim A As New Cube(2700, 1.5)A.SideLength = 3

End Sub

1.2.

ConsoleApplication2.Module1.Cube.SideLength' is not accessible in thiscontext because it is 'Private'.

01.02.03.04.05.06.07.08.09.10.11.12.13.14.15.16.

Module Module1Class Cube

'Quando gli specificatori di accesso sono anteposti alla'dichiarazione di una variabile, si toglie il "Dim"Private SideLength As SinglePrivate Density As Single

Sub New(ByVal SideLengthValue As Single, ByVal DensityValue As Single)

SideLength = SideLengthValueDensity = DensityValue

End Sub

Function GetSurfaceArea() As SingleReturn (SideLength ^ 2)

End Function

Page 70: Guida visual basic

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 keywor 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:

17.18.19.20.21.22.23.24.25.26.

Function GetVolume() As Single

Return (SideLength ^ 3)End Function

Function GetMass() As Single

Return (Density * GetVolume())End Function

End Class'...

End Module

01.02.03.04.05.06.07.08.09.10.11.12.13.14.15.

Public Class Classe1Private Class Classe2

'...End Class

Class Classe3

'...End Class

End Class Class Classe4

Public Class Classe5Private Class Classe6

'...

Page 71: Guida visual basic

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).

Specificatori di accesso nelle StruttureAnche 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:

che equivale a:

Ovviamente, anche le str uttur e stesse hanno sempr e uno scope, così come qualsiasi altr a entità del .NET.

Un esempio intelligenteEcco 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!

16.17.18.19.20.21.22.23.

End ClassEnd Class

End Class Module Module1

Sub Main()'...

End SubEnd Module

1.2.3.

Structure ContactDim Name, Surname, PhoneNumber As String

End Structure

1.2.3.

Structure ContactPublic Name, Surname, PhoneNumber As String

End Structure

001.002.003.004.005.006.007.008.009.

Module Module1'Dato che usiamo la classe solo in questo programma, possiamo'evitare di dichiararla Public, cosa che sarebbe ideale in'una libreriaClass BubbleSorter

'Enumeratore pubblico: sarà accessibile da tutti. In questo'caso è impossibile dichiararlo come Private, poiché'uno dei prossimi metodi richiede come parametro una

Page 72: Guida visual basic

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 'CrescenteDescending 'DecrescenteNone 'Nessun ordinamento

End Enum

'Mantiene in memoria il senso di ordinamento della lista,'per evitare di riordinarla nel caso fosse richiesto due'volte lo stessoPrivate 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'arrayPrivate Buffer() As Double

'Memorizza in Buffer l'array passato come parametroPublic 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 = NothingEnd If'Copia l'array: ricordate come si comportano i tipi'reference e pensate a quali ripercussioni tale'comportamento potrà avere sul codiceBuffer = Array'Annulla CurrentOrderCurrentOrder = SortOrder.None

End Sub

'Procedura che ordina l'array secondo il senso specificatoPublic Sub Sort(ByVal Order As SortOrder)

'Se il senso è None, oppure è uguale a quello corrente,'è inutile proseguire, quindi si ferma ed esceIf (Order = SortOrder.None) Or (Order = CurrentOrder) Then

Exit SubEnd If

'Questa variabile tiene conto di tutti gli scambi'effettuatiDim 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 = 0For I As Int32 = 0 To Buffer.Length - 2

If Buffer(I) > Buffer(I + 1) ThenDim Temp As Double = Buffer(I)Buffer(I) = Buffer(I + 1)Buffer(I + 1) = TempOccurrences += 1

End IfNext

Loop Until Occurrences = 0

'Se l'ordine era discendente, inverte l'arrayIf Order = SortOrder.Descending Then

Array.Reverse(Buffer)End If

'Memorizza l'ordine

Page 73: Guida visual basic

Ricapitolando...Ricapitolando, quindi, davanti a ogni membr o si può specificar e una keywor 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à

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.

CurrentOrder = OrderEnd Sub

'Restituisce l'array ordinatoPublic Function PopArray() As Double()

Return BufferEnd Function

End Class

Sub Main()'Crea un array temporaneoDim a As Double() = {1, 6, 2, 9, 3, 4, 8}'Crea un nuovo oggetto BubbleSorterDim b As New BubbleSorter()

'Vi inserisce l'arrayb.PushArray(a)'Invoca la procedura di ordinamentob.Sort(BubbleSorter.SortOrder.Descending)

'E per ogni elemento presente nell'array finale'(quello restituito dalla funzione PopArray), ne stampa'il valore a schermoFor Each n As Double In (b.PopArray())

Console.Write(n & " ")Next

Console.ReadKey()

End SubEnd Module

Page 74: Guida visual basic

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:

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.02.03.04.05.06.07.08.09.

Property [Nome]() As [Tipo]Get

'...Return [Valore restituito]

End GetSet(ByVal value As [Tipo])

'...End Set

End Property

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.

Module Module1Class Example

'Campo pubblico di tipo Single.Public _Number As Single

'La proprietà Number media, in questo caso, l'uso'del campo _Number.Public Property Number() As Single

Get'Quando viene chiesto il valore di Number, viene'restituito il valore della variabile _Number. Si'vede che la proprietà non fa altro che manipolare'una variabile esistente e non contiene alcun'dato di per séReturn _Number

End GetSet(ByVal value As Single)

'Quando alla proprietà viene assegnato un valore,'essa modifica il contenuto di _Number impostandolo'esattamente su quel valore_Number = value

End SetEnd Property

End Class

Page 75: Guida visual basic

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 nascosta 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:

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.

Sub Main()

Dim A As New Example()

'Il codice di Main sta impostando il valore di A.Number.'Notare che una proprietà si usa esattamente come una'comunissima variabile di istanza.'La proprietà, quindi, richiama il suo blocco Set come'una procedura e assegna il valore 20 al campo A._NumberA.Number = 20 'Nella prossima riga, invece, viene richiesto il valore'di Number per poterlo scrivere a schermo. La proprietà'esegue il blocco Get come una funzione e restituisce al'chiamante (ossia il metodo/oggetto che ha invocato Get,'in questo caso Console.WriteLine) il valore di A._NumberConsole.WriteLine(A.Number) 'Per gli scettici, facciamo un controllo per vedere se'effettivamente il contenuto di A._Number è cambiato.'Potrete constatare che è uguale a 20.Console.WriteLine(A._Number)

Console.ReadLine()

End SubEnd Module

01.02.03.04.05.06.07.08.09.10.11.12.13.

Module Module1'Questa classe rappresenta un semplice sistema inerziale,'formato da un piano orizzontale scabro (con attrito) e'una massa libera di muoversi su di essoClass InertialFrame

Private _DynamicFrictionCoefficient As SinglePrivate _Mass As SinglePrivate _GravityAcceleration As Single

'Coefficiente di attrito radente (dinamico), μPublic Property DynamicFrictionCoefficient() As Single

Get

Page 76: Guida visual basic

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:

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.

Return _DynamicFrictionCoefficientEnd GetSet(ByVal value As Single)

_DynamicFrictionCoefficient = valueEnd Set

End Property

'Massa, mPublic Property Mass() As Single

GetReturn _Mass

End GetSet(ByVal value As Single)

_Mass = valueEnd Set

End Property

'Accelerazione di gravità che vale nel sistema, gPublic Property GravityAcceleration() As Single

GetReturn _GravityAcceleration

End GetSet(ByVal value As Single)

_GravityAcceleration = valueEnd Set

End Property

'Calcola e restituisce la forza di attrito che agisce'quando la massa è in motoPublic Function CalculateFrictionForce() As Single

Return (Mass * GravityAcceleration) * DynamicFrictionCoefficientEnd 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.ReadLineConsole.Write("Massa (Kg) = ")F.Mass = Console.ReadLineConsole.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 SubEnd Module

01.02.03.04.05.06.07.08.09.

Module Module1Class InertialFrame

Private _DynamicFrictionCoefficient As SinglePrivate _Mass As SinglePrivate _GravityAcceleration As Single

Public Property DynamicFrictionCoefficient() As Single

Get

Page 77: Guida visual basic

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.

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.

Return _DynamicFrictionCoefficientEnd GetSet(ByVal value As Single)

If (value > 0) And (value <= 1) Then_DynamicFrictionCoefficient = value

ElseConsole.WriteLine(value & " non è un valore consentito!")Console.WriteLine("Coefficiente attrito dinamico = 0.1")_DynamicFrictionCoefficient = 0.1

End IfEnd Set

End Property

Public Property Mass() As SingleGet

Return _MassEnd GetSet(ByVal value As Single)

If value > 0 Then_Mass = value

ElseConsole.WriteLine(value & " non è un valore consentito!")Console.WriteLine("Massa = 1")_Mass = 1

End IfEnd Set

End Property

Public Property GravityAcceleration() As SingleGet

Return _GravityAccelerationEnd GetSet(ByVal value As Single)

_GravityAcceleration = Math.Abs(value)End Set

End Property

Public Function CalculateFrictionForce() As SingleReturn (Mass * GravityAcceleration) * DynamicFrictionCoefficient

End Function

End Class

'...End Module

Page 78: Guida visual basic

Come esempio user ò questa pr opr ietà:

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 :

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 :

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

oppur e

viene sostituita con la cor r ispondente r iga:

oppur e:

Ad esempio, il seguente codice:

viene tr asfor mato, dur ante la compilazione, in:

01.02.03.04.05.06.07.08.09.10.11.12.

Property Number() As SingleGet

Return _NumberEnd GetSet(ByVal value As Single)

If (value > 30) And (value < 100) Then_Number = value

Else_Number = 31

End IfEnd Set

End Property

1.2.3.

Function get_Number() As SingleReturn _Number

End Function

1.2.3.4.5.6.7.

Sub set_Number(ByVal value As Single)If (value > 30) And (value < 100) Then

_Number = valueElse

_Number = 31End If

End Sub

1. [Proprietà] = [Valore]

1. [Valore] = [Proprietà]

1. set_[Nome Proprietà]([Valore])

1. [Valore] = get_[Nome Proprietà]

1.2.3.

Dim A As New ExampleA.Number = 20Console.WriteLine(A.Number)

1.2.3.

Dim A As New ExampleA.set_Number(20)Console.WriteLine(A.get_Number())

Page 79: Guida visual basic

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 amewor k .NET.

Page 80: Guida visual basic

A21. Le Proprietà - Parte II

Proprietà ReadOnly e WriteOnlyFin'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 keywor 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:

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:

1.2.3.4.5.6.

ReadOnly Property [Nome]() As [Tipo]Get

'...Return [Valore]

End GetEnd Property

01.02.03.04.05.06.

Class CubePrivate _SideLength As SinglePrivate _Density As Single Public Property SideLength() As Single

Page 81: Guida visual basic

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:

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.

GetReturn _SideLength

End GetSet(ByVal value As Single)

If value > 0 Then_SideLength = value

Else_SideLength = 1

End IfEnd Set

End Property Public Property Density() As Single

GetReturn _Density

End GetSet(ByVal value As Single)

If value > 0 Then_Density = value

Else_Density = 1

End IfEnd Set

End Property Public ReadOnly Property SurfaceArea() As Single

GetReturn (SideLength ^ 2)

End GetEnd Property Public ReadOnly Property Volume() As Single

GetReturn (SideLength ^ 3)

End GetEnd Property Public ReadOnly Property Mass() As Single

GetReturn (Volume * Density)

End GetEnd Property

End Class

01.02.03.04.05.06.07.08.09.10.11.12.13.14.15.16.17.18.19.20.21.

Module Esempio3Class LogFile

Private _FileName As StringPrivate _CreationTime As Date

'Niente deve modificare il nome del file, altrimenti'potrebbero verificarsi errori nella lettura o scrittura'dello stesso, oppure si potrebbe chiudere un file'che non esiste ancoraPublic ReadOnly Property FileName() As String

GetReturn _FileName

End GetEnd Property

'Allo stesso modo non si pu� modificare la data di'creazione di un file: una volta creato, viene'prelevata l'ora e il giorno e impostata la'variabile. Se potesse essere modificata'non avrebbe più alcun significato

Page 82: Guida visual basic

Una nota sui tipi referenceC'è 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:

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 egnando 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.

22.23.24.25.26.27.28.29.30.31.32.

Public ReadOnly Property CreationTime() As DateGet

Return _CreationTimeEnd Get

End Property

Public Sub New(ByVal Path As String)_FileName = Path_CreationTime = Date.Now

End SubEnd Class

End Module

01.02.03.04.05.06.07.08.09.10.11.12.13.14.15.16.

Class ASystemPrivate _Box As Cube Public ReadOnly Property Box() As Cube

GetReturn _Box

End GetEnd Property '...

End Class '... Dim S As New ASystem()S.Box.SideLength = 4

Page 83: Guida visual basic

A22. Le Proprietà - Parte III

Proprietà con parametriNei 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.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.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.

Module Module1'Classe che rappresenta un estrattore di numeri'casualiClass NumberExtractor

Private _ExtractedNumbers() As Byte'Generatore di numeri casuali. Random è una classe'del namespace SystemPrivate Rnd As Random

'Questa proprietà ha un parametro, Index, che'specifica a quale posizione dell'array si intende recarsi'per prelevarne il valore. Nonostante l'array abbia solo 6'elementi di tipo Byte, l'indice viene comunemente sempre'indicato come intero a 32 bit. È una specie di'convenzione, forse derivante dalla maggior facilità di'elaborazione su macchine a 32 bitPublic ReadOnly Property ExtractedNumbers(ByVal Index As Int32) As Byte

GetIf (Index >= 0) And (Index < _ExtractedNumbers.Length) Then

Return _ExtractedNumbers(Index)Else

Return 0End If

End GetEnd Property

Public Sub New()

'Essendo di tipo reference, si deve creare un nuovo'oggetto Random e assegnarlo a Rnd. La ragione per cui'Rnd è un membro di classe consiste nel fatto'che se fosse stata variabile temporanea del corpo'della procedura ExtractNumbers, sarebbero usciti'gli stessi numeri. Questo perchè la sequenza'pseudocasuale creata dalla classe non cambia se'non glielo si comunica espressamente usando un altro'costruttore. Non tratterò questo argomento oraRnd = New Random()ReDim _ExtractedNumbers(5)

End Sub

Public Sub ExtractNumbers()'Estrae 6 numeri casuali tra 1 e 90 e li pone nell'arrayFor I As Int32 = 0 To 5

_ExtractedNumbers(I) = Rnd.Next(1, 91)Next

End SubEnd Class

Sub Main()

Dim E As New NumberExtractor()

E.ExtractNumbers()Console.WriteLine("Numeri estratti: ")For I As Int32 = 0 To 5

Console.Write(E.ExtractedNumbers(I) & " ")

Page 84: Guida visual basic

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:

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 defaultCon 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:

57.58.59.60.

Next

Console.ReadKey()End Sub

End Module

1.2.3.4.5.

Public ReadOnly Property ExtractedNumbers() As Byte()Get

Return _ExtractedNumbersEnd Get

End Property

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.32.

Module Module1Class NumberExtractor

Private _ExtractedNumbers() As BytePrivate Rnd As Random

'Una proprietà di default si dichiara come una'normalissima proprietà, ma anteponendo allo specificatore'di accesso la keyword DefaultDefault Public ReadOnly Property ExtractedNumbers(ByVal Index As Int32) As Byte

GetIf (Index >= 0) And (Index < _ExtractedNumbers.Length) Then

Return _ExtractedNumbers(Index)Else

Return 0End If

End GetEnd Property

Public Sub New()

Rnd = New Random()ReDim _ExtractedNumbers(5)

End Sub

Public Sub ExtractNumbers()For I As Int32 = 0 To 5

_ExtractedNumbers(I) = Rnd.Next(1, 91)Next

End SubEnd Class

Sub Main()

Page 85: Guida visual basic

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 indows 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:

Ebbene, Char s è una pr opr ietà di default, ossia è possibile scr iver e:

Get e Set con spec ificatori di accessoAnche 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 keywor 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

33.34.35.36.37.38.39.40.41.42.43.44.45.46.47.

Dim E As New NumberExtractor()

E.ExtractNumbers()Console.WriteLine("Numeri estratti: ")For I As Int32 = 0 To 5

'Ecco l'utilità delle proprietà di default: si possono'richiamare anche omettendone il nome. In questo caso'E(I) è equivalente a scrivere E.ExtractedNumbers(I),'ma poiché ExtractedNumbers è di default,'viene desunta automaticamenteConsole.Write(E(I) & " ")

Next

Console.ReadKey()End Sub

End Module

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

1.2.3.

Dim S As String = "Ciao"Dim C As Char = S(1)' > C = "i"

Page 86: Guida visual basic

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:

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 keywordIn questi ultimi capitoli ho spiegato un bel po' di keywor d diver se, e specialmente nelle pr opr ietà può accader e di dover

specificar e molte keywor 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):

E or a quelle che conoscete sono ancor a poche... pr ovate voi a scr iver e una pr opr ietà del gener e:

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

1.2.3.4.5.6.7.8.

Public Property A() As ByteGet

'...End GetPrivate Set(ByVal value As Byte)

'...End Set

End Property

1. [Default] [ReadOnly/WriteOnly] [Public/Friend/Private/...] Property ...

1.

2.3.4.5.

Default Protected Friend Overridable Overloads ReadOnly Property A(ByVal Index As Int32) AsByteGet

'...End Get

End Property

Page 87: Guida visual basic

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 uno 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 shared, o condiv 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.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.32.33.34.35.36.37.38.39.40.41.42.43.44.45.46.47.48.49.50.

Module Module1

Class CubePrivate _SideLength As SinglePrivate _Density As Single

'Questo campo è Shared, condiviso. Come vedete,'per dichiarare un membro come tale, si pone la'keyword Shared dopo lo specificatore di accesso. Questa'variabile conterrà il numero di cubi creati'dal nostro programma.'N.B.: I campi Shared sono di default Private...Private Shared _CubesCount As Int32 = 0

Public Property SideLength() As Single

GetReturn _SideLength

End GetSet(ByVal value As Single)

If value > 0 Then_SideLength = value

Else_SideLength = 1

End IfEnd Set

End Property

Public Property Density() As SingleGet

Return _DensityEnd GetSet(ByVal value As Single)

If value > 0 Then_Density = value

Else_Density = 1

End IfEnd Set

End Property

Public ReadOnly Property SurfaceArea() As SingleGet

Return (SideLength ^ 2)End Get

End Property

Public ReadOnly Property Volume() As SingleGet

Return (SideLength ^ 3)

Page 88: Guida visual basic

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;

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 GetEnd Property

Public ReadOnly Property Mass() As Single

GetReturn (Volume * Density)

End GetEnd Property

'Allo stesso modo, la proprietà che espone il membro'shared deve essere anch'essa sharedPublic Shared ReadOnly Property CubesCount() As Int32

GetReturn _CubesCount

End GetEnd 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 memoriaSub New()

_CubesCount += 1End Sub

End Class Sub Main()

Dim Cube1 As New Cube()Cube1.SideLength = 1Cube1.Density = 2700

Dim Cube2 As New Cube()Cube2.SideLength = 0.9Cube2.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'modoConsole.WriteLine(Cube.CubesCount)

Console.ReadKey()

End SubEnd Module

Page 89: Guida visual basic

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

Classi SharedCome!?!? 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é:

Il codice sopr a r ipor tato pr ovocher à il seguente er r or e:

Inoltr e, è anche possibile acceder e a membr i di un modulo senza specificar e il nome del modulo, ad esempio:

Questo fenomeno è anche noto col nome di Imports 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:

1.2.3.4.5.6.7.

Module Module1Shared Sub Hello() End Sub '...

End Sub

1. Methods in a Module cannot be declared 'Shared'.

01.02.03.04.05.06.07.08.09.10.11.12.

Module Module2Sub Hello()

Console.WriteLine("Hello!")End Sub

End Module Module Module1

Sub Main()Hello() ' = Module2.Hello()Console.ReadKey()

End SubEnd Module

01.02.03.04.05.06.07.08.09.10.11.12.13.14.15.

Module Module2Sub Hello()

Console.WriteLine("Hello Module2!")End Sub

End Module Class Class2

Shared Sub Hello()Console.WriteLine("Hello Class2!")

End SubEnd Class Module Module1

Sub Main()

Page 90: Guida visual basic

16.17.18.19.20.21.22.23.

'Per richiamare l'Hello di Class2, è sempre'necessaria questa sintassi:Class2.Hello()'Per invocare l'Hello di Module2, invece, basta'questa, a causa dell'Imports StaticoHello()Console.ReadKey()

End SubEnd Module

Page 91: Guida visual basic

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.

ArrayListSi 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ì:

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 espande 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:

1. Dim A(100) As Int32

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.

Module Module1

Class Cube'...

End Class Sub Main()

'Crea un nuovo arraylistDim Cubes As New ArrayList

Console.WriteLine("Inserismento cubi:")Console.WriteLine()Dim Cmd As CharDo

Console.WriteLine()Dim C As New Cube'Scrive il numero del cuboConsole.Write((Cubes.Count + 1) & " - ")Console.Write("Lato (m): ")C.SideLength = Console.ReadLine

Console.Write(" Densità (kg/m<sup>3</sup>): ")C.Density = Console.ReadLine

'Aggiunge un nuovo cubo alla collezioneCubes.Add(C)

Console.WriteLine("Termina inserimento? y/n")

Page 92: Guida visual basic

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.

HashtableL'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 chiave (key) con un valore (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:

Notar e che è anche possibile far e il contr ar io, ossia:

30.31.32.33.34.35.36.37.38.39.40.41.42.43.44.45.46.47.

Cmd = Console.ReadKey().KeyCharLoop Until Char.ToLower(Cmd) = "y"

'Calcola la massa totale di tutti i cubi nella listaDim TotalMass As Single = 0'Notate che l'ArrayList si può usare come un'normale array. L'unica differenza sta nel fatto che'esso espone la proprietà Count al posto di Length.'In genere, tutte le liste espongono Count, che comunque'ha sempre lo stesso significato: restituisce il numero'di elementi nella listaFor I As Int32 = 0 To Cubes.Count - 1

TotalMass += Cubes(I).MassNext

Console.WriteLine("Massa totale: " & TotalMass)Console.ReadKey()

End SubEnd Module

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 stringaH.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"))

1.2.

Dim H As New Hashtable

Page 93: Guida visual basic

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 nulla 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:

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.

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

chiave.

3.4.5.

H.Add(1000, "Acqua")H.Add(2700, "Alluminio")H.Add(10490, "Argento")H.Add(8800, "Nichel")

1.2.3.4.

For Each V As String In H.Values'Enumera tutti gli elementi di H' V = "Acqua", "Alluminio", "Argento", ...

Next

1.2.3.4.

For Each K As Int32 In H.Keys'Enumera tutte le chiavi'K = 1000, 2700, 10490, ...

Next

Page 94: Guida visual basic

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 ima 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.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.32.33.34.35.36.37.38.39.40.41.42.43.44.45.46.47.48.49.

Module Module1Class Document

'Campo statico che contiene tutti i documenti'aperi fin'oraPrivate Shared Documents As New Hashtable'Identificatore del documento: un paragrafo nel prossimo'capitolo spiegherà in dettaglio i significato e'l'utilità delle variabili ReadOnlyPrivate ReadOnly _ID As Int16'Nome del file e testo contenuto in essoPrivate ReadOnly _FileName, _Text As String

Public ReadOnly Property ID() As Int16

GetReturn _ID

End GetEnd Property

Public ReadOnly Property FileName() As String

GetReturn _FileName

End GetEnd Property

Public ReadOnly Property Text() As String

GetReturn _Text

End GetEnd Property

'Da notare il costruttore Private: nessun client al di'fuori della classe può inizializzare il nuovo'oggetto. Solo il metodo factory lo può farePrivate Sub New(ByVal ID As Int16, ByVal Path As String)

Me._ID = IDMe._FileName = PathMe._Text = IO.File.ReadAllText(Path)'Me fa riferimento alla classe stessaDocuments.Add(ID, Me)

End Sub

'Il metodo factory crea un documento se non esiste l'ID'e se il percorso su disco è diverso, altrimenti'restituisce il documento che esiste giàPublic Shared Function Create(ByVal ID As Int16, _

ByVal Path As String) As DocumentIf Documents.ContainsKey(ID) Then

'Ottiene il documento già esistente con questo ID

Page 95: Guida visual basic

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:

Classi factory e oggetti immutabiliUna 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 immutabili: 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:

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.

Dim D As Document = Documents(ID)'Se coincidono sia l'ID che il nome del file,'allora restituisce l'oggetto già esistenteIf D.FileName = Path Then

Return DElse

'Altrimenti restituisce Nothing, dato che non'possono esistere due documenti con uguale ID,'o si farebbe confusioneReturn Nothing

End IfEnd If'Se non esiste un documento con questo ID, lo creaReturn New Document(ID, Path)

End FunctionEnd Class

Sub Main()

Dim D As Document = Document.Create(0, "C:\testo.txt")Dim E As Document = Document.Create(0, "C:\testo.txt")Dim F As Document = Document.Create(0, "C:\file.txt")Dim G As Document = Document.Create(1, "C:\file.txt")

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

End SubEnd Module

1.2.

TrueTrue

1.2.3.4.5.

'Questa stringa è immutabileDim S As String = "Ciao"'Viene creata una nuova stringa temporanea con valore "Buongiorno"'e assegnata a S. "Ciao" verrà distrutta dal Garbage ColletcionS = "Buongiorno"

Page 96: Guida visual basic

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 keywor 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:

Quando si fa cor r er e il pr ogr amma si ha questo output:

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.

Variabili ReadOnlyHo 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,

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.

Module Module1'ClasseClass Esempio

'Costante pubblicaPublic Const Costante As Byte = 56'Variabile pubblica che non pu� essere modificataPublic ReadOnly Nome As String'Variabile privataPrivate Variabile As Char

'Costruttore della classe: accetta un parametroSub New(ByVal Nome As String)

Console.WriteLine("Sto inizializzando un oggetto Esempio...")'Le variabili ReadOnly sono assegnabli solo nel'costruttore della classeMe.Nome = NomeMe.Variabile = "c"

End SubEnd Class

'Costruttore del ModuloSub New()

Console.WriteLine("Sto inizializzando il Modulo...")End Sub

Sub Main()

Dim E As New Esempio("Ciao")E.Nome = "Io" ' Sbagliato: Nome è ReadOnlyConsole.ReadKey()

End SubEnd Module

1.2.

Sto inizializzando il Modulo...Sto inizializzando un oggetto Esempio...

Page 97: Guida visual basic

cosa più impor tante, possono assumer e il valor e der ivante da un'espr essione o da una funzione. Ad esempio:

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 SharedI costr uttor i Shar ed sono detti costruttor i statici e vengono eseguiti solamente quando è cr eata la pr ima istanza di

una data classe: per questo sono detti anche costruttor 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:

L'ouput è:

1.2.

Public Const Data_Creazione_C As Date = Date.Now 'Sbagliato!Public ReadOnly Data_Creazione_V As Date = Date.Now ' Giusto

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.32.33.34.35.36.37.38.

Module EsempioClass Errors

'Variabile statica che rappresenta un oggetto in grado'di scrivere su un filePublic Shared File As IO.StreamWriter

'Costruttore statico che inizializza l'oggetto StreamWriter'Da notare è che un costruttore statico NON può avere'parametri: il motivo è semplice. Se li potesse avere'e ci fossero più costruttori normali il compilatore'non saprebbe cosa fare, poichè Shared Sub New'potrebbe avere parametri diversi dagli altriShared Sub New()

Console.WriteLine("Costruttore statico: sto creando il log...")File = New IO.StreamWriter("Errors.log")

End Sub

'Questo è il costruttore normaleSub New()

Console.WriteLine("Costruttore normale: sto creando un oggetto...")End Sub

Public Sub WriteLine(ByVal Text As String)

File.WriteLine(Text)End Sub

End Class

Sub Main()'Qui viene eseguito il costruttore statico e quello normaleDim E1 As New Errors'Qui solo quello normaleDim E2 As New Errors

E1.WriteLine("Nessun errore")

Console.ReadKey()

End SubEnd Module

1.2.3.

Costruttore statico: sto creando il log...Costruttore normale: sto creando un oggetto...Costruttore normale: sto creando un oggetto...

Page 98: Guida visual basic

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 PrivateI 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.

Page 99: Guida visual basic

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 amewor 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:

Come si vede, la sintassi è molto simile a quella usata per dichiar ar e una funzione, ad eccezione della keywor 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 SEMPRE 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 massimo 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 deve 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)

1.2.3.4.

Shared Operator [Simbolo]([Parametri]) As [Tipo Restituito]'...Return [Risultato]

End Operator

Page 100: Guida visual basic

< (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.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.

Module Module1

Public Structure Fraction'Numeratore e denominatorePrivate _Numerator, _Denumerator As Int32

Public Property Numerator() As Int32

GetReturn _Numerator

End GetSet(ByVal value As Int32)

_Numerator = valueEnd Set

End Property

Public Property Denumerator() As Int32Get

Return _DenumeratorEnd GetSet(ByVal value As Int32)

If value <> 0 Then_Denumerator = value

Else'Il denominatore non può mai essere 0'Dovremmo lanciare un'eccezione, ma vedremo più'avanti come si fa. Per ora lo impostiamo a uno_Denumerator = 1

End IfEnd Set

End Property

'Costruttore con due parametri, che inizializza numeratore'e denominatoreSub New(ByVal N As Int32, ByVal D As Int32)

Me.Numerator = NMe.Denumerator = D

End Sub

'Restituisce la Fraction sottoforma di stringaFunction Show() As String

Return Me.Numerator & " / " & Me.DenumeratorEnd Function

'Semplifica la FractionSub Semplify()

Page 101: Guida visual basic

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 divisoreX = 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 -1If (Me.Numerator < 0) And (Me.Denumerator < 0) Then

Me.Numerator /= -1Me.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 MCDDo Until ((Me.Numerator Mod X = 0) And (Me.Denumerator Mod X = 0))

X -= 1Loop

'Divide numeratore e denominatore per l'MCDMe.Numerator /= XMe.Denumerator /= X

End Sub

'Somma due frazioni e restituisce la sommaShared Operator +(ByVal F1 As Fraction, ByVal F2 As Fraction) _

As FractionDim F3 As Fraction

'Se i denumeratori sono uguali, si limita a sommare'i numeratoriIf F1.Denumerator = F2.Denumerator Then

F3.Denumerator = F1.DenumeratorF3.Numerator = F1.Numerator + F2.Numerator

Else'Altrimenti esegue tutta l'operazione'x a x*b + a*y'- + - = ---------'y b y*bF3.Denumerator = F1.Denumerator * F2.DenumeratorF3.Numerator = F1.Numerator * F2.Denumerator + F2.Numerator * F1.Denumerator

End If

'Semplifica la FractionF3.Semplify()Return F3

End Operator

'Sottrae due Fraction e restituisce la differenzaShared Operator -(ByVal F1 As Fraction, ByVal F2 As Fraction) _

As Fraction'Somma l'opposto del secondo membroF2.Numerator = -F2.NumeratorReturn F1 + F2

End Operator

'Moltiplica due frazioni e restituisce il prodottoShared 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'denominatoriDim F3 As Fraction = New Fraction(F1.Numerator * F2.Numerator, _

F1.Denumerator * F2.Denumerator)

Page 102: Guida visual basic

CTypeCType è 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:

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 sempre se una conver sione è Widening (di espansione, ossia senza per dita di dati) o Narrowing (di

r iduzione, con possibile per dita di dati). Per questo motivo si deve specificar e una delle suddette keywor 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:

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.

F3.Semplify()Return F3

End Operator

'Divide due frazioni e restituisce il quozienteShared Operator /(ByVal F1 As Fraction, ByVal F2 As Fraction) _

As Fraction'Inizializza F3 eseguendo l'operazione:'a x a y'- / - = - * -'b y b xDim F3 As Fraction = New Fraction(F1.Numerator * F2.Denumerator, _

F1.Denumerator * F2.Numerator)F3.Semplify()Return F3

End OperatorEnd 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 + BConsole.WriteLine("A + B = " & C.Show())

Console.ReadKey()

End SubEnd Module

1.2.3.

Dim F As Fraction'...Dim D As Double = CType(F, Double)

1.2.3.

Shared Widening Operator CType(ByVal F As Fraction) As DoubleReturn F.Numerator / F.Denumerator

End Operator

Page 103: Guida visual basic

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:

Operatori di confrontoGli 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.

È 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.

Shared Narrowing Operator CType(ByVal F As Fraction) As Int32'Notare l'operatore \ di divisione intera (per maggiori'informazioni sulla divisione intera, vedere capitolo A6)Return F.Numerator \ F.Denumerator

End Operator

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 valoriReturn (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

1.2.3.4.5.

'...a = 10b = 20Console.WriteLine("a è maggiore di b: " & (a > b))'A schermo compare: "a è maggiore di b: False"

Page 104: Guida visual basic

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 amewor 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è.

MemorizzazioneIniziamo 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

sempre 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, valor 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

Page 105: Guida visual basic

essendo impor tanti, non sono le più impor tanti.

Paradigma di programmazione ad oggettiEd 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.

Page 106: Guida visual basic

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 ivata, 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 ereditar ietà indiretta.

Passiamo or a a veder e come si dichiar a una classe der ivata:

La keywor 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à:

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

1.2.3.4.

Class [Nome]Inherits [Classe base]'Membri della classe

End Class

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

GetReturn FirstName & " " & LastName

End GetEnd Property

End Class 'Lo studente, ovviamente, è una personaClass Student

'Student eredita da PersonInherits Person

'In più, definisce anche questi campi pubblici'La scuola frequentataPublic School As String'E l'anno di corsoPublic Grade As Byte

End Class

Page 107: Guida visual basic

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 ivate 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 Protected 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.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.

Module EsempioClass Person

'Due campi protectedProtected _FirstName, _LastName As String'Un campo private readonly: non c'è ragione di rendere'questo campo Protected poichè la data di nascita non'cambia ed è sempre accessibile tramite la proprietà'pubblica BirthDayPrivate ReadOnly _BirthDay As Date

Public Property FirstName() As String

GetReturn _FirstName

End GetSet(ByVal Value As String)

If Value <> "" Then_FirstName = Value

End IfEnd Set

End Property

Public Property LastName() As StringGet

Return _LastNameEnd GetSet(ByVal Value As String)

If Value <> "" Then

Page 108: Guida visual basic

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 = ValueEnd If

End SetEnd Property

Public ReadOnly Property BirthDay() As Date

GetReturn _BirthDay

End GetEnd Property

Public ReadOnly Property CompleteName() As String

GetReturn _FirstName & " " & _LastName

End GetEnd Property

'Costruttore che accetta tra parametri obbligatoriSub New(ByVal FirstName As String, ByVal LastName As String, _

ByVal BirthDay As Date)Me.FirstName = FirstNameMe.LastName = LastNameMe._BirthDay = BirthDay

End SubEnd Class

'Lo studente, ovviamente, è una personaClass Student

'Student eredita da PersonInherits Person

'La scuola frequentataPrivate _School As String'E l'anno di corsoPrivate _Grade As Byte

Public Property School() As String

GetReturn _School

End GetSet(ByVal Value As String)

If Value <> "" Then_School = Value

End IfEnd Set

End Property

Public Property Grade() As ByteGet

Return _GradeEnd GetSet(ByVal Value As Byte)

If Value > 0 Then_Grade = Value

End IfEnd 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 studentePublic 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 baseReturn _FirstName & " " & _LastName & ", nato il " & _BirthDay.ToShortDateString & " frequenta l'anno " & __Grade & " alla scuola " & _School

End GetEnd Property

Page 109: Guida visual basic

L'output:

(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 sempre r ichiamar e il costr uttor e della classe base, e questo avviene tr amite la keywor 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.

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.

'Altra clausola importante: il costruttore della classe'derivata deve sempre richiamare il costruttore della'classe baseSub 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 = SchoolMe.Grade = Grade

End SubEnd 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 spiegateConsole.WriteLine(S.CompleteName)'E in più ha anche i suoi nuovi membriConsole.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 PersonP = SConsole.WriteLine(P.CompleteName)

Console.ReadKey()

End SubEnd Module

1.2.3.4.5.

Pinco PallinoTizio CaioTizio Caio, nato il 23/5/1992 frequenta l'anno 2 alla scuola Liceo Classico UgoFoscoloTizio Caio

Page 110: Guida visual basic

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 keywor 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 nome

ug uale e sig nature 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 ShowTex 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:

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:

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

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.

Module Module1Class Person

Protected _FirstName, _LastName As StringPrivate ReadOnly _BirthDay As Date

Public Property FirstName() As String

GetReturn _FirstName

End GetSet(ByVal Value As String)

If Value <> "" Then_FirstName = Value

End IfEnd Set

End Property

'Questa proprietà sarà ridefinita nella classe TeacherPublic Overridable Property LastName() As String

GetReturn _LastName

End GetSet(ByVal Value As String)

If Value <> "" Then_LastName = Value

End IfEnd Set

End Property

Public ReadOnly Property BirthDay() As DateGet

Return _BirthDayEnd Get

End Property

'Questa proprietà sarà ridefinita nella classe TeacherPublic Overridable ReadOnly Property CompleteName() As String

GetReturn _FirstName & " " & _LastName

End Get

Page 111: Guida visual basic

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 obbligatoriSub New(ByVal FirstName As String, ByVal LastName As String, _

ByVal BirthDay As Date)Me.FirstName = FirstNameMe.LastName = LastNameMe._BirthDay = BirthDay

End SubEnd Class

Class Teacher

Inherits PersonPrivate _Subject As String

Public Property Subject() As String

GetReturn _Subject

End GetSet(ByVal Value As String)

If Value <> "" Then_Subject = Value

End IfEnd Set

End Property

'Ridefinisce la proprietà LastName in modo da aggiungere'anche il titolo di Professore al cognomePublic Overrides Property LastName() As String

GetReturn "Prof. " & _LastName

End GetSet(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 ValueMyBase.LastName = Value

End SetEnd Property

'Ridefinisce la proprietà CompleteName in modo da'aggiungere anche la materia insegnata e il titolo di'ProfessorePublic Overrides ReadOnly Property CompleteName() As String

Get'Anche qui viene richiamata la vecchia versione di'CompleteName, che restituisce semplicemente il'nome completoReturn "Prof. " & MyBase.CompleteName & _", dottore in " & Subject

End GetEnd 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 SubEnd Class

Sub Main()

Dim T As New Teacher("Mario", "Rossi", Date.Parse("01/01/1950"), _"Letteratura italiana")

'Usiamo le nuove proprietà, ridefinite nella classe'derivataConsole.WriteLine(T.LastName)'> "Prof. Rossi"

Page 112: Guida visual basic

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:

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:

È 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:

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

113.114.115.116.117.

Console.WriteLine(T.CompleteName)'> "Prof. Mario Rossi, dottore in Letteratura italiana"

Console.ReadKey()

End SubEnd Module

1.2.3.

Dim P As Person = TConsole.WriteLine(P.LastName)Console.WriteLine(P.CompleteName)

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

01.02.03.04.05.06.07.08.09.10.11.12.13.14.15.16.17.18.19.20.21.

Class APublic 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 basePublic Overrides Sub ShowText()

Console.WriteLine("B: Testo di prova")End Sub

End Class

Page 113: Guida visual basic

ShadowingSe 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 keywor d da

usar e è Shadows, 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:

Come si vede, la sintassi è come quella di Over r ides: Shadows 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 Shadows, viene omessa la keywor d Dim.

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.

Module EsempioClass Base

Friend Control As ByteEnd Class

Class Deriv

Inherits BasePublic Shadows Sub Control(ByVal Msg As String)

Console.WriteLine("Control, seconda versione: " & Msg)End Sub

End Class

Sub Main()Dim B As New BaseDim D As New Deriv

'Entrambe le classe hanno lo stesso membro di nome'"Control", ma nella prima è un campo friend,'mentre nella seconda è una procedura pubblicaConsole.WriteLine(B.Control)D.Control("Ciao")

Console.ReadKey()

End SubEnd Module

Page 114: Guida visual basic

A31. Conversioni di dati

Il Fr amewor 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 widening (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, narrowing

(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.

CTypeCType è 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:

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.

DirectCastDir 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:

1.2.3.

Dim I As Int32 = 50'Converte I in un valore ByteDim B As Byte = CType(I, Byte)

Page 115: Guida visual basic

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.

TryCastTr 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.

ConvertEsiste, poi, una classe statica definita del namespace System - il namespace più impor tante di tutto il Fr amewor 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:

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 wikipedia.

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.

01.02.03.04.05.06.07.08.09.

Dim I As Int32 = 34Dim D As Double = Convert.ToDouble(I)' D = 34.0Dim S As String = Convert.ToString(D)' S = "34"Dim N As Single = Convert.ToSingle(S)' N = 34.0Dim K As String = "31/12/2008"Dim A As Date = Convert.ToDate(K)

Page 116: Guida visual basic

ParseUn'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 amewor 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:

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:

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

TryParseUna 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:

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.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!

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

1. TryParse(ByVal s As String, ByRef result As [Tipo]) As Boolean

01.02.03.04.05.06.07.08.09.

Dim S As String = "56/0/1000"'S contiene una data non validaDim D As Date If Date.TryParse(S, D) Then

Console.WriteLine(D.ToLongDateString())Else

Console.WriteLine("Data non valida!")End If

Page 117: Guida visual basic

TypeOfTypeOf 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:

Oppur e:

Ed infine un esempio sulle inter facce, che potr ete tor nar e a guar dar e da qui a qualche capitolo:

1.2.3.4.

Dim I As Int32If TypeOf I Is Int32 Then'Questo blocco viene eseguito poichè I è di tipo Int32

End If

1.2.3.4.5.6.

Dim T As StudentIf TypeOf T Is Person Then

'Questo blocco viene eseguito perchè T, essendo Student, è'anche di tipo Person, in quanto Student è una sua classe'derivata

End If

1.2.3.4.5.

Dim K(9) As Int32If TypeOf Is IEnumerable Then

'Questo blocco viene eseguito poiché gli array implementano'sempre l'interfaccia IEnumerable

End If

Page 118: Guida visual basic

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:

Il pr imo par ametr o pr ende il nome di str ing a di formato, 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:

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:

1. Console.WriteLine("stringa", arg0, arg1, arg2, arg3, ...)

1.2.3.4.

A = 1B = 3Console.WriteLine("La somma di {0} e {1} è {2}.", A, B, A + B)'> "La somma di 1 e 3 è 4."

01.02.03.04.05.06.07.08.09.10.11.12.

Module Module1

'Restituisce il numero di secondi passati dalla data D a oggiPrivate Function GetElapsed(ByVal D As Date) As Single

Return (Date.Now - D).TotalSecondsEnd Function

'Come sopra, ma il parametro è di tipo intero e indica'un anno qualsiasiPrivate Function GetElapsed(ByVal Year As Int32) As Single

'Utilizza Year per costruire un nuovo valore Date

Page 119: Guida visual basic

Come avr ete notato, nell'esempio pr ecedente non ho usato la keywor 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 keywor d; eccone un esempio:

13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.32.33.

'e usa la precedente variante del metodo per'ottenere il risultatoReturn GetElapsed(New Date(Year, 1, 1))

End Function

'Come le due sopra, ma il parametro è di tipo stringa'e indica la dataPrivate Function GetElapsed(ByVal D As String) As Single

Return GetElapsed(Date.Parse(D))End Function

Sub Main()

'GetElapsed viene chiamata con tre tipi di parametri'diversi, ma sono tutti lecitiDim El1 As Single = GetElapsed(New Date(1987, 12, 4))Dim El2 As Single = GetElapsed(1879)Dim El3 As Single = GetElapsed("12/12/1991")

Console.ReadKey()

End Sub End Module

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.32.33.34.35.36.37.38.39.

Module Module1Class Person

Protected _FirstName, _LastName As StringPrivate ReadOnly _BirthDay As Date

Public Property FirstName() As String

GetReturn _FirstName

End GetSet(ByVal Value As String)

If Value <> "" Then_FirstName = Value

End IfEnd Set

End Property

Public Overridable Property LastName() As StringGet

Return _LastNameEnd GetSet(ByVal Value As String)

If Value <> "" Then_LastName = Value

End IfEnd Set

End Property

Public ReadOnly Property BirthDay() As DateGet

Return _BirthDayEnd Get

End Property

Public Overridable ReadOnly Property CompleteName() As StringGet

Return _FirstName & " " & _LastNameEnd Get

End Property

Page 120: Guida visual basic

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 keywor 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.

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.

'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 pocoPublic Overloads Overrides Function ToString() As String

Return CompleteNameEnd 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 modificarePublic Overloads Function ToString(ByVal FormatString As String) _

As StringDim Temp As String = FormatString'Sostituisce {F} con FirstNameTemp = Temp.Replace("{F}", _FirstName)'Sostituisce {L} con LastNameTemp = Temp.Replace("{L}", _LastName)

Return Temp

End Function

Sub New(ByVal FirstName As String, ByVal LastName As String, _ByVal BirthDay As Date)Me.FirstName = FirstNameMe.LastName = LastNameMe._BirthDay = BirthDay

End SubEnd 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 SubEnd Module

Page 121: Guida visual basic

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, Eccezioni 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:

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:

1.2.3.4.5.

Try'Codice che potrebbe generare l'eccezione

Catch [Variabile] As [Tipo Eccezione]'Gestisce l'eccezione [Tipo Eccezione]

End Try

01.02.03.04.05.06.07.08.09.10.11.12.13.14.15.16.

Module Module1Sub Main()

Dim a, b As Single'ok controlla se a e b sono coerentiDim ok As Boolean = False

Do

'Tenta di leggere i numeri da tastieraTry

Console.WriteLine("Inserire due numeri non nulli: ")a = Console.ReadLineb = Console.ReadLine'Se il codice arriva fino a questo punto, significa'che non si sono verificate eccezioniok = True

Page 122: Guida visual basic

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:

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'".

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.

Catch Ex As InvalidCastException'Se, invece, il programma arriva in questo blocco,'vuol dire che abbiamo "preso" (catch) un'eccezione'di tipo InvalidCastException, che è stata'"lanciata" dal codice precedente. Tutti i dati'relativi a quella eccezione sono ora conservati'nella variabile Ex.'Possiamo accedervi oppure no, come in questo caso,'ma sono in ogni caso informazioni utili, come'vedremo fra pocoConsole.WriteLine("I dati inseriti non sono numeri!")'I dati non sono coerenti, quindi ok = Falseok = False

End Try'Richiede gli stessi dati fino a che non si tratta'di due numeri

Loop Until ok

'Esegue il controllo su b e poi effettua la divisioneIf b <> 0 Then

Console.WriteLine("{0} / {1} = {2}", a, b, a / b)Else

Console.WriteLine("Divisione impossibile!")End If

Console.ReadKey()

End SubEnd Module

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.32.

Module Module1Sub Main()

'a e b sono interi short, ossia possono assumere'valori da -32768 a +32767Dim a, b As Int16Dim ok As Boolean = False

Do

TryConsole.WriteLine("Inserire due numeri non nulli: ")a = Console.ReadLineb = Console.ReadLineok = True

Catch Ex As Exception'Catturiamo una qualsiasi eccezione e stampiamo il'messaggio'ad essa relativo. Il messaggio è contenuto nella'proprietà Message dell'oggetto Ex.Console.WriteLine(Ex.Message)ok = False

End TryLoop Until ok

If b <> 0 Then

Console.WriteLine("{0} / {1} = {2}", a, b, a / b)Else

Console.WriteLine("Divisione impossibile!")End If

Console.ReadKey()

End SubEnd Module

Page 123: Guida visual basic

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 FinallyIl 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:

Le istr uzioni eseguite sar anno:

Lanciare un'eccezione e creare eccezioni personalizzateAmmettiamo 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 amewor 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.02.03.04.05.06.07.08.09.10.11.12.13.14.

TryABCDEF

Catch Ex As ExceptionGH

FinallyIL

End Try

01.02.03.04.05.06.07.08.09.

ABC'Eccezione: salta nel blocco CatchGH'Alla fine esegue comunque il FinallyIL

01.02.03.04.05.06.07.08.09.10.11.12.13.14.15.16.

Module Module1'Questa classe rappresenta l'errore lanciato quando una'password imessa è sbagliata. Per convenzione, tutte le'classi che rappresentano un'eccezione devono terminare'con la parola "Exception" Class IncorrectPasswordException

Inherits System.Exception 'Eredita da Exception

'Queste proprietà ridefiniscono quelle della classe'Exception tramite polimorfismo, perciò sono'dichiarate Overrides 'Sovrascrive il messaggio di errorePublic Overrides ReadOnly Property Message() As String

Get

Page 124: Guida visual basic

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 ectPasswor dEx ception nello stessa linea di codice in cui

l'abbiamo lanciato.

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.

Return "La password inserita � sbagliata!"End Get

End Property

'Modifica il link di aiutoPublic Overrides Property HelpLink() As String

GetReturn "http://totem.altervista.org"

End GetSet(ByVal Value As String)

MyBase.HelpLink = valueEnd 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

Try

Console.WriteLine("Inserire la password:")NewPass = Console.ReadLineIf NewPass <> Pass Then

'Lancia l'eccezione usando la keyword ThrowThrow New IncorrectPasswordException

End IfCatch IPE As IncorrectPasswordException

'Visualizza il messaggioConsole.WriteLine(IPE.Message)'E il link d'aiutoConsole.WriteLine("Help: " & IPE.HelpLink)

End Try

Console.ReadKey()End Sub

End Module

Page 125: Guida visual basic

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 amewor k è molto diver so, e or a vediamo come oper a.

Garbage CollectionQuesto è il nome del pr ocesso sul quale si basa la gestione della memor ia del Fr amewor 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.

FinalizeIl 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 mai 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,

Page 126: Guida visual basic

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.HasShutdownStar 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 non deve mai esser e fatto in

un'applicazione r eale, poichè potr ebbe compr ometter ne le pr estazioni.

L'output sar à:

Come si vede, l'oggetto viene distr utto dopo 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:

Ciò che appar ir à sullo scher mo è:

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.

01.02.03.04.05.06.07.08.09.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.

Module Module1Class Oggetto

Sub New()Console.WriteLine("Un oggetto sta per essere creato.")

End Sub'La procedura Finalize è definita in System.Object, quindi,'per ridefinirla dobbiamo usare il polimorfismo. Inoltre'deve essere dichiarata Protected, poichè non può'essere richiamata da altro ente se non dal GC e allo'stesso tempo è ereditabileProtected Overrides Sub Finalize()

Console.WriteLine("Un oggetto sta per essere distrutto.")'Blocca il programma per 4 secondi circa, consentendoci'di vedere cosa viene scritto a schermoSystem.Threading.Thread.CurrentThread.Sleep(4000)

End SubEnd Class

Sub Main()

Dim O As New OggettoConsole.WriteLine("Oggetto = Nothing")Console.WriteLine("L'applicazione sta per terminare.")

End SubEnd Module

1.2.3.4.

Un oggetto sta per essere creato.Oggetto = NothingL'applicazione sta per terminare.Un oggetto sta per essere distrutto.

01.02.03.04.05.06.07.08.09.10.11.12.13.14.

Sub Main()Dim O As New OggettoO = NothingConsole.WriteLine("Oggetto = Nothing")

'NON PROVATECI A CASA!'Forza una garbage collectionGC.Collect()'Attende che tutti i metodi Finalize siano stati eseguitiGC.WaitForPendingFinalizers()

Console.WriteLine("L'applicazione sta per terminare.")Console.ReadKey()

End Sub

1.2.3.4.

Un oggetto sta per essere creato.Oggetto = NothingUn oggetto sta per essere distrutto.L'applicazione sta per terminare.

Page 127: Guida visual basic

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 resurrezione

dell'og 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.

DisposeSi 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.

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:

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 FinalizeCi 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

01.02.03.04.05.06.07.08.09.10.11.12.13.14.15.16.

Class Oggetto'Implementa l'interfaccia IDisposableImplements IDisposable'File da scrivere:Dim W As IO.StreamWriter

Sub New()

'Inizializza l'oggettoW = New IO.StreamWriter("C:\test.txt")

End Sub

Public Sub Dispose() Implements IDisposable.Dispose'Chiude il fileW.Close()

End SubEnd Class

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

Page 128: Guida visual basic

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.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.

Module Module1Class FileWriter

Implements IDisposable

Private Writer As IO.StreamWriter'Indica se l'oggetto è già stato distrutto con DisposePrivate Disposed As Boolean'Indica se il file è apertoPrivate Opened As Boolean

Sub New()

Disposed = FalseOpened = FalseConsole.WriteLine("FileWriter sta per essere creato.")'Questa procedura comunica al GC di non richiamare più'il metodo Finalize per questo oggetto. Scriviamo ciò'perchè se file non viene esplicitamente aperto con'Open non c'è alcun bisogno di chiuderloGC.SuppressFinalize(Me)

End Sub

'Apre il filePublic Sub Open(ByVal FileName As String)

Writer = New IO.StreamWriter(FileName)Opened = TrueConsole.WriteLine("FileWriter sta per essere aperto.")'Registra l'oggetto per eseguire Finalize: ora il file'è aperto e può quindi essere chiusoGC.ReRegisterForFinalize(Me)

End Sub

'Scrive del testo nel filePublic Sub Write(ByVal Text As String)

If Opened ThenWriter.Write(Text)

End IfEnd Sub

'Una procedura analoga a Open aiuta a impostare meglio'l'oggetto e non fa altro che richiamare Dispose: è'più una questione di completezza

Page 129: Guida visual basic

L'output:

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.

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 correttamenteProtected Overridable Overloads Sub Dispose(ByVal Disposing _

As Boolean)'Esegue il codice solo se l'oggetto esiste ancoraIf Disposed Then

'Se è distrutto, esce dalla proceduraExit Sub

End If If Disposing Then

'Qui possiamo chiamare altri oggetti con la'sicurezza che esistano ancoraConsole.WriteLine("FileWriter sta per essere distrutto.")

ElseConsole.WriteLine("FileWriter sta per essere finalizzato.")

End If

'Chiude il fileWriter.Close()

Disposed = TrueOpened = False

End Sub

Public Overloads Sub Dispose() Implements IDisposable.Dispose'L'oggetto è stato distruttoDispose(True)'Quindi non deve più essere finalizzatoGC.SuppressFinalize(Me)

End Sub

Protected Overrides Sub Finalize()'Processo di finalizzazione:Dispose(False)

End SubEnd Class

Sub Main()

Dim F As New FileWriter'Questo blocco mostra l'esecuzione di DisposeF.Open("C:\test.txt")F.Write("Ciao")F.Close()

'Questo mostra l'esecuzione di FinalizeF = New FileWriterF.Open("C:\test2.txt")F = Nothing

GC.Collect()GC.WaitForPendingFinalizers()

Console.ReadKey()

End SubEnd Module

1.2.

FileWriter sta per essere creato.

Page 130: Guida visual basic

3.4.5.6.

FileWriter sta per essere aperto.FileWriter sta per essere distrutto.FileWriter sta per essere creato.FileWriter sta per essere aperto.FileWriter sta per essere finalizzato.

Page 131: Guida visual basic

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.

Dichiarazione di un delegateUn nuovo tipo delegate viene dichiar ato con questa sintassi:

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 sempre il costr uttor e di default, ossia quello pr edefinito, che

1. Delegate [Sub/Function] [Nome]([Elenco parametri])

Page 132: Guida visual basic

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":

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:

La signatur e di un delegate non 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:

1. AddressOf [NomeMetodo]

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.32.33.34.35.36.37.38.39.40.41.42.43.44.45.46.47.

Module Module1'Dichiarazione di un tipo delegate Sub che accetta un parametro'di tipo stringa.Delegate Sub Display(ByVal Message As String)

'Una procedura dimostrativaSub Write1(ByVal S As String)

Console.WriteLine("1: " & S)End Sub

'Un'altra procedura dimostrativaSub Write2(ByVal S As String)

Console.WriteLine("2: " & S)End Sub

Sub Main()

'Variabile D di tipo Display, ossia il nuovo tipo'delegate appena definito all'inizio del moduloDim D As Display 'Inizializa D con un nuovo oggetto delegate contenente'un riferimento al metodo Console.WriteLineD = New Display(AddressOf Console.WriteLine)

'Invoca il metodo referenziato da D: in questo caso'equivarrebbe a scrivere Console.WriteLine("Ciao")D("Ciao")

'Reinizializza D, assegnandogli l'indirizzo di Write1D = New Display(AddressOf Write1)'è come chiamare Write1("Ciao")D("Ciao")

'Modo alternativo per inizializzare un delegate: si omette'New e si usa solo AddressOf. Questo genera una conversione'implicita che dà errore di cast nel caso in cui Write1'non sia compatibile con la signature del delegateD = AddressOf Write2D("Ciao")

'Notare che D può contenere metodi di istanza'(come Console.WriteLine) e metodi statici (come Write1'e Write2) Console.ReadKey()

End SubEnd Module

001.002.

Module Module1

Page 133: Guida visual basic

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 SingleDelegate 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 BooleanDelegate 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 ParamArrayFunction CalculateAverage(ByVal ParamArray Data() As Single) As Single

Dim Total As Single = 0

For I As Int32 = 0 To Data.Length - 1Total += 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 è indefinitoFunction 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) ^ 2Next

Return (Result / Data.Length)

End Function

'Procedura che stampa i valori di un array in ordine normale'o inverso. Notare che il secondo parametro è opzionaleSub PrintNormal(ByVal Data() As Single, _

Optional ByVal ReverseOrder As Boolean = False)If ReverseOrder Then

For I As Int32 = Data.Length - 1 To 0 Step -1Console.WriteLine(Data(I))

NextElse

For I As Int32 = 0 To Data.Length - 1Console.WriteLine(Data(I))

NextEnd 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'è opzionaleSub PrintIndexed(ByVal Data() As Single, _

Optional ByVal ReverseOrder As Boolean = False)If ReverseOrder Then

For I As Int32 = Data.Length - 1 To 0 Step -1Console.WriteLine("{0}) {1}", Data.Length - I, Data(I))

NextElse

For I As Int32 = 0 To Data.Length - 1Console.WriteLine("{0}) {1}", (I + 1), Data(I))

NextEnd If

End Sub

Sub Main()Dim Process As ProcessDataDim Print As PrintDataDim Data() As SingleDim Len As Int32Dim Cmd As Char

Page 134: Guida visual basic

Un esempio più significativoI delegate sono par ticolar mente utili per r ispar miar e spazio nel codice. Tr amite i delegate, infatti, possiamo usar e lo

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.

Console.WriteLine("Quanti valori inserire?")Len = Console.ReadLine

ReDim Data(Len - 1)For I As Int32 = 1 To Len

Console.Write("Inserire il valore " & I & ": ")Data(I - 1) = Console.ReadLine

Next

Console.Clear()

Console.WriteLine("Scegliere l'operazione da eseguire: ")Console.WriteLine("m - Calcola la media dei valori;")Console.WriteLine("v - Calcola la varianza dei valori;")Cmd = Console.ReadKey().KeyCharSelect Case Cmd

Case "m"Process = New ProcessData(AddressOf CalculateAverage)

Case "v"Process = New ProcessData(AddressOf CalculateVariance)

Case ElseConsole.WriteLine("Comando non valido!")Exit Sub

End SelectConsole.WriteLine()Console.WriteLine("Scegliere il metodo di stampa: ")Console.WriteLine("s - Stampa i valori;")Console.WriteLine("i - Stampa i valori con il numero ordinale a fianco.")Cmd = Console.ReadKey().KeyCharSelect Case Cmd

Case "s"Print = New PrintData(AddressOf PrintNormal)

Case "i"Print = New PrintData(AddressOf PrintIndexed)

Case ElseConsole.WriteLine("Comando non valido!")Exit Sub

End Select

Console.Clear()

Console.WriteLine("Valori:")'Eccoci arrivati al punto. Come detto prima, i delegate'non possono definire una signature che comprenda parametri'opzionali o indefiniti, ma si'può aggirare questa limitazione semplicemente dichiarando'un array di valori al posto del ParamArray (in quanto si'tratta comunque di due vettori) e lo stesso parametro'non opzionale al posto del parametro opzionale.'L'inconveniente, in questo ultimo caso, è che il'parametro, pur essendo opzionale va sempre specificato'quando il metodo viene richiamato attraverso un oggetto'delegate. Questo escamotage permette di aumentare la'portata dei delegate, includendo anche metodi che'possono essere stati scritti tempo prima in un'altra'parte inaccessibile del codice: così'non è necessario riscriverli!Print(Data, False)Console.WriteLine("Risultato:")Console.WriteLine(Process(Data))

Console.ReadKey()

End Sub End Module

Page 135: Guida visual basic

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.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.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.

Module Module2'Nome del file da cercareDim File As String

'Questo delegate referenzia una funzione che accetta un'parametro stringa e restituisce un valore booleanoDelegate Function IsMyFile(ByVal FileName As String) As Boolean

'Funzione 1, stampa il contenuto del file a schermoFunction PrintFile(ByVal FileName As String) As Boolean

'Io.Path.GetFileName(F) restituisce solo il nome del'singolo file F, togliendo il percorso delle cartelleIf IO.Path.GetFileName(FileName) = File Then

'IO.File.ReadAllText(F) restituisce il testo contenuto'nel file F in una sola operazioneConsole.WriteLine(IO.File.ReadAllText(FileName))Return True

End IfReturn False

End Function

'Funzione 2, copia il file sul desktopFunction CopyFile(ByVal FileName As String) As Boolean

If IO.Path.GetFileName(FileName) = File Then'IO.File.Copy(S, D) copia il file S nel file D:'se D non esiste viene creato, se esiste viene'sovrascrittoIO.File.Copy(FileName, _My.Computer.FileSystem.SpecialDirectories.Desktop & _

"\" & File)Return True

End IfReturn False

End Function

'Procedura ricorsiva che cerca il fileFunction SearchFile(ByVal Dir As String, ByVal IsOK As IsMyFile) _

As Boolean'Ottiene tutte le sottodirectoryDim Dirs() As String = IO.Directory.GetDirectories(Dir)'Ottiene tutti i filesDim Files() As String = IO.Directory.GetFiles(Dir)

'Analizza ogni file per vedere se è quello cercatoFor Each F As String In Files

'È il file cercato, basta cercareIf IsOK(F) Then

'Termina la funzione e restituisce Vero, cosicché'anche nel for sulle cartelle si termini'la ricercaReturn True

End IfNext

'Analizza tutte le sottocartelleFor Each D As String In Dirs

If SearchFile(D, IsOK) Then'Termina ricorsivamente la ricercaReturn True

End IfNext

End Function

Sub Main()Dim Dir As String

Page 136: Guida visual basic

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.

67.68.69.70.71.72.73.74.75.76.77.78.79.80.81.

Console.WriteLine("Inserire il nome file da cercare:")File = Console.ReadLine

Console.WriteLine("Inserire la cartella in cui cercare:")Dir = Console.ReadLine

'Cerca il file e lo scrive a schermoSearchFile(Dir, AddressOf PrintFile)

'Cerca il file e lo copia sul desktopSearchFile(Dir, AddressOf CopyFile)

Console.ReadKey()

End SubEnd Module

Page 137: Guida visual basic

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 invocation 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:

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:

N.B.: Quando un delegate multicast contiene delle funzioni e viene r ichiamato, il valor e r estituito è quello della pr ima

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.32.

Module Module2'Vedi esempio precedenteSub Main()

Dim Dir As StringDim D As IsMyFile

Console.WriteLine("Inserire il nome file da cercare:")File = Console.ReadLine

Console.WriteLine("Inserire la cartella in cui cercare:")Dir = Console.ReadLine

'Crea un delegate multicast, unendo PrintFile e CopyFile.'Da notare che in questa espressione è necessario usare'delle vere e proprie variabili delegate, poiché'l'operatore AddressOf da solo non è valido in questo casoD = System.Delegate.Combine(New IsMyFile(AddressOf PrintFile), _

New IsMyFile(AddressOf CopyFile))'Per la cronaca, Combine è un metodo factory

'Ora il file trovato viene sia visualizzato che copiato'sul desktopSearchFile(Dir, D)

'Se si vuole rimuovere uno o più riferimenti a metodi del'delegate multicast si deve utilizzare il metodo statico Remove:D = System.Delegate.Remove(D, New IsMyFile(AddressOf CopyFile))'Ora D farà visualizzare solamente il file trovato

Console.ReadKey()

End SubEnd Module

1. DirectCast(System.Delegate.Combine(A, B), IsMyFile)

Page 138: Guida visual basic

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 tipoDelegate Function FileFilter(ByVal FileName As String) As Boolean'Il prossimo delegate rappresenta un'operazione su un fileDelegate 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 fileDim RenameFormat As String'Posizione di un file nella cartellaDim FileIndex As Int32'Directory in cui copiare i fileDim CopyDirectory As String'File in cui scrivere. Il tipo StreamWriter permette di scrivere'facilmente stringhe su un file usando WriteLine come in ConsoleDim LogFile As IO.StreamWriter'Limitazioni sulla data di creazione del fileDim CreationDateFrom, CreationDateTo As Date'Limitazioni sulla data di ultimo accesso al fileDim LastAccessDateFrom, LastAccessDateTo As Date'Limitazioni sulla dimensioneDim SizeFrom, SizeTo As Int64

'Rinomina un fileSub Rename(ByVal Path As String)

'Ne prende il nome semplice, senza estensioneDim Name As String = IO.Path.GetFileNameWithoutExtension(Path)'Apre un oggetto contenente le informazioni sul file'di percorso PathDim Info As New IO.FileInfo(Path)

'Formatta il nome secondo la stringa di formato RenameFormatName = String.Format(RenameFormat, _

Name, FileIndex, Info.Length, Info.LastAccessTime, Info.CreationTime)'E aggiunge ancora l'estensione al nome modificatoName &= IO.Path.GetExtension(Path)'Copia il vecchio file nella stessa cartella, ma con il nuovo nomeIO.File.Copy(Path, IO.Path.GetDirectoryName(Path) & "\" & Name)'Elimina il vecchio fileIO.File.Delete(Path) 'Aumenta l'indice di unoFileIndex += 1

End Sub

'Funzione che richiede i dati necessari per far funzionare'il metodo RenameSub InputRenameFormat()

Page 139: Guida visual basic

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 PathSub Delete(ByVal Path As String)

IO.File.Delete(Path)End Sub

'Copia il file da Path alla nuova cartellaSub 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 creaSub InputCopyDirectory()

Console.WriteLine("Inserire una cartella valida in cui copiare i file:")CopyDirectory = Console.ReadLineIf Not IO.Directory.Exists(CopyDirectory) Then

IO.Directory.CreateDirectory(CopyDirectory)End If

End Sub

'Scrive il nome del file sul file apertoSub Archive(ByVal Path As String)

LogFile.WriteLine(IO.Path.GetFileName(Path))End Sub

'Chiede il nome di un file su cui scrivere tutte le informazioniSub 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 fissatiFunction 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 fileSub 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 fissatiFunction 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 fileSub InputLastAccessDates()

Console.WriteLine("Verranno considerati solo i file con data di creazione:")Console.Write("Da: ")LastAccessDateFrom = Date.Parse(Console.ReadLine)Console.Write("A: ")

Page 140: Guida visual basic

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 fissatiFunction 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 fileSub InputSizeLimit()

Console.WriteLine("Verranno considerati solo i file con dimensione compresa:")Console.Write("Tra (bytes):")SizeFrom = Console.ReadLineConsole.Write("E (bytes):")SizeTo = Console.ReadLine

End Sub

'Classe che rappresenta un'operazione eseguibile su fileClass Operation

Private _Description As StringPrivate _Execute As MassFileOperationPrivate _RequireData As AskForDataPrivate _Enabled As Boolean

'DescrizionePublic Property Description() As String

GetReturn _Description

End GetSet(ByVal value As String)

_Description = valueEnd Set

End Property

'Variabile che contiene l'oggetto delegate associato'a questa operazione, ossia un riferimento a una delle Sub'definite poco sopraPublic Property Execute() As MassFileOperation

GetReturn _Execute

End GetSet(ByVal value As MassFileOperation)

_Execute = valueEnd 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

GetReturn _RequireData

End GetSet(ByVal value As AskForData)

_RequireData = valueEnd Set

End Property

'Determina se l'operazione va eseguita oppure noPublic Property Enabled() As Boolean

GetReturn _Enabled

End GetSet(ByVal value As Boolean)

_Enabled = valueEnd Set

End Property

Page 141: Guida visual basic

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 = DescriptionMe.Execute = ExecuteMethodMe.RequireData = RequireDataMethodMe.Enabled = False

End SubEnd Class

'Classe che rappresenta una condizione a cui sottoporre'i file nella cartella: verranno elaborati solo quelli che'soddisfano tutte le condizioniClass Condition

Private _Description As StringPrivate _Verify As FileFilterPrivate _RequireData As AskForDataPrivate _Enabled As Boolean

Public Property Description() As String

GetReturn _Description

End GetSet(ByVal value As String)

_Description = valueEnd Set

End Property

'Contiene un oggetto delegate associato a una delle'precedenti funzioniPublic Property Verify() As FileFilter

GetReturn _Verify

End GetSet(ByVal value As FileFilter)

_Verify = valueEnd Set

End Property

Public Property RequireData() As AskForDataGet

Return _RequireDataEnd GetSet(ByVal value As AskForData)

_RequireData = valueEnd Set

End Property

Public Property Enabled() As BooleanGet

Return _EnabledEnd GetSet(ByVal value As Boolean)

_Enabled = valueEnd Set

End Property

Sub New(ByVal Description As String, _ByVal VerifyMethod As FileFilter, _ByVal RequireDataMethod As AskForData)Me.Description = DescriptionMe.Verify = VerifyMethodMe.RequireData = RequireDataMethod

End SubEnd Class

Sub Main()

'Contiene tutte le operazioni da eseguire: sarà, quindi, un'delegate multicastDim DoOperations As MassFileOperation'Contiene tutte le condizioni da verificareDim VerifyConditions As FileFilter

Page 142: Guida visual basic

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 fileDim Folder As String'Hashtable di caratteri-Operation o carattri-Condition. Il'carattere indica quale tasto è necessario'premere per attivare/disattivare l'operazione/condizioneDim Operations As New HashtableDim Conditions As New HashtableDim 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 dilunghezza 1 e caratteri, al contrario di come accade in CWith 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'altracartella;", _

New MassFileOperation(AddressOf Copy), _New AskForData(AddressOf InputCopyDirectory)))

.Add("a"c, New Operation("Scrive il nome di tutti i file nella cartella su unfile;", _

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 esistentiWith Conditions

.Add("r"c, New Condition("Seleziona i file da elaborare in base alla data dicreazione;", _

New FileFilter(AddressOf IsCreationDateValid), _New AskForData(AddressOf InputCreationDates)))

.Add("l"c, New Condition("Seleziona i file da elaborare in base all'ultimoaccesso;", _

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)

DoConsole.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 IfConsole.Write("] ")'Scrive quindi il carattere da premere e vi associa la descrizione

Page 143: Guida visual basic

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.

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

NextCmd = Console.ReadKey().KeyCharIf Operations.ContainsKey(Cmd) Then

Operations(Cmd).Enabled = Not Operations(Cmd).EnabledEnd If

Loop Until Cmd = "e"c

DoConsole.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 IfConsole.Write("] ")Console.Write(Key)Console.Write(" - ")Console.WriteLine(Conditions(Key).Description)

NextCmd = Console.ReadKey().KeyCharIf Conditions.ContainsKey(Cmd) Then

Conditions(Cmd).Enabled = Not Conditions(Cmd).EnabledEnd 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 formaIf Op.RequireData IsNot Nothing Then

Op.RequireData.Invoke()End If'Se DoOperations non contiene ancora nulla, vi inserisce Op.ExecuteIf DoOperations Is Nothing Then

DoOperations = Op.ExecuteElse

'Altrimenti, combina gli oggetti delegate già memorizzati'con il nuovoDoOperations = System.Delegate.Combine(DoOperations, Op.Execute)

End IfEnd If

Next

For Each C As Condition In Conditions.ValuesIf C.Enabled Then

If C.RequireData IsNot Nothing ThenC.RequireData.Invoke()

End IfIf VerifyConditions Is Nothing Then

VerifyConditions = C.VerifyElse

Page 144: Guida visual basic

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 amewor k, e scopr ir emo nella sezione B che i delegate sono il meccanismo fondamentale alla base di tutto il sistema

degli eventi.

Alcuni membri importanti per i delegate multicastLa 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

423.424.425.426.427.428.429.430.431.432.433.434.435.436.437.438.439.440.441.442.443.444.445.446.447.448.449.450.451.452.453.454.455.456.457.458.459.460.461.462.463.464.465.

VerifyConditions = System.Delegate.Combine(VerifyConditions, C.Verify)End If

End IfNext

FileIndex = 0For Each File As String In IO.Directory.GetFiles(Folder)

'Ok indica se il file ha passato le condizioniDim Ok As Boolean = True'Se ci sono condizioni da applicare, le verificaIf VerifyConditions IsNot Nothing Then

'Dato che nel caso di delegate multicast contenenti'rifermenti a funzione, il valore restituito è'solo quello della prima funzione e a noi interessano'<b>tutti</b> i valori restituiti, dobbiamo enumerare'ogni singolo oggetto delegate presente nel'delegate multicast e invocarlo singolarmente.'Ci viene in aiuto il metodo di istanza GetInvocationList,'che restituisce un array di delegate singoli.For Each C As FileFilter In VerifyConditions.GetInvocationList()

'Tutte le condizioni attive devono essere verificate,'quindi bisogna usare un AndOk = Ok And C(File)

NextEnd If'Se le condizioni sono verificate, esegue le operazioniIf Ok Then

TryDoOperations(File)

Catch Ex As ExceptionConsole.WriteLine("Impossibile eseguire l'operazione: " & Ex.Message)

End TryEnd If

Next'Chiude il file di log se era apertoIf LogFile IsNot Nothing Then

LogFile.Close()End If

Console.WriteLine("Operazioni eseguite con successo!")Console.ReadKey()

End Sub End Module

Page 145: Guida visual basic

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

Page 146: Guida visual basic

A36. Classi Astratte, Sigillate e Parziali

Classi AstratteLe 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 keywor 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 uno s cheletro o una

base di as trazione per altr e classi. Pr endiamo come esempio uno dei miei pr ogr ammi, che potete tr ovar e nella

sezione download, 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:

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:

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

1.2.

<Serializable()> _Public MustInherit Class Chart

1. Public MustOverride Sub Draw()

Page 147: Guida visual basic

adottar e: eccone una dimostr azione nel pr ossimo esempio:

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.070.071.

Module Module1

'Classe astratta che rappresenta un risolutore di equazioni.'Dato che di equazioni ce ne possono essere molte tipologie'differenti, non ha senso rendere questa classe istanziabile.'Provando a scrivere qualcosa come:' Dim Eq As New EquationSolver()'Vi verrà comunicato un errore, in quanto le classi'astratte sono per loro natura non istanziabiliMustInherit Class EquationSolver

'Per lo stesso discorso fatto prima, se non conosciamo come'è fatta l'equazione che questo tipo contiene non'possiamo neppure tentare di risolverla. Perciò'ci limitiamo a dichiarare una funzione Solve come MustOverride.'Notate che il tipo restituito è un array di Single,'in quanto le soluzioni saranno spesso più di una.Public MustOverride Function Solve() As Single()

End Class

'La prossima classe rappresenta un risolutore di equazioni'polinomiali. Dato che la tipologia è ben definita,'avremmo potuto anche <i>non</i> rendere astratta la classe'e, nella funzione Solve, utilizzare un Select Case per'controllare il grado dell'equazione. Ad ogni modo, è'utile vedere come si comporta l'erediterietà attraverso'più classi astratte.'Inoltre, ci ritornerà molto utile in seguito disporre'di questa classe astratta intermediaMustInherit Class PolynomialEquationSolver

Inherits EquationSolver

Private _Coefficients() As Single

'Array di Single che contiene i coefficienti dei'termini di i-esimo grado all'interno dell'equazione.'L'elemento 0 dell'array indica il coefficiente del'termine a grado massimo.Public Property Coefficients() As Single()

GetReturn _Coefficients

End GetSet(ByVal value As Single())

_Coefficients = valueEnd Set

End Property

'Ecco quello a cui volevo arrivare. Se un metodo astratto'lo si vuole mantenere tale anche nella classe derivata,'non basta scrivere:' MustOverride Function Solve() As Single()'Percè in questo caso verrebbe interpretato come'un membro che non c'entra niente con MyBase.Solve,'e si genererebbe un errore in quanto stiamo tentando'di dichiarare un nuovo membro con lo stesso nome'di un membro della classe base.'Per questo motivo, dobbiamo comunque usare il polimorfismo'come se si trattasse di un normale metodo e dichiararlo'Overrides. In aggiunta a questo, deve anche essere'astratto, e perciò aggiungiamo MustOverride:Public MustOverride Overrides Function Solve() As Single()

'Anche in questo caso usiamo il polimorfismo, ma ci riferiamo'alla semplice funzione ToString, derivata dalla classe base'di tutte le entità esistenti, System.Object.'Questa si limita a restituire una stringa che rappresenta'l'equazione a partire dai suoi coefficienti. Ad esempio:' 3x^2 + 2x^1 + 4x^0 = 0'Potete modificare il codice per eliminare le forme ridondanti'x^1 e x^0.Public Overrides Function ToString() As String

Page 148: Guida visual basic

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 - 1If I > 0 Then

Result &= " + "End IfResult &= String.Format("{0}x^{1}", _

Me.Coefficients(I), Me.Coefficients.Length - 1 - I)Next

Result &= " = 0"

Return Result

End FunctionEnd 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 biancoClass NonPolynomialEquationSolver

Inherits EquationSolver

Public Overrides Function Solve() As Single()Return Nothing

End FunctionEnd 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 = 0Class LinearEquationSolver

Inherits PolynomialEquationSolver

Public Property a() As SingleGet

Return Me.Coefficients(0)End GetSet(ByVal value As Single)

Me.Coefficients(0) = valueEnd Set

End Property

Public Property b() As SingleGet

Return Me.Coefficients(1)End GetSet(ByVal value As Single)

Me.Coefficients(1) = valueEnd 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 sviluppoPublic Overrides Function Solve() As Single()

If a <> 0 ThenReturn New Single() {-b / a}

Page 149: Guida visual basic

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.

ElseReturn Nothing

End IfEnd Function

End Class

'Risolutore di equazioni di secondo grado:' ax<sup>2</sup> + bx + c = 0Class QuadraticEquationSolver

Inherits LinearEquationSolver

Public Property c() As SingleGet

Return Me.Coefficients(2)End GetSet(ByVal value As Single)

Me.Coefficients(2) = valueEnd 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}

ElseReturn Nothing

End IfEnd 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 SinglePrivate _IntervalLowerBound, _IntervalUpperBound As Single

'Errore desiderato: l'algoritmo si fermerà una volta'raggiunta una precisione inferiore a EpsilonPublic Property Epsilon() As Single

GetReturn _Epsilon

End GetSet(ByVal value As Single)

_Epsilon = valueEnd Set

End Property

'Limite inferiore dell'intervallo in cui cercare la soluzionePublic Property IntervalLowerBound() As Single

GetReturn _IntervalLowerBound

End GetSet(ByVal value As Single)

_IntervalLowerBound = valueEnd Set

End Property

Page 150: Guida visual basic

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 soluzionePublic Property IntervalUpperBound() As Single

GetReturn _IntervalUpperBound

End GetSet(ByVal value As Single)

_IntervalUpperBound = valueEnd 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 xPrivate Function EvaluateFunction(ByVal x As Single) As Single

Dim Result As Single = 0

For I As Int16 = 0 To Me.Coefficients.Length - 1Result += Me.Coefficients(I) * x ^ (Me.Coefficients.Length - 1 - I)

Next

Return ResultEnd Function

Public Overrides Function Solve() As Single()

Dim a, b, c As SingleDim fa, fb, fc As SingleDim Interval As Single = 100Dim I As Int16 = 0Dim Result As Single

a = IntervalLowerBoundb = IntervalUpperBound

'Non esiste uno zero tra a e b se f(a) e f(b) hanno'lo stesso segnoIf EvaluateFunction(a) * EvaluateFunction(b) > 0 Then

Return NothingEnd If

Do

'c è il punto medio tra a e bc = (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 cicloIf fa = 0 Then

c = aExit Do

End IfIf fb = 0 Then

c = bExit Do

End IfIf fc = 0 Then

Exit DoEnd 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 questiIf fa * fc < 0 Then

b = cElse

a = cEnd If

Loop Until Math.Abs(a - b) < Me.Epsilon

Page 151: Guida visual basic

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 FunctionEnd 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 EquationSolverDim x() As SingleDim 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().KeyCharConsole.Clear()

If Cmd <> "e" Then

'Ancora, sappiamo che si tratta di un'equazione polinomiale'ma non di quale gradoDim Poly As PolynomialEquationSolver

'Ottiene i dati relativi a ciascuna equazioneSelect 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 Int16Console.WriteLine("Inserire il numero di coefficienti: ")CoefNumber = Console.ReadLineReDim High.Coefficients(CoefNumber - 1)Console.WriteLine("Inserire i limti dell'intervallo in cui cercare gli

zeri:")High.IntervalLowerBound = Console.ReadLineHigh.IntervalUpperBound = Console.ReadLineConsole.WriteLine("Inserire la precisione (epsilon):")High.Epsilon = Console.ReadLinePoly = 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

Page 152: Guida visual basic

Eccovi un'immagine dell'ultimo commento:

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.

'caso più particolare al più generale:' - Abbiamo creato un oggetto specifico per un certo grado' di un'equazione polinomiale (Linear, Quadratic, High);' - Abbiamo messo quell'oggetto in uno che si riferisce' genericamente a tutti i polinomi;' - Infine, abbiamo posto quest'ultimo in uno ancora più' generale che si riferisce a tutte le equazioni;'Questo percorso porta da oggetto molto specifici e ricchi di membri'(tante proprietà e tanti metodi), a tipi molto generali'e poveri di membri (nel caso di Eq, un solo metodo).Eq = Poly

Else'Inseriamo in Eq un nuovo oggetto per risolvere equazioni non'polinomiali, anche se il codice è al momento vuotoEq = New NonPolynomialEquationSolverConsole.WriteLine("Non implementato")

End If

'Risolviamo l'equazione. Richiamare la funzione Solve da un oggetto'EquationSolver potrebbe non dirvi nulla, ma ricordate che dentro Eq'è memorizzato un oggetto più specifico in cui'è stata definita la funzione Solve(). Per questo motivo,'anche se Eq è di tipo classe base, purtuttavia contiene'al proprio interno un oggetto di tipo classe derivata, ed'è questo che conta: viene usato il metodo Solve della classe'derivata.'Se ci pensate bene, vi verrà più spontaneo capire,'poiché noi, ora, stiamo guardando ATTRAVERSO il tipo'EquationSolver un oggetto di altro tipo. È come osservare'attraverso filtri via via sempre più fitti (cfr'immagine seguente)x = Eq.Solve()

If x IsNot Nothing Then

Console.WriteLine("Soluzioni trovate: ")For Each s As Single In x

Console.WriteLine(s)Next

ElseConsole.WriteLine("Nessuna soluzione")

End If

Console.ReadKey()End Sub

End Module

Page 153: Guida visual basic

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 SigillateLe classi sigillate sono esattamente l'opposto di quelle astr atte, ossia non possono mai esser e er editate. Si dichiar ano

con la keywor d NotInher itable:

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.

Inoltr e, le classi sigillate non 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 ParzialiUna 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 keywor d associate

alle classi in un solo capitolo. Semplicemente, una classe par ziale si dichiar a in questo modo:

È 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:

1.2.3.

NotInheritable Class Example'...

End Class

01.02.03.04.05.06.07.08.09.10.11.12.13.14.15.16.

Class ASub 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 stessoNotOverridable Overrides Sub DoSomething()

'...End Sub

End Class

1.2.3.

Partial Class [Nome]'...

End Class

01.02.03.04.

'Nel file Codice1.vb :Partial Class A

Sub One()

Page 154: Guida visual basic

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.vbClass A

Sub Two()'...

End SubEnd Class 'Nel file Codice3.vbClass A

Sub Three()'...

End SubEnd 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

Page 155: Guida visual basic

A37. Le Interfacce

Scopo delle InterfacceLe inter facce sono un'entità davver o singolar e all'inter no del .NET Fr amewor 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 implementare 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:

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 amewor 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 implementano 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".

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 AlFor Each K As Int32 In Al

Console.WriteLine(K)Next

Page 156: Guida visual basic

Dichiarazione e implementazioneLa sintassi usata per dichiar ar e un'inter faccia è la seguente:

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:

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:

Si capisce meglio con un esempio:

1.2.3.

Interface [Nome]'Membri

End Interface

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'uomoInterface IIdentifiable

ReadOnly Property Id() As Int32Function 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 GenericsInterface IResettable

Property DefaultValue() As ObjectSub Reset()

End Interface 'Come avete visto, i nomi di interfaccia iniziano per convenzione'con la lettera I maiuscola

1.2.3.4.5.

Class ExampleImplements [Nome Interfaccia] [Membro] Implements [Nome Interfaccia].[Membro]

End Class

01.02.03.04.05.06.07.

Module Module1Interface IIdentifiable

ReadOnly Property Id() As Int32Function ToString() As String

End Interface

Page 157: Guida visual basic

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 spedireClass Pack

'Implementa l'interfaccia IIdentifiable, in quanto un pacco'dovrebbe poter essere ben identificatoImplements IIdentifiable 'Notate bene che l'interfaccia ci obbliga a definire una'proprietà, ma non ci obbliga a definire un campo'ad essa associatoPrivate _Id As Int32Private _Destination As StringPrivate _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 XDPublic ReadOnly Property Id() As Integer Implements IIdentifiable.Id

GetReturn _Id

End GetEnd Property

'Destinazione del pacco.'Il fatto che l'interfaccia ci obblighi a definire quei due'membri non significa che non possiamo definirne altriPublic Property Destination() As String

GetReturn _Destination

End GetSet(ByVal value As String)

_Destination = valueEnd Set

End Property

'Piccolo ripasso delle proprietà indicizzate e'della gestione degli erroriPublic Property Dimensions(ByVal Index As Int32) As Single

GetIf (Index >= 0) And (Index < 3) Then

Return _Dimensions(Index)Else

Throw New IndexOutOfRangeException()End If

End GetSet(ByVal value As Single)

If (Index >= 0) And (Index < 3) Then_Dimensions(Index) = value

ElseThrow New IndexOutOfRangeException()

End IfEnd Set

End Property

Public Overrides Function ToString() As String Implements IIdentifiable.ToStringReturn 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 FunctionEnd Class Sub Main()

'...End Sub

Page 158: Guida visual basic

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:

End Module

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.

Module Module1

Interface IIdentifiableReadOnly Property Id() As Int32Function ToString() As String

End Interface

Class PackImplements IIdentifiable

Private _Id As Int32Private _Destination As StringPrivate _Dimensions(2) As Single

Public ReadOnly Property Id() As Integer Implements IIdentifiable.Id

GetReturn _Id

End GetEnd Property

Public Property Destination() As String

GetReturn _Destination

End GetSet(ByVal value As String)

_Destination = valueEnd Set

End Property

Public Property Dimensions(ByVal Index As Int32) As SingleGet

If (Index >= 0) And (Index < 3) ThenReturn _Dimensions(Index)

ElseThrow New IndexOutOfRangeException()

End IfEnd GetSet(ByVal value As Single)

If (Index >= 0) And (Index < 3) Then_Dimensions(Index) = value

ElseThrow New IndexOutOfRangeException()

End IfEnd Set

End Property

Sub New(ByVal Id As Int32)_Id = Id

End Sub

Public Overrides Function ToString() As String Implements IIdentifiable.ToStringReturn 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 FunctionEnd Class

Page 159: Guida visual basic

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 TelegramImplements IIdentifiable

Private _Id As Int32Private _Recipient As StringPrivate _Message As String

Public ReadOnly Property Id() As Integer Implements IIdentifiable.Id

GetReturn _Id

End GetEnd Property

Public Property Recipient() As String

GetReturn _Recipient

End GetSet(ByVal value As String)

_Recipient = valueEnd Set

End Property

Public Property Message() As StringGet

Return _MessageEnd GetSet(ByVal value As String)

_Message = valueEnd Set

End Property

Sub New(ByVal Id As Int32)_Id = Id

End Sub

Public Overrides Function ToString() As String Implements IIdentifiable.ToStringReturn String.Format("{0:0000}: Telegramma per {1} ; Messaggio = {2}", _

Me.Id, Me.Recipient, Me.Message)End Function

End Class

Class MoneyOrderImplements IIdentifiable

Private _Id As Int32Private _Recipient As StringPrivate _Money As Single

Public ReadOnly Property Id() As Integer Implements IIdentifiable.Id

GetReturn _Id

End GetEnd Property

Public Property Recipient() As String

GetReturn _Recipient

End GetSet(ByVal value As String)

_Recipient = valueEnd Set

End Property

Public Property Money() As SingleGet

Return _MoneyEnd GetSet(ByVal value As Single)

_Money = valueEnd Set

End Property

Page 160: Guida visual basic

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.ToStringReturn 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 interfacciaClass PostalProcessor

'Tanto per tenersi allenati coi delegate, ecco una'funzione delegate che funge da filtro per i vari idPublic Delegate Function IdSelector(ByVal Id As Int32) As Boolean

Private _StorageCapacity As Int32Private _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 elementoPublic Property StorageCapacity() As Int32

GetReturn _StorageCapacity

End GetSet(ByVal value As Int32)

_StorageCapacity = valueReDim Preserve Storage(value)

End SetEnd Property

'Modifica od ottiene un riferimento all'Index-esimo'oggetto nell'array StoragePublic Property Item(ByVal Index As Int32) As IIdentifiable

GetIf (Index >= 0) And (Index < Storage.Length) Then

Return Me.Storage(Index)Else

Throw New IndexOutOfRangeException()End If

End GetSet(ByVal value As IIdentifiable)

If (Index >= 0) And (Index < Storage.Length) ThenMe.Storage(Index) = value

ElseThrow New IndexOutOfRangeException()

End IfEnd 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'arrayPublic ReadOnly Property FirstPlaceAvailable() As Int32

GetFor I As Int32 = 0 To Me.Storage.Length - 1

If Me.Storage(I) Is Nothing Then

Page 161: Guida visual basic

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.

Return IEnd If

NextReturn (-1)

End GetEnd Property

'Tutti gli oggetti che inizializzeremo avranno bisogno'di un id: ce lo fornisce la stessa classe Processor'tramite questa proprietà che si autoincrementaPublic ReadOnly Property NextId() As Int32

Get_NextId += 1Return _NextId

End GetEnd 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 magazzinoPublic Sub New(ByVal Capacity As Int32)

Me.StorageCapacity = CapacityEnd 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 StorageIf K Is Nothing Then

Continue ForEnd IfIf Selector.Invoke(K.Id) Then

Console.WriteLine(K.ToString())End If

NextEnd Sub

'Stampa l'oggetto con Id specificatoPublic Sub PrintById(ByVal Id As Int32)

For Each K As IIdentifiable In StorageIf K Is Nothing Then

Continue ForEnd IfIf K.Id = Id Then

Console.WriteLine(K.ToString())Exit For

End IfNext

End Sub

'Cerca tutti gli elementi che contemplano all'interno'della propria descrizione la stringa Str e li'restituisce come array di IdPublic Function SearchItems(ByVal Str As String) As Int32()

Dim Temp As New ArrayList

For Each K As IIdentifiable In StorageIf K Is Nothing Then

Continue ForEnd IfIf K.ToString().Contains(Str) Then

Temp.Add(K.Id)End If

Next

Page 162: Guida visual basic

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 Int32For I As Int32 = 0 To Temp.Count - 1

Result(I) = Temp(I)Next

Temp.Clear()Temp = Nothing

Return Result

End FunctionEnd Class

Private Processor As New PostalProcessor(10)Private Cmd As CharPrivate 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.ReadLineConsole.Write("Larghezza: ")P.Dimensions(0) = Console.ReadLineConsole.Write("Lunghezza: ")P.Dimensions(1) = Console.ReadLineConsole.Write("Altezza: ")P.Dimensions(2) = Console.ReadLineProcessor.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.ReadLineConsole.Write("Messaggio: ")T.Message = Console.ReadLineProcessor.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.ReadLineConsole.Write("Somma: ")M.Money = Console.ReadLineProcessor.Item(Place) = M

Case ElseConsole.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;")

Page 163: Guida visual basic

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.

Cmd = Console.ReadKey().KeyChar

Console.Clear()Select Case Cmd

Case "c"Dim Str As StringConsole.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().KeyCharConsole.WriteLine()

If Cmd = "y" Then

For Each Id As Int32 In IdsProcessor.PrintById(Id)

NextEnd If

Case "v"Console.WriteLine("Visualizzare gli elementi")Console.Write("Da Id: ")IdFrom = Console.ReadLineConsole.Write("A Id: ")IdTo = Console.ReadLineProcessor.PrintByFilter(AddressOf SelectId)

Case ElseConsole.WriteLine("Comando sconosciuto.")

End Select

Console.ReadKey()End Sub

Sub Main()

DoConsole.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 IfCase "m"

Console.WriteLine("Attuale capacità: " & Processor.StorageCapacity)Console.WriteLine("Inserire una nuova dimensione: ")Processor.StorageCapacity = Console.ReadLineConsole.WriteLine("Operazione effettuata.")Console.ReadKey()

Case "p"ProcessData()

End SelectConsole.Clear()

Loop Until Cmd = "e"End Sub

End Module

Page 164: Guida visual basic

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:

Definizione di tipi in un'interfacc iaCosì 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.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.32.33.34.35.36.37.

Module Module1'... Interface IPayable

Function CalculateSendCost() As SingleEnd Interface Class Telegram

'Nel caso di più interfacce, le si separa con la virgolaImplements IIdentifiable, IPayable

'...

Public Function CalculateSendCost() As Single Implements IPayable.CalculateSendCost

'Come vedremo nel capitolo dedicato alle stringhe,'la funzione Split(c) spezza la stringa in tante'parti, divise dal carattere c, e le restituisce'sottoforma di array. In questo caso, tutte le sottostringhe'separate da uno spazio sono all'incirca tante'quanto il numero di parole nella fraseSelect Case Me.Message.Split(" ").Length

Case Is <= 20Return 4.39

Case Is <= 50Return 6.7

Case Is <= 100Return 10.3

Case Is <= 200Return 19.6

Case Is <= 500Return 39.75

End SelectEnd Function

End Class '...

End Class

01.02.03.04.05.06.07.08.09.10.11.12.13.

Interface ISaveableStructure FileInfo

'Assumiamo per brevità che queste variabili Public'siano in realtà proprietàPublic Path As String'FileAttribues è un enumeratore su bit che contiene'informazioni sugli attributi di un file (nascosto, a sola'lettura, archivio, compresso, eccetera...)Public Attributes As FileAttributes

End StructureProperty SaveInfo() As FileInfoSub Save()

Page 165: Guida visual basic

Ereditarietà, polimorfismo e overloading per le interfacceAnche 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:

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.

Perchè preferire un'interfacc ia a una c lasse astrattaLa differ enza sostanziale tr a una classe astr atta e un'inter faccia è che la pr ima definisce l'es s enza di un oggetto (che

cosa è), mentr e la seconda ne indica il comportamento (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

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

1.2.3.4.5.6.7.8.

Interface AProperty PropA() As Int32

End Interface Interface B

Inherits ASub SubB()

End Interface

Page 166: Guida visual basic

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 IComparerUn oggetto che implementa ICompar able comunica implicitamente al .NET Fr amewor 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.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.32.33.34.35.36.37.38.39.40.41.42.43.

Module Module1Sub Main()

Dim A As Int32

Console.WriteLine("Inserisci un numero intero:")A = Console.ReadLine

'Tutti i tipi di base espongono il metodo CompareTo, poichè'tutti implementano l'interfaccia IComparable:If A.CompareTo(10) = 1 Then

Console.WriteLine(A & " è maggiore di 10")ElseIf A.CompareTo(10) = 0 Then

Console.WriteLine(A & " è uguale a 10")Else

Console.WriteLine(A & " è minore di 10")End If

'Il fatto che i tipi di base siano confrontabili implica'che si possano ordinare tramite il metodo Sort di una'qualsiasi collezione o array di elementiDim B() As Int32 = {1, 5, 2, 8, 10, 56}'Ordina l'arrayArray.Sort(B)'E visualizza i numeri in ordine crescenteFor I As Int16 = 0 To UBound(B)

Console.WriteLine(B(I))Next

'Anche String espone questo metodo, quindi si può ordinare'alfabeticamente un insieme di stringhe:Dim C As New ArrayListC.Add("Banana")C.Add("Zanzara")C.Add("Anello")C.Add("Computer")'Ordina l'insiemeC.Sort()For I As Int16 = 0 To C.Count - 1

Console.WriteLine(C(I))Next

Console.ReadKey()

Page 167: Guida visual basic

Dopo aver immesso un input, ad esempio 8, avr emo la seguente scher mata:

Inserire un numero intero:88 è minore di 1012581056AnelloBananaComputerZanzara

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:

44.End Sub

End Module

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.32.33.34.35.36.37.38.39.40.41.42.43.

Module Module1Class Person

Implements IComparablePrivate _FirstName, _LastName As StringPrivate ReadOnly _BirthDay As Date

Public Property FirstName() As String

GetReturn _FirstName

End GetSet(ByVal Value As String)

If Value <> "" Then_FirstName = Value

End IfEnd Set

End Property

Public Property LastName() As StringGet

Return _LastNameEnd GetSet(ByVal Value As String)

If Value <> "" Then_LastName = Value

End IfEnd Set

End Property

Public ReadOnly Property BirthDay() As DateGet

Return _BirthDayEnd Get

End Property

Public ReadOnly Property CompleteName() As StringGet

Return _FirstName & " " & _LastNameEnd Get

End Property

'Per definizione, purtroppo, CompareTo deve sempre usare'un parametro di tipo Object: risolveremo questo problema

Page 168: Guida visual basic

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:

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.

'più in là utilizzando i GenericsPublic 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 1End If'Tenta di convertire obj in PersonDim P As Person = DirectCast(obj, Person)'E restituisce il risultato dell'operazione di'comparazione tra stringhe dei rispettivi nomiReturn String.Compare(Me.CompleteName, P.CompleteName)

End Function

Sub New(ByVal FirstName As String, ByVal LastName As String, _ByVal BirthDay As Date)Me.FirstName = FirstNameMe.LastName = LastNameMe._BirthDay = BirthDay

End SubEnd Class Sub Main()

'Crea un array di oggetti PersonDim 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.CompareToArray.Sort(Persons)

For I As Int16 = 0 To UBound(Persons)

Console.WriteLine(Persons(I).CompleteName)Next

Console.ReadKey()

End SubEnd Module

01.02.03.04.05.06.07.08.09.10.11.12.13.14.15.16.17.18.

Module Module2'Questa classe fornisce un metodo per comparare oggetti Person'utilizzando la proprietà BirthDay.'Per convenzione, classi che implementano IComparer dovrebbero'avere un suffisso "Comparer" nel nome.'Altra osservazione: se ci sono molte interfacce il cui nome'termina in "-able", definendo una caratteristica dell'oggetto'che le implementa (ad es.: un oggetto enumerabile,'comparabile, distruggibile, ecc...), ce ne sono altrettante'che terminano in "-er", indicando, invece, un oggetto'che "fa" qualcosa di specifico.'Nel nostro esempio, oggetti di tipo BirthDayComparer'hanno il solo scopo di comparare altre oggettiClass BirthDayComparer

'Implementa l'interfacciaImplements IComparer

Page 169: Guida visual basic

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.

IDisposableNel 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.

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.

'Anche questa funzione deve usare parametri objectPublic 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'ugualiIf x Is Nothing And y Is Nothing Then

Return 0ElseIf x Is Nothing Then

'Se x è Nothing, y è maggioreReturn -1

ElseIf y Is Nothing Then'Se y è Nothing, x è maggioreReturn 1

ElseDim P1 As Person = DirectCast(x, Person)Dim P2 As Person = DirectCast(y, Person)'Compara le dateReturn Date.Compare(P1.BirthDay, P2.BirthDay)

End IfEnd 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 BirthDayComparerArray.Sort(Persons, New BirthDayComparer())

For I As Int16 = 0 To UBound(Persons)

Console.WriteLine(Persons(I).CompleteName)Next

Console.ReadKey()

End SubEnd Module

Page 170: Guida visual basic

A39. Utilizzo delle Interfacce - Parte II

IEnumerable e IEnumeratorUna classe che implementa IEnumer able diventa enumerabile agli occhi del .NET Fr amewor 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.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.32.33.34.35.36.37.38.39.40.41.42.43.44.45.46.47.48.49.50.51.52.53.54.55.

Module Module1Class PersonCollection

Implements IEnumerable'La lista delle personePrivate _Persons As New ArrayList

'Teoricamente, si dovrebbero ridefinire tutti i metodi'di una collection comune, ma per mancanza di spazio,'accontentiamociPublic ReadOnly Property Persons() As ArrayList

GetReturn _Persons

End GetEnd Property

'Restituisce l'età media. TimeSpan è una struttura che si'ottiene sottraendo fra loro due oggetti date e indica un'intervallo di tempoPublic ReadOnly Property AverageAge() As String

Get'Variabile temporaneaDim Temp As TimeSpan'Somma tutte le etàFor Each P As Person In _Persons

Temp = Temp.Add(Date.Now - P.BirthDay)Next'Divide per il numero di personeTemp = TimeSpan.FromSeconds(Temp.TotalSeconds / _Persons.Count)

'Dato che TimeSpan può contenere al massimo'giorni e non mesi o anni, dobbiamo fare qualche'calcoloDim Years As Int32'Gli anni, ossia il numero dei giorni fratto 365'Divisione interaYears = Temp.TotalDays \ 365'Sottrae gli anni: da notare che'(Temp.TotalDays \ 365) * 365) non è un passaggio'inutile. Infatti, per determinare il numero di'giorni che rimangono, bisogna prendere la'differenza tra il numero totale di giorni e'il multiplo più vicino di 365Temp = _Temp.Subtract(TimeSpan.FromDays((Temp.TotalDays \ 365) * 365)) Return Years & " anni e " & CInt(Temp.TotalDays) & " giorni"

End GetEnd Property

'La funzione GetEnumerator restituisce un oggetto di tipo'IEnumerator che vedremo fra breve: esso permette di'scorrere ogni elemento ordinatamente, dall'inizio'alla fine. In questo caso, poichè non abbiamo ancora'analizzato questa interfaccia, ci limitiamo a restituisce

Page 171: Guida visual basic

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:

56.57.58.59.60.61.62.63.64.65.66.67.68.69.70.71.72.73.74.75.76.77.78.79.

'l'IEnumerator predefinito per un ArrayListPublic Function GetEnumerator() As IEnumerator _

Implements IEnumerable.GetEnumeratorReturn _Persons.GetEnumerator

End FunctionEnd Class

Sub Main()

Dim Persons As New PersonCollectionWith 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)NextConsole.WriteLine("Età media: " & Persons.AverageAge)'> 41 anni e 253 giorni

Console.ReadKey()

End SubEnd Module

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.

Module Module1Class PersonCollection

Implements IEnumerable'La lista delle personePrivate _Persons As New ArrayList

'Questa classe ha il compito di scorrere ordinatamente gli'elementi della lista, dal più vecchio al più giovanePrivate Class PersonAgeEnumerator

Implements IEnumerator

'Per enumerare gli elementi, la classe ha bisogno di un'riferimento ad essi: perciò si deve dichiarare ancora'un nuovo ArrayList di Person. Questo passaggio è'facoltativo nelle classi nidificate come questa, ma è'obbligatorio in tutti gli altri casiPrivate Persons As New ArrayList'Per scorrere la collezione, si userà un comune indicePrivate Index As Int32

'Essendo una normalissima classe, è lecito definire un'costruttore, che in questo caso inizializza la'collezioneSub New(ByVal Persons As ArrayList)

'Ricordate: poichè ArrayList deriva da Object, è'un tipo reference. Assegnare Persons a Me.Persons'equivale ad assegnarne l'indirizzo e quindi ogni'modifica su questo arraylist privato si rifletterà'su quello passato come parametro. Si può'evitare questo problema clonando la lista

Page 172: Guida visual basic

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 -1Index = -1

'Dato che l'enumeratore deve scorrere la lista'secondo l'anno di nascita, bisogna prima ordinarlaMe.Persons.Sort(New BirthDayComparer)

End Sub

'Restituisce l'elemento correntePublic ReadOnly Property Current() As Object _

Implements System.Collections.IEnumerator.CurrentGet

Return Persons(Index)End Get

End Property

'Restituisce True se esiste l'elemento successivo e lo'imposta, altrimenti FalsePublic Function MoveNext() As Boolean _

Implements System.Collections.IEnumerator.MoveNextIf Index = Persons.Count - 1 Then

Return FalseElse

Index += 1Return True

End IfEnd Function

'Resetta il cicloPublic Sub Reset() _

Implements System.Collections.IEnumerator.ResetIndex = -1

End SubEnd Class

Public ReadOnly Property Persons() As ArrayList

GetReturn _Persons

End GetEnd Property

Public ReadOnly Property AverageAge() As String

GetDim Temp As TimeSpanFor Each P As Person In _Persons

Temp = Temp.Add(Date.Now - P.BirthDay)NextTemp = TimeSpan.FromSeconds(Temp.TotalSeconds / _Persons.Count)

Dim Years As Int32Years = Temp.TotalDays \ 365Temp = _Temp.Subtract(TimeSpan.FromDays((Temp.TotalDays \ 365) * 365))Return Years & " anni e " & CInt(Temp.TotalDays) & " giorni"

End GetEnd 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ù giovanePublic Function GetEnumerator() As IEnumerator _

Implements IEnumerable.GetEnumeratorReturn New PersonAgeEnumerator(_Persons)

End FunctionEnd Class

Sub Main()

Page 173: Guida visual basic

ICloneableCome 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 nuovo 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:

L'inter faccia, invece, come accadeva per IEnumer able e ICompar able, indica al .NET Fr amewor k che l'oggetto è clonabile.

Questo codice mostr a la funzione Close all'oper a:

104.105.106.107.108.109.110.111.112.113.114.115.116.117.118.119.120.121.122.123.124.

Dim Persons As New PersonCollectionWith 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

'Enumera ora per data di nascita, ma senza modificare'l'ordine degli elementiFor Each P As Person In Persons

Console.WriteLine(P.BirthDay.ToShortDateString & ", " & _P.CompleteName)

Next

'Stampa la prima persona, dimostrando che l'ordine'della lista è intattoConsole.WriteLine(Persons.Persons(0).CompleteName)

Console.ReadKey()

End SubEnd Module

01.02.03.04.05.06.07.08.09.10.11.12.13.14.15.16.17.18.19.20.21.22.

Module EsempioSub Main()

'Il tipo ArrayList espone il metodo CloneDim S1 As New ArrayListDim S2 As New ArrayList

S2 = S1

'Verifica che S1 e S2 puntano lo stesso oggettoConsole.WriteLine(S1 Is S2)'> True

'Clona l'oggettoS2 = S1.Clone'Verifica che ora S2 referenzia un oggetto differente,'ma di valore identico a S1Console.WriteLine(S1 Is S2)'> False

Console.ReadKey()

End SubEnd Module

01.02.03.04.05.06.07.08.09.10.

Module Module1Class UnOggetto

Implements ICloneablePrivate _Campo As Int32

Public Property Campo() As Int32

GetReturn _Campo

End Get

Page 174: Guida visual basic

Or a, è impor tante distinguer e due tipi di copia: quella Shallow 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.

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.

Set(ByVal Value As Int32)_Campo = Value

End SetEnd Property

'Restituisce una copia dell'oggettoPublic 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 casoReturn Me.MemberwiseClone

End Function

'L'operatore = permette di definire de due oggetti hanno un'valore ugualeShared Operator =(ByVal O1 As UnOggetto, ByVal O2 As UnOggetto) As _

BooleanReturn O1.Campo = O2.Campo

End Operator

Shared Operator <>(ByVal O1 As UnOggetto, ByVal O2 As UnOggetto) As _BooleanReturn Not (O1 = O2)

End OperatorEnd Class

Sub Main()

Dim O1 As New UnOggettoDim O2 As UnOggetto = O1.Clone

'I due oggetti NON sono lo stesso oggetto: il secondo'è solo una copia, disgiunta da O1Console.WriteLine(O1 Is O2)'> False

'Tuttavia hanno lo stesso identico valoreConsole.WriteLine(O1 = O2)'> True

Console.ReadKey()

End SubEnd Module

Page 175: Guida visual basic

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

tipizzazione debole, 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 tipizzazione for 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().

Page 176: Guida visual basic

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 Download: 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 lassiPer 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:

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:

1.2.3.

Class Class1 End Class

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.

Namespace PostalManagement

Public Interface IIdentifiableReadOnly Property Id() As Int32Function ToString() As String

End Interface

Public Class PackImplements IIdentifiable

Private _Id As Int32Private _Destination As StringPrivate _Dimensions(2) As Single

Public ReadOnly Property Id() As Integer Implements IIdentifiable.Id

GetReturn _Id

End GetEnd Property

Public Property Destination() As String

GetReturn _Destination

End GetSet(ByVal value As String)

_Destination = valueEnd Set

End Property

Public Property Dimensions(ByVal Index As Int32) As SingleGet

Page 177: Guida visual basic

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) ThenReturn _Dimensions(Index)

ElseThrow New IndexOutOfRangeException()

End IfEnd GetSet(ByVal value As Single)

If (Index >= 0) And (Index < 3) Then_Dimensions(Index) = value

ElseThrow New IndexOutOfRangeException()

End IfEnd Set

End Property

Public Sub New(ByVal Id As Int32)_Id = Id

End Sub

Public Overrides Function ToString() As String Implements IIdentifiable.ToStringReturn 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 FunctionEnd Class

Public Class Telegram

Implements IIdentifiable

Private _Id As Int32Private _Recipient As StringPrivate _Message As String

Public ReadOnly Property Id() As Integer Implements IIdentifiable.Id

GetReturn _Id

End GetEnd Property

Public Property Recipient() As String

GetReturn _Recipient

End GetSet(ByVal value As String)

_Recipient = valueEnd Set

End Property

Public Property Message() As StringGet

Return _MessageEnd GetSet(ByVal value As String)

_Message = valueEnd Set

End Property

Public Sub New(ByVal Id As Int32)_Id = Id

End Sub

Public Overrides Function ToString() As String Implements IIdentifiable.ToStringReturn String.Format("{0:0000}: Telegramma per {1} ; Messaggio = {2}", _

Me.Id, Me.Recipient, Me.Message)End Function

End Class

Public Class MoneyOrderImplements IIdentifiable

Private _Id As Int32Private _Recipient As String

Page 178: Guida visual basic

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.IdGet

Return _IdEnd Get

End Property

Public Property Recipient() As StringGet

Return _RecipientEnd GetSet(ByVal value As String)

_Recipient = valueEnd Set

End Property

Public Property Money() As SingleGet

Return _MoneyEnd GetSet(ByVal value As Single)

_Money = valueEnd Set

End Property

Public Sub New(ByVal Id As Int32)_Id = Id

End Sub

Public Overrides Function ToString() As String Implements IIdentifiable.ToStringReturn String.Format("{0:0000}: Vaglia postale per {1} ; Ammontare = {2}€", _

Me.Id, Me.Recipient, Me.Money)End Function

End Class

Public Class PostalProcessorPublic Delegate Function IdSelector(ByVal Id As Int32) As Boolean

Private _StorageCapacity As Int32Private _NextId As Int32 = 0Private Storage() As IIdentifiable

Public Property StorageCapacity() As Int32

GetReturn _StorageCapacity

End GetSet(ByVal value As Int32)

_StorageCapacity = valueReDim Preserve Storage(value)

End SetEnd Property

Public Property Item(ByVal Index As Int32) As IIdentifiable

GetIf (Index >= 0) And (Index < Storage.Length) Then

Return Me.Storage(Index)Else

Throw New IndexOutOfRangeException()End If

End GetSet(ByVal value As IIdentifiable)

If (Index >= 0) And (Index < Storage.Length) ThenMe.Storage(Index) = value

ElseThrow New IndexOutOfRangeException()

End IfEnd Set

End Property

Public ReadOnly Property FirstPlaceAvailable() As Int32Get

Page 179: Guida visual basic

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 - 1If Me.Storage(I) Is Nothing Then

Return IEnd If

NextReturn (-1)

End GetEnd Property

Public ReadOnly Property NextId() As Int32

Get_NextId += 1Return _NextId

End GetEnd 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 ThenContinue For

End IfIf Selector.Invoke(K.Id) Then

Console.WriteLine(K.ToString())End If

NextEnd Sub

Public Sub PrintById(ByVal Id As Int32)

For Each K As IIdentifiable In StorageIf K Is Nothing Then

Continue ForEnd IfIf K.Id = Id Then

Console.WriteLine(K.ToString())Exit For

End IfNext

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 ThenContinue For

End IfIf K.ToString().Contains(Str) Then

Temp.Add(K.Id)End If

Next

Dim Result(Temp.Count - 1) As Int32For I As Int32 = 0 To Temp.Count - 1

Result(I) = Temp(I)Next

Temp.Clear()Temp = Nothing

Return Result

End FunctionEnd Class

Page 180: Guida visual basic

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 progettoUna 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 imento, 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"):

249. End Namespace

Page 181: Guida visual basic

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 Download):

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:

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:

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

1. Imports [Nome Libreria].[Nome Namespace]

1. Imports PostalManagement.PostalManagement

Page 182: Guida visual basic

tr ovar e il name Pack nel contesto cor r ente). Ecco un esempio:

che equivale a:

Nella scheda ".NET" che vedete nella seconda immagine di sopr a, ci sono molte libr er ie facenti par te del Fr amewor k che

user emo nelle pr ossime sezioni della guida.

01.02.03.04.05.06.07.08.09.10.11.12.13.

Imports PostalManagement.PostalManagement Module Module1

Sub Main()Dim P As New PostalProcessor(10)Dim Pk As New Pack(P.NextId)

P.Item(P.FirstPlaceAvailable) = Pk'...

End Sub End Module

01.02.03.04.05.06.07.08.09.10.11.

Module Module1

Sub Main()Dim P As New PostalManagement.PostalManagement.PostalProcessor(10)Dim Pk As New PostalManagement.PostalManagement.Pack(P.NextId)

P.Item(P.FirstPlaceAvailable) = Pk'...

End Sub End Module

Page 183: Guida visual basic

A41. I Generics - Parte I

Panoramica sui GenericsI 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:

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:

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:

1. [NomeEntità](Of [NomeTipo])

01.02.03.04.05.06.07.08.09.10.

Dim A As New ArrayListA.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 EachFor Each V As Int32 In AConsole.WriteLine(V)

Next

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)

Page 184: Guida visual basic

E questa è una dimostr azione dell'incr emento delle pr estazioni:

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.

Generics StandardUna 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:

06.07.08.09.10.11.

A.Add(1)A.Add(4)A.Add(8)'A.Add("C") '<- ImpossibileFor Each V As Int32 In AConsole.WriteLine(V)

Next

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.

Module Module1Sub Main()

Dim TipDebole As New ArrayListDim TipForte As New List(Of Int32)Dim S As New Stopwatch

'Cronometra le operazioni su ArrayListS.Start()For I As Int32 = 1 To 1000000

TipDebole.Add(I)NextS.Stop()Console.WriteLine(S.ElapsedMilliseconds & _

" millisecondi per ArrayList!")

'Cronometra le operazioni su ListS.Reset()S.Start()For I As Int32 = 1 To 1000000

TipForte.Add(I)NextS.Stop()Console.WriteLine(S.ElapsedMilliseconds & _

" millisecondi per List(Of T)!")

Console.ReadKey()End Sub

End Module

001.002.003.004.005.006.007.008.009.010.011.

Module Module1'Collezione generica che contiene un qualsiasi tipo T di'oggetto. T si dice "tipo generic aperto"Class Collection(Of T)

'Per ora limitiamoci a dichiarare un array interno'alla classe.'Vedremo in seguito che è possibile ereditare da'una collezione generics già esistente.'Notate che la variabile è di tipo T: una volta che'abbiamo dichiarato la classe come generics su un tipo T,

Page 185: Guida visual basic

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

GetIf (Index >= 0) And (Index < _Values.Length) Then

Return _Values(Index)Else

Throw New IndexOutOfRangeException()End If

End GetSet(ByVal value As T)

If (Index >= 0) And (Index < _Values.Length) Then_Values(Index) = value

ElseThrow New IndexOutOfRangeException()

End IfEnd Set

End Property

'Proprietà che restituiscono il primo e l'ultimo'elemento della collezionePublic ReadOnly Property First() As T

GetReturn _Values(0)

End GetEnd Property

Public ReadOnly Property Last() As T

GetReturn _Values(_Values.Length - 1)

End GetEnd 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 _ValuesIf V IsNot Nothing Then

Console.WriteLine(V.ToString())End If

NextEnd Sub

'Inizializza la collezione con Count elementi, tutti del'valore DefaultValueSub New(ByVal Count As Int32, ByVal DefaultValue As T)

If Count < 1 ThenThrow New ArgumentOutOfRangeException()

End If

ReDim _Values(Count - 1)

For I As Int32 = 0 To _Values.Length - 1_Values(I) = DefaultValue

NextEnd 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

Page 186: Guida visual basic

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:

Metodi Generics e tipi generics collegati implic itiSe 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...).

Ecco un semplice esempio:

084.085.086.087.088.089.090.091.092.093.094.095.096.097.098.099.100.101.102.103.104.

'generico T, si dicono "tipi generic collegati",'poiché collegano il tipo fittizio T con un'reale tipo esistenteDim Strings As New Collection(Of String)(10, "null")Dim Integers As New Collection(Of Int32)(5, 12)Dim Dates As New Collection(Of Date)(7, Date.Now)Dim Persons As New Collection(Of Person)(10, Nothing)

Strings.Values(0) = "primo"Integers.Values(3) = 45Dates.Values(6) = New Date(2009, 1, 1)Persons.Values(3) = New Person("Mario", "Rossi", Dates.Last)

Strings.PrintAll()Integers.PrintAll()Dates.PrintAll()Persons.PrintAll()

Console.ReadKey()

End Sub End Module

1.2.

Console.WriteLine(Strings.GetType() Is Integers.GetType())'Output : False

1.2.3.4.5.6.7.

Sub [NomeProcedura](Of T)([Parametri])'...

End Sub Function [NomeFunzione](Of T)([Parametri]) As [TipoRestituito]

'...End Function

01.02.03.04.05.06.07.08.09.10.11.12.13.14.15.16.17.

Module Module1

'Scambia i valori di due variabili, passate'per indirizzoPublic Sub Swap(Of T)(ByRef Arg1 As T, ByRef Arg2 As T)

Dim Temp As T = Arg1Arg1 = Arg2Arg2 = Temp

End Sub

Sub Main()Dim X, Y As DoubleDim Z As SingleDim A, B As String

X = 90.0

Page 187: Guida visual basic

Generics multipliQuando, 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:

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.

Y = 67.58Z = 23.01A = "Ciao"B = "Mondo"

'Nelle prossime chiamate, Swap non presenta un'tipo generics collegato: il tipo viene dedotto dai'tipi degli argomenti 'X e Y sono Double, quindi richiama il metodo con'T = DoubleSwap(X, Y)'A e B sono String, quindi richiama il metodo con'T = StringSwap(A, B) 'Qui viene generato un errore: nonostante Z sia'convertibile in Double implicitamente senza perdita'di dati, il suo tipo non corrisponde a quello di X,'dato che c'è un solo T, che può assumere'un solo valore-tipo. Per questo è necessario'utilizzare una scappatoia'Swap(Z, X) 'Soluzione 1: si esplicita il tipo generic collegatoSwap(Of Double)(Z, X)'Soluzione 2: si converte Z in double esplicitamenteSwap(CDbl(Z), X)

Console.ReadKey()

End SubEnd Module

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.32.

Module Module2'Una relazione qualsiasi fra due oggetti di tipo indeterminatoPublic Class Relation(Of T1, T2)

Private Obj1 As T1Private Obj2 As T2

Public ReadOnly Property FirstObject() As T1

GetReturn Obj1

End GetEnd Property

Public ReadOnly Property SecondObject() As T2

GetReturn Obj2

End GetEnd Property

Sub New(ByVal Obj1 As T1, ByVal Obj2 As T2)

Me.Obj1 = Obj1Me.Obj2 = Obj2

End SubEnd Class

Sub Main()

'Crea una relazione fra uno studente e un insegnante,'utilizzando le classi create nei capitoli precedentiDim R As Relation(Of Student, Teacher)Dim S As New Student("Pinco", "Pallino", Date.Parse("25/06/1990"), _

"Liceo Scientifico N. Copernico", 4)Dim T As New Teacher("Mario", "Rossi", Date.Parse("01/07/1950"), _

Page 188: Guida visual basic

Notate che è anche possibile cr ear e una r elazione tr a due r elazioni (e la cosa diventa complicata):

Alcune regole per l'uso dei Generics

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:

Tuttavia si può er editar e da una classe gener ics specificando come tipo gener ics collegato lo stesso tipo aper to:

Allo stesso modo, non si può implementar e T come se fosse un'inter faccia:

Ma si può implementar e un'inter faccia gener ics di tipo T:

33.34.35.36.37.38.39.40.41.

"Matematica")

'Crea una nuova relazione tra lo studente e l'insegnanteR = New Relation(Of Student, Teacher)(S, T)Console.WriteLine(R.FirstObject.CompleteName)Console.WriteLine(R.SecondObject.CompleteName)

Console.ReadKey()

End SubEnd Module

01.

02.03.04.05.06.07.08.09.

10.11.12.13.14.15.16.17.18.

Dim S As New Student("Pinco", "Pallino", Date.Parse("25/06/1990"), "Liceo Scientifico N.Copernico", 4)

Dim T As New Teacher("Mario", "Rossi", Date.Parse("01/07/1950"), "Matematica")Dim StudentTeacherRelation As Relation(Of Student, Teacher)Dim StudentClassRelation As Relation(Of Student, String)Dim Relations As Relation(Of Relation(Of Student, Teacher), Relation(Of Student, String)) StudentTeacherRelation = New Relation(Of Student, Teacher)(S, T)StudentClassRelation = New Relation(Of Student, String)(S, "5A")Relations = New Relation(Of Relation(Of Student, Teacher), Relation(Of Student, String))

(StudentTeacherRelation, StudentClassRelation) 'Relations.FirstObject.FirstObject' > Student "Pinco Pallino"'Relations.FirstObject.SecondObject' > Teacher "Mario Rossi"'Relations.SecondObject.FirstObject' > Student "Pinco Pallino"'Relations.SecondObject.SecondObject' > String "5A"

1.2.3.4.

Class Example(Of T)Inherits T' SBAGLIATO

End Class

1.2.3.4.

Class Example(Of T)Inherits List(Of T)' CORRETTO

End Class

1.2.3.4.

Class Example(Of T)Implements T' SBAGLIATO

End Class

1.2.3.4.

Class Example(Of T)Implements IEnumerable(Of T)' CORRETTO

Page 189: Guida visual basic

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:

End Class

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

Page 190: Guida visual basic

A42. I Generics - Parte II

Interfacce GenericsPr 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.

I VincoliI 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:

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

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.32.33.34.35.36.37.38.39.40.

Module Module1'Questa classe implementa un comaparatore di oggetti Student'in base al loro anno di corsoClass StudentByGradeComparer

Implements IComparer(Of Student)

'Come potete osservare, in questo metodo non viene eseguito'nessun tipo di cast, poiché l'interfaccia IComparer(Of T)'prevede un metodo Compare a tipizzazione forte. Dato che'abbiamo specificato come tipo generic collegato Student,'anche il tipo a cui IComparer si riferisce sarà'Student. Possiamo accedere alle proprietà di x e y'senza nessun late binding (per ulteriori informazioni,'vedere i capitoli sulla reflection)Public Function Compare(ByVal x As Student, ByVal y As Student) As Integer Implements

IComparer(Of Student).CompareReturn x.Grade.CompareTo(y.Grade)

End FunctionEnd Class

Sub Main()

'Crea un nuovo array di oggeti StudentDim S(2) As Student

'Inizializza ogni oggettoS(0) = New Student("Mario", "Rossi", New Date(1993, 2, 3), "Liceo Classico Ugo

Foscolo", 2)S(1) = New Student("Luigi", "Bianchi", New Date(1991, 6, 27), "Liceo Scientifico

Fermi", 4)S(2) = New Student("Carlo", "Verdi", New Date(1992, 5, 12), "ITIS Cardano", 1)

'Ordina l'array con il comparer specificatoArray.Sort(S, New StudentByGradeComparer())

'Stampa il profilo di ogni studente: vedrete che essi sono'in effetti ordinati in base all'anno di corsoFor Each St As Student In S

Console.WriteLine(St.Profile)NextConsole.ReadKey()

End Sub End Module

1. If A = B Then '...

Page 191: Guida visual basic

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 incolo, 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.

Vincolo di Interfacc iaIl 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:

Ecco un esempio:

1. (Of T As [Interfaccia])

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.

Module Module1

'Questa classe rappresenta una collezione di'elementi che possono essere comparati. Per questo'motivo, il tipo T espone un vincolo di interfaccia'che obbliga tutti i tipi generics collegati ad'implementare tale interfaccia.'Notate bene che in questo caso particolare ho usato'un generics doppio, poiché il vincolo non'si riferisce a IComparable, ma a IComparable(Of T).'D'altra parte, è abbastanza ovvio che se'una collezione contiene un solo tipo di dato,'basterà che la comparazione sia possibile'solo attraverso oggetti di quel tipoClass ComparableCollection(Of T As IComparable(Of T))

'Ereditiamo direttamente da List(Of T), acquisendone'automaticamente tutti i membri base e le caratteristiche.'In questo modo, godremo di due grandi vantaggi:' - non dovremo definire tutti i metodi per aggiungere,' rimuovere o cercare elementi, in quanto vengono tutti' ereditati dalla classe base List;' - non dovremo neanche implementare l'interfaccia' IEnumerable(Of T), poiché la classe base la' implementa di per sé.Inherits List(Of T)

'Dato che gli oggetti contenuti in oggetti di'questo tipo sono per certo comparabili, possiamo'trovarne il massimo ed il minimo. 'Trova il massimo elementoPublic ReadOnly Property Max() As T

Get

Page 192: Guida visual basic

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 ThenDim Result As T = Me(0)

For Each Element As T In Me

'Ricordate che A.CompareTo(B) restituisce'1 se A > BIf Element.CompareTo(Result) = 1 Then

Result = ElementEnd If

Next

Return ResultElse

Return NothingEnd If

End GetEnd Property

'Trova il minimo elementoPublic ReadOnly Property Min() As T

GetIf Me.Count > 0 Then

Dim Result As T = Me(0)

For Each Element As T In MeIf Element.CompareTo(Result) = -1 Then

Result = ElementEnd If

Next

Return ResultElse

Return NothingEnd If

End GetEnd Property

'Trova tutti gli elementi uguali ad A e ne restituisce'gli indiciPublic Function FindEquals(ByVal A As T) As Int32()

Dim Result As New List(Of Int32)

For I As Int32 = 0 To Me.Count - 1If 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 elementiReturn Result.ToArray()

End Function

End Class Sub Main()

'Tre collezioni, una di interi, una di stringhe e'una di dateDim 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())' > 4Console.WriteLine(A.Max())' > 90Console.WriteLine(B.Min())

Page 193: Guida visual basic

Vincolo 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:

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:

Vincoli di c lasse e strutturaIl 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:

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:

106.107.108.109.110.111.112.113.114.115.116.117.118.119.120.121.122.

' > accaConsole.WriteLine(B.Max())' > zenConsole.WriteLine(C.Min().ToShortDateString)' > 31/12/1999Console.WriteLine(C.Max().ToShortDateString)' > 12/4/2100

'Trova la posizione degli elementi uguali a 4Dim AEqs() As Int32 = A.FindEquals(4)' > 0 6 8Dim BEqs() As Int32 = B.FindEquals("casa")' > 1 4

Console.ReadKey()

End Sub End Module

1.2.3.4.

Class IsARelation(Of T, U As T)Public Base As TPublic Derived As U

End Class

1.2.3.4.

Dim P As PersonDim S As Student'...Dim A As New IsARelation(Of Person, Student)(P, S)

1.2.

(Of T As Class)(Of T As Structure)

Page 194: Guida visual basic

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.

Vincolo NewQuesto 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:

Vincoli multipliUn 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:

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.32.

Module Module1

'Con molta fantasia, il vincolo New si dichiara postponendo'"As New" al tipo generic aperto.Function CreateArray(Of T As New)(ByVal Count As Int32) As T()

Dim Result(Count - 1) As T

For I As Int32 = 0 To Count - 1'Possiamo usare il costruttore perchè il'vincolo ce lo assicuraResult(I) = New T()

Next

Return ResultEnd Function

Sub Main()

'Crea 10 flussi di dati in memoria. Non abbiamo'mai usato questa classe perchè rientra in'un argomento che tratterò più avanti, ma'è una classe particolarmente utile e versatile'che trova applicazioni in molte situazioni.'Avere un bel metodo generics che ne crea 10 in una'volta è una gran comodità.'Ovviamente possiamo fare la stessa cosa con tutti'i tipi che espongono almeno un New senza parametriDim Streams As IO.MemoryStream() = CreateArray(Of IO.MemoryStream)(10)

'...

End Sub

End Module

1. (Of T As {Vincolo1, Vincolo2, ...})

Page 195: Guida visual basic

Ecco un esempio:

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.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.

Module Module1

'Classe che filtra dati di qualsiasi naturaClass DataFilter(Of T)

Delegate Function FilterData(ByVal Data As T) As Boolean

'La signature chilometrica è fatta apposta per'farvi impazzire XD Vediamo le parti una per una:' - TSerach: deve essere un tipo uguale a T o derivato' da T, in quanto stiamo elaborando elementi di tipo T;' inoltre deve anche essere clonabile, poiché' salveremo solo una copia dei valor trovati.' Questo implica che TSearch sia un tipo reference, e che' quindi lo sia anche T: questa complicazione è solo' per mostrare dei vincoli multipli e potete anche' rimuoverla se vi pare;' - TList: deve essere un tipo reference, esporre un' costruttore senza parametri ed implementare' l'interfaccia IList(Of TSearch), ossia deve' essere una lista;' - ResultList: lista in cui riporre i risultati (passata' per indirizzo);' - Filter: delegate che punta alla funzione usata per' selezionare i valori;' - Data: paramarray contenente i valori da filtrare.Sub Filter(Of TSearch As {ICloneable, T}, TList As {IList(Of TSearch), New, Class}) _

(ByRef ResultList As TList, ByVal Filter As FilterData, ByVal ParamArray Data() AsTSearch)

'Se la lista è Nothing, la inizializza.'Notare che non avremmo potuto compararla a Nothing'senza il vincolo Class, né inizializzarla'senza il vincolo NewIf ResultList Is Nothing Then

ResultList = New TList()End If

'Itera sugli elementi di dataFor Each Element As TSearch In Data

'E aggiunge una copia di quelli che'soddisfano la condizioneIf Filter.Invoke(Element) Then

'Aggiunge una copia dell'elemento alla lista.'Anche in questo non avremmo potuto richiamare'Add senza il vincolo interfaccia su IList, né'clonare Element senza il vincolo interfaccia ICloneableResultList.Add(Element.Clone())

End IfNext

End SubEnd Class

'Controlla se la stringa A è palindromaFunction IsPalindrome(ByVal A As String) As Boolean

Dim Result As Boolean = True

For I As Int32 = 0 To (A.Length / 2) - 1If A.Chars(I) <> A.Chars(A.Length - 1 - I) Then

Result = FalseExit For

End IfNext

Return Result

End Function

Sub Main()Dim DF As New DataFilter(Of String)'Lista di stringhe: notare che la variabile non'contiene nessun oggetto perchè non abbiamo usato New.

Page 196: Guida visual basic

71.72.73.74.75.76.77.78.79.80.81.82.83.84.85.86.

'Serve per mostrare che verrà inizializzata'da DF.Filter.Dim L As List(Of String)

'Analizza le stringhe passate, trova quelle palindrome'e le pone in LDF.Filter(L, AddressOf IsPalindrome, _

"casa", "pane", "anna", "banana", "tenet", "radar")

For Each R As String In LConsole.WriteLine(R)

Next

Console.ReadKey()End Sub

End Module

Page 197: Guida visual basic

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:

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:

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.02.03.04.05.06.07.08.09.

If Cell.Value IsNot DBNull.Value ThenVariable = Cell.Value

ElseVariable = 0'Per impostare il valore di default, bisognava ripetere'questi If tante volte quanti erano i tipi in gioco, poiché'non c'era modo di assegnare un valore Null a tutti'in un solo colpo

End If

1.2.3.4.5.

'Dichiarazione esplicita:Dim [Nome] As Nullable(Of [Tipo]) 'Dichiarazione implicita:Dim [Nome] As [Tipo]?

01.02.03.04.05.06.07.08.

Module Module1

Sub Main()'Tre variabili di tipo value dichiarate come'nullable nei due modi diversi consentitiDim Number As Integer?Dim Data As Nullable(Of Date)

Page 198: Guida visual basic

Logica booleana a tre valoriUn 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 Oper ator e Valor e 2 Risultato

Tr ue And Null Null

False And Null False

Tr ue Or Null Tr ue

False Or Null Null

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.

Dim Cost As Double?Dim Sent As Nullable(Of Boolean)

'Ammettiamo di star controllando un database:'questo array di oggetti rappresenta il contenuto'di una rigaDim RowValues() As Object = {DBNull.Value, New Date(2009, 7, 1), 67.99, DBNull.Value}

'Con un solo ciclo trasforma tutti i DBNull.Value'in Nothing, poiché i nullable supportano solo'Nothing come valore nulloFor I As Int16 = 0 To RowValues.Length - 1

If RowValues(I) Is DBNull.Value ThenRowValues(I) = Nothing

End IfNext

'Assegna alle variabili i valori contenuti nell'array:'non ci sono mai problemi in questo codice, poiché,'trattandosi di tipi nullable, questi oggetti possono'accettare anche valori Nothing. In questo esempio,'Number e Sent riceveranno un Nothing come valore: la'loro proprietà HasValue varrà False.Number = RowValues(0)Data = RowValues(1)Cost = RowValues(2)Sent = RowValues(3)

'Scrive a schermo il valore di ogni variabile, se ne'contiene uno, oppure il valore di default se non'contiene alcun valore.Console.WriteLine("{0} {1} {2} {3}", _

Number.GetValueOrDefault, _Data.GetValueOrDefault, _Cost.GetValueOrDefault, _Sent.GetValueOrDefault)

'Provando a stampare una variabile nullable priva'di valore senza usare la funzione GetValueOrDefault,'semplicemente non stamperete niente:' Console.WriteLine(Number)'Non stampa niente e va a capo. Console.ReadKey()

End Sub End Module

Page 199: Guida visual basic

Tr ue Xor Null Null

False Xor Null Null

Page 200: Guida visual basic

A44. La Reflection - Parte I

Con il ter mine gener ale di r eflection si intendono tutte le classi del Fr amewor k che per mettono di acceder e o

manipolar e assembly e moduli.

Assembly

L'assembly è l'unità logica più piccola su cui si basa il Fr amewor k .NET. Un assembly altr o non è che un pr ogr amma o

una libr er ia di classi (compilati in .NET). Il Fr amewor k stesso è composto da una tr entina di assembly pr incipali che

costituiscono le libr er ie di classi più impor tanti per la pr ogr ammazione .NET (ad esempio System.dll,

System.Dr aw ing.dll, System.Cor e.dll, ecceter a...).

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 amewor 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 dinamico 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.

Page 201: Guida visual basic

I ContestiPr 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 questo modo, un

possibile cr ash di entr ambi i pr ogr ammi o del sistema stesso. Questa politica di coor dinamento, quindi, r ende sicur a e

isolata l'esecuzione di un pr ogr amma. Il CLR del .NET, tuttavia, aggiunge un'ulter ior e suddivisione, basata sui domini

applicativ i o AppDomain o contesti di esecuzione. All'inter no di un singolo pr ocesso possono esister e più domini

applicativi, i quali sono tr a lor o isolati come se fosser o due pr ocessi differ enti: in questo modo, un assembly

appar tenente ad un cer to AppDomain non può modificar e un altr o assembly in un altr o AppDomain. Tuttavia, come è

lecito scambiar e dati fr a pr ocessi, è anche lecito scambiar e dati tr a contesti di esecuzione: l'unica differ enza sta nel

fatto che questi ultimi sono allocati nello stesso pr ocesso e, quindi, possono comunicar e molto più velocemente. Così

facendo, un singolo pr ogr ama può cr ear e due domini applicativi che cor r ono in par allelo come se fosser o pr ocessi

differ enti, ma attr aver so i quali è molto più semplice la comunicazione e lo scambio di dati. Un semplice esempio lo

potr ete tr ovar e osser vando il Task Manager di Windows quando ci sono due finestr e di Fir eFox aper te allo stesso

tempo: noter e che vi è un solo pr ocesso fir efox .ex e associato.

Page 202: Guida visual basic

Caricare un assemblyUn assembly è r appr esentato dalla classe System.Reflection.Assembly. Tutte le oper azioni effettuabili su di esso sono

esposte mediante metodi della classe assembly. Pr imi fr a tutti, spiccano i metodi per il car icamento, che si distinguono

dagli altr i per la lor o copiosa quantità. Esistono, infatti, ben sette metodi statici per car icar e od ottener e un

r ifer imento ad un assembly, e tutti offr ono una modalità di car icamento diver sa dagli altr i. Eccone una lista:

Assembly.GetEx cecutingAssembly()

Restituisce un r ifer imento all'assembly che è in esecuzione e dal quale questa chiamata a funzione viene lanciata.

In poche par ole, l'oggetto che ottenete invocando questo metodo si r ifer isce al pr ogr amma o alla libr er ia che

state scr ivendo;

Assembly.GetAssembly(ByVal T As System.Type) oppur e T.Assembly()

Restituiscono un r ifer imento all'assembly in cui è definito il tipo T specificato;

Assembly.Load("Nome")

Car ica un assembly a par tir e dal nome completo o par ziale. Ad esempio, si può car icar e System.Xml.dll

dinamicamente con Assembly.Load("System.Xml"). Restituisce un r ifer imento all'assembly car icato. "Nome" può

Page 203: Guida visual basic

anche esser e il nome completo dell'assembly, che compr ende nome, ver sione, cultur a e token della chiave

pubblica. La chiave pubblica è un lunghissimo codice for mato da cifr e esadecimali che identificano univocamente

il file; il suo token ne è una ver sione "abbr eviata", utile per non scr iver e la chiave inter a. Vedr emo tr a poco una

descr izione dettagliata del nome di un assembly.

Se un assembly viene car icato con Load, esso diviene par te del contesto di esecuzione cor r ente, e inoltr e il

Fr amewor k è capace di tr ovar e e car icar e le sue dipendenze da altr i file, ossia tutti gli assembly che ser vono a

questo per funzionar e (in gener e tutti quelli specificati nelle dir ettive Impor ts). In ger go, quest'ultima azione si

dice "r isolver e le dipendenze";

Assembly.LoadFr om("File")

Car ica un assembly a par tir e dal suo per cor so su disco, che può esser e r elativo o assoluto, e ne r estituisce un

r ifer imento. Il file car icato in questo modo diventa par te del contesto di esecuzione di LoadFr om. Inoltr e, il

Fr amewor k è in gr ado di r isolver ne le dipendenze solo nel caso in cui queste siano pr esenti nella car tella

pr incipale dell'applicazione;

Assembly.LoadFile("File")

Agisce in modo analogo a LoadFr om, ma l'assembly viene car icato in un contesto di esecuzione differ ente, e il

Fr amewor k non è in gr ado di r isolver ne le dipendenze, a meno che queste non siano state già car icate con i

metodi sopr a r ipor tati;

Assembly.ReflectionOnlyLoad("Nome")

Restituisce un r ifer imento all'assembly con dato Nome. Questo non viene car icato in memor ia, poichè il metodo

ser ve solamente a ispezionar ne gli elementi;

Assembly.ReflectionOnlyLoadFr om("File")

Restituisce un r ifer imento all'assembly specificato nel per cor so File. Questo non viene car icato in memor ia,

poichè il metodo ser ve solamente a ispezionar ne gli elementi.

Gli ultimi due metodi hanno anche un par ticolar e effetto collater ale. Anche se gli assembly non vengono car icati in

memor ia, ossia non diventano par te attiva dal dominio applicativo, pur tuttavia vengono posti in un altr o contesto

speciale, detto contesto di ispezione. Quest'ultimo è unico per ogni pr ocesso e condiviso da tutti gli AppDomain

pr esenti nel pr ocesso.

Nome dell'assembly e analisi superfic ialeUna volta ottenuto un r ifer imento ad un oggetto di tipo Assembly, possiamo usar ne i membr i per ottener e le più var ie

infor mazioni. Ecco una br eve lista delle pr opr ietà e dei metodi più significativi:

Fullname : r estituisce il nome completo dell'assembly, specificando nome, cultur a, ver sione e token della chiave

pubblica;

CodeBase : nel caso l'assembly sia scar icato da inter net, ne r estituisce la locazione in for mato oppor tuno;

Location : r estituisce il per cor so su disco dell'assembly;

GlobalAssemblyChace : pr opr ietà che value Tr ue nel caso l'assembly sia stato car icato dalla GAC;

Global A ssembly Cache (GAC)

La car tella fisica in cui vengono depositati tutti gli assembly pubblici. Per assembly pubblico, infatti, s'intende

ogni assembly accessibile da ogni applicazione su una deter minata macchina. Gli assembly pubblici sono,

solitamente, tutti quelli di base del Fr amewor k .NET, ma è possibile aggiunger ne altr i con deter minati

comandi. La GAC di Windows è di solito posizionata in C:\WINDOWS\assembly e contiene tutte le libr er ie base

del Fr amewor k. Ecco per chè basta specificar e il nome dell'assembly pubblico per car icar lo (la car tella è nota a

pr ior i).

Page 204: Guida visual basic

ReflectionOnly : r estituisce Tr ue se l'assembly è stato car icato per soli scopi di analisi (r eflection);

GetName() : r estituisce un oggetto AssemblyName associato all'assembly cor r ente;

GetTypes() : r estituisce un ar r ay di Type che definiscono ogni tipo dichiar ato all'inter no dell'assembly.

Pr ima di ter minar e il capitolo, esaminiamo le par ticolar ità del nome dell'assembly. In gener e il nome completo di un

assembly ha questo for mato:

[Nome Principale], Version=a.b.c.d, Culture=[Cultura], PublicKeyToken=[Token]

Il nome pr incipale è deter minato dal pr ogr ammator e e di solito indica il namespace pr incipale contenuto nell'assembly.

La ver sione è un numer o di ver sione a quattr o par ti, divise solitamente, in or dine, come segue: Major (numer o di

ver sione pr incipale) , Minor (numer o di ver sione minor e, secondar io), Revision (numer o della r evisione a cui si è giunti

per questa ver sione), Build (numer o di compilazioni eseguite per questa r evisione). Il numer o di ver sione indica di

solito la ver sione del Fr amewor k per cui l'assembly è stato scr itto: se state usando VB2005, tutte le ver sioni sar anno

uguali o infer ior i a 2.0.0.0; con VB2008 sar anno uguali o infer ior i a 3.5.0.0. Cultur e r appr esenta la cultur a in cui è

stato scr itto l'assembly: di solito è semplicmente "neutr al", neutr ale, ma nel caso in cui sia differ ente, influenza alcuni

aspetti secondar i come la r appr esentazione dei numer i (sepr ator i decimali e delle migliaia), dell'or ar io, i simboli di

valuta, ecceter a... Il token della chiave pubblica è un insieme di otto bytes che identifica univocamente la chiave

pubblica (è una sua ver sione "abbr eviata"), la quale identifica univocamente l'assembly. Viene usato il token e non tutta

la chiave per questioni di lunghezza. Ecco un esempio che ottiene questi dati:

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.32.33.34.35.36.37.38.39.40.41.42.

Module Module1

Sub Main()'Carica un assembly per soli scopi di analisi.'mscorlib è l'assembly più importante di'tutto il Framework, da cui deriva pressochè ogni'cosa. Data la sua importanza, non ha dipendenze,'perciò non ci saranno problemi nel risolverle.'Se volete caricare un altro assembly, dovrete usare'uno dei metodi in grado di risolvere le dipendenze.Dim Asm As Assembly = Assembly.ReflectionOnlyLoad("mscorlib")Dim Name As AssemblyName = Asm.GetName

Console.WriteLine(Asm.FullName)Console.WriteLine("Nome: " & Name.Name)Console.WriteLine("Versione: " & Name.Version.ToString)Console.WriteLine("Cultura: " & Name.CultureInfo.Name)

'Il formato X indica di scrivere un numero usando la'codifica esadecimale. X2 impone di occupare sempre almeno'due posti: se c'è una sola cifra, viene inserito'uno zero.Console.Write("Public Key: ")For Each B As Byte In Name.GetPublicKey()

Console.Write("{0:X2}", B)NextConsole.WriteLine()

Console.Write("Public Key token: ")For Each B As Byte In Name.GetPublicKeyToken

Console.Write("{0:X2}", B)NextConsole.WriteLine()

Console.WriteLine("Processore: " & _

Name.ProcessorArchitecture.ToString)

Console.ReadKey()

End Sub End Module

Page 205: Guida visual basic

Con quello che abbiamo visto fin'or a si potr ebbe scr iver e una pr ocedur a che enumer i tutti gli assembly pr esenti nel

contesto cor r ente:

01.02.03.04.05.06.07.08.09.10.11.12.13.14.15.16.17.18.19.

Sub EnumerateAssemblies()Dim Asm As AssemblyDim Name As AssemblyName

'AppDomain è una variabile globale, oggetto singleton, da cui'si possono trarre informazioni sull'AppDomain corrente o'crearne degli altri.For Each Asm In AppDomain.CurrentDomain.GetAssemblies

Name = Asm.GetNameConsole.WriteLine("Nome: " & Name.Name)Console.WriteLine("Versione: " & Name.Version.ToString)Console.Write("Public Key Token: ")For Each B As Byte In Name.GetPublicKeyToken

Console.Write(Hex(B))NextConsole.WriteLine()Console.WriteLine()

NextEnd Sub

Page 206: Guida visual basic

A45. La Reflection - Parte II

La c lasse System.TypeLa classe Type è una classe davver o par ticolar e, poiché r appr esenta un tipo. Con tipo indichiamo tutte le possibili

tipologie di dato esistenti: tipi base, enumer ator i, str uttur e, classi e delegate. Per ogni tipo contemplato, esiste un

cor r ispettivo oggetto Type che lo r appr esenta: avevo detto all'inizio della guida, infatti, che ogni cosa in .NET è un

oggetto, ed i tipi non fanno eccezione. Vi sor pr ender ebbe saper e tutto ciò che può esser e r appr esentato da una classe

e fr a poco vi sveler ò un segr eto... Ma per or a concentr iamoci su Type. Questi oggetti r appr esentanti un tipo - che

possiamo chiamar e per br evità OT (non è un ter mine tecnico) - vengono cr eati dur ante la fase di inizializzazione del

pr ogr amma e ne esiste una e una sola copia per ogni singolo tipo all'inter no di un singolo AppDomain. Ciò significa che

due contesti applicativi differ enti avr anno due OT diver si per r appr esentar e lo stesso tipo, ma non analizzer emo

questa peculiar e casistica. Ci limiter emo, invece, a studiar e gli OT all'inter no di un solo dominio applicativo, coincidente

con il nostr o pr ogr amma.

Come per gli assembly, esistono molteplici modi per ottener e un OT:

Tr amite l'oper ator e GetType(Tipo);

Tr amite la funzione d'istanza GetType();

Tr amite la funzione condivisa Type.GetType("nometipo").

Ecco un semplice esempio di come funzionano questi metodi:

Or a che ho esemplificato come ottener e un OT, vor r ei mostr ar e l'unicità di OT ottenuti in modi differ enti: anche se

usassimo tutti i tr e metodi sopr a menzionati per ottener e un OT per il tipo Str ing, otter r emo un r ifer imento allo

stesso oggetto, poiché il tipo Str ing è unico:

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.

Module Module1

Sub Main()'Ottiene un OT per il tipo double tramite'l'operatore GetTypeDim DoubleType As Type = GetType(Double)Console.WriteLine(DoubleType.FullName) 'Ottiene un OT per il tipo string tramite'la funzione statica Type.GetType. Essa richiede'come parametro il nome (possibilmente completo)'del tipo. Nel caso il nome non corrisponda a'nessun tipo, verrà restituito NothingDim StringType As Type = Type.GetType("System.String")Console.WriteLine(StringType.FullName)

'Ottiene un OT per il tipo ArrayList tramite'la funzione d'istanza GetType. Da notare che,'mentre le precedenti usavano come punto'di partenza direttamente un tipo (o il suo nome),'questa richiede un oggetto di quel tipo.Dim A As New ArrayListDim ArrayListType As Type = A.GetType()Console.WriteLine(ArrayListType.FullName)

Console.ReadKey()

End Sub End Module

01.02.03.04.

Module Module1

Sub Main()

Page 207: Guida visual basic

Questo non vale per il tipo System.Type stesso, poiché il metodo d'istanza GetType r estituisce un oggetto RuntimeType.

Questi dettagli, tuttavia, non vi inter esser anno se non tr a un bel po' di tempo, quindi possiamo anche evitar e di

soffer mar ci e pr oceder e con la spiegazione.

Ogni oggetto Type espone una quantità inimmaginabile di membr i e penso che potr ebbe esser e la classe più ampia di

tutto il Fr amewor k. Di questa massa enor me di infor mazioni, ve ne è un sottoinsieme che per mette di saper e in che

modo il tipo è stato dichiar ato e quali sono le sue car atter istiche pr incipali. Possiamo r icavar e, ad esempio, gli

specificator i di accesso, gli eventuali modificator i, possiamo saper e se si tr atta di una classe, un enumer ator e, una

str uttur a o altr o, e, nel pr imo caso, se è astr atta o sigillata; possiamo saper e le sua classe base, le inter facce che

implementa, se si tr atta di un ar r ay o no, ecceter a... Di seguito elenco i membr i di questo sottoinsieme:

Assembly : r estituisce l'assembly a cui il tipo appar tiene (ossia in cui è stato dichiar ato);

AssemblyQualifiedName : r estituisce il nome dell'assembly a cui il tipo appar tiene;

BaseType : se il tipo cor r ente er edita da una classe base, questa pr opr ietà r estituisce un oggetto Type in

r ifer imento a tale classe;

Declar ingMethod : se il tipo cor r ente è par ametr o di un metodo, questa pr opr ietà r estituisce un oggetto

MethodBase che r appr esenta tale metodo;

Declar ingType : se il tipo cor r ente è membr o di una classe, questa pr opr ietà r estituisce un oggetto Type che

r appr esenta tale classe; questa pr opr ietà viene valor izzata, quindi, solo se il tipo è stato dichiar ato all'inter no

di un altr o tipo (ad esempio classi nidificate);

FullName : il nome completo del tipo cor r ente;

IsAbstr act : deter mina se il tipo è una classe astr atta;

IsAr r ay : deter mina se è un ar r ay;

IsClass : deter mina se è una classe;

IsEnum : deter mina se è un enumer ator e;

IsInter face : deter mina se è un'inter faccia;

IsNested : deter mina se il tipo è nidificato: questo significa che r appr esenta un membr o di classe o di str uttur a;

di conseguenza tutte le pr opr ietà il cui nome inizia per "IsNested" ser vono a deter minar e l'ambito di visibilità

del membr o, e quindi il suo specificator e di accesso;

IsNestedAssembly : deter mina se il membr o è Fr iend;

IsNestedFamily : deter mina se il membr o è Pr otected;

IsNestedFamORAssem : deter mina se il membr o è Pr otected Fr iend;

IsNestedPr ivate : deter mina se il membr o è Pr ivate;

IsNestedPublic : deter mina se il membr o è Public;

IsNotPublic : deter mina se il tipo non è Public (solo per tipi non nidificati). Vi r icor do, infatti, che all'inter no di

un namespace, gli unici specificator i possibili sono Public e Fr iend (gli altr i si adottano solo all'inter no di una

classe);

IsPointer : deter mina se è un puntator e;

05.06.07.08.09.10.11.12.13.14.15.16.17.18.19.

Dim Type1 As Type = GetType(String)Dim Type2 As Type = Type.GetType("System.String")Dim Type3 As Type = "Ciao".GetType()

Console.WriteLine(Type1 Is Type2)'> TrueConsole.WriteLine(Type2 Is Type3)'> True

'Gli OT contenuti in Type1, Type2 e Type3'SONO lo stesso oggetto Console.ReadKey()

End Sub End Module

Page 208: Guida visual basic

IsPr imitive : deter mina se è uno dei tipi pr imitivi;

IsPublic : deter mina se il tipo è Public (solo per tipi non nidificati);

IsSealed : deter mina se è una classe sigillata;

IsValueType : deter mina se è un tipo value;

Name : il nome del tipo cor r ente;

Namespace : il namespace in cui è contenuto il tipo cor r ente.

Con questa abbondante manciata di pr opr ietà possiamo iniziar e a scr iver e un metodo di analisi un po' più

appr ofondito. Nella fattispecie, la pr ossima pr ocedur a Enumer ateTypes accetta come par ametr o il r ifer imento ad un

assembly e scr ive a scher mo tutti i tipi ivi definiti:

Il nostro piccolo segretoPr ima di pr oceder e con l'enumer azione dei membr i, vor r ei mostr ar e che in r ealtà tutti i tipi sono classi, soltanto con

r egole "speciali" di er editar ietà e di sintassi. Questo codice r intr accia tutte le classi basi di un tipo, costr uendone

l'alber o di er editar ietà fino alla r adice (che sar à ovviamente System.Object):

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.32.33.34.35.36.37.38.

Module Module1

Sub EnumerateTypes(ByVal Asm As Assembly)Dim Category As String

'GetTypes restituisce un array di Type che'indicano tutti i tipi definiti all'interno'dell'assembly AsmFor Each T As Type In Asm.GetTypes

If T.IsClass ThenCategory = "Class"

ElseIf T.IsInterface ThenCategory = "Interface"

ElseIf T.IsEnum ThenCategory = "Enumerator"

ElseIf T.IsValueType ThenCategory = "Structure"

ElseIf T.IsPrimitive ThenCategory = "Base Type"

End IfConsole.WriteLine("{0} ({1})", T.Name, Category)

NextEnd Sub

Sub Main()

'Ottiene un riferimento all'assembly in esecuzione,'quindi al programma. Non otterrete molti tipi'usando questo codice, a meno che il resto del'modulo non sia pieno di codice vario come nel'mio caso XDDim Asm As Assembly = Assembly.GetExecutingAssembly()

EnumerateTypes(Asm)

Console.ReadKey()

End Sub End Module

01.02.03.04.05.06.07.08.09.

Module Module1

'Analizza l'albero di ereditarietà di un tipoSub AnalyzeInheritance(ByVal T As Type)

'La proprietà BaseType restituisce la classe'base da cui T è derivataIf T.BaseType IsNot Nothing Then

Console.WriteLine("> " & T.BaseType.FullName)

Page 209: Guida visual basic

L'output mostr a che il tipo Integer e la str uttur a Ex ample der ivano entr ambi da System.ValueType, che a sua volta

der iva da Object. La definizione r igor osa di "tipo value", quindi, sar ebbe "qualsiasi tipo der ivato da System.ValueType".

Infatti, al par i dei pr imi due, anche l'enumer ator e der iva indir ettamente da tale classe, anche se mostr a un passaggio

in più, attr aver so il tipo System.Enum. Allo stesso modo, il delegate Sample der iva dalla classe DelegateMulticast, la

quale der ivata da Delegate, la quale der iva da Object. La differ enza sostanziale tr a tipi value e r efer ence, quindi,

r isiede nel fatto che i pr imi hanno almeno un passaggio di er editar ietà attr aver so la classe System.ValueType, mentr e

i secondi der ivano dir ettamente da Object.

System.Enum e System.Delegate sono classi astr atte che espongono utili metodi statici che potete ispezionar e da soli

(sono pochi e di facile compr ensione). Ma or a che sapete che tutti i tipi sono classi, potete anche esplor ar e i membr i

esposti dai tipi base.

Enumerare i membriFino ad or a abbiamo visto solo come analizzar e i tipi, ma ogni tipo possiede anche dei membr i (var iabili, metodi,

pr opr ietà, eventi, ecceter a...). La Reflection per mette anche di ottener e infor mazioni sui membr i di un tipo, e la

classe in cui queste infor mazioni vengono poste è Member Info, del namespace System.Reflection. Dato che ci sono

diver se categor ie di membr i, esistono altr ettante classi der ivate da Member Info che ci r accontano una stor ia tutta

diver sa a seconda di cosa stiamo guar dando: MethodInfo contiene infor mazioni su un metodo, Pr oper tyInfo su una

pr opr ietà, Par amter Info su un par ametr o, FieldInfo su un campo e via dicendo. Fr a le molteplici funzioni esposte da

Type, ce ne sono alcune che ser vono pr opr io a r eper ir e questi dati; eccone un elenco:

GetConstr uctor s() : r estituisce un ar r ay di Constr uctor Info, ognuno dei quali r appr esenta uno dei costr uttor i

definiti per quel tipo;

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.

AnalyzeInheritance(T.BaseType)End If

End Sub

Enum StatusEnabledDisabledStandby

End Enum

Structure ExampleDim A As Int32

End Structure

Delegate Sub Sample()

Sub Main()Console.WriteLine("Integer:")AnalyzeInheritance(GetType(Integer))Console.WriteLine()

Console.WriteLine("Enum Status:")AnalyzeInheritance(GetType(Status))Console.WriteLine()

Console.WriteLine("Structure Example:")AnalyzeInheritance(GetType(Example))Console.WriteLine()

Console.WriteLine("Delegate Sample:")AnalyzeInheritance(GetType(Sample))Console.WriteLine()

Console.ReadKey()

End Sub End Module

Page 210: Guida visual basic

GetEvents() : r estituisce un ar r ay di EventInfo, ognuno dei quali r appr esenta uno degli eventi dichiar ati in quel

tipo;

GetFields() : r estituisce un ar r ay di FieldInfo, ognuno dei quali r appr esenta uno dei campi dichiar ati in quel tipo;

GetInter faces() : r estituisce un ar r ay di Type, ognuno dei quali r appr esenta una delle inter facce implementate

da quel tipo;

GetMember s() : r estituisce un ar r ay di Member Info, ognuno dei quali r appr esenta uno dei membr i dichiar ati in

quel tipo;

GetMethods() : r estituisce un ar r ay di MethodInfo, ognuno dei quali r appr esenta uno dei metodi dichiar ati in

quel tipo;

GetNestedTypes() : r estituisce un ar r ay di Type, ognuno dei quali r appr esenta uno dei tipi dichiar ati in quel

tipo;

GetPr oper ties() : r estituisce un ar r ay di Pr oper tyInfo, ognuno dei quali r appr esenta una delle pr opr ietà

dichiar ate in quel tipo;

La funzione GetMember s, da sola, ci for nisce una lista gener ale di tutti i membr i di quel tipo:

Eseguendo il codice appena pr oposto, potr ete notar e che a scher mo appaiono tutti i membr i di Str ing, ma molti sono

r ipetuti: questo si ver ifica per chè i metodi che possiedono delle var ianti in over load vengono r ipor tati tante volte

quante sono le var ianti; natur alemnte, ogni oggetto MethodInfo sar à diver so dagli altr i per le infor mazioni sulla

quantità e sul tipo di par ametr i passati a tale metodo. Accanto a questa str anezza, noter ete, poi, che per ogni

pr opr ietà ci sono due metodi definiti come get_NomePr opr ietà e set_NomePr opr ietà: questi metodi vengono cr eati

automaticamente quando il codice di una pr opr ietà viene compilato, e vengono eseguiti al momento di impostar e od

ottener e il valor e di tale pr opr ietà. Altr a str anezza è che tutti i costr uttor i si chiamano ".ctor " e non New. Stiamo

cominciando ad entr ar e nel mondo dell'Inter mediate Language, il linguaggio inter medio simil-macchina in cui vengono

conver titi i sor genti una volta compilati. Di fatto, noi stiamo eseguendo il pr ocesso inver so della compilazione, ossia la

decompilazione. Alcune infor mazioni vengono manipolate nel passaggio da codice a IL, e quando si tor na indietr o, le

si vede in altr o modo, ma tutta l'infor mazione necessar ia è ancor a contenuta lì dentr o. Non esiste, tuttavia, una classe

già scr itta che r itr aduca in codice tutto il linguaggio inter medio: ciò che il Fr amew or k ci for nisce ci consente solo di

conoscer e "a pezzi" tutta l'infor mazione ivi contenuta, ma sottolineiamo "tutta". Sar ebbe, quindi, possibile - ed infatti è

già stato fatto - r itr adur r e tutti questi dati in codice sor gente. Per or a, ci limiter emo a "r icostr uir e" la signatur e di

un metodo.

Pr ima di pr oceder e, vi for nisco un br eve elenco dei membr i significativi di ogni der ivato di Member Info:

Member Info

Declar ingType : la classe che dichiar a questo membr o;

Member Type : categor ia del membr o;

Name : il nome del membr o;

01.02.03.04.05.06.07.08.09.10.11.12.13.14.15.

Module Module1Sub Main()

Dim T As Type = GetType(String)

'Elenca tutti i membri di StringFor Each M As MemberInfo In T.GetMembers

'La proprietà MemberType restituisce un enumeratore che'specifica di che tipo sia il membro, se una proprietà,'un metodo, un costruttore, eccetera...Console.WriteLine(M.MemberType.ToString & " " & M.Name)

Next

Console.ReadKey()End Sub

End Module

Page 211: Guida visual basic

ReflectedType : il tipo usato per ottener e un r ifer imento a questo membr o tr amite r eflection;

MethodInfo

GetBaseDefinition() : se il metodo è modificato tr amite polimor fismo, r estituisce la ver sione della classe base (se

non è stato sottoposto a polimor fismo, r estituisce Nothing);

GetCur r entMethod() : r estituisce un MethodInfo in r ifer imento al metodo in cui questa funzione viene chiamata;

GetMethodBody() : r estituisce un oggetto MethodBody (che vedr emo in seguito) contenente infor mazioni sulle

var iabili locali, le eccezioni e il codice IL;

GetPar ameter s() : r estituisce un elenco di Par ameter Info r appr esentanti i par ametr i del metodo;

IsAbstr act : deter mina se il metodo è MustOver r ide;

IsConstr uctor : deter mina se è un costr uttor e;

IsFinal : deter mina se è NotOver r idable;

IsStatic : deter mina se è Shar ed;

IsVir tual : deter mina se è Over r idable;

Retur nPar ameter : qualor a il metodo fosse una funzione, r estituisce infor mazioni sul valor e r estituito;

Retur nType : in una funzione, r estituisce l'oggetto Type associato al tipo r estituito. Se il metodo non è una

funzione, r estituisce Nothing o uno speciale OT in r ifer imento al tipo System.Void.

FieldInfo

GetRawCostantValue() : se il campo è una costante, ne r estituisce il valor e;

IsLiter al : deter mina se è una costante;

IsInitOnly : deter mina se è una var iabile r eadonly;

PropertyInfo

CanRead : deter mina se si può legger e la pr opr ietà;

CanWr ite : deter mina se si può impostar e la pr opr ietà;

GetGetMethod() : r estituisce un MethodInfo cor r ispondente al blocco Get;

GetSetMethod() : r estituisce un MethodInfo cor r ispondente al blocco Set;

GetPr oper tyType() : r estituisce un oggetto Type in r ifer imento al tipo della pr opr ietà.

EventInfo (per ulter ior i infor mazioni, veder e i capitoli della sezione B sugli eventi)

GetAddMethod() : r estituisce un r ifer imento al metodo usato per aggiunger e gli handler d'evento;

GetRaiseMethod() : r estituisce un r ifer imento al metodo che viene r ichiamato quando si scatena l'evento;

GetRemoveMethod() : r estituisce un r ifer imento al metodo usato per r imuover e gli handler d'evento;

IsMulticast : indica se l'evento è gestito tr amite un delegate multicast.

001.002.003.004.005.006.007.008.009.010.

Module Module1'Analizza il metodo rappresentato dall'oggetto MISub AnalyzeMethod(ByVal MI As MethodInfo)

'Il nomeDim Name As String'Il nome completo, con scpecificatori di accesso,'modificatori, signature e tipo restituito. Per'ulteriori informazioni sul tipo StringBuilder,'vedere il capitolo "Magie con le stringhe"

Page 212: Guida visual basic

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.

Dim CompleteName As New System.Text.StringBuilder'Lo specificatore di accessoDim Scope As String'Gli eventuali modificatoriDim Modifier As String'La categoria: Sub o FunctionDim Category As String'La signature del metodo, che andremo a costruireDim Signature As New System.Text.StringBuilder

'Di solito, tutti i metodi hanno un tipo restituito,'poiché, in analogia con la sintassi del C#, una'procedura è una funzione che restituisce Void,'ossia niente. Per questo bisogna controllare anche il'nome del tipo di ReturnParameterIf MI.ReturnParameter IsNot Nothing AndAlso _

MI.ReturnType.FullName <> "System.Void" ThenCategory = "Function"

ElseCategory = "Sub"

End If

If MI.IsConstructor ThenName = "New"

ElseName = MI.Name

End If

If MI.IsAssembly ThenScope = "Friend"

ElseIf MI.IsFamily ThenScope = "Protected"

ElseIf MI.IsFamilyOrAssembly ThenScope = "Protected Friend"

ElseIf MI.IsPrivate ThenScope = "Private"

ElseScope = "Public"

End If

If MI.IsFinal Then'Vorrei far notare una sottigliezza. Se il metodo è'Final, ossia NotOverridable, significa che non può'essere modificato nelle classi derivate. Ma tutti i'membri non dichiarati esplicitamente Overridable'non sono modificabili nelle classi derivate. Quindi,'definire un metodo senza modificatori polimorfici'(come quelli che seguono qua in basso), equivale a'definirlo NotOverridable. Perciò non si'aggiunge nessun modificatore in questo caso

ElseIf MI.IsAbstract ThenModifier = "MustOverride"

ElseIf MI.IsVirtual ThenModifier = "Overridable"

ElseIf MI.GetBaseDefinition IsNot Nothing AndAlso _MI IsNot MI.GetBaseDefinition ThenModifier = "Overrides"

End If

If MI.IsStatic ThenIf Modifier <> "" Then

Modifier = "Shared " & ModifierElse

Modifier = "Shared"End If

End If

'Inizia la signature con una parentesi tonda aperta.'Append aggiunge una stringa a SignatureSignature.Append("(")For Each P As ParameterInfo In MI.GetParameters

'Se P è un parametro successivo al primo, lo separa dal

Page 213: Guida visual basic

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.144.145.146.147.148.149.150.151.152.153.154.

'precedente con una virgolaIf P.Position > 0 Then

Signature.Append(", ")End If

'Se P è passato per valore, ci vuole ByVal, altrimenti'ByRef. IsByRef è un membro di Type, ma viene'usato solo quando il tipo in questione indica il tipo'di un parametroIf P.ParameterType.IsByRef Then

Signature.Append("ByRef ")Else

Signature.Append("ByVal ")End If

'Se P è opzionale, ci vuole la keyword OptionalIf P.IsOptional Then

Signature.Append("Optional ")End If

Signature.Append(P.Name)If P.ParameterType.IsArray Then

Signature.Append("()")End If'Dato che la sintassi del nome è in stile C#, al'posto delle parentesi tonde in un array ci sono delle'quadre: rimediamoSignature.Append(" As " & P.ParameterType.Name.Replace("[]",""))

'Si ricordi che i parametri optional hanno un valore'di defaultIf P.IsOptional Then

Signature.Append(" = " & P.DefaultValue.ToString)End If

NextSignature.Append(")")

If MI.ReturnParameter IsNot Nothing AndAlso _

MI.ReturnType.FullName <> "System.Void" ThenSignature.Append(" As " & MI.ReturnType.Name)

End If

'Ed ecco il nome completoCompleteName.AppendFormat("{0} {1} {2} {3}{4}", Scope, Modifier, _Category, Name, Signature.ToString)Console.WriteLine(CompleteName.ToString)Console.WriteLine()

'Ora ci occupiamo del corpoDim MB As MethodBody = MI.GetMethodBody

If MB Is Nothing Then

Exit SubEnd If

'Massima memoria occupata sullo stackConsole.WriteLine("Massima memoria stack : {0} bytes", _

MB.MaxStackSize)Console.WriteLine()

'Variabili locali (LocalVariableInfo è una variante di'FieldInfo)Console.WriteLine("Variabili locali:")For Each L As LocalVariableInfo In MB.LocalVariables

'Dato che non si può ottenere il nome, ci si deve'accontentare di un indiceConsole.WriteLine(" Var({0}) As {1}", L.LocalIndex, _

L.LocalType.Name)NextConsole.WriteLine()

Page 214: Guida visual basic

Analizzando il metodo Test, si otter r à questo output:

Public Shared Sub Test(ByVal Num As Int32, ByVal S As String)

Massima memoria stack : 2 bytes

Variabili locali: Var(0) As DateTime Var(1) As String Var(2) As ArithmeticException Var(3) As ArgumentException

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.

'Gestione delle eccezioniConsole.WriteLine("Gestori di eccezioni:")For Each Ex As ExceptionHandlingClause In MB.ExceptionHandlingClauses

'Tipo di clausola: distingue tra filtro (When),'clausola (Catch) o un blocco FinallyConsole.WriteLine(" Tipo : {0}", Ex.Flags.ToString)'Se si tratta di un blocco Catch, ne specifica la'naturaIf Ex.Flags = ExceptionHandlingClauseOptions.Clause Then

Console.WriteLine(" Catch Ex As " & Ex.CatchType.Name)End If'Offset, ossia posizione in bytes nel Try, del gestoreConsole.WriteLine(" Offset : {0}", Ex.HandlerOffset)'Lunghezza, in bytes, del codice eseguibile del gestoreConsole.WriteLine(" Lunghezza : {0}", Ex.HandlerLength)Console.WriteLine()

NextEnd Sub

Sub Test(ByVal Num As Int32, ByVal S As String)

Dim T As DateDim V As String

Try

Console.WriteLine("Prova 1, 2, 3")Catch Ex As ArithmeticException

Console.WriteLine("Errore 1")Catch Ex As ArgumentException

Console.WriteLine("Errore 2")Finally

Console.WriteLine("Ciao")End Try

End Sub

Sub Main()Dim T As Type = GetType(Module1)Dim Methods() As MethodInfo = T.GetMethodsDim Index As Int16

Console.WriteLine("Inserire un numero tra i seguenti per analizzare il metodo

corrispondente:")Console.WriteLine()For I As Int16 = 0 To Methods.Length - 1

Console.WriteLine("{0} - {1}", I, Methods(I).Name)NextConsole.WriteLine()Index = Console.ReadLine

If Index >= 0 And Index &rt; Methods.Length Then

AnalyzeMethod(Methods(Index))End If

Console.ReadKey()

End Sub

End Module

Page 215: Guida visual basic

Gestori di eccezioni: Tipo : Clause Catch Ex As ArithmeticException Offset : 15 Lunghezza : 26 Tipo : Clause Catch Ex As ArgumentException Offset : 41 Lunghezza : 26 Tipo : Finally Offset : 67 Lunghezza : 13

Page 216: Guida visual basic

A46. La Reflection - Parte III

Reflection dei genericsI gener ics si compor tano in modo differ ente in molti ambiti, e la Reflection r icade pr opr io fr a questi. Infatti, un Type

che r appr esenta un tipo gener ic non ha lo stesso nome di quando è stato dichiar ato nel codice, ma possiede una for ma

contr atta e diver sa. Ad esempio, ammettendo che l'assembly che stiamo analizzando contenga questa classe:

quando tr over emo l'oggetto Type che la r appr esenta dur ante l'enumer azione dei tipi, scopr ir emo che il suo nome è

molto str ano. Sar à molto simile a questa str inga:

Example`2

In questa par ticolar e for mattazione, il due indica che la classe ex ample lavor a su due tipi gener ics: i lor o nome

"vir tuali" non vengono r ipor tati nel nome, cosicché anche confr ontando i nomi di due OT indicanti tipi gener ics, magar i

pr ovenienti da AppDomain diver si, si capisce che in r ealtà sono pr opr io lo stesso tipo, poiché la ver a differ enza sta

solo nel nome e nella quantità di par ametr i gener ics (l'identificator e di questi ultimi, infatti, essendo solo un

segnaposto, è ininfluente). Nonostante l'assenza di dettagli, ci sono delle pr opr ietà che ci per mettono di r ecuper ar e il

nome dei tipi gener ics aper ti, ossia "T" e "K" in questo caso. In gener ale, per lavor ar e su classi o tipi genr ics, è

impor tante far e affidamento su questi membr i di Type:

IsGener icTypeDefinition : deter mina se questo Type r appr esenta una definizione di un tipo gener ics. Fate

attenzione ai dettagli, poiché esiste un'altr a pr opr ietà molto simile con la quale ci si può confonder e. Affinché

questa pr oper ietà r estituisca Tr ue è necessar io (e sufficiente) che il tipo che stiamo esaminando contenga una

definizione di uno o più tipi gener ics APERTI (e non collegati). Ad esempio:

1.2.3.

Class Example(Of T, K)'...

End Class

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.

Module Module1

'Dichiaro questa classe e la prossima variabile come'pubblici perchè se fossero Friend bisognerebbe'usare un overload troppo lungo di GetField e'GetNestedTypes specificando ci cercare i membri non'pubblici. Di default, le funzioni di ricerca operano'solo su membri pubblici Public Class Example(Of T)

End Class

Public E As Example(Of Int32)

Sub Main()

'Ottiene il tipo di questo moduloDim ModuleType As Type = GetType(Module1)

'Enumera tutti i tipi presenti nel modulo fino a'trovare la classe Example. Ho usato un for perchè'non si può usare GetType (in qualsiasi'sua versione) su una classe generics senza specificare'un tipo generics collegato, cosa che noi non'vogliamo affatto. Per ottenere il riferimento a'Example(Of T) bisogna per forza usare una funzione'che restituisca tutti i tipi esistenti e poi'cercarlo tra questi.For Each T As Type In ModuleType.GetNestedTypes()

Page 217: Guida visual basic

A scher mo appar ir à lo stesso nome due volte, ma in un caso IsGener icTypeDefinition sar à Tr ue e nell'altr o False.

Questo per chè il tipo della var iabile E è sì dichiar ato come gener ic, ma all'atto pr atico lavor a su un solo tipo:

Int32; per ciò non si tr atta di una defin izione di tipo gener ic, ma di un uso di un tipo gener ic;

IsGener icType : molto simile alla pr ecedente, ma funziona al contr ar io, ossia r estituisce Tr ue se il tipo NON è

una definizione di tipo gener ic, ma una sua applicazione mediante tipi collegati. Nell'esempio di pr ima,

EType.IsGener icType sar ebbe stato Tr ue;

GetGener icAr guments() : se almeno uno tr a IsGener icTypeDefinition e IsGener icType è ver o, allor a abbiamo a

che far e con tipi gener ics. Questa funzione r estituisce gli OT dei tipi gener ics aper ti (nel pr imo caso) o collegati

(nel secondo caso). Tr a br eve ne vedr emo un esempio.

Ecco un esempio di come enumer ar e tutti i tipi gener ics di un assembly:

31.32.33.34.35.36.37.38.39.40.41.

42.43.44.45.46.

If T.Name.StartsWith("Example") ThenConsole.WriteLine("{0} - IsGenericTypeDefinition: {1}", _

T.Name, T.IsGenericTypeDefinition)End If

Next

'Ottiene un riferimento al campo E dichiarayo sopraDim EField As FieldInfo = ModuleType.GetField("E")'E ne ottiene il tipoDim EType As Type = EField.FieldType

Console.WriteLine("{0} - IsGenericTypeDefinition: {1}", EType.Name,

EType.IsGenericTypeDefinition)

Console.ReadKey()End Sub

End Module

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.32.33.34.35.36.37.

Module Module1

Sub EnumerateGenerics(ByVal Asm As Assembly)For Each T As Type In Asm.GetTypes

'Controlla se si tratta di un tipo contenente'tipi generics apertiIf T.IsGenericTypeDefinition Then

'Ottiene il nome semplice di quel tipo (la'versione completa è troppo lunga XD)Dim Name As String = T.Name

'Controlla che il nome contenga l'accento tonico.'Infatti, possono esistere casi in cui la'propietà IsGeneircTypeDefinition è vera,'ma non ci troviamo di fronte a un tipo la cui'signature contenga effettivamente tipi generics.'Ne darò un esempio dopo...If Not Name.Contains("`") Then

Continue ForEnd If

'Ottiene una stringa in cui elimina tutti i'caratteri a partire dall'indice del'accentoName = T.Name.Remove(T.Name.IndexOf("`"))'E poi gli aggiunge un "(Of ", per far vedere che'si sta iniziando una dichiarazione genericName &= "(Of "'Quindi aggiunge tutti gli argomenti genericFor Each GenT As Type In T.GetGenericArguments

'Se il parametro non è il primo, lo separa dal'precedente con una virgola.If GenT.GenericParameterPosition > 0 Then

Name &= ", "End If'Quindi vi aggiunge il nomeName &= GenT.Name

Page 218: Guida visual basic

Ecco alcuni dei miei r isultati:

ThreadSafeObjectProvider(Of T)Collection(Of T)ComparableCollection(Of T)Relation(Of T1, T2)IsARelation(Of T, U)DataFilter(Of T)

Riguar do all'if posto nel ciclo enumer ativo, vor r ei far notar e che IsGener icTypeDefinition r estituisce tr ue se r intr accia

nel tipo un r ifer imento ad un tipo gener ic aper to, indipendentemente che questo sia dichiar ato nel tipo o da un'altr a

par te. Ad esempio:

L'enumer azione r aggiunge anche DoSomething, poiché è anch'esso un tipo, anche se nidificato, accessibile a tutti i

membr i dell'assembly (o, se pubblico, a tutti); ed anche in quel caso, la pr opr ietà IsGener icTypeDefinition è Tr ue, poiché

la sua signatur e contiene un tipo gener ic aper to (T). Tuttavia, il suo nome non contiene accenti tonici, poiché il

gener ics è stato dichiar ato a livello di classe.

Ecco un altr o esempio, ma sui tipi gener ic collegati:

38.39.40.41.42.43.44.45.46.47.48.49.50.51.52.53.54.55.56.57.58.59.60.61.

Next'E chiude la parentesiName &= ")"Console.WriteLine(Name)

End IfNext

End Sub 'Notate che la classe Type espone molte proprietà che'si possono usare solo in determinati casi. Ad esempio, in'questo codice è lecito richiamare GenericParametrPosition'poiché sappiamo a priori che quel Type indica un tipo'generic in una signature generic. Ma un in un qualsiasi OT'non ha alcun senso usare tale proprietà!

Sub Main()

'Ottiene un riferimento all'assembly correnteDim Asm As Assembly = Assembly.GetExecutingAssembly()

EnumerateGenerics(Asm)

Console.ReadKey()

End Sub

End Module

1.2.3.4.

Class Example(Of T)Delegate Sub DoSomething(ByVal Data As T)'...

End Class

01.02.03.04.05.06.07.08.09.10.11.12.13.14.15.16.17.

Module Module1

'Enumera solo i campi generic di un tipoSub EnumerateGenericFieldMembers(ByVal T As Type)

For Each F As FieldInfo In T.GetFields()If F.FieldType.IsGenericType Then

Dim Name As String = F.FieldType.NameDim I As Int16 = 0

If Not Name.Contains("`") Then

Continue ForEnd If

Name = Name.Remove(Name.IndexOf("`"))Name &= "(Of "For Each GenP As Type In F.FieldType.GetGenericArguments

Page 219: Guida visual basic

L'uso della ReflectionFino ad or a non abbiamo fatto altr o che enumer ar e membr i e tipi. Devo dir lo, una cosa un po' noiosa... Tuttavia ci è

ser vita per compr ender e come far e per acceder e a cer te infor mazioni che si celano negli assembly. Anche se non

user emo quasi mai la r eflection per enumer ar e le par ti di un assembly (a meno che non decidiate di scr iver e un object

br owser ), or a sappiamo quali infor mazioni possiamo r aggiunger e e come pr ender le. Questo è impor tante sopr attutto

quando si lavor a con assembly che vengono car icati dinamicamente, ad esempio in un sistema di plug-ins, come

mostr er ò fr a poco. Per dar vi un assaggio della potenza della r eflection, ho scr itto un semplice codice che per mette di

acceder e a tutte le infor mazioni di un oggetto, qualsiasi esso sia, di qualunque tipo e in qualunque assembly. Per far lo,

mi è bastato ottener ne le pr opr ietà:

18.19.20.21.22.23.24.25.26.27.28.29.30.31.32.33.34.35.36.37.38.39.40.

'Dato che non si stanno analizzando dei'parametri generic, non si può utilizzare'la proprietà GenericParameterPositionIf I > 0 Then

Name &= ", "End IfName &= GenP.NameI += 1

NextName &= ")"Console.WriteLine("Dim {0} As {1}", F.Name, Name)

End IfNext

End Sub

Public L As New List(Of Integer)Public I As Int32?

Sub Main()

EnumerateGenericFieldMembers(GetType(Module1))

Console.ReadKey()End Sub

End Module

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.

Module Module1

'Stampa tutte le informazioni ricavabili dalle'proprietà di un dato oggetto O. Indent è solo'una variabile d'appoggio per la formattazione, in modo'da indentare bene le righe nel caso i valori delle'proprietà siano altri oggetti.Public Sub PrintInfo(ByVal O As Object, ByVal Indent As String)

'Ottiene il tipo di ODim T As Type = O.GetType()

Console.WriteLine("{0}Object of type {1}", Indent, T.Name)'Enumera tutte le proprietàFor Each Prop As PropertyInfo In T.GetProperties()

'Ottiene il tipo restituito dalla proprietàDim PropType As Type = Prop.PropertyType()

'Se si tratta di una proprietà parametrica,'la salta: in questo esempio non volevo dilungarmi,'ma potete completare il codice se desiderate.If Prop.GetIndexParameters().Count > 0 Then

Continue ForEnd If

'Se è un di tipo base o una stringa (giacché le'stringhe non sono tipo base ma reference), ne stampa'direttamente il valore a schermoIf (PropType.IsPrimitive) Or (PropType Is GetType(String)) Then

Console.WriteLine("{0} {1} = {2}", _

Page 220: Guida visual basic

L'output sar à questo:

Object of type Person FirstName = Mario LastName = Rossi CompleteName = Mario Rossi

Object of type Teacher Subject = Storia LastName = Prof. Bianchi CompleteName = Prof. Luigi Bianchi, dottore in Storia FirstName = Luigi

Object of type Relation`2 FirstObject = Object of type Person FirstName = Mario LastName = Rossi CompleteName = Mario Rossi

SecondObject = Object of type Teacher Subject = Storia LastName = Prof. Bianchi CompleteName = Prof. Luigi Bianchi, dottore in Storia FirstName = Luigi

Object of type List`1 Capacity = 0 Count = 0

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.

Indent, Prop.Name, Prop.GetValue(O, Nothing))

'Altrimenti, se si tratta di un oggetto, lo analizza a'sua voltaElseIf PropType.IsClass Then

Console.WriteLine("{0} {1} = ", Indent, Prop.Name)PrintInfo(Prop.GetValue(O, Nothing), Indent & " ")

End IfNextConsole.WriteLine()

End Sub

Sub Main()'Crea alcuni oggetti variDim P As New Person("Mario", "Rossi", New Date(1982, 3, 17))Dim T As New Teacher("Luigi", "Bianchi", New Date(1879, 8, 21), "Storia")Dim R As New Relation(Of Person, Teacher)(P, T)Dim Q As New List(Of Int32)Dim K As New Text.StringBuilder()

'Ne stampa le proprietà, senza sapere nulla a priori'sulla natura degli oggetti.'Notate che i nomi generics rimangono con l'accento...PrintInfo(P, "")PrintInfo(T, "")PrintInfo(R, "")PrintInfo(Q, "")PrintInfo(K, "")

Console.ReadKey()

End SubEnd Module

Page 221: Guida visual basic

Object of type StringBuilder Capacity = 16 MaxCapacity = 2147483647 Length = 0

Per scr iver e questo codice mi sono basato sul metodo GetValue esposto dalla classe Pr oper tyInfo. Esso per mette di

ottener e il valor e che la pr opr ietà r appr esentata dall'oggetto Pr oper tyInfo da cui viene invocato possiede nell'oggetto

specificato come par ametr o. In gener ale, GetValue accetta due par ametr i: il pr imo è l'oggetto da cui estr ar r e il valor e

della pr opr ietà, mentr e il secondo è un ar r ay di oggetti che r appr esenta i par ametr i da passar e alla pr opr ietà. Come

avete visto, ho enumer ato solo pr opr ietà non par ametr iche e per ciò non c'er a bisogno di for nir e alcun par ametr o:

ecco per chè ho messo Nothing.

Al par i di GetValue c'è SetValue che per mette di impostar e, invece, la pr opr ietà (ma solo se non è in sola lettur a, ossia

se CanWr ite è Tr ue). Ovviamente SetValue ha un par ametr o in più, ossia il valor e da impostar e (secondo par ametr o).

Ecco un esempio:

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.32.33.34.35.36.37.38.39.40.41.42.43.44.45.46.47.48.49.50.51.

Module Module1

'Non riscrivo PrintInfo, ma considero che stia'ancora in questo modulo

Sub Main()

Dim P As New Person("Mario", "Rossi", New Date(1982, 3, 17))Dim T As New Teacher("Luigi", "Bianchi", New Date(1879, 8, 21), "Storia")Dim R As New Relation(Of Person, Teacher)(P, T)Dim Q As New List(Of Int32)Dim K As New Text.StringBuilder()Dim Objects() As Object = {P, T, R, Q, K}Dim Cmd As Int32

Console.WriteLine("Oggetti nella collezione: ")For I As Int32 = 0 To Objects.Length - 1

Console.WriteLine("{0} - Istanza di {1}", _I, Objects(I).GetType().Name)

NextConsole.WriteLine("Inserire il numero corrispondente all'oggetto da modificare: ")Cmd = Console.ReadLine

If Cmd < 0 Or Cmd > Objects.Length - 1 Then

Console.WriteLine("Nessun oggetto corrispondente!")Exit Sub

End If

Dim Selected As Object = Objects(Cmd)Dim SelectedType As Type = Selected.GetType()Dim Properties As New List(Of PropertyInfo)

For Each Prop As PropertyInfo In SelectedType.GetProperties()

If (Prop.PropertyType.IsPrimitive Or Prop.PropertyType Is GetType(String)) And _Prop.CanWrite ThenProperties.Add(Prop)

End IfNext

Console.Clear()Console.WriteLine("Proprietà dell'oggetto:")For I As Int32 = 0 To Properties.Count - 1

Console.WriteLine("{0} - {1}", _I, Properties(I).Name)

NextConsole.WriteLine("Inserire il numero corrispondente alla proprietà da modificare:")Cmd = Console.ReadLine

If Cmd < 0 Or Cmd > Objects.Length - 1 Then

Console.WriteLine("Nessuna proprietà corrispondente!")Exit Sub

Page 222: Guida visual basic

Chi ha letto anche la ver sione pr ecedente della guida, avr à notato che manca il codice per l'assembly br owser , ossia

quel pr ogr amma che elenca tutti i tipi (e tutti i membr i di ogni tipo) pr esenti in un assembly. Mi sembr ava tr oppo

noioso e labor ioso e tr oppo poco inter essante per r ipr opor lo anche qui, ma siete liber i di dar ci un'occhiata (al r elativo

capitolo della ver sione 2).

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.

End If

Dim SelectedProp As PropertyInfo = Properties(Cmd)Dim NewValue As Object

Console.Clear()Console.WriteLine("Nuovo valore: ")NewValue = Console.ReadLine

'Imposta il nuovo valore della proprietà. Noterete che'si ottiene un errore di cast con tutti i tipi che'non siano String. Questo accade poiché viene'eseguito un matching sul tipo degli argomenti: se essi'sono diversi, indipendentemente dal fatto che possano'essere convertiti l'uno nell'altro (al contrario di'quanto dice il testo dell'errore), viene sollevata'quell'eccezione. Per aggirare il problema, si'dovrebbe eseguire un cast esplicito controllando prima'il tipo della proprietà:' If SelectedProp.PropertyType Is GetType(Int32) Then' NewValue = CType(NewValue, Int32)' ElseIf SelectedProp. ...'È il prezzo da pagare quando si lavora con'uno strumento così generale come la Reflection.'[Generalmente si conosce in anticipo il tipo]SelectedProp.SetValue(Selected, NewValue, Nothing)

Console.WriteLine("Proprietà modificata!")

PrintInfo(Selected, "")

Console.ReadKey()

End SubEnd Module

Page 223: Guida visual basic

A47. La Reflection - Parte IV

Compilazione di codice a runtimeBene, or a che sappiamo scr iver e del nor male codice per una qualsiasi applicazione e che sappiamo come analizzar e

codice ester no, che ne dite di scr iver e pr ogr ammi che pr oducano pr ogr ammi? La questione è molto diver tente:

esistono delle apposite classi, in .NET, che consentono di compilar e codice che viene pr odotto dur ante l'esecuzione

dal'applicazione stessa, gener ando così nuovi assembly per gli scopi più var i. Una volta mi sono ser vito in manier a

intensiva di questa capacità del .NET per scr iver e un installer : non solo esso cr eava altr i pr ogr ammi (autoestr aenti),

ma questi a lor o volta cr eavano altr i pr ogr ammi per estr ar r e le infor mazioni memor izzate negli autoestr aenti stessi:

pr ogr ammi che scr ivono pr ogr ammi che scr ivono pr ogr ammi! Ma or a vediamo più nel dettaglio cosa usar e nello

specifico per attivar e queste inter essanti funzionalità.

Pr ima di tutto, è necessar io impor tar e un paio di namespace: System.CodeDom e System.CodeDom.Compiler . Essi

contengono le classi che fanno al caso nostr o per il mestier e. Il pr ocesso di compilazione si svolge alltr aver so queste

fasi:

Pr ima si ottiene il codice da compilar e, che può esser e memor izzato in un file o pr odotto dir ettamente dal

pr ogr amma sottofor ma di nor male str inga;

Si impostano i par ametr i di compilazione: ad esempio, si può sceglier e il tipo di output (*.ex e o *.dll), i

r ifer imenti da includer e, se mantener e i file tempor anei, se cr ear e l'assembly e salvar lo in memor ia, se

tr attar e gli war ning come er r or i, ecceter a... Insomma, tutto quello che noi scegliamo tr amite l'inter faccia

dell'ambiente di sviluppo o che ci tr oviamo già impostato gr azie all'IDE stesso;

Si compila il codice r ichiamando un pr ovider di compilazione;

Si leggono i r isultati della compilazione. Nel caso ci siano stati er r or i, i r isultati conter r anno tutta la lista degli

er r or i, con r elative infor mazioni sulla lor o posizione nel codice; in caso contr ar io, l'assembly ver r à gener ato

cor r ettamente;

Se l'assembly conteneva codice che ser ve al pr ogr amma, si usa la Reflection per ottener ne e invocar ne i metodi.

Queste cinque fasi cor r ispondono a cinque oggetti che dovr emo usar e nel codice:

Str ing : ovviamente, il codice memor izzato sottofor ma di str inga;

Compiler Par ameter s : classe del namespace CodeDom.Compiler . Contiene come pr opr ietà tutte le opzioni che ho

esemplificato nella lista pr ecedente;

VBCodePr ovider : pr ovider di compilazione per il linguaggio Visual Basic. Esiste un pr ovider per ogni linguaggio

.NET, anche se può non tr ovar si sempr e nello stesso namespace. Esso for nir à i metodi per avviar e la

compilazione;

Compiler Results : contiene tutte le infor mazioni r elative all'output della compilazione. Se si sono ver ificati

er r or i, ne espone una lista; se la compilazion è andata a buon file, r ifer isce dove si tr ova l'assembly compilato e,

se ci sono, dove sono posti i file tempor anei;

Assembly : classe che abbiamo già analizzato. Per mette di car icar e in memor ia l'assembly pr odotto come output

e r ichiamar ne il codice od usar ne le classi ivi definite.

Il pr ossimo esempio costituisce uno dei casi più evidenti di quando conviene r ivolger si alla r eflection, e sono sicur o che

potr ebbe tor nar vi utile in futur o:

001.002.003.004.005.006.

Module Module1

'Questa classe rappresenta una funzione matematica:'ne ho racchiuso il nome tra parentesi quadre poiché Function'è una keyword del linguaggio, ma in questo caso la si

Page 224: Guida visual basic

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.075.076.077.078.

'vuole usare come identificatore. In generale, si possono usare'le parentesi quadre per trasformare ogni keyword in un normale'nome.Class [Function]

Private _Expression As StringPrivate EvaluateFunction As MethodInfo

'Contiene l'espressione matematica che costruisce il valore'della funzione in base alla variabile xPublic Property Expression() As String

GetReturn _Expression

End GetSet(ByVal value As String)

_Expression = valueMe.CreateEvaluator()

End SetEnd Property

'La prossima è la procedura centrale di tutto l'esempio.'Il suo compito consiste nel compilare una libreria di'classi in cui è definita una funzione che, ricevuto'come parametro un x decimale, ne restituisce il valore'f(x). Il corpo di tale funzione varia in base'all'espressione immessa dall'utente.Private Sub CreateEvaluator()

'Crea il codice della libreria: una sola classe'contenente la sola funzione statica Evaluate. Gli {0}'verranno sostituti con caratteri di "a capo", mentre'in {1} verrà posta l'espressione che produce'il valore desiderato, ad esempio "x ^ 2 + 1".Dim Code As String = String.Format( _"Imports Microsoft.VisualBasic{0}" & _"Imports System{0}" & _"Imports System.Math{0}" & _"Public Class Evaluator{0}" & _" Public Shared Function Evaluate(ByVal X As Double) As Double{0}" & _" Return {1}{0}" & _" End Function{0}" & _"End Class", Environment.NewLine, Me.Expression)

'Crea un nuovo oggetto CompilerParameters, per'contenere le informazioni relative alla compilazioneDim Parameters As New CompilerParameters With Parameters

'Indica se creare un eseguibile o una libreria di'classi: in questo caso ci interessa la seconda,'quindi impostiamo la proprietà su False.GenerateExecutable = False

'Gli warning vengono considerati come errori.TreatWarningsAsErrors = True'Non vogliamo tenere alcun file temporaneo: ci'interessa solo l'assembly compilato.TempFiles.KeepFiles = False'L'assembly verrà tenuto in memoria temporanea.GenerateInMemory = True

'I due riferimenti di cui abbiamo bisogno, che si'trovano nella GAC (quindi basta specificarne il'nome). In questo caso, si richiede anche'l'estensione (*.dll).ReferencedAssemblies.Add("Microsoft.VisualBasic.dll").ReferencedAssemblies.Add("System.dll")

End With

'Crea un nuovo provider di compilazioneDim Provider As New VBCodeProvider'E compila il codice seguendo i parametri di'compilazione forniti dall'oggetto Parameters. Il'valore restituito dalla funzione

Page 225: Guida visual basic

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.144.145.146.147.148.149.150.

'CompileAssemblyFromSource è di tipo'CompilerResults e viene salvato in CompResultsDim CompResults As CompilerResults = _

Provider.CompileAssemblyFromSource(Parameters, Code)

'Se ci sono errori, lancia un'eccezioneIf CompResults.Errors.Count > 0 Then

Throw New FormatException("Espressione non valida!")Else

'Altrimenti crea un riferimento all'assembly di'output. La proprietà CompiledAssembly di'CompResults contiene un riferimento diretto a'quell'assembly, quindi ci è molto comoda.Dim Asm As Reflection.Assembly = CompResults.CompiledAssembly'Dall'assembly ottiene un OT che rappresenta'l'unico tipo ivi definito, e da questo ne'estrae un MethodInfo con informazioni sul'metodo Evaluate (l'unico presente).Me.EvaluateFunction = _

Asm.GetType("Evaluator").GetMethod("Evaluate")End If

End Sub

'Per richiamare la funzione, basta invocare il metodo'Evaluate estratto precedentemente. Al pari delle'proprietà e dei campi, che possono essere letti o'scritti dinamicamente, anche i metodi possono essere'invocati dinamicamete attraverso MethodInfo. Si usa'la funzione Invoke: il primo parametro da'passare è l'oggetto da cui richiamare il metodo, mentre'il secondo è un array di oggetti che indicano i'parametri da passare a tale metodo.'In questo caso, il primo parametro è Nothing poiché'Evaluate è una funzione statica e non ha bisgno di nessuna'istanza per essere richiamata.Public Function Apply(ByVal X As Double) As Double

Return EvaluateFunction.Invoke(Nothing, New Object() {X})End Function

End Class

Sub Main()

Dim F As New [Function]()

DoTry

Console.Clear()Console.WriteLine("Inserisci una funzione: ")Console.Write("f(x) = ")F.Expression = Console.ReadLineExit Do

Catch ex As ExceptionConsole.WriteLine("Espressione non valida!")Console.ReadKey()

End TryLoop

Dim Input As StringDim X As Double

Do

TryConsole.Clear()Console.WriteLine("Immettere 'stop' per terminare.")Console.WriteLine("Il programma calcola il valore di f in X: ")Console.Write("x = ")Input = Console.ReadLineIf Input <> "stop" Then

X = CType(Input, Double)Console.WriteLine("f(x) = {0}", F.Apply(X))Console.ReadKey()

Else

Page 226: Guida visual basic

In questo esempio ho utilizzato solo alcuni dei membr i esposti dalle classi sopr a menzionate. Di seguito elenco tutti

quelli più r ilevanti, che potr ebber o ser vir vi in futur o:

CompilerParameter s

Compiler Options : contiene sottofor ma di str inghe dei par ametr i aggiuntivi da passar e al compilator e.

Vedr emo solo più avanti di cosa si tr atta e di come possano gener almente esser e modificati dall'IDE nell'ambito

del nostr o pr ogetto;

EmbeddedResour ces : una lista di str inghe, ognuna delle quali indica il per cor so su disco di un file di r isor se da

includer e nell'assembly compilato. Di questi file par ler ò nella sezione B;

Gener ateEx cutable : deter mina se gener ar e un eseguibile o una libr er ia di classi;

Gener ateInMemor y : deter mina se non salvar e l'assembly gener ato su un suppor to per manente (disco fisso o

altr e memor ie non volatili);

IncludeDebugInfor mations : deter mina se includer e nell'eseguibile anche le infor mazioni r elative al debug. Di

solito questo non è molto utile per ché è possibile acceder e pr ima a queste infor mazioni tr amite l'IDE facendo il

debug del codice stesso che compila altr o codice XD;

MainClass : imposta il nome della classe pr incipale dell'assembly. Se si sta compilando una libr er ia di classi, questa

pr opr ietà è inutile. Se, invece, si sta compilando un pr ogr amma, questa pr opr ietà indica il nome della classe

dove è contenuta la pr ocedur a Main, il punto di ingr esso nell'applicazione. Gener almente il compilator e r iesce ad

individuar e da solo tale classe, ma nel caso ci siano più classi contenenti un metodo Main bisogna specificar lo

esplicitamente. Nel caso l'applicazione da compilar e sia di tipo w indows for m, come vedr emo nella sezione B, la

MainClass può anche indicar e la classe che r appr esenta la finestr a iniziale;

OutputAssembly : imposta il per cor so dell'assembly da gener ar e. Nel caso questa pr opr ietà non venga impostata

pr ima della compilazione, sar à il pr ovider di compilazione a pr eoccupar si di cr ear e un nome casuale per

l'assembly e di salvar lo nella stessa car tella del nostr o pr ogr amma;

Refer encedAssemblis : anche questa è una collezione di str inghe, e contiene il nome degli assemblies da includer e

come r ier imeneto per il codice cor r ente. Dovete sempr e includer e almeno System.dll (quello più impor tante). Gli

altr i assemblies pubblici sono facoltativi e var iano in funzione del compito da svolger e: se doveste usar e file

x ml, impor ter ete anche System.Xml.dll, ad esempio;

TempFiles : collezione che contiene i per cor si dei file tempor anei. Espone qualche pr opr ietà e metodo in più

r ispetto a una nor male collezione;

Tr eatWar ningsAsEr r or s : tr atta gli war ning come se fosser o er r or i. Questo impedisce di por tar e a ter mine la

compilazione quando ci sono degli war ning;

War ningLevel : livello da cui il compilator e inter r ompe la compilazione. La documentazione su questa pr opr ietà

non è molto chiar a e non si capisce bene cosa intenda. È pr obabile che ogni war ning abbia un cer to livello di

aller ta e questo valor e dovr ebbe comunicar e al compilator e di visualizzar e solo gli w ar ning con livello maggior e

o uguale a quello specificato... solo ipotesi, tuttavia.

CompilerResults

CompiledAssembly : r estituisce un oggetto Assembly in r ifer imento all'assembly compilato;

Er r or s : collezione di oggetti di tipo Compiler Er r or . Ognuno di questi oggetti espone delle pr opr ietà utili a

151.152.153.154.155.156.157.158.

Exit DoEnd If

Catch Ex As ExceptionConsole.WriteLine(Ex.Message)Console.ReadKey()

End TryLoop

End SubEnd Module

Page 227: Guida visual basic

identificar e il luogo ed il motivo dell'er r or e. Alcune sono: Line e Column (linea e colonna dell'er r or e), IsWar ning

(se è un war ning o un er r or e), Er r or Number (numer o identificativo dell'er r or e), Er r or Tex t (testo dell'er r or e) e

FileName (nome del file in cui si è ver ificato);

NativeCompiler Retur nValue : r estituisce il valor e che a sua volta il compilator e ha r estituito al pr ogr amma una

volta ter minata l'esecuzione. Vi r icor do, infatti, che compilator e, editor di codice e debugger sono tr e

pr ogr ammi differ enti: l'ambiente di sviluppo integr ato for nisce un'inter faccia che sembr a unir li in un solo

applicativo, ma r imangono sempr e entità distinti. Come tali, un pr ogr amma può r estituir e al suo chiamante un

valor e, solitamente inter o: pr opr io come si compor ta una funzione;

PathToAssembly : il per cor so su disco dell'assembly gener ato;

TempFiles : i file tempor aneai r imasti.

Avr ete notato che anche VBCodePr ovider espone molti metodi, ma la maggior par te di questi ser vono per la

gener azione di codice. Questo meccanismo per mette di assemblar e una collezione di oggetti ognuno dei quali

r appr esenta un'istr uzione di codice, e poi di gener ar e codice per un qualsiasi linguaggio .NET. Sebbene sia un

funzionalità potente, non la tr atter ò in questa guida.

Generazione di programmiIl pr ossimo sor gente è un esempio che, secondo me, sar ebbe stato MOLTO più fr uttuoso se usato in un'applicazione

w indows for ms. Tuttavia siamo nella sezione A e qui si fa solo teor ia, per ciò, pur tr oppo, dovr ete sor bir vi questo

entusiasmante esempio come applicazione console.

Ammettiamo che un'impr esa abbia un softwar e di gestione dei suoi mater iali, e che abbastanza spesso acquisti nuove

tipologie di pr odotti o semilavor ati. Gli addetti al magazzino dovr anno intr odur r e i dati dei nuovi oggetti, ma per far

ciò, è necessar io un pr ogr amma adatto per gestir e quel tipo di oggetti: in questo modo, ogni volta, ser ve un

pr ogr amma nuovo. L'esempio che segue è una possibile soluzione a questo pr oblema: il pr ogr amma pr incipale r ichiede

di immetter e infor mazioni su un nuovo tipo di dato e cr ea un pr ogr amma apposta per la gestione di quel tipo di dato

(notate che ho scr itto cinque volte pr ogr amma sulla stessa colonna XD).

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.

Module Module1

'Classe che rappresenta il nuovo tipo di dato, ed'espone una funzione per scriverne il codiceClass TypeCreator

Private _Fields As Dictionary(Of String, String)Private _Name As String

'Fields è un dizionario che contiene come'chiavi i nomi delle proprietà da definire'nel nuovo tipo e come valori il loro tipiPublic ReadOnly Property Fields() As Dictionary(Of String, String)

GetReturn _Fields

End GetEnd Property

'Nome del nuovo tipoPublic Property Name() As String

GetReturn _Name

End GetSet(ByVal value As String)

_Name = valueEnd Set

End Property

Public Sub New()_Fields = New Dictionary(Of String, String)

End Sub

Page 228: Guida visual basic

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.

'Genera il codice della proprietàPublic Function GenerateCode() As String

Dim Code As New Text.StringBuilder()

Code.AppendLine("Class " & Name)For Each Field As String In Me.Fields.Keys

Code.AppendFormat("Private _{0} As {1}{2}", _Field, Me.Fields(Field), Environment.NewLine)

Code.AppendFormat( _"Public Property {0} As {1}{2}" & _" Get{2}" & _" Return _{0}{2}" & _" End Get{2}" & _" Set(ByVal value As {1}){2}" & _" _{0} = value{2}" & _" End Set{2}" & _"End Property{2}", _Field, Me.Fields(Field), Environment.NewLine)

NextCode.AppendLine("End Class")

Return Code.ToString()

End Function

End Class

'Classe statica contenente la funzione per scrivere'e generare il nuovo programmaClass ProgramCreator

'Accetta come input il nuovo tipo di dato'da gestire. Restituisce in output il percorso'dell'eseguibile creatoPublic Shared Function CreateManagingProgram(ByVal T As TypeCreator) As String

Dim Code As New Text.StringBuilder()

Code.AppendLine("Imports System")Code.AppendLine("Imports System.Collections.Generic")Code.AppendLine("Module Module1")Code.AppendLine(T.GenerateCode())

Code.AppendLine("Sub Main()")Code.AppendLine(" Dim Storage As New List(Of " & T.Name & ")")Code.AppendLine(" Dim Cmd As Char")Code.AppendLine(" Do")Code.AppendLine(" Console.Clear()")Code.AppendLine(" Console.WriteLine(""Inserimento di oggetti " & T.Name & """)")Code.AppendLine(" Console.WriteLine()")Code.AppendLine(" Console.Writeline(""Scegliere un'operazione: "")")Code.AppendLine(" Console.WriteLine("" i - inserimento;"")")Code.AppendLine(" Console.WriteLine("" e - elenca;"")")Code.AppendLine(" Console.WriteLine("" u - uscita."")")Code.AppendLine(" Cmd = Console.ReadKey().KeyChar")Code.AppendLine(" Console.Clear()")Code.AppendLine(" Select Case Cmd")Code.AppendLine(" Case ""i"" ")Code.AppendLine(" Dim O As New " & T.Name & "()")Code.AppendLine(" Console.WriteLine(""Inserire i dati: "")")'Legge ogni membro del nuovo tipo. Usa la CType'per essere sicuri che tutto venga interpretato nel'modo corretto.For Each Field As String In T.Fields.Keys

Code.AppendFormat("Console.Write("" {0} = ""){1}", Field,Environment.NewLine)

Code.AppendFormat("O.{0} = CType(Console.ReadLine(), {1}){2}", Field,T.Fields(Field), Environment.NewLine)

NextCode.AppendLine(" Storage.Add(O)")Code.AppendLine(" Console.WriteLine(""Inserimento completato!"")")Code.AppendLine(" Case ""e"" ")Code.AppendLine(" For I As Int32 = 0 To Storage.Count - 1")Code.AppendLine(" Console.WriteLine(""{0:000} + "", I)")

Page 229: Guida visual basic

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.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.

'Fa scrivere una linea per ogni proprietà'dell'oggetto, mostrandone il valoreFor Each Field As String In T.Fields.Keys

Code.AppendFormat("Console.WriteLine("" {0} = "" & Storage(I).{0}.ToString()){1}", Field, Environment.NewLine)

NextCode.AppendLine(" Next")Code.AppendLine(" Console.ReadKey()")Code.AppendLine(" End Select")Code.AppendLine(" Loop Until Cmd = ""u""")

Code.AppendLine("End Sub")Code.AppendLine("End Module")

Dim Parameters As New CompilerParameters

With Parameters

.GenerateExecutable = True

.TreatWarningsAsErrors = True

.TempFiles.KeepFiles = False

.GenerateInMemory = False

.ReferencedAssemblies.Add("Microsoft.VisualBasic.dll")

.ReferencedAssemblies.Add("System.dll")End With

Dim Provider As New VBCodeProviderDim CompResults As CompilerResults = _

Provider.CompileAssemblyFromSource(Parameters, Code.ToString())

If CompResults.Errors.Count = 0 ThenReturn CompResults.PathToAssembly

ElseFor Each E As CompilerError In CompResults.Errors

StopNextReturn Nothing

End IfEnd Function

End Class

Sub Main()

Dim NewType As New TypeCreator()Dim I As Int16Dim Field, FieldType As String

Console.WriteLine("Creazione di un tipo")Console.WriteLine()

Console.Write("Nome del tipo = ")NewType.Name = Console.ReadLine

Console.WriteLine("Inserisci il nome del campo e il suo tipo:")

I = 1Do

Console.Write("Nome campo {0}: ", I)Field = Console.ReadLine

If String.IsNullOrEmpty(Field) Then

Exit DoEnd If

Console.Write("Tipo campo {0}: ", I)FieldType = Console.ReadLine

'Dovrete immettere il nome completo e con'le maiuscole al posto giusto. Ad esempio:' System.String'e non string o system.String o STring.If Type.GetType(FieldType) Is Nothing Then

Console.WriteLine("Il tipo {0} non esiste!", FieldType)

Page 230: Guida visual basic

174.175.176.177.178.179.180.181.182.183.184.185.186.187.188.189.190.

Console.ReadKey()Else

NewType.Fields.Add(Field, FieldType)I += 1

End IfLoop

Dim Path As String = ProgramCreator.CreateManagingProgram(NewType)

If Not String.IsNullOrEmpty(Path) Then

Console.WriteLine("Programma di gestione per il tipo {0} creato!", NewType.Name)Console.WriteLine("Avviarlo ora? y/n")If Console.ReadKey().KeyChar = "y" Then

Process.Start(Path)End If

End IfEnd Sub

End Module

Page 231: Guida visual basic

A48. Gli Attributi

Cosa sono e a cosa servonoGli attr ibuti sono una par ticolar e categor ia di oggetti che ha come unico scopo quello di for nir e infor mazioni su altr e

entità. Tutte le infor mazioni contenute in un attr ibuto vanno sotto il nome di metadati. Attr aver so i metadati, il

pr ogr ammator e può definir e ulter ior i dettagli su un membr o o su un tipo e sul suo compor tamento in r elazione con le

altr e par ti del codice. Gli attr ibuti, quindi, non sono str umenti di "pr ogr ammazione attiva", poiché non fanno nulla, ma

dicono semplicemente qualcosa; si avvicinano, per cer ti ver si, alla pr ogr ammazione dichiar ativa. Inoltr e, essi non

possono esser e usati né r intr acciati dal codice in cui sono definiti: non si tr atta di var iabili, metodi, classi, str uttur e o

altr o; gli attr ibuti, semplicemente par lando, non "esistono" se non come par te invisibile di infor mazione attaccata a

qualche altr o membr o. Sono come dei par assiti: hanno senso solo se attr ibuiti, appunto, a qualcosa. Per questo motivo,

l'unico modo per utilizzar e le infor mazioni che essi si por tano dietr o consiste nella Reflection.

La sintassi con cui si assegna un attr ibuto a un membr o (non "si dichiar a", né "si inizializza", ma "si assegna" a

qualcosa) è questa:

Faccio subito un esempio. Il Visual Basic per mette di usar e ar r ay con indici a base maggior e di 0: questa è una sua

peculiar ità, che non si tr ova in tutti i linguaggi .NET. Le Common Language Specifications del Fr amewor k specificano

che un qualsiasi linguaggio, per esser e qualificato come .NET, deve dar e la possibilità di gestir e ar r ay a base 0. VB fa

questo, ma espone anche quella par ticolar ità di pr ima che gli der iva dal VB classico: questa potenzialità è detta non

CLS-Compliant, ossia che non r ispetta le specifiche del Fr amewor k. Noi siamo liber i di usar la, ad esempio in una

libr er ia, ma dobbiamo avver tir e gli altr i che, qualor a usasser o un altr o linguaggio .NET, non potr ebber o

pr obabilmente usufr uir e di quella par te di codice. L'unico modo di far e ciò consiste nell'assegnar e un attr ibuto

CLSCompliant a quel membr o che non r ispetta le specifiche:

Or a, se un pr ogr ammator e usasse la libr er ia in cui è posto questo codice, pr obabilmente il compilator e pr odur r ebbe

un War ning aggiuntivo indicano esplicitamente che il metodo Cr eateAr r ay non è CLS-Compliant, e per ciò non è sicur o

usar lo.

Allo stesso modo, un altr o esempio potr ebbe esser e l'attr ibuto Obsolete. Specialmente quando si lavor a su gr andi

pr ogetti di cui esistono più ver sioni e lo sviluppo è in costante evoluzione, capita che vengano scr itte nuove ver sioni di

membr i o tipi già esistenti: quelle vecchie sar anno molto pr obabilmente mantenute per assicur ar e la compatibilità con

1. <Attributo(Parametri)> [Entità]

01.02.03.04.05.06.07.08.09.10.11.12.

13.14.15.16.17.18.19.20.

<CLSCompliant(False)> _Function CreateArray(Of T)(ByVal IndexFrom As Int32, ByVal IndexTo As Int32) As Array

'Per creare un array a estremi variabili è necessario'usare una funzione della classe Array, ed è altrettanto'necessario dichiarare l'array come di tipo Array, anche se'questo è solitamente sconsigliato. Per creare il'suddetto oggetto, bisogna passare alla funzione tre'parametri:' - il tipo degli oggetti che l'array contiene;' - un array contenente le lunghezze di ogni rango dell'array;' - un array contenente gli indici iniziali di ogni rango.Return Array.CreateInstance(GetType(T), New Int32() {IndexTo - IndexFrom}, New Int32()

{IndexTo})End Function Sub Main()

Dim CustomArray As Array = CreateArray(Of Int32)(3, 9)

CustomArray(3) = 1'...

End Sub

Page 232: Guida visual basic

softwar e datati, ma sar anno comunque mar cate con l'attr ibuto obsolete. Ad esempio, con questa r iga di codice potr ete

ottener e l'indir izzo IP del mio sito:

Tuttavia otter r ete un War ning che r ipor ta la seguente dicitur a: 'Public Shared Function Res olve(hos tName As String)

As Sys tem.Net.IPHos tEntry' is obs olete: Res olve is obs oleted for this type, pleas e us e GetHos tEntry ins tead.

http://go.micros oft.com/fwlink/?linkid=14202 . Questo è un esempio di un metodo, esistente dalla ver sione 1.1 del

Fr amewor k, che è stato r impiazzato e quindi dichiar ato obsoleto.

Un altr o attr ibuto molto inter essante è, ad esempio, Conditional, che per mette di eseguir e o tr alasciar e del codice a

seconda che sia definita una cer ta costante di compilazione. Queste costanti sono impostabili in una finestr a di

compilazione avanzata che vedr emo solo più avanti. Tuttavia, quando l'applicazione è in modalità debug, è di default

definita la costante DEBUG.

Usando Cr eateObject possiamo monitor ar e la quantità di memor ia allocata dal pr ogr amma, ma solo in modalità debug,

ossia quando la costante di compilazione DEBUG è definita.

Come avr ete notato, tutti gli esempi che ho fatto comunicavano infor mazioni dir ettamente al compilator e, ed infatti

una buona par te degli attr ibuti ser ve pr opr io per definir e compor tamente che non si potr ebber o indicar e in altr o

modo. Un attr ibuto può esser e usato, quindi, nelle manier e più var ie e intr odur r ò nuovi attr ibuti molto impor tanti

nelle pr ossime sezioni. Questo capitolo non ha lo scopo di mostr ar e il funzionamento di ogni attr ibuti esistente, ma di

insegnar e a cosa esso ser va e come agisca: ecco per chè nel pr ossimo par agr afo ci cimenter emo nella scr ittur a di un

nuovo attr ibuto.

Dichiarare nuovi attributiFor malmente, un attr ibuto non è altr o che una classe der ivata da System.Attr ibute. Ci sono alcune convenzioni

r iguar do la scr ittur a di queste classi, per ò:

Il nome della classe deve sempr e ter minar e con la par ola "Attr ibute";

Gli unici membr i consentiti sono: campi, pr opr ietà e costr uttor i;

Tutte le pr opr ietà che vengono impostate nei costr uttor i devono esser e ReadOnly, e vicever sa.

Il pr imo punto è solo una convenzione, ma gli altr i sono di utilità pr atica. Dato che lo scopo dell'attr ibuto è contener e

infor mazione, è ovvio che possa contener e solo pr opr ietà, poiché non spetta a lui usar ne il valor e. Ecco un esempio

semplice con un attr ibuto senza pr opr ietà:

1. Dim IP As Net.IPHostEntry = System.Net.Dns.Resolve("www.totem.altervista.org")

01.02.03.04.05.06.07.08.09.10.11.12.13.14.15.16.17.18.19.20.21.

<Conditional("DEBUG")> _Sub WriteStatus()

'Scriva a schermo la quantità di memoria usata,'senza aspettare la prossima garbage collectionConsole.WriteLine("Memory: {0}", GC.GetTotalMemory(False))

End Sub 'Crea un nuovo oggettoFunction CreateObject(Of T As New)() As T

Dim Result As New T'Richiama WriteStatus: questa chiamata viene IGNORATA'in qualsiasi caso, tranne quando siamo in debug.WriteStatus()Return Result

End Function Sub Main()

Dim k As Text.StringBuilder = _CreateObject(Of Text.StringBuilder)()

'...End Sub

Page 233: Guida visual basic

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.070.071.072.

'In questo codice, cronometreremo dei metodi, per'vedere quale è il più veloce!Module Module1

'Questo è un nuovo attributo completamente vuoto.'L'informazione che trasporta consiste nel fatto stesso'che esso sia applicato ad un membro.'Nel metodo di cronometraggio, rintracceremo e useremo'solo i metodi a cui sia stato assegnato questo attributo.Public Class TimeAttribute

Inherits Attribute

End Class

'I prossimi quattro metodi sono procedure di test. Ognuna'esegue una certa operazione 100mila o 10 milioni di volte. <Time()> _Sub AppendString()

Dim S As String = ""For I As Int32 = 1 To 100000

S &= "a"NextS = Nothing

End Sub

<Time()> _Sub AppendBuilder()

Dim S As New Text.StringBuilder()For I As Int32 = 1 To 100000

S.Append("a")NextS = Nothing

End Sub

<Time()> _Sub SumInt32()

Dim S As Int32For I As Int32 = 1 To 10000000

S += 1Next

End Sub

<Time()> _Sub SumDouble()

Dim S As DoubleFor I As Int32 = 1 To 10000000

S += 1.0Next

End Sub

'Questa procedura analizza il tipo T e ne estrae tutti'i metodi statici e senza parametri marcati con l'attributo'Time, quindi li esegue e li cronometra, poi riporta'i risultati a schermo per ognuno.'Vogliamo che i metodi siano statici e senza parametri'per evitare di raccogliere tutte le informazioni per la'funzione Invoke.Sub ReportTiming(ByVal T As Type)

Dim Methods() As MethodInfo = T.GetMethods()Dim TimeType As Type = GetType(TimeAttribute)Dim TimeMethods As New List(Of MethodInfo)

'La funzione GetCustomAttributes accetta due parametri'nel secondo overload: il primo è il tipo di'attributo da cercare, mentre il secondo specifica se'cercare tale attributo in tutto l'albero di'ereditarietà del membro. Restituisce come'risultato un array di oggetti contenenti gli attributi'del tipo voluto. Vedremo fra poco come utilizzare'questo array: per ora limitiamoci a vedere se non è

Page 234: Guida visual basic

Ecco i r isultati del benchmar king (ter mine tecnico) sul mio por tatile:

Method: AppendString Time: 4765msMethod: AppendBuilder Time: 2msMethod: SumInt32 Time: 27msMethod: SumDouble Time: 34ms

Come potete osser var e, concatenar e le str inghe con & è enor memente meno efficiente r ispetto all'Append della classe

Str ingBuilder . Ecco per chè, quando si hanno molti dati testuali da elabor ar e, consiglio sempr e di usar e il secondo

metodo. Per quando r iguar da i numer i, le pr estazioni sono comunque buone, se non che i Double occupano 32 bit in più

e ci vuole più tempo anche per elabor ar li. In questo esempio avete visto che gli attr ibuti possono esser e usati solo

attr aver so la Reflection. Pr ima di pr oceder e, bisogna dir e che esiste uno speciale attr ibuto applicabile solo agli

attr ibuti che definisce quali entità possano esser e mar cate con dato attr ibuto. Esso si chiama Attr ibuteUsage. Ad

esempio, nel codice pr ecedente, Time è stato scr itto con l'intento di mar car e tutti i metodi che sar ebber o stati

sottoposti a benchmar king, ossia è "nato" per esser e applicato solo a metodi, e non a classi, enumer ator i, var iabili o

altr o. Tuttavia, per come l'abbiamo dichiar ato, un pr ogr ammator e può applicar lo a qualsiasi cosa. Per r estr inger e il

suo campo d'azione si dovr ebbe modificar e il sor gente come segue:

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.

'vuoto, ossia se il metodo è stato marcato con TimeFor Each M As MethodInfo In Methods

If M.GetCustomAttributes(TimeType, False).Length > 0 And _M.GetParameters().Count = 0 And _M.IsStatic ThenTimeMethods.Add(M)

End IfNext

Methods = Nothing

'La classe Stopwatch rappresenta un cronometro. Start'per farlo partire, Stop per fermarlo e Reset per'resettarlo a 0 secondi.Dim Crono As New Stopwatch

For Each M As MethodInfo In TimeMethods

Crono.Reset()Crono.Start()M.Invoke(Nothing, New Object() {})Crono.Stop()Console.WriteLine("Method: {0}", M.Name)Console.WriteLine(" Time: {0}ms", Crono.ElapsedMilliseconds)

Next

TimeMethods.Clear()TimeMethods = Nothing

End Sub

Sub Main()Dim This As Type = GetType(Module1)

'Non vi allarmate se il programma non stampa nulla'per qualche secondo. Il primo metodo è molto'lento XDReportTiming(This)

Console.ReadKey()

End SubEnd Module

1.2.

<AttributeUsage(AttributeTargets.Method)> _

Page 235: Guida visual basic

Attr ibuteTar gets è un enumer ator e codificato a bit.

Ma veniamo or a agli attr ibuti con par ametr i:

3.4.5.

Public Class TimeAttributeInherits Attribute

End Class

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.

Module Module1

'UserInputAttribute specifica se una certa proprietà'debba essere valorizzata dall'utente e se sia'obbligatoria o meno. Il costruttore impone un solo'argomento, IsUserScope, che deve essere per forza'specificato, altrimenti non sarebbe neanche valsa la'pena di usare l'attributo, dato che questa è la'sua unica funzione.'Come specificato dalle convenzioni, la proprietà'impostata nel costruttore è ReadOnly, mentre'le altre (l'altra) è normale.<AttributeUsage(AttributeTargets.Property)> _Class UserInputAttribute

Inherits Attribute

Private _IsUserScope As BooleanPrivate _IsCompulsory As Boolean = False

Public ReadOnly Property IsUserScope() As Boolean

GetReturn _IsUserScope

End GetEnd Property

Public Property IsCompulsory() As Boolean

GetReturn _IsCompulsory

End GetSet(ByVal value As Boolean)

_IsCompulsory = valueEnd Set

End Property

Sub New(ByVal IsUserScope As Boolean)_IsUserScope = IsUserScope

End Sub

End Class

'CuboClass Cube

Private _SideLength As SinglePrivate _Density As SinglePrivate _Cost As Single

'Se i parametri del costruttore vanno specificati'tra parentesi quando si assegna l'attributo, allora'come si fa a impostare le altre proprietà'facoltative? Si usa un particolare operatore di'assegnamento ":=" e si impostano esplicitamente'i valori delle proprietà ad uno ad uno,'separati da virgole, ma sempre nelle parentesi.<UserInput(True, IsCompulsory:=True)> _Public Property SideLength() As Single

GetReturn _SideLength

End GetSet(ByVal value As Single)

_SideLength = valueEnd Set

End Property

Page 236: Guida visual basic

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.131.132.133.134.135.

<UserInput(True)> _Public Property Density() As Single

GetReturn _Density

End GetSet(ByVal value As Single)

_Density = valueEnd Set

End Property

'Cost non verrà chiesto all'utente<UserInput(False)> _Public Property Cost() As Single

GetReturn _Cost

End GetSet(ByVal value As Single)

_Cost = valueEnd Set

End Property

End Class

'Crea un oggetto di tipo T richiendendo all'utente di'impostare le proprietà marcate con UserInput'in cui IsUserScope è True.Function GetInfo(ByVal T As Type) As Object

Dim O As Object = T.Assembly.CreateInstance(T.FullName)

For Each PI As PropertyInfo In T.GetProperties()If Not PI.CanWrite Then

Continue ForEnd If

Dim Attributes As Object() = PI.GetCustomAttributes(GetType(UserInputAttribute),

True)

If Attributes.Count = 0 ThenContinue For

End If

'Ottiene il primo (e l'unico) elemento dell'array,'un oggetto di tipo UserInputAttribute che rappresenta'l'attributo assegnato e contiene tutte le informazioni'passate, sottoforma di proprietà.Dim Attr As UserInputAttribute = Attributs(0)

'Se la proprietà non è richiesta all'utente,'allora continua il cicloIf Not Attr.IsUserScope Then

Continue ForEnd If

Dim Value As Object = Nothing'Se è obbligatoria, continua a richiederla'fino a che l'utente non immette un valore corretto.If Attr.IsCompulsory Then

DoTry

Console.Write("* {0} = ", PI.Name)Value = Convert.ChangeType(Console.ReadLine, PI.PropertyType)

Catch Ex As ExceptionValue = NothingConsole.WriteLine(Ex.Message)

End TryLoop Until Value IsNot Nothing

Else'Altrimenti la richiede una sola voltaTry

Console.Write("{0} = ", PI.Name)Value = Convert.ChangeType(Console.ReadLine, PI.PropertyType)

Catch Ex As Exception

Page 237: Guida visual basic

Vi lascio immaginar e cosa faccia il metodo Conver t.ChangeType...

136.137.138.139.140.141.142.143.144.145.146.147.148.149.150.151.152.153.154.155.156.157.158.159.

Value = NothingEnd Try

End IfIf Value IsNot Nothing Then

PI.SetValue(O, Value, Nothing)End If

Next

Return OEnd Function

Sub Main()

Dim O As Object Console.WriteLine("Riempire i campi (* = obbligatorio):") O = GetInfo(GetType(Cube))

'Stampa i valori con il metodo PrintInfo scritto qualche'capitolo faPrintInfo(O, "")

Console.ReadKey()

End SubEnd Module

Page 238: Guida visual basic

A49. Modificare le opzioni di compilazione

Esistono più modi di influir e sulla compilazione di un sor gente e di modificar e il compor tamento del compilator e ver so

le sue par ti. Alcuni di questi "modi" consistono nel modificar e le opzioni di compilazione, che si suddividuono in quattr o

voci pr incipali: Ex plicit, Compar e, Infer e Str ict. Per attivar e o disattivar e ognuna di esse, è possibile usar e delle

dir ettive speciali poste in testa al codice o acceder e alla finestr a dell'ambiente di sviluppo r elativa alla compilazione.

Nel secondo caso, baster à che clicchiate, dal menù pr incipale, Pr oject > [NomePr getto] Pr oper ties; dalle pr opr ietà,

scegliete la seconda scheda cliccando sull'etichetta Compile sulla sinistr a:

Da questo pannelo potr ete anche decider e il compor tamento da adottar e ver so cer te cir costanze di codice, ossia se

tr attar le come war ning, er r or i, o se non segnalar le neppur e.

Option Explic itQuando Ex plicit è attiva, tutte le var iabili devono esser e esplicitamente dichiar ate pr ima del lor o uso: d'altr a par te,

questa è sempr e stata la pr assi che abbiamo adottato fin dall'inizio del cor so e non ci sono par ticolar i motivi per

combiar la. Quando l'opzione è disattivata, ogni nome sconosciuto ver r à tr attato come una nuova var iabile e cr eato al

momento. Ecco un esempio in cui disattivo Ex plicit da codice:

01.02.03.04.05.06.07.08.09.10.11.12.13.14.15.

Option Explicit Off Module Module1

Sub Main()'La variabile Stringa non viene dichiarata, ma è'lecito usarla e non viene comunicato alcun erroreStringa = "Ciao"

'Stessa cosa per la variabile I, che non è stata'dichiarata da nessuna parteFor I = 1 To 20

Console.WriteLine(Stringa)Next

Page 239: Guida visual basic

Le dir ettive per l'attivazione/disattivazione di un'opzione di compilazione devono tr ovar si sempr e in cima al sor gente,

anche pr ima di ogni altr a dir ettiva Impor ts, e hanno una sintassi pr essoché costante:

Anche se è possibile disattivar e Ex plicit, è fortemente s cons igliato far lo: può pr odur r e molti più danni che benefici. È

pur ver o che si usa meno codice, ma questo diventa anche meno compr ensibile, e gli er r or i di battitur a possono

condannar vi a settimane di insonnia. Ad esempio:

Il codice dovr ebbe, nelle vostr e intenzioni, scr iver e "Ciao" solo 10 volte, e poi stampar e 11, 12, 13, ecceter a... Tuttavia

questo non succede, per chè avete dimenticato una "i" e il compilator e non vi segnala nessun er r or e a causa della

dir ettiva Option; non r icever ete neppur e un war ning del tipo "Unused local var iable", per chè la r iga in cui è pr esente

Str nga consiste in un assegnamento. Er r or i stupidi possono causar e gr andi per dite di tempo.

Option CompareQuesta opzione non assume i valor i On/Off, ma Binar y e Tex t. Quando Compar e è impostata su Binar y, la

compar azione tr a due str inghe viene effettuata confr ontando il valor e dei singoli bytes che la compongono e, per ciò, il

confr onto diventa cas e-s ens itive (maiuscole e minuscole della stessa letter a sono consider ate differ enti). Se, al

contr ar io, è impostata su Tex t, viene compar ato solo il testo che le str inghe contengono, senza far e distinzioni su

letter e maiuscole o minuscole (cas e-ins ens itive). Eccone un esempio:

Scr ivendo "Binar y" al posto di "Tex t", otter r emo un messaggio differ ente a r untime!

Option StrictQuesta opzione si occupa di r egolar e le conver sioni implicite tr a tipi di dato differ enti. Quando è attiva, tutti i cast

16.17.

Console.ReadKey()End Sub

End Module

1. Option [Nome] On/Off

01.02.03.04.05.06.07.08.09.10.11.12.13.14.15.16.

Option Explicit Off Module Module1

Sub Main()Stringa = "Ciao"

For I = 1 To 20

If I > 10 ThenStrnga = I

End IfConsole.WriteLine(Stringa)

Next

Console.ReadKey()End Sub

End Module

01.02.03.04.05.06.07.08.09.10.11.

Option Compare TextModule Module1Sub Main()If "CIAO" = "ciao" ThenConsole.WriteLine("Option Compare Text")

ElseConsole.WriteLine("Option Compare Binary")

End IfConsole.ReadKey()

End SubEnd Module

Page 240: Guida visual basic

impliciti vengono segnalati e consider ati come er r or i: non si può passar e, ad esempio, da Double a Integer o da una

classe base a una der ivata:

Per evitar e di r icever e le segnalazioni di er r or e, bisogna utilizzar e un oper ator e di cast esplicito come CType.

Gener almente, Str ict viene mantenuta su Off, ma se siete par ticolar mente r igor osi e volete evitar e le conver sioni

implicite, siete liber i di attivar la.

Option InferQuesta opzione di compilazione è stata intr odotta con la ver sione 2008 del linguaggio e, se attivata, per mette di

infer ir e il tipo di una var iabile senza tipo (ossia senza clausola As) analizzando i valor i che le vengono passati. All'inizio

della guida, ho detto che una var iabile dichiar ata solo con Dim (ad esempio "Dim I") viene consider ata di tipo Object:

questo è ver o dalla ver sione 2005 in giù, e nella ver sioni 2008 e successive solo se Option Infer è disattivata. Ecco un

esempio:

Pr ovando ad impostar e Infer su On, non otter r ete nessuna segnalazione dur ante la scr ittur a, ma appena il pr ogr amma

sar à avviato, ver r à lanciata un'eccezione di cast, poiché il tipo di I viene dedotto dal valor e assegnatole (2) e la fa

diventar e, da quel momento in poi, una var iabile Integer a tutti gli effetti, e "ciao" non è conver tibile in inter o.

01.02.03.04.05.06.07.08.09.10.11.12.13.14.15.16.

Option Strict OnModule Module1

Sub Main()Dim I As Int32Dim P As Student

'Conversione implicita da Double a Int32: viene'segnalata come erroreI = 4.0'Conversione implicita da Person a Student: viene'segnalata come erroreP = New Person("Mario", "Rossi", New Date(1968, 9, 12))

Console.ReadKey()

End SubEnd Module

01.02.03.04.05.06.07.08.09.10.11.12.13.14.15.

Option Infer Off Module Module1

Sub Main()'Infer è disattivata: I viene considerata di'tipo ObjectDim I = 2

'Dato che I è Object, può contenere'qualsiasi cosa, e quindi questo codice non genera'alcun erroreI = "ciao"

End Sub

End Module

Page 241: Guida visual basic

A50. Comprendere e implementare un algoritmo

For se sar ebbe stato oppor tuno tr attar e questo ar gomento moooolto pr ima nella guida piuttosto che alla fine della

sezione; non per niente, la compr ensione degli algor itmi è la pr ima cosa che viene r ichiesta in un qualsiasi cor so di

infor matica. Tuttavia, è pur ver o che, per r isolver e un pr oblema, bisogna dispor r e degli str umenti giusti e,

pr efer ibilmente, conoscer e tutti gli str umenti a pr opr ia disposizione. Ecco per chè, pr ima di giunger e a questo punto,

ho voluto spiegar e tutti i concetti di base e la sintassi del linguaggio, poiché questi sono solo str umenti nelle mani del

pr ogr ammator e, la per sona che deve occupar si della stesur a del codice.

Iniziamo col dar e una classica definizione di algor itmo, mutuata da Wikipedia:

Ins ieme di is truzioni elementari univocamente interpretabili che, es eguite in un ordine s tabilito, permettono la

s oluzione di un problema in un numero fin ito di pas s i.

Vor r ei innanzitutto por r e l'attenzione sulle pr ime par ole: "insieme di istr uzioni elementar i" (io aggiunger e "insieme

finito", per esser e r igor osi, ma mi sembr a abbastanza difficile for nir e un insieme infinito di istr uzioni :P).

Sottolineiamo elementar i: anche se i linguaggi .NET si tr ovano ad un livello molto distante dal pr ocessor e, che è in

gr ado di eseguir e un numer o molto limitato di istr uzioni - fr a cui And, Or , Not, +, Shift, lettur a e scr ittur a - la gamma

di "comandi" disponibile è altr ettanto esigua. Dopotutto, cosa possiamo scr iver e che sia una ver a e pr opr ia istr uzione?

Assegnamenti, oper azioni matematiche e ver ifia di condizioni. Punto. I cicli? Non fanno altr o che r ipeter e istr uzioni. I

metodi? Non fanno altr o che r ichiamar e altr o codice in cui si cono istr uzioni elementar i. Dobbiamo impar ar e, quindi, a

far e il meglio possibile con ciò ci cui disponiamo. Vi sar à utile, a questo pr oposito, disegnar e dei diagr ammi di flusso

per i vostr i algor itmi.

Inoltr e, r equsitio essenziale, è che ogni istr uzione sia unvicamente inter pr etabile, ossia che non possa esser ci

ambiguità con qualsiasi altr a istr uzione. Dopotutto, questa condizione viene soddisfatta dall'elabor ator e stesso, per

come è costr uito. Altr o aspetto impor tante è l'or dine: eseguir e pr ima A e poi B o vicever sa è differ ente; molto spesso

questa inver sione può causar e er r or i anche gr avi. Infine, è necessar io che l'algor itmo r estituisca un r isultato in un

numer o finito di passi: e questo è abbastanza ovvio, ma se ne per de di vista l'impor tanza, ad esempio, nelle funzioni

r icor sive.

In gener e, il pr oblema più gr ande di un pr ogr ammator e che deve scr iver e un algor itmo è r ender si conto di come

l'uomo pensa. Ad esempio: dovete scr iver e un pr ogr amma che contr olla se due str inghe sono l'una l'anagr amma

dell'altr a. A occhio è facile pensar e a come far e: basta qualche tentativo per veder e se r iusciamo a for mar e la pr ima

con le letter e della seconda o vicever sa, ma, domanda classica, "come si tr aduce in codice"? Ossia, dobbiamo domandar ci

come abbiamo fatto a giunger e alla conclusione, scomponendo le istr uzioni che il nostr o cer vello ha eseguito. Infatti,

quasi sempr e, per istinto o abitudine, siamo por tati ad eseguir e molti passi logici alla volta, senza r ender cene conto:

questo il computer non è in gr ado di far lo, e per istr uir lo a dover e dobbiamo abbassar ci al suo livello. Ecco una

semplice lista di punti in cui espongo come passer ei dal "linguaggio umano" al "linguaggio macchina":

Definizione di anagr amma da Wikipedia: "Un anagr amma è il r isultato della per mutazione delle letter e di una o

più par ole compiuta in modo tale da cr ear e altr e par ole o eventualmente fr asi di senso compiuto." ;

Bisogna contr ollar e se, spostando le letter e, è possibile for mar e l'altr a par ola;

Ma questo è possibile solo se ogni letter a compar e lo stesso numer o di volte in entr ambe le par ole;

Per ver ificar e quest'ultimo dato è necessar io contar e ogni letter a di entr ambe le par ole, e quindi contr ollar e

che tutte abbiano lo stesso numer o di occor r enze;

Ser ve per pr ima cosa memor izzar e i dati: due var iabili Str ing per le str inghe. Per gli altr i dati, bisogna

associar e ad una letter a (Char ) un numer o (Int32), quindi una chiave ad un valor e: il tipo di dato ideale è un

Page 242: Guida visual basic

Dictionar y(Of Char , Int32). Si possono usar e due dizionar i o uno solo a seconda di cosa vi viene meglio (per

semplicità ne user ò due);

Ovviamente, a pr ior i, se le str inghe hanno lunghezza diver sa, sicur amente non sono anagr ammi;

Or a è necessar io analizzar e le str inghe. Per scor r er e una str inga, basta ser vir si di un ciclo For e per ottener e

un dato car atter e a una data posizione, la pr opr ietà (di default) Char s;

In questo ciclo, se il dizionar io contiene il car atter e, allor a questo è già stato tr ovato almeno una volta, quindi

se ne pr ende il valor e e lo si incr ementa di uno; in caso contr ar io, si aggiunge una nuova chiave con il valor e 1;

Una volta ottenuti i due dizionar i, per pr ima cosa, devono aver e lo stesso numer o di chiavi, altr imenti una

str inga avr ebbe dei car atter i che non compaiono nell'altr a; poi bisogna veder e se ogni chiave del pr imo

dizionar io esiste anche nel secondo, ed infine se il valor e ad essa associato è lo stesso. Se una sola di queste

condizioni è falsa, allor a le str inghe NON sono anagr ammi.

Pr ima di veder e il codice, notate che è più semplice ver ificar e quando NON succede qualcosa, poiché basta un solo

(contr o)esempio per confutar e una teor ia, ma ne occor r ono infiniti per dimostr ar la:

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.32.33.34.35.36.37.38.39.40.41.42.43.44.45.46.47.48.49.50.51.52.

Module Module1

Sub Main()Dim S1, S2 As StringDim C1, C2 As Dictionary(Of Char, Int32)Dim Result As Boolean = True

Console.Write("Stringa 1: ")S1 = Console.ReadLineConsole.Write("Stringa 2: ")S2 = Console.ReadLine

If S1.Length = S2.Length Then

C1 = New Dictionary(Of Char, Int32)For I As Int16 = 0 To S1.Length - 1

If C1.ContainsKey(S1.Chars(I)) ThenC1(S1.Chars(I)) += 1

ElseC1.Add(S1.Chars(I), 1)

End IfNext

C2 = New Dictionary(Of Char, Int32)For I As Int16 = 0 To S2.Length - 1

If C2.ContainsKey(S2.Chars(I)) ThenC2(S2.Chars(I)) += 1

ElseC2.Add(S2.Chars(I), 1)

End IfNext

If C1.Keys.Count = C2.Keys.Count Then

For Each C As Char In C1.KeysIf Not C2.ContainsKey(C) Then

Result = FalseElseIf C1(C) <> C2(C) Then

Result = FalseEnd IfIf Not Result Then

Exit ForEnd If

NextElse

Result = FalseEnd If

ElseResult = False

End If

If Result ThenConsole.WriteLine("Sono anagrammi!")

Page 243: Guida visual basic

53.54.55.56.57.58.

ElseConsole.WriteLine("Non sono anagrammi!")

End If

Console.ReadKey()End Sub

End Module

Page 244: Guida visual basic

A51. Il miglior codice

Il fine giustifica i mezzi... beh non sempr e. In questo caso mi sto r ifer endo allo stile in cui il codice sor gente viene

scr itto: infatti, si può ottener e un r isultato che all'occhio dell'utente del pr ogr amma sembr a buono, se non ottimo, ma

che visto da un pr ogr ammator e osser vando il codice non è per niente affidabile. Quando si pubblicano i pr opr i

pr ogr ammi open sour ce, con sor genti annessi, si dovr ebbe far e par ticolar e attenzione anche a come si scr ive,

per mettendo agli altr i pr ogr ammator i di usufr uir e del pr opr io codice in manier a veloce e intuitiva. In questo modo ne

tr ar r anno vantaggio non solo gli altr i, ma anche voi stessi, che potr este tr ovar vi a r iveder e uno stesso sor gente

molto tempo dopo la sua stesur a e non r icor dar vi più niente. A tal pr oposito, elencher ò or a alcune buone nor me da

seguir e per miglior ar e il pr opr io stile.

CommentareÈ buona nor ma commentar e il sor gente nelle sue var ie fasi, per spiegar ne il funzionamento o anche solo lo scopo.

Mentr e il commento può esser e tr alasciato per oper azione str aor dinar iamente lampanti e semplici, dovr ebbe

diventar e una r egola quando scr ivete pr ocedur e funzioni o anche solo pezzi di codice più complessi o cr eati da voi ex

novo (il che li r ende sconosciuti agli occhi altr ui). Vi faccio un piccolo esempio:

Questo potr ebbe esser e qualsiasi cosa: non c'è alcuna indicazione di cosa le letter e r appr esentino, nè del per chè venga

effettuata pr opr io quell'oper azione. Ripr oviamo in questo modo, con il sor gente commentato, e vediamo se capite a

cosa la for mula si r ifer isca:

Così è molto meglio: possiamo capir e sia lo scopo della for mula sia il pr ocedimento logico per mezzo del quale ci si è

ar r ivati.

Dare un nomeQuando si cr eano nuovi contr olli all'inter no della w indows for m, i lor o nomi vengono gener ati automaticamente

tr amite un indice, pr eceduto dal nome della classe a cui il contr ollo appar tiene, come, ad esempio, Button1 o

TabContr ol2. Se per applicazioni veloci, che devono svolger e pochissime, semplici oper azioni e per le quali basta una

finestr a anche piccola, non c'è pr oblema a lasciar e i contr olli innominati in questo modo, quasi sempr e è utile, anzi,

molto utile, r inominar li in modo che il lor o scopo sia compr ensibile anche da codice e che il lor o nome sia molto più

facile da r icor dar e. Tr oppe volte vedo nei sor genti dei Tex tBox 3, Button34, ToolStr ipItem7 che non si sa cosa siano. A

mio par er e, invece, è necessar io adottar e uno stile ben pr eciso anche per i nomi. Dal canto mio, utilizzo sempr e come

nome una str inga composta per i pr imi tr e car atter i dalla sigla del tipo di contr ollo (ad esempio cmd o btn per i

button, lst per le liste, cmb per le combobox , tx t per le tex tbox e così via) e per i r imanenti da par ole che ne

descr ivano la funzione. Esemplificando, un pulsante che debba cr ear e un nuovo file si chiamer à btnNewFile, una lista

che debba contener e degli indir izzi di posta sar à lstEmail: quest'ultima notazione è detta "notazione ungher ese". A tal

1. X = Math.Sqrt((1 - (Y ^ 2 / B ^ 2)) * A ^ 2)

01.02.03.04.05.06.07.08.09.10.

'Data l'equazione di un'ellisse:'x^2 y^2.'--- + --- = 1'a^2 b^2'Ricava x:'x^2 / a^2 = 1 - (y^2 / b^2)'x^2 = (1 - (y^2 / b^2)) * a^2'x = sqrt((1 - (y^2 / b^2)) * a^2)'Prende la soluzione positiva:X = Math.Sqrt((1 - (Y ^ 2 / B ^ 2)) * A ^ 2)

Page 245: Guida visual basic

pr oposito, vi voglio sugger ir e quest'ar ticolo e vi invito caldamente a legger lo.

VariabiliE se il nome dei contr olli deve esser e accur ato, lo deve esser e anche quello delle var iabili. Pr ogr ammar e non è far e

algebr a, non si deve cr eder e di poter usar e solo a, c, x , m, ki ecceter a. Le var iabili dovr ebber o aver e dei nomi

significativi. Bisogna utilizzar e le stesse nor me sopr a descr itte, anche se a mio par er e il pr efisso per i tipi (obj è

object, sng single, int integer ...) si può anche tr alasciar e.

Risparmiare memoriaRispar miar e memor ia r ender à anche le oper azioni più semplici per il computer . Spesso è bene valutar e quale sia il tipo

più adatto da utilizzar e, se Integer , Byte, Double, Object o altr i. Per ciò bisogna anche pr evedr e quali sar anno i casi che

si potr ano ver ificar e. Se steste costr uendo un pr ogr amma che r iguar di la fisica, dovr este usar e numer i in vir gola

mobile, ma quali? Più è alta la pr ecisioe da utilizzar e, più vi ser vir à spazio: se dovete r isolver e pr oblemi da liceo

user ete il tipo Decimal (28 decimali, estensione da -7,9e+28 a 7,9e+28), o al massimo Single (38 decimali, estensione da

-3,4e+38 a +3,4e+38), ma se state facendo calcoli specialistici ad esempio per un acceler ator e di par ticelle

(megalomani!) avr este bisogno di tutta la potenza di calcolo necessar ia e user este quindi Double (324 decimali,

estensione da -1,7e308 a +1,7e+308). Ricor datevi anche dell'esistenza dei tipi Unsigned, che vi per mettono di ottener e

un'estensione di numer i doppia sopr a lo zer o, così UInt16 avr à tanti numer i positivi quanti Int32.

Ricor date, inoltr e, di distr ugger e sempr e gli oggetti che utilizzate dopo il lor o ciclo di vita e di r ichiamar e il

r ispettivo distr uttor e se ne hanno uno (Dispose).

Il rasoio di OccamLa soluzione più semplice è quella esatta. E per questo mi r ifer isco allo scr iver e anche in ter mini di lunghezza di

codice. È inutile scr iver e funzioni lunghissime quando è possibile eguagliar le con pochissime r ighe di codice, più

compatto, incisivo ed efficace.

01.02.03.04.05.06.07.08.09.10.11.12.13.14.15.16.17.18.19.20.21.

Public Function Fattoriale(ByVal X as byte) As UInt64If X = 1 ThenReturn 1

ElseDim T As UInt64 = 1For I As Byte = 1 To XT *= I

NextReturn T

End IfEnd Function 'Diventa: Public Function Fattoriale(ByVal X as byte) As UInt64If X = 1 ThenReturn 1

ElseReturn X * Fattoriale(X - 1)

End IfEnd Function

01.02.03.04.

Sub Copia(ByVal Da As String, ByVal A As String)Dim R As <font class="keyword">New</font> IO.SreamReader(Da)Dim W As <font class="keyword">New</font> IO.StreamWriter(A)

Page 246: Guida visual basic

IncapsulamentoL'incapsulamento è uno dei tr e fondamentali del par adigma di pr ogr ammazione ad Oggetti (gli altr i due sono

polimor fismo ed er editar ietà, che abbiamo già tr attato). Come r icor der ete, all'inizio del cor so, ho scr itto che il vb.net

pr esenta tr e aspetti peculiar i e ve li ho spiegati. Tuttavia essi non costituiscono il ver o par adigma di pr ogr ammazione

ad oggetti e questo mi è stato fatto notar e da Netar r ow, che r ingr azio :P. Tr atter ò quindi, in questo momento tale

ar gomento. Nonostante il nome possa sugger ir e un concetto difficile, non è complicato. Scr iver e un pr ogr amma usando

l'incapsulamento significa str uttur ar lo in sezioni in modo tale che il cambiamento di una di esse non si r iper cuota sul

funzionamento delle altr e. Facendo lo stesso esempio che por ta Wikipedia, potr este usar e tr e var iabili x , y e z per

deter minar e un punto e poi cambiar e idea e usar e un ar r ay di tr e elementi. Se avete str uttur ato il pr ogr amma nella

manier a suddetta, dovr este modificar e legger mente solo i metodi della sezione (modulo, classe o altr o) che è

impegnata nella lor o modifica e non tutto il pr ogr amma.

Convenzioni di denominazioneNei capitoli pr ecedenti ho spesse volte r ipor tato quali siano le "convenzioni" per la cr eazione di nomi appositi, come

quelli per le pr opr ietà o per le inter facce. Esistono anche altr i canoni, stabiliti dalla Micr osoft, che dovr ebber o r ender e

il codice miglior e in ter mini di velocità di lettur a e chiar ezza. Pr ima di elencar li, espongo una br eve ser ie di

definizioni dei tipi di nomenclatura usati:

Pascal Case : nella notazione Pascal, ogni par te che for ma un nome deve iniziar e con una letter a maiuscola, ad

esempio una var iabile che conetenga il per cor so di un file sar à FileName, o una pr ocedur a che analizza un

oggetto sar à ScanObject. Si consiglia sempr e, in nomi composti da sostantivi e ver bi, di anticipar e i ver bi e

posticipar e i sostantivi: il metodo per eseguir e la stampa di un documento sar à Pr intDocument e non

DocumentPr int.

Camel Case : nella notazione camel, la pr ima par te del nome inizia con la letter a minuscola, e tutte le

successive con una maiuscola. Ad esempio, il titolo di un libr o sar à bookTitle, o l'indir izzo di una per sona

addr ess (un solo nome).

Notazione Ung herese : nella notazione ungher ese, il nome del membr o viene composto come in quella Pascal,

ma è pr eceduto da un pr efisso alfanumer ico con l'iniziale minuscola che indica il tipo di membr o. Ad esempio una

casella di testo (Tex tBox ) che contenga il nome di una per sona sar à txtName, o una lista di oggetti lstObject.

A ll Case : nella notazione All, tutte le letter e sono maiuscole.

Detto questo, le seguenti dir ettive specificano quando usar e quale tipo di notazione:

Nome di un metodo : Pascal

Campo di una classe : Pascal

Nome di una classe : Pascal

Membr i pubblici : Pascal

05.06.07.08.09.10.11.12.13.14.15.

W.Write(R.ReadToEnd)

R.Close()W.Close()

End Sub 'Diventa IO.File.Copy(Da, A)'Spesso anche il non conoscere tutte le possibilità'si trasforma in uno spreco di tempo e spazio

Page 247: Guida visual basic

Membr i pr otected : Pascal

Campi di enumer ator i : Pascal

Membr i pr ivati : Camel

Var iabili locali : Camel

Par ametr i : Camel

Nomi di contr olli : Ungher ese

Nomi costituiti da acr onimi: All se di 2 car atter i, altr imenti Pascal

Page 248: Guida visual basic

B1. IDE: Uno sguardo approfondito

Fino ad or a ci siamo ser viti dell'ambiente di sviluppo integr ato - in br eve, IDE - come di un mer o suppor to per lo

sviluppo di semplici applicazioni console: ne abbiamo fatto uso in quanto dotato di editor di testo "intelligente", un

comodo debugger e un compilator e integr ato nelle funzionalità. E non potr ete negar e che anche solo per questo non ne

avr emmo potuto far e a meno. Tuttavia, iniziando a sviluppar e applicazioni dotate di GUI (Gr aphical User Inter face) a

finestr e, l'IDE diventa uno str umento ancor a più impor tante. Le sue numer ose featur es ci per mettono di "disegnar e" le

finestr e del pr ogr amma, associar vi codice, navigar e tr a i sor genti, modificar e pr opr ietà con un click, or ganizzar e le

var ie par ti dell'applicativo, ecceter a ecceter a... Pr ima di intr odur vi all'ambito Windows For ms, far ò una r apida

panor amica dell'IDE, più appr ofondita di quella pr oposta all'inizio.

Primo impattoFate click su File > New Pr oject, scegliete "Windows For m Application" e, dopo aver scelto un qualsiasi nome per il

pr ogetto, confer mate la scelta. Vi appar ir à una scher mata più o meno simile a quella che segue:

Non vi allar mate se manca qualcosa, poiché i settaggi standar d dell'IDE sar anno molto pr obabilmente diver si da quelli

che uso io. L'inter faccia dell'ambiente di sviluppo, comunque, è completamente customizzabile, dato che è anch'essa

str uttur ata a finestr e: potete aggiunger e, r imuover e, fonder e o divider e finestr e semplicemente tr ascinandole con il

mouse. Ecco un esempio di come manipolar e le par ti dell'IDE in questo v ideo.

Menù princ ipaleIl menù pr incipale è costituito dalla pr ima bar r a di voci appena sotto il bor do super ior e della finestr a. Esso per mette

Page 249: Guida visual basic

di acceder e ad ogni oper azione possibile all'inter no dell'IDE. Per or a ci ser vir emo di questi:

File: il menù File per mette di cr ear e nuovi pr ogetti, salvar li, chiuder e quelli cor r enti e/o apr ir e file r ecenti;

Edit: contiene le var ie oper azioni effettuabili all'inter no dell'editor di testo: taglia, copia, incolla, undo, r edo,

cer ca, sostituisci, seleziona tutto ecceter a...

View: i sottomenù consentono di nasconder e o visualizzar e finestr e:

Code per mette di visualizzar e il codice sor gente associato a una finestr a; Designer por ta in pr imo piano l'ar ea

r iser vata alla cr eazione delle finestr e; Database ex plor er per mette di navigar e tr a le tabelle di un database

aper to nell'IDE; Solution Ex plor er consente di veder e le singole par ti del pr ogetto (vedi par agr afo successivo);

Er r or List visualizza la finestr a degli er r or i e Pr oper ties Window quella delle pr opr ietà. Il sottomenù di

Toolbar s per mette di aggiunger e o r imuover e nuove categor ie di pulsanti alla bar r a degli str umenti. Le altr e

voci per or a non ci inter essano;

Pr oject : espone alcune opzioni per il pr ogetto ed in par ticolar e consente di acceder e alle pr opr ietà di pr ogetto;

Build : for nisce diver se opzioni per la compilazione del pr ogetto e/o della soluzione.

Infine, cone Tools > Options, potr ete modificar e qualsiasi opzione r iguar dante l'ambiente di sviluppo, dal color e del

testo nell'editor , agli spazi usati per l'indentazione, all'autosalvataggio, ecceter a... (non vale la pena di analizzar e tutte

le voci disponibili, per chè sono ver amente tr oppe!).

Page 250: Guida visual basic

Solution ExplorerLa finestr a denominata "Solution Ex plor er " per mette di navigar e all'inter no della soluzione cor r ente e veder ne le var ie

par ti. Una soluzione è l'insieme di due o più pr ogetti, o, se si tr atta di un pr ogetto singolo, coincide con esso.

Come vedete ci sono cinque pulsanti sulla bar r a super ior e: il pr imo per mette di apr ir e una finestr a delle pr opr ietà per

l'elemento selezionato; il secondo visualizza tutti i files fisicamente esistenti nella car tella della soluzione, il ter zo

aggior na il solution ex plor er (nel caso di files aggiunti dall'ester no dell'IDE), mentr e quar to e quinto per mettono di

passar e dal codice al visual designer e vicever sa. Pr emendo il secondo pulsante, potr emo ossevar e che c'è molto più di

ciò che appar e a pr ima vista:

Page 251: Guida visual basic

La pr ima car tella contiene dei files che vanno a costr uir e uno dei namespace più utili in un'applicazione w indows, My,

di cui ci occuper emo nella sezione C. La seconda car tella mostr a l'elenco di tutti i r ifer imenti inclusi nel pr ogetto: il

numer o e il tipo di assembly impor tati var ia a seconda della ver sione dell'IDE e nella 2008 quelli elencati sono gli

elementi di default. Per i nostr i pr ogetti, solamente tr e sar anno di vitale impor tanza: System, System.Dr aw ing e

System.Windows.For ms. Potete r imuover e gli altr i senza pr eoccupazione (questo ci far à r ispar miar e anche un

megabyte di RAM). La car tella bin contiene a sua volta una o due car telle (Debug e Release) in cui tr over ete il

pr ogr amma compilato o in modalità debug o in modalità r elease. obj, invece, è dedicato ai file che contengono il codice

oggetto, una ser ie di bytes molto simili al codice compilato, ma ancor a in attesa di esser e assemblati in un unico

eseguibile.

Dopo tutti questi elementi, che per or a ci inter essano poco, tr oviamo la For m1, di default la pr ima finestr a

dell'applicazione. Possiamo notar e che esiste anche un altr o file, oltr e a For m1.vb (in cui è contenuto il codice che

scr iviamo noi): For m1.Designer .vb. Quest'ultimo sor gente è pr odotto automaticamente dal Designer e contiene

istr uzioni che ser vono a inizializzar e e costr uir e l'inter faccia gr afica; in esso sono anche dichiar ati tutti i contr olli che

abbiamo tr ascinato sulla for m. Ogni for m, quindi, è costituita da due file sor genti diver si, i quali contengono, tuttavia,

infor mazioni sulla stessa classe (For m1 in questo caso). Se r icor date, avevo detto che esiste una par ticolar e categor ia

di classi che possono esser e scr itte su file diver si: le classi par ziali. In gener e, infatti, le for ms sono classi par ziali, in

cui il codice "gr afico" viene tenuto nascosto e pr odotto dall'IDE e il codice di utilità viene scr itto dal pr ogr ammator e.

Finestra delle proprietàContiene un elenco di tutte le pr opr ietà dell'elemento selezionato nel designer e per mette di modificar le dir ettamente

dall'ambiente di sviluppo. Il box sottostante contiene anche una br eve descr izione della pr opr ietà selezionata.

Page 252: Guida visual basic

Pr emendo il pulsante con l'icona del fulmine in cima alla finestr a, si apr ir à la finestr a degli Eventi, che contiene,

appunto, una lista di tutti gli eventi che il contr ollo possiede (vedi pr ossimo capitolo).

Page 253: Guida visual basic

B2. Gli Eventi

Cosa sonoOr a che stiamo per entr ar e nel mondo della pr ogr ammazione visuale, è necessar io allontanar si da quello ster eotipo di

applicazione che ho usato fin dall'inizio della guida. In questo nuovo contesto, "non esiste" una Sub Main (o, per meglio

dir e, esiste ma possiede una semplice funzione di inizializzazione): il codice da eseguir e, quindi, non viene posto in un

singolo blocco ed eseguito dall'inizio alla fine seguendo un flusso ben definito. Piuttosto, esiste un oggetto standar d, la

For m - nome tecnico della finestr a - che viene cr eato all'avvio dell'applicazione e che l'utente può veder e e manipolar e a

suo piacimento. L'appr occio cambia: il pr ogr ammator e non vincola il flusso di esecuzione, ma dice semplicemente al

pr ogr amma "come compor tar si" in r eazione all'input dell'utente. Ad esempio, viene pr emuto un cer to pulsante: bene, al

click esegui questo codice; viene inser ito un testo in una casella di testo: quando l'utente digita un car atter e, esegui

quest'altr o codice, e così via... Il codice viene scr itto, quindi, per eventi. Volendo dar e una definizione teor ico-

concettuale di evento, potr emmo dir e che è un qualsiasi atto che modifica lo stato attuale di un oggetto. Ho di

pr oposito detto "oggetto", poiché le For ms non sono le uniche entità a posseder e eventi. Passando ad un ambito più

for male e r igor oso, infatti, un evento non è altr o che una speciale var iabile di tipo delegate (multicast). Essendo di tipo

delegate, tale var iabile può contener e r ifer imenti a uno o più metodi, i quali vengono comunemente chiamati g estor i

d'evento (o event's handler ). La pr ogr ammazione visuale, in sostanza, r ichiede di scr iver e tanti gestor i d'evento

quanti sono gli eventi che vogliamo gestir e e, quindi, tanti quanti possono esser e le azioni che il nostr o pr ogr amma

consente all'utente di eseguir e. Mediante queste definizioni, delineamo il compor tamento di tutta l'applicazione.

Sintassi e invocazione degli eventiLe facilitazioni che l'IDE mette a disposizione per la scr ittur a dei gestor i d'evento por tano spesso i pr ogr ammator i

novelli a non saper e cosa siano e come funzionino r ealmente gli eventi, anche a causa di una consider evole pr esenza di

tutor ial del tipo HOW-TO che spiegano in due o tr e passaggi come costr uir e "il tuo pr imo pr ogr amma". Inutile dir e che

queste scor ciatie fanno più male che bene. Per questo motivo ho deciso di intr odur r e l'ar gomento quanto pr ima, per

metter vi subito al cor r ente di come stanno le cose.

Iniziamo con l'intr odur r e la sintassi con cui si dichiar a un evento:

Dove [Nome] è il nome dell'evento e [Tipo] il suo tipo. Data la natur a di ciò che staimo definendo, il tipo sar à sempr e un

tipo delegate. Possiamo sceglier e di utilizzar e un delegate già definito nelle libr er ie standar d del Fr amewor k - come il

classico EventHandler - oppur e decider e di scr iver ne uno noi al momento. Scegliendo il secondo caso, tuttavia, si devono

r ispettar e delle convenzioni:

Il nome del delegate deve ter minar e con la par ola "Handler ";

Il delegate deve espor r e solo due par ametr i;

Il pr imo par ametr o è solitamente chiamato "sender " ed è comunemente di tipo Object. Questa convenzione è

abbastanza r estr ittiva e non è necessar io seguir la sempr e;

Il secondo par ametr o è solitamente chiamato "e" ed il suo tipo è una classe che er edita da System.EventAr gs. Allo

stesso modo, possiamo definir e un nuovo tipo der ivato da tale classe per il nuovo delegate, ma il nome di questo

tipo deve ter minar e con "EventAr gs".

Come avr ete notato sono un po' fissato sulle convenzioni. Ser vono a r ender e il codice più chiar o e "standar d" (quando

non ci sono r egole da seguir e, ognuno fa come meglio cr ede: vedi, ad esempio, i compilator i C). Ad ogni modo, sender

r appr esenta l'oggetto che ha gener ato l'evento, mentr e e contiene tutte le infor mazioni r elative alle cir costanze in cui

1. Event [Nome] As [Tipo]

Page 254: Guida visual basic

questo evento si è ver ificato. Se e è di tipo EventAr gs, non contiene alcun membr o: il fatto che l'evento sia stato

gener ato è di per sé significativo. Ad esempio, per un ipotetico evento Click non avr emmo bisogno di conoscer e

nessun'altr a infor mazione: ci basta saper e che è stato fatto click col mouse. Invece, per l'evento KeyDown (pr essione di

un tasto sulla tastier a) sar ebbe inter essante saper e quale tasto è stato pr emuto, il codice associato ad esso ed

eventualmente il car atter e. Ma or a passiamo a un piccolo esempio sul pr imo caso, mantenendoci ancor a per qualche

r iga in una Console Application:

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.

Module Module1

'Questa classe rappresenta una collezione generica di'elementi che può essere ordinata con l'algoritmo'Bubble Sort già analizzatoPublic Class BubbleCollection(Of T As IComparable)

'Eredita tutti i membri pubblici e protected della classe'a tipizzazione forte List(Of T), il che consente di'disporre di tutti i metodi delle liste scrivendo'solo una linea di codiceInherits List(Of T)

'Questo campo indica il numero di millisecondi impiegati'ad ordinare tutta la collezionePrivate _TimeElapsed As Single = 0

Public ReadOnly Property TimeElapsed() As Single

GetReturn _TimeElapsed

End GetEnd Property

'Ecco gli eventi:'Il primo viene lanciato prima che inizi la procedura di'ordinamento, e per tale motivo è di tipo'CancelEventHandler. Questo delegate espone come'secondo parametro della signature una variabile "e"'al cui intero è disponibile una proprietà'Cancel che indica se cancellare oppure no l'operazione.'Se si volesse cancellare l'operazione sarebbe possibile'farlo nell'evento BeforeSorting.'In genere, si usa il tipo CancelEventHandler o un suo'derivato ogni volta che bisogna gestire un evento'che inizia un'operazione annullabile.Event BeforeSorting As System.ComponentModel.CancelEventHandler'Il secondo viene lanciato dopo aver terminato la procedura'di ordinamento e serve solo a notificare un'azione'avvenuta. Il tipo è un semplicissimo EventHandlerEvent AfterSorting As EventHandler

'Scambia l'elemento alla posizione Index con il suo'successivoPrivate Sub SwapInList(ByVal Index As Int32)

Dim Temp As T = Me(Index + 1)Me.RemoveAt(Index + 1)Me.Insert(Index, Temp)

End Sub

'In List(Of T) è già presente un metodo Sort,'perciò bisogna oscurarlo con Shadows (in quanto non'è sovrascrivibile con il polimorfismo)Public Shadows Sub Sort()

Dim Occurrences As Int32Dim J As Int32Dim Time As New Stopwatch'Attenzione! non bisogna confondere EventHandlers con'EventArgs: il primo è un tipo delegate e costituisce'il tipo dell'evento; il secondo è un normale tipo'reference e rappresenta tutti gli argomenti opzionali'inerenti alle operazioni svolteDim e As New System.ComponentModel.CancelEventArgs

Page 255: Guida visual basic

Questo codice mostr a anche l'uso dell'istr uzione RaiseEvent, usata per gener ar e un evento. Essa non fa altr o che

scor r er e tutta l'invocation list di tale evento ed invocar e tutti i gestor i d'evento ivi contenuti. Le invocazioni si

svolgono, di nor ma, una dopo l'altr a (sono sincr one).

Or a che abbiamo ter minato la classe, tuttavia, bisogner ebbe anche poter la usar e, ma mancano ancor a due impor tanti

infor mazioni per esser e in gr ado di gestir la cor r ettamente. Pr ima di tutto, la var iabile che user emo per contener e

l'unica istanza di BubbleCollection deve esser e dichiar ata in modo diver so dal solito. Se nor malmente potr emmo

scr iver e:

in questo caso, non basta. Per poter usar e gli eventi di un oggetto, è necessar io comunicar lo esplicitamente al

compilator e usando la keywor d WithEvents, da antepor r e (o sostituir e) a Dim:

Infine, dobbiamo associar e dei gestor i d'evento ai due eventi che Bubble espone (NB: non è obbligator io associar e

handler a tutti gli eventi di un oggetto, ma basta far lo per quelli che ci inter essano). Per associar e un metodo a un

evento e far lo diventar e gestor e di quell'evento, si usa la clausola Handles, molto simile come sintassi alla clausola

Implements analizzata nei capitoli sulle inter facce.

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.

'Viene generato l'evento. RaiseEvent si occupa di'richiamare tutti i gestori d'evento memorizzati'nell'evento BeforeSorting (che, ricordo, è un'delegate multicast). A tutti i gestori d'evento'vengono passati i parametri Me ed e. Al termine'di questa operazione, se un gestore d'evento ha'modificato una qualsiasi proprietà di e (e volendo,'anche di quest'oggetto), possiamo sfruttare tale'conoscenza per agire in modi diversi.RaiseEvent BeforeSorting(Me, e)'In questo caso, se e.Cancel = True si'cancella l'operazioneIf e.Cancel Then

Exit SubEnd If

Time.Start()J = 0Do

Occurrences = 0For I As Int32 = 0 To Me.Count - 1 - J

If I = Me.Count - 1 ThenContinue For

End IfIf Me(I).CompareTo(Me(I + 1)) = 1 Then

SwapInList(I)Occurrences += 1

End IfNextJ += 1

Loop Until Occurrences = 0Time.Stop()_TimeElapsed = Time.ElapsedMilliseconds

'Qui genera semplicemente l'eventoRaiseEvent AfterSorting(Me, EventArgs.Empty)

End Sub

End Class '...

End Module

1. Dim Bubble As New BubbleCollection(Of Int32)

1. WithEvents Bubble As New BubbleCollection(Of Int32)

1.2.3.

Sub [Nome Gestore](ByVal sender As Object, ByVal e As [Tipo]) Handles [Oggetto].[Evento]'...

Page 256: Guida visual basic

I gestor i d'evento sono sempr e pr ocedur e e mai funzioni: questo è ovvio, poiché eseguono solo istr uzioni e nessuno

r ichiede alcun valor e in r itor no da lor o. Ecco l'esempio completo:

End Sub

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.

Module Module1

Public Class BubbleCollection(Of T As IComparable)Inherits List(Of T)

Private _TimeElapsed As Single = 0

Public ReadOnly Property TimeElapsed() As Single

GetReturn _TimeElapsed

End GetEnd Property

Event BeforeSorting As System.ComponentModel.CancelEventHandlerEvent AfterSorting As EventHandler

Private Sub SwapInList(ByVal Index As Int32)

Dim Temp As T = Me(Index + 1)Me.RemoveAt(Index + 1)Me.Insert(Index, Temp)

End Sub

Public Shadows Sub Sort()Dim Occurrences As Int32Dim J As Int32Dim Time As New StopwatchDim e As New System.ComponentModel.CancelEventArgs RaiseEvent BeforeSorting(Me, e)If e.Cancel Then

Exit SubEnd If

Time.Start()J = 0Do

Occurrences = 0For I As Int32 = 0 To Me.Count - 1 - J

If I = Me.Count - 1 ThenContinue For

End IfIf Me(I).CompareTo(Me(I + 1)) = 1 Then

SwapInList(I)Occurrences += 1

End IfNextJ += 1

Loop Until Occurrences = 0Time.Stop()_TimeElapsed = Time.ElapsedMilliseconds

'Qui genera semplicemente l'eventoRaiseEvent AfterSorting(Me, EventArgs.Empty)

End Sub

End Class

'Bubble è WithEvents poiché ne utilizzeremo'gli eventiWithEvents Bubble As New BubbleCollection(Of Int32)Sub Main()

Dim I As Int32

Console.WriteLine("Inserire degli interi (0 per terminare):")I = Console.ReadLineDo While I <> 0

Page 257: Guida visual basic

Anche per i nomi dei gestor i d'evento c'è questa convenzione: "[Oggetto che gener a l'evento]_[Evento gestito]".

Ciò che abbiamo appena visto consente di eseguir e una sor ta di ear ly binding, ossia legar e l'evento a un gestor e

dur ante la scr ittur a del codice. C'è, par imenti, una tecnica par allela più simile al late binding, che consente di associar e

un gestor e ad un evento dinamicamente. La sintassi è:

Il codice sopr a potr ebbe esser e stato modificato come segue:

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.

Bubble.Add(I)I = Console.ReadLine

Loop

'Il corpo di Main termina con l'esecuzione di Sort, ma'il programma non finisce qui, poiché Sort'scatena due eventi, BeforeSorting e AfterSorting.'Questi comportano l'esecuzione prima del metodo'Bubble_BeforeSorting e poi di Bubble_AfterSorting.'Vedrete bene il risultato eseguendo il programmaBubble.Sort()

End Sub

Private Sub Bubble_BeforeSorting(ByVal sender As Object, ByVal e AsSystem.ComponentModel.CancelEventArgs) Handles Bubble.BeforeSortingIf Bubble.Count = 0 Then

e.Cancel = TrueConsole.WriteLine("Lista vuota!")Console.ReadKey()

End IfEnd Sub

Private Sub Bubble_AfterSorting(ByVal sender As Object, ByVal e As EventArgs) Handles

Bubble.AfterSortingConsole.WriteLine("Lista ordinata:")'Scrive a schermo tutti gli elementi di Bubble'mediante un delegate generico.Bubble.ForEach(AddressOf Console.WriteLine)Console.WriteLine("Tempo impiegato: {0} ms", Bubble.TimeElapsed)Console.ReadKey()

End Sub 'Handles significa "gestisce". In questo come in molti altri'casi, il codice è molto simile al linguaggio.'Ad esempio, traducendo in italiano si avrebbe:' Bubble_AfterSorting gestisce Bubble.AfterSorting'Il VB è molto chiaro nelle keywords

End Module

1.2.3.4.

'Add Handler = Aggiungi Gestore; molto intuitiva come keywordAddHandler [Oggetto].[Evento], AddressOf [Gestore]'E per rimuovere il gestore dall'invocation list:RemoveHandler [Oggetto].[Evento], AddressOf [Gestore]

01.02.03.04.05.06.07.08.09.10.11.12.13.14.15.16.17.18.19.20.

Module Module1

'... WithEvents Bubble As New BubbleCollection(Of Int32)Sub Main()

Dim I As Int32

AddHandler Bubble.BeforeSorting, AddressOf Bubble_BeforeSortingAddHandler Bubble.AfterSorting, AddressOf Bubble_AfterSorting Console.WriteLine("Inserire degli interi (0 per terminare):")I = Console.ReadLineDo While I <> 0

Bubble.Add(I)I = Console.ReadLine

Loop

'Il corpo di Main termina con l'esecuzione di Sort, ma

Page 258: Guida visual basic

Ovviamente se usate questo metodo, non potr ete usar e allo stesso tempo anche Handles, o aggiunger este due volte lo

stesso gestor e!

21.22.23.24.25.26.27.28.

29.30.31.32.33.34.35.36.37.38.39.40.41.42.

'il programma non finisce qui, poiché Sort'scatena due eventi, BeforeSorting e AfterSorting.'Questi comportano l'esecuzione prima del metodo'Bubble_BeforeSorting e poi di Bubble_AfterSorting.'Vedrete bene il risultato eseguendo il programmaBubble.Sort()

End Sub

Private Sub Bubble_BeforeSorting(ByVal sender As Object, ByVal e AsSystem.ComponentModel.CancelEventArgs)If Bubble.Count = 0 Then

e.Cancel = TrueConsole.WriteLine("Lista vuota!")Console.ReadKey()

End IfEnd Sub

Private Sub Bubble_AfterSorting(ByVal sender As Object, ByVal e As EventArgs)

Console.WriteLine("Lista ordinata:")Bubble.ForEach(AddressOf Console.WriteLine)Console.WriteLine("Tempo impiegato: {0} ms", Bubble.TimeElapsed)Console.ReadKey()

End SubEnd Module

Page 259: Guida visual basic

B3. I Controlli

La base delle applicazioni Windows FormSe gli eventi sono il pr incipale meccanismo con cui scr iver e un'applicazione visuale, i contr olli sono i pr incipali oggetti

da usar e. For malmente, un contr ollo non è altr o che una classe der ivata da System.Windows.For ms.Contr ol. In pr atica,

esso r appr esenta un qualsiasi componente dell'inter faccia gr afica di un pr ogr amma: pulsanti, menù, caselle di testo,

liste var ie, e anche le finestr e, sono tutti contr olli. Per questa r agione, se volete cr ear e una GUI (Gr aphical User

Inter face) per il vostr o applicativo, dovr ete necessar iamente conoscer e quali contr olli le libr er ie standar d vi mettono a

disposizione (e questo avviene in tutti i linguaggi che suppor tino libr er ie visuali). Conoscer e un contr ollo significa

pr incipalmente saper e quali pr opr ietà, metodi ed eventi esso possiede e come usar li.

Una volta aper to il pr ogetto Windows For m, tr over ete che l'IDE ha cr eato per noi la pr ima For m, ossia la pr ima

finestr a dell'applicazione. Essa sar à la pr ima ad esser e aper ta quando il pr ogr amma ver r à fatto cor r er e e, per i

pr ossimi capitoli, sar à anche l'unica che user emo. L'esecuzione ter mina automaticamente quando tale finestr a viene

chiusa. Come avr ete visto, inoltr e, tr a le mer avigliose funzionalità del nostr o ambiente di sviluppo c'è anche un'ar ea

gr afica - detta Designer - che ci per mette di veder e un'antepr ima della For m e di modificar la o aggiunger ci nuovi

elementi. Per modificar e l'aspetto o il compor tamento della For m, è sufficiente modificar e le r elative pr opr ietà nella

finestr a delle pr opr ietà

Mentr e per aggiunger e elementi alla super ficie liber a della finestr a, è sufficiente tr ascinar e i contr olli desider ati dalla

toolbox nel designer . La toolbox è di solito nascosta e la si può mostr ar e soffer mandosi un secondo sulla linguetta

"Toolbox " che spunta fuor i dal lato sinistr o della scher mata dell'IDE:

La c lasse ControlLa classe Contr ol è la classe base di tutti i contr olli (ma non è astr atta). Essa espone un buon numer o di metodi e

pr opr ietà che vengono er editati da tutti i suoi der ivati. Tr a questi membr i di default, sono da r icor dar e:

AllowDr op : specifica se il contr ollo suppor ta il Dr ag and Dr op (per ulter ior i infor mazioni su questa tecnica,

veder e capitolo r elativo);

Anchor : pr opr ietà enumer ata codificata a bit (vedi capitolo sugli enumer ator i) che per mette di impostar e a

quali lati del for m i cor r ispondenti lati del contr ollo r estano "ancor ati" dur ante il pr ocesso di

r idimensionamento. Dir e che un un contr ollo è ancor ato a destr a, per esempio, significa che il suo lato destr o

manter r à sempr e la stessa distanza dal lato destr o del suo contenitor e (il contenitor e per eccellenza è la For m

stessa). Seguendo questa logica, ancor ando un contr ollo a tutti i lati, si otter r à come r isultato che quel contr ollo

si ingr andir à quanto il suo contenitor e;

BackColor : color e di sfondo;

Backgr oundImage : immagine di sfondo;

Contex tMenuStr ip : il menù contestuale associato al contr ollo;

Contr ols : l'elenco dei contr olli contenuti all'inter no del contr ollo cor r ente. Un contr ollo può, infatti, far e da

"contenitor e" per altr i contr olli. La finestr a, la For m, è un classico esempio di contenitor e, ma nel cor so delle

lezioni vedr emo altr i contr olli specializzati e molto ver satili pensati apposta per questo compito;

DoDr agDr op() : inizia un'oper azione di Dr ag and Dr op da questo contr ollo;

Page 260: Guida visual basic

Enabled : deter mina se il contr ollo è abilitato. Quando disabilitato, esso è di color e gr igio scur o e non è possibile

alcuna inter azone tr a l'utente e il contr ollo stesso;

Focus() : attiva il contr ollo;

Focused : deter mina se il contr ollo è attivo;

Font : car atter e con cui il testo viene scr itto sul contr ollo (se è pr esente del testo);

For eColor : color e del testo;

Height : altezza, in pix el, del contr ollo;

Location : posizione del contr ollo r ispetto al suo contenitor e (r estituisce un valor e di tipo Point);

MousePosition : posizione del mouse r ispetto al contr ollo (anche questa r estituisce un Point);

Name : il nome del contr ollo (molto spesso coincide col nome della var iabile che r appr esenta quel contr ollo nel

for m);

Size : dimensione del contr ollo (r estituisce un valor e di tipo Size);

TabIndex : for se non tutti sanno che con il pulsante Tab (tabulazione) è possibile scor r er e or dinatamente i

contr olli. Ad esempio, in una finestr a con due caselle di testo, è possibile spostar si dalla pr ima alla seconda

pr emendo Tab. Questo accade anche nei moduli Web. La pr opr ietà TabIndex deter mina l'indice associato al

contr ollo in questo meccanismo. Così, se una casella di testo ha TabIndex = 0 e un menù a discesa TabIndex = 1,

una volta selezionata la casella di testo sar à possibile spostar si sul menù a discesa pr emendo Tab. L'iter azione

può continuar e indefinitamente per un qualsiasi numer o di contr olli e, una volta r aggiunta la fine, r einizia

daccapo;

Tag : qualsiasi oggetto associato al contr ollo. Tag è di tipo Object ed è molto utile per immagazzinar e

infor mazioni di var io gener e che non è possibile por r e in nessun'altr a pr opr ietà;

Tex t : testo visualizzato sul contr ollo (se il contr ollo pr evede del testo);

Visible : deter mina se il contr ollo è visibile;

Width : lar ghezza, in pix el, del contr ollo.

La c lasse FormFor m è la classe che r appr esenta una finestr a. Ogni finestr a che noi usiamo nelle applicazioni è r appr esentata da una

classe der ivata da For m. Oltr e ai membr i di Contr ol, essa ne espone molti altr i. Ecco una lista molto sintetica di alcuni

membr i che potr ebber o inter essar vi ad or a:

AllowTr anspar ency : deter mina se il for m può esser e r eso tr aspar ente (vedi pr opr ietà Opacity);

AutoScr oll : deter mina se sulla finestr a venga automaticamente mostr ata una bar r a di scor r imento quando i

contr olli che essa contiene spor gono oltr e il suo bor do visibile;

Close() : chiude la for m. Se si tr atta della pr ima for m, l'applicazione ter mina (è possibile modificar e questo

compor tamento, come vedr emo in seguito);

For mBor der Style : imposta il tipo di bor do della finestr a (nessuno, singolo, doppio: singolo equivale a non poter

r idimensionar e la finestr a);

HelpButton : deter mina se il pulsante help (?) è visualizzato nella bar r a del titolo, accanto agli altr i;

Hide() : nasconde la for m, ossia la r ende invisibile, ma non la chiude;

Icon : indica l'icona mostr ata nell'angolo super ior e sinistr o della finestr a, vicino al titolo. Questà pr opr ietà è di

tipo System.Dr aw ing.Icon;

Max imizeBox : deter mina se l'icona che per mette di ingr andir e la finestr a a scher mo inter o è visualizzata;

Max imumSize : massima dimensione consentita;

MinimizeBox : deter mina se il pulsante che per mette di r idur r e la finestr a a icona è visualizzato;

MinimumSize : minima dimensione consentita;

Opacity : imposta l'opacità della finestr a: 0 per r ender la invisibile, 1 per r ender la totalmente opaca (nor male);

Page 261: Guida visual basic

Show() : visualizza la for m nel caso sia nascosta o comunque non attualmente visibile sullo scher mo;

ShowDialog() : come Show(), ma la finestr a viene mostr ata in modalità Dialog. In questo modo, l'utente può

inter agir e solo con essa e con nessun'altr a for m del pr ogr amma fino a che questa non sia stata chiusa,

confer mando una scelta o annullando l'oper azione. Restituisce come r isultato un valor e enumer ato che indica che

azione l'utente abbia compiuto;

Show Icon : deter mina se visualizzar e l'icona nella bar r a del titolo;

Show InTaskBar : deter mina se visualizzar e la finestr a nella bar r a delle applicazioni;

TopMost : deter mina se la finestr a è sempr e in pr imo piano;

WindowState : indica lo stato della finestr a (nor male, massimizzata, r idotta a icona).

Questi sono solo alcuni dei molteplici membr i che la classe espone. Ho elencato sopr attutto quelli che vi per metter anno

di modificar e l'aspetto ed il compor tamento della for m, in quanto, allo stato attuale delle cose, non siete in gr ado di

gestir e e compr ender e il r esto delle funzionalità. Nel cor so di questa sezione, comunque, intr odur r ò via via nuovi

dettagli r iguar do questa classe e spiegher ò come usar li. Ma or a passiamo alla scr ittur a del pr imo pr ogr amma...

Il controllo ButtonPer il pr ossimo esempio, dovr emo usar e un nuovo contr ollo, che possiamo indicar e senza r emor e come il pr incipale e

più usato meccanismo di inter azione: il pulsante. Esso viene r appr esentato dal contr ollo Button. Dopo aver aper to un

nuovo pr ogetto Windows For m vuoto, tr ascinate un nuovo pulsante dalla toolbox sulla super ficie della finestr a e

posizionatelo dove più vi aggr ada. Il nome di questo contr ollo sar à btnHello, ad esempio.

Or a che abbiamo disposto l'unico elemento della GUI, bisogna cr ear e un gestor e d'evento che si occupi di eseguir e del

codice quando l'utente clicca sul pulsante. Per far e ciò, possiamo scr iver e il codice a mano o semplicemente far e doppio

click sul pulsante nel Designer e l'IDE scr iver à automaticamente il codice associato. Questo succede per chè ogni contr ollo

ha un "evento di default", ossia quell'evento che viene usato più spesso: il doppio click su un elemento dell'inter faccia

gr afica ci per mette di delegar e all'ambiente di sviluppo la stesur a del pr ototipo per la Sub che dovr emo cr ear e per tale

evento. Nel caso di Button, l'evento più usato è Click. Il codice automaticamente gener ato sar à:

Or a, all'inter no del cor po della pr ocedur a possiamo por r e ciò che vogliamo. In questo esempio, visualizzer emo a

scher mo il messaggio "Hello, Wor ld!", ma in modo diver so dalle applicazioni console. In questo ambiente, si è soliti usar e

una par ticolar e classe che ser ve per visualizzar e finestr e di avver timento. Tale classe è MessageBox e ha un solo

metodo statico, Show :

Show accetta come minimo un par ametr o, ossia il messaggio da visualizzar e. Tutti gli altr i par ametr i sono "opzionali"

(non nel ver o senso del ter mine, ma esisteono 18 ver sioni diver se dello stesso metodo Show modificate tr amite

over loading). In questo caso, il secondo indica il titolo della finestr a di avviso, il ter zo i pulsanti visualizzati (un solo

pulsante "OK") ed il quar to l'icona mostr ata in fianco al messaggio (una "I" bianca su sfondo blu, che significa

"Infor mazione").

1.

2.3.

Private Sub btnHello_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) HandlesbtnHello.Click

End Sub

1.2.3.

4.

5.6.7.

Public Class Form1

Private Sub btnHello_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)Handles btnHello.ClickMessageBox.Show("Hello, World!", "Esempio", MessageBoxButtons.OK,

MessageBoxIcon.Information)End Sub

End Class

Page 262: Guida visual basic
Page 263: Guida visual basic

B4. Label e TextBox

In questo capitolo mi occuper ò di altr i due comunissimi contr olli: label (etichetta) e tex tbox (casella di testo). L'esempio

della lezione consiste nello scr iver e un pr ogr amma che, dato il r aggio, calcola l'ar ea del cer chio.

LabelIl contr ollo Label ser ve per visualizzar e un qualsiasi messaggio o testo sulla super ficie della w indows for m. Per questo

pr ogetto, occor r e aggiunger e una label all'inter no del for m designer e impostar e il testo su "Intr odur r e il r aggio di un

cer chio:". Poichè questo tipo di contr ollo è utilizzatissimo, è inutile assegnar e un nome significativo a ogni sua istanza,

a meno che non la si debba modificar e dur ante l'esecuzione del pr ogr amma. Solo due pr opr ietà mer itano di esser e

menzionate:

AutoSize : se attiva, r idimensiona la label per ader ir e alla lunghezza del testo. Il r idimensionamento avviene

solo in lunghezza, a meno che il testo non contenga esplicitamente un car atter e "a capo". Se disattivata, invece,

il testo della label ver r à automaticamente spostato per r ientr ar e nei limiti imposti dalla sua dimensione;

Tex tAlign : per mette di allinear e il testo in 9 modi diver si, combinando i tr e valor i di allineamente ver ticale

(Top, Center , Bottom) con i tr e valor i di allineamento or izzontale (Left, Center , Right). L'allineamento non è

effettivo se AutoSize = Tr ue.

Dopo aver modificato le pr opr ietà della for m come nella lezione scor sa, l'inter faccia si pr esenter à pr essapoco così:

TextBoxCostituisce il contr ollo di input per eccellenza, il più usato in tutte quelle situazioni che r ichiedono all'utente di

immetter e dati. Le pr opr ietà r ilevanti sono:

Max Length : massima lunghezza del testo, in car atter i;

AutoCompleteMode : modalità di autocompletamento. Fr a i pr egi della Tex tBox vi è la possibilità di "sugger ir e"

all'utente cosa digitar e nel caso le pr ime letter e pr emute cor r ispondano all'inizio di una delle par ole che il

pr ogr amma ha già elabor ato. Per far e un esempio pr atico, si compor ta allo stesso modo del sistema di

composizione T9 dei cellular i, in cui il r esto della par ola viene "sugger ita" pr ima del suo completamento.

L'enumer ator e può assumer e quattr o valor i: None (assente), Suggest (viene sugger ita la par ola facendo

appar ir e sotto la tex tbox un menù a discesa con tutte le possibili var ianti), Append (viene sugger ita la par ola

accodando alle letter e digitate il pezzo mancante evidenziato il blu), AppendSuggest (un'unione di entr ambe le

pr ecedenti opzioni);

AutoCompleteSour ce : fonte dalla quale pr elevar e le par ole dell'autocompletamento. I valor i pr edefiniti indicano

Page 264: Guida visual basic

r isor se di sistema, quali la cr onologia (Histor yList, nel caso, ad esempio, la tex tbox funga da contenitor e di

indir izzi inter net), le car telle (FileSystemDir ector ies, ad esempio per facilitar e l'immissione di un per cor so da

tastier a), i file (FileSystem), i files o i pr ogr ammi aper ti di r ecente (RecentlyUsedList), oppur e tutti questi

insieme (AllSystemResour ces). Se impostato su CustomSour ce, sar à la pr opr ietà AutoCompleteCustomSour ce a

deter minar e la fonte da cui attinger e infor mazioni;

Char acter Casing : indica il casing delle letter e. Ci sono tr e valor i possibili: None (tutte le letter e vengono

lasciate così come sono), Upper (tutte le letter e sono conver tite in maiuscole) o Lower (tutte in minuscole);

Lines : r estituisce un ar r ay di str inghe r appr esentanti tutte le r ighe di testo della tex tbox , nel caso di una

tex tbox Multiline;

Multiline : se impostata su Tr ue, la tex tbox sar à r idimensionabile e l'utente potr à inser ir e un testo che

compr ende più r ighe. Quando la pr opr ietà è False, il car atter e "a capo" viene r espinto;

Passwor dChar : un valor e di tipo Char che deter mina il car atter e da visualizzar e al posto delle letter e qualor a

la tex tbox debba contener e una passwor d. In questo modo si evita che occhi indiscr eti possano intr aveder e le

str inghe digitate. Impostando questa pr opr ietà, si mascher a automaticamente il testo;

ReadOnly : deter mina se l'utente può modificar e il testo della tex tbox ;

Scr ollBar s : pr opr ietà enumer ata che specifica se le bar r e di scor r imento devono esser e pr esenti.

L'enumer ator e accetta quattr o valor i: None (nessuna scr ollbar ), Ver tical (solo ver ticale), Hor izontal (solo

or izzontale), Both (entr ambe);

Or a aggiungiamo una tex tbox di nome tx tRadius, appena sotto la label.

Finire il programma di calcoloUltima cosa essenziale per concluder e il pr ogr amma è un pulsante che avvii il calcolo, altr imenti non si potr ebbe saper e

quando l'utente ha finito l'immissione e vuole conoscer e il r isultato. Dopo aver aggiunto il button btnAr ea, la finestr a

sar à simile a questa:

Doppio click sul pulsante per apr ir e l'editor di codice sull'evento Click di btnAr ea:

Pr ima di far cor r er e il pr ogr amma, bisogna r icor dar si che i numer i decimali immessi in input devono aver e la vir gola,

e non il punto.

Da notar e che abbiamo assegnato una str inga a un valor e single: come già detto, in VB.NET, le conver sioni implicite

vengono eseguite automaticamente quando sono possibili e Option Str ict è disattivata.

01.02.03.

04.05.06.07.

08.09.10.

Public Class Form1

Private Sub btnArea_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)Handles btnArea.ClickDim Radius As Single = txtRadius.TextDim Area As SingleArea = Radius ^ 2 * Math.PIMessageBox.Show("L'area del cerchio è " & Area & ".", Me.Text, MessageBoxButtons.OK,

MessageBoxIcon.Information)End Sub

End Class

Page 265: Guida visual basic

Tuttavia, se l'utente immettesse una par ola, il pr ogr amma andr ebbe in cr ash: vediamo quindi di r affinar e il codice così

da inter cettar e l'eccezione gener ata.

Ma non basta ancor a. I numer i negativi o nulli vengono comunque accetati, ma per definizione una lunghezza non può

aver e misur a non positiva, per ciò:

01.02.03.

04.05.06.07.08.

09.10.

11.12.13.14.

Public Class Form1

Private Sub btnArea_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)Handles btnArea.ClickTry

Dim Radius As Single = txtRadius.TextDim Area As SingleArea = Radius ^ 2 * Math.PIMessageBox.Show("L'area del cerchio è " & Area & ".", Me.Text,

MessageBoxButtons.OK, MessageBoxIcon.Information)Catch ICE As InvalidCastException

MessageBox.Show("Inserire un valore numerico valido!", Me.Text,MessageBoxButtons.OK, MessageBoxIcon.Error)

End TryEnd Sub

End Class

01.02.03.

04.05.06.07.08.09.10.11.12.13.

14.15.

16.17.

18.19.20.21.

Public Class Form1

Private Sub btnArea_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)Handles btnArea.ClickTry

Dim Radius As Single = txtRadius.Text

If Radius <= 0 ThenThrow New ArgumentException()

End If

Dim Area As SingleArea = Radius ^ 2 * Math.PIMessageBox.Show("L'area del cerchio è " & Area & ".", Me.Text,

MessageBoxButtons.OK, MessageBoxIcon.Information)Catch ICE As InvalidCastException

MessageBox.Show("Inserire un valore numerico valido!", Me.Text,MessageBoxButtons.OK, MessageBoxIcon.Error)

Catch AE As ArgumentExceptionMessageBox.Show("Il raggio non può essere negativo o nullo!", Me.Text,

MessageBoxButtons.OK, MessageBoxIcon.Exclamation)End Try

End Sub End Class

Page 266: Guida visual basic

B5. Input e Output su file

Gli StreamLe oper azioni di input e output, in .NET come in molti altr i linguaggi, hanno come tar get uno str eam, ossia un flusso di

dati. In .NET, tale flusso viene r appr esentato da una classe astr atta, System.IO.Str eam, che espone alcuni metodi per

acceder e e manipolar e i dati ivi contenuti. Dato che si tr atta di una classe astr atta, non possiamo utilizzar la

dir ettamente, poiché, appunto, r appr esenta un concetto astr atto non istanziabile. Come già spiegato nel capitolo

r elativo, classi del gener e r appr esentano un ar chetipo per diver se altr e classi der ivate. Infatti, un flusso di dati può

esser e tante cose, e pr ovenir e da molti posti diver si:

può tr attar si di un file, come vedr emo fr a poco; allor a la classe der ivata oppor tuna sar à FileStr eam;

può tr attar si di dati gr ezzi pr esenti in memor ia, ed avr emo, ad esempio, Memor yStr eam;

potr ebbe tr attar si, invece, di un flusso di dati pr oveniente dal ser ver a cui siamo collegati, e ci sar à allor a, un

Networ kStr eam;

e così via, per molti diver se casistiche...

Globalmente par lando, quindi, si può associar e uno str eam al flusso di dati pr oveniente da un qualsiasi dispositivo

vir tuale o fisico o da qualunque entità astr atta all'inter no della macchina: ad esempio è possibile aver e uno str eam

associato a una stampante, a uno scanner , allo scher mo, ad un file, alla memor ia tempor anea, a qualsiasi altr a cosa.

Per ognuno di questi casi, esister à un'oppor tuna classe der ivata di Str eam studiata per adempier e a quello specifico

compito.

In questo capitolo, vedr emo cinque classi del gener e, ognuna altamente specializzata: FileStr eam, Str eamReader ,

Str eamWr iter , Binar yReader e Binar yWr iter .

FileStreamQuesta classe offr e funzionalità gener iche per l'accesso a un file. Il suo costr uttor e più semplice accetta due par ametr i:

il pr imo è il per cor so del file a cui acceder e ed il secondo indica le modalità di aper tur a. Quest'ultimo par ametr o è di

tipo IO.FileMode, un enumer ator e che contiene questi campi:

Append : apr e il file e si posiziona alla fine (in questo modo, potr emo velocemente aggiungere dati senza

sovr ascr iver e quelli pr ecedentemente esistenti);

Cr eate : cr ea un nuovo file con il per cor so dato nel pr imo par ametr o; se il file esiste già, sar à sovr ascr itto;

Cr eateNew : cr ea un nuovo file con il per cor so dato nel pr imo par ametr o del costr uttor e; se il file esiste già,

ver r à sollevata un'eccezione;

Open : apr e il file e si posiziona all'inizio;

OpenOr Cr eate : apr e il file, se esiste, e si posiziona all'inizio; se non esiste, cr ea il file;

Tr uncate : apr e il file, cancella tutto il suo contenuto, e si posiziona all'inizio.

Un ter zo par ametr o opzionale può specificar e i per messi (solo lettur a, solo scr ittur a o entr ambe), ma per or a non lo

user emo.

Pr ima di veder e un esempio del suo utilizzo, è necessar io dir e che questa classe consider a i file aper ti come file binar i.

Si par la di file binar io quando esiste una cor r ispondenza biunivoca tr a i bytes esistenti in esso e i dati letti. Questa

condizione non si ver ifica con i file di testo, in cui, ad esempio, il singolo car atter e "a capo" cor r isponde a due bytes: in

questo caso non si può par lar e di file binar i, ma è comunque possibile legger li come tali, e ciò che si otter r à sar à solo

Page 267: Guida visual basic

una sequenza di numer i. Ma vedr emo meglio queste differ enze nel par agr afo successivo.

Or a, ammettendo di aver e aper to il file, sia che si voglia legger e, sia che si voglia scr iver e, sar à necessar io adottar e

un buffer, ossia un ar r ay di bytes che conter r à tempor aneamente i dati letti o scr itti. Tutti i metodi di

lettur a/scr ittur a binar i del Fr amewor k, infatti, r ichiedono come minimo tr e par ametr i:

buffer : un ar r ay di bytes in cui por r e i dati letti o da cui pr elevar e i dati da scr iver e;

index : indice del buffer da cui iniziar e l'oper azione;

length : numer o di bytes da pr ocessar e.

Seguendo questa logica, avr emo la funzione Read:

Read(buffer, index, length)

che legge length bytes dallo str eam aper to e li pone in buffer (a par tir e da index ); e, par imenti, la funzione Wr ite:

Write(buffer, index, length)

che scr ive sullo str eam length bytes pr elevati dall'ar r ay buffer (a par tir e da index ). Ecco un esempio:

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.32.33.34.35.36.37.38.39.40.41.42.43.44.45.46.47.

Module Module1

Sub Main()Dim File As IO.FileStreamDim FileName As String

Console.WriteLine("Inserire il percorso di un file:")FileName = Console.ReadLine

'IO.File.Exists(path) restituisce True se il percorso'path indica un file esistente e False in caso contrarioIf Not IO.File.Exists(FileName) Then

Console.WriteLine("Questo file non esiste!")Console.ReadKey()Exit Sub

End If

Console.Clear()

'Apre il file specificato, posizionandosi all'inizioFile = New IO.FileStream(FileName, IO.FileMode.Open)

Dim Buffer() As ByteDim Number, ReadBytes As Int32

'Chiede all'utente quanti bytes vuole leggere, e'memorizza tale numero in NumberConsole.WriteLine("Quanti bytes leggere?")Number = CType(Console.ReadLine, Int32)'Se Number è un numero positivo e non siamo ancora'arrivati alla fine del file, allora legge quei bytes.'La proprietà Position restituisce la posizione'corrente all'interno del file (a iniziare da 0), mentre'File.Length restituisce la lunghezza del file, in bytes.Do While (Number > 0) And (File.Position < File.Length - 1)

'Ridimensiona il bufferReDim Buffer(Number - 1)'Legge Number bytes e li mette in Buffer, a partire'dall'inizio dell'array. Read è una funzione, e'restituisce come risultato il numero di bytes'effettivamente letti dallo stream.ReadBytes = File.Read(Buffer, 0, Number)

Console.WriteLine("Bytes letti:")For I As Int32 = 0 To ReadBytes - 1

Console.Write("{0:000} ", Buffer(I))

Page 268: Guida visual basic

Bisogna sempr e r icor dar si di chiuder e il flusso di dati quando si è finito di utilizzar lo. FileStr eam, e in gener ale anche

Str eam, implementa l'inter faccia IDisposable e il metodo Close non è altr o che un modo indir etto per r ichiamar e

Dispose (a cui, comunque, possiamo far e r icor so). Allo stesso modo, possiamo usar e la funzione Wr ite per scr iver e dati,

oppur e Wr iteByte per scr iver e un byte alla volta.

Come avr ete notato, la classe Str eam espone anche delle pr opr ietà in sola lettur a come CanRead, CanWr ite e CanSeek.

Infatti, non tutti i flussi di dato suppor tano tutte le oper azioni di lettur a, scr ittur a e r icer ca: un esempio può esser e il

Networ kStr eam (che analizzer emo nella sezione dedicata al Web) associato alle r ichieste http, il quale non suppor ta le

oper azioni di r icer ca e r estituisce un er r or e se si pr ova ad utilizzar e il metodo Seek. Questo metodo ser ve per

spostar si velocemente da una par te all'altr a del flusso di dati, e accetta solo due ar gomenti:

Seek(offset, origin)

offset è un inter o che specifica la posizione a cui r ecar si, mentr e or igin è un valor e enumer ato di tipo IO.SeekOr igin

che può assumer e tr e valor i: Begin (si r ifer isce all'inizio del file), Cur r ent (si r ifer isce alla posizione cor r ente) ed End

(si r ifer isce alla fine del file). Ad esempio:

Cer to che legger e e scr iver e dati un byte alla volta non è molto comodo. Vediamo, allor a, la pr ima categor ia di file: i

file testuali.

Lettura/scrittura di file testualiI file testuali sono così denominati per chè contengono solo testo, ossia bytes codifcabili in una delle codifiche standar d

dei car atter i (ASCII, UTF-8, ecceter a...). Alcuni par ticolar i bytes vengono intepr etati in modi diver si, come ad esempio

la tabulazione, che viene r appr esentata con uno spazio più lungo; altr i vengono tr alasciati nella visualizzazione e

sembr ano non esister e, ad esempio il NULL ter minator , che r appr esenta la fine di una str inga, oppur e l'EOF (End Of

File); altr i ancor a vengono pr esi a gr uppi, come il car atter e a capo, che in r ealtà è for mato da una sequenza di due

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.

NextConsole.WriteLine()

'Se abbiamo letto tanti bytes quanti ne erano stati'chiesti, allora non siamo ancora arrivati alla'fine del file. Richiede all'utente un numeroIf ReadBytes = Number Then

Console.WriteLine("Quanti bytes leggere?")Number = CType(Console.ReadLine, Int32)

End IfLoop

'Controlla se si è raggiunta la fine del file.'Infatti, il ciclo potrebbe terminare anche se l'utente'immettesse 0.If File.Position >= File.Length - 1 Then

Console.WriteLine("Raggiunta fine del file!")End If

'Chiude il fileFile.Close()

Console.ReadKey()

End Sub End Module

1.2.3.4.5.6.

'Si sposta alla posizione 100File.Seek(100, IO.SeekOrigin.Begin)'Si sposta di 250 bytes indietro rispetto alla posizione correnteFile.Seek(-250, IO.SeekOrigin.Current)'Si sposta a 100 bytes dalla fine del fileFile.Seek(-100, IO.SeekOrigin.End)

Page 269: Guida visual basic

bytes (Car r iage Retur n e Line Feed, r ispettivamente 13 e 10). La differ enza insita in questi tipi di file r ispetto a quelli

binar i è il fatto di non poter legger e i singoli bytes per chè non ce n'è necessità: quello che impor ta è l'infor mazione che

il testo por ta al suo inter no. La classe usata per la lettur a è Str eamReader , mentr e quella per la scr ittur a

Str eamWr iter : il costr uttor e di entr ambi accetta un unico par ametr o, ossia il per cor so del file in questione; esistono

anche altr i over loads dei costr uttor i, ma il più usato e quindi il più impor tante di tutti è quello appena citato. Ecco un

piccolo esempio di come utilizzar e tali classi in una semplice applicazione console:

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.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.

Module Module1Sub Main()

Dim File As StringDim Mode As Char

Console.WriteLine("Premere R per leggere un file, W per scriverne uno.")'Console.ReadKey restituisce un oggetto ConsoleKeyInfo,'al cui interno ci sono tre proprietà: Key,'enumeratore che definisce il codice del pulsante premuto;'KeyChar, il carattere corrispondente a quel pulsante;'Modifier, enumeratore che definisce i modificatori attivi,'ossia Ctrl, Shift e Alt.'Quello che serve ora è solo KeyCharMode = Console.ReadKey.KeyChar'Dato che potrebbe essere attivo il Bloc Num, ci si'assicura che Mode contenga un carattere maiuscolo'con la funzione statica ToUpper del tipo base CharMode = Char.ToUpper(Mode)'Pulisce lo schermoConsole.Clear()

Select Case Mode

Case "R"Console.WriteLine("Inserire il percorso del file da leggere:")File = Console.ReadLine

'Cosntrolla che il file esistaIf Not IO.File.Exists(File) Then

'Se non esiste, visualizza un messggio ed esceConsole.WriteLine("Il file specificato non esiste!")Console.ReadKey()Exit Sub

End If

Dim Reader As New IO.StreamReader(File)

'Legge ogni singola riga del file, fintanto che non'si è raggiunta la fineDo While Not Reader.EndOfStream

'Come Console.Readline, la funzione d'istanza'ReadLine restituisce una linea di testo'dal fileConsole.WriteLine(Reader.ReadLine)

Loop

'Quindi chiude il fileReader.Close()

Case "W"Console.WriteLine("Inserire il percorso del file da creare:")File = Console.ReadLine

Dim Writer As New IO.StreamWriter(File)Dim Line As String

Console.WriteLine("Immettere il testo del file, " & _

"premere due volte invio per terminare")'Fa immettere righe di testo fino a quando'si terminaDo

Line = Console.ReadLine'Come Console.WriteLine, la funzione d'istanza'WriteLine scrive una linea di testo sul file

Page 270: Guida visual basic

Ovviamente esistono anche i metodi Read e Wr ite, che scr ivono del testo senza mandar e a capo: inoltr e, Wr ite e

Wr iteLine hanno degli over loads che accettano anche str inghe di for mato come quelle viste nei capitoli pr ecedenti.

Come si è visto, le classi analizzate (e quelle che andr emo a veder e tr a br eve) hanno metodi molti simili a quelli di

Console: questo per chè anche la console è uno str eam, capace di input e output allo stesso tempo. Per color o che

pr ovengono dal C non sar à difficile r ichiamar e questo concetto.

Lettura/scrittura di file binariCome già accennato nel par agr afo pr ecedente, la distinzione tr a file binar i e testuali avviene tr amite l'inter pr etazione

dei singoli bytes. Con questo tipo di file, c'è una cor r ispondenza biunivoca tr a i bytes del file e i dati letti dal

pr ogr amma: infatti, non a caso, l'I/O viene gestito attr aver so un ar r ay di byte. Binar yWr iter e Binar yReader

espongono, oltr e alle canoniche Read e Wr ite già analizzate per FileStr eam, altr e pr ocedur e di lettur a e scr ittur a, che,

di fatto, scendono a più basso livello. Ad esempio, all'inizio della guida ho illustr ato alcuni tipi di dato basilar i,

r ipor tando anche la lor o gr andezza (in bytes). Integer occupa 4 bytes, Int16 ne occupa 2, Single he occupa 4 e così via.

Valor i di tipo base vengono quindi salvati in memor ia in notazione binar ia, r ispettando quella specifica dimensione.

Or a, esistono modi ben definiti per conver tir e un numer o in base 10 in una sequenza di bit facilmente manipolabile

dall'elabor ator e: mi r ifer isco, ad esempio, alla notazione in complemento a 2 per gli inter i e al for mato in vir gola

mobile per i r eali. Potete documentar vi su queste modalità di r appr esentazione dell'infor mazione altr ove: in questo

momento ci inter essa saper e che i dati sono "pensati" dal calcolator e in manier a diver sa da come li concepiamo noi.

Binar yWr iter e Binar yReader sono classi appositamente cr eate per far da tr amite tr a ciò che capiamo noi e ciò che

capisce il computer . Pr opr io per chè sono dei "mezzi", il lor o costr uttor e deve specificar e lo str eam (già aper to) su cui

lavor ar e. Ecco un esempio:

Io ho inser ito questi numer i: -10 -5 0 1 20 8000 19001 -345 90 22. Pr ovando ad apr ir e il file con un editor di testo

64.65.66.67.68.69.70.71.72.73.74.

Writer.WriteLine(Line)Loop While Line <> ""

'Chiude il fileWriter.Close()

Case ElseConsole.WriteLine("Comando non valido!")

End Select

Console.ReadKey()End Sub

End Module

01.02.03.04.05.06.07.08.09.10.11.12.13.14.15.16.17.18.19.20.21.22.

Module Module1

Sub Main()'Apre il file "prova.dat", creandolo o sovrascrivendoloDim File As New IO.FileStream("prova.dat", IO.FileMode.Create)'Writer è lo strumento che ci permette di scrivere'sullo stream File con codifica binariaDim Writer As New IO.BinaryWriter(File)Dim Number As Int32

Console.WriteLine("Inserisci 10 numeri da scrivere sul file:")For I As Int32 = 1 To 10

Console.Write("{0}: ", I)Number = CType(Console.ReadLine, Int32)Writer.Write(Number)

NextWriter.Close()

Console.ReadKey()

End Sub End Module

Page 271: Guida visual basic

vedr ete solo car atter i str ani, in quanto questo non è un file testuale. Apr endolo, invece, con un editor esadecimale,

otter r ete questo:

f6 ff ff ff fb ff ff ff 00 00 00 00 01 00 00 0014 00 00 00 40 lf 00 00 39 4a 00 00 a7 fe ff ff5a 00 00 00 16 00 00 00

Ogni gr uppetto di quattr o bytes r appr esenta un numer o inter o codificato in binar io. Potr emmo far e la stessa cosa con

Single, Double, Date, Boolean, Str ing e altr i tipi base per veder e cosa succede.

Binar yWr iter e Binar yReader sono molto utili quando bisogna legger e dati in codifica binar ia, ad esempio per molti

famosi for mati di file, come mp3, wav (vedi sezione FFS), zip, mpg, ecceter a...

Esempio: steganografia su immaginiLa steganogr afia è l'ar te di nasconder e del testo all'inter no di un'immagine. Per i più cur iosi, mi avventur er ò nella

scr ittur a di un semplicissimo pr ogr amma di steganogr afia su immagini, nascondendo del testo al lor o inter no.

Per pr ima cosa, si costr uisca l'inter faccia gr afica, con questi contr olli:

Una Label, Label1, Tex t = "Intr odur r e il per cor so di un'immagine:"

Una Tex tBox , tx tPath, cone AutoCompleteMode = Suggest e AutoCompleteSour ce = FileSystem. In questo modo, la

tex tbox sugger ir à il nome di file e car telle esistenti mentr e state digitando, r endendo più semplice

l'indtr oduzione del per cor so;

Una Tex tBox , tx tTex t, Scr ollBar s = Both, MultiLine = Tr ue

Un Button, btnHide, Tex t = "Nascondi"

Un Button, btnRead, Tex t = "Leggi"

Ed ecco il codice ampiamente commentato:

01.02.03.

04.05.

06.07.08.09.10.

11.

Public Class Form1

Private Sub btnHide_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)Handles btnHide.ClickIf Not IO.File.Exists(txtPath.Text) Then

MessageBox.Show("File inesistente!", Me.Text, MessageBoxButtons.OK,MessageBoxIcon.Error)

Exit SubEnd If

If IO.Path.GetExtension(txtPath.Text) <> ".jpg" Then

MessageBox.Show("Il file deve essere in formato JPEG!", Me.Text,MessageBoxButtons.OK, MessageBoxIcon.Exclamation)

Page 272: Guida visual basic

Il testo accodato può esser e r ilevato facilmente con un Hex Editor , per questo lo si dovr ebbe cr iptar e con una

passwor d: per ulter ior i infor mazioni sulla cr iptazione in .NET, veder e capitolo r ekativo.

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.

Exit SubEnd If

Dim File As New IO.FileStream(txtPath.Text, IO.FileMode.Open)'Converte il testo digitato in una sequenza di bytes,'secondo gli standard della codifica UTF8Dim TextBytes() As Byte = _

System.Text.Encoding.UTF8.GetBytes(txtText.Text)

'Va alla fine del fileFile.Seek(0, IO.SeekOrigin.End)'Scrive i bytesFile.Write(TextBytes, 0, TextBytes.Length)File.Close()

MessageBox.Show("Testo nascosto con successo!", Me.Text, MessageBoxButtons.OK,

MessageBoxIcon.Information)End Sub

Private Sub btnRead_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)

Handles btnRead.ClickIf Not IO.File.Exists(txtPath.Text) Then

MessageBox.Show("File inesistente!", Me.Text, MessageBoxButtons.OK,MessageBoxIcon.Error)

Exit SubEnd If

If IO.Path.GetExtension(txtPath.Text) <> ".jpg" Then

MessageBox.Show("Il file deve essere in formato JPEG!", Me.Text,MessageBoxButtons.OK, MessageBoxIcon.Exclamation)

Exit SubEnd If

Dim File As New IO.FileStream(txtPath.Text, IO.FileMode.Open)Dim TextBytes() As ByteDim B1, B2 As Byte

'Legge un byteB1 = File.ReadByte()Do

'Legge un altro byteB2 = File.ReadByte()'Se i bytes formano la sequenza FF D9, si ferma.'In Visual Basic, in numeri esadecimali si scrivono'facendoli precedere da "&H"If B1 = &HFF And B2 = &HD9 Then

Exit DoEnd If'Passa il valore di B2 in B1B1 = B2

Loop While (File.Position < File.Length - 1)

ReDim TextBytes(File.Length - File.Position - 1)'Legge ciò che rimane dopo FF D9File.Read(TextBytes, 0, TextBytes.Length)File.Close()

txtText.Text = System.Text.Encoding.UTF8.GetString(TextBytes)

End SubEnd Class

Page 273: Guida visual basic

B6. ListBox e ComboBox

Questi contr olli sono liste con stile visuale pr opr io in gr ado di contener e elementi. La gestione di tali elementi è molto

simile a quella delle List gener ic o degli Ar r ayList. L'unica differ enza sta nel fatto che in questo caso, tutte le modifiche

vengono r ese visibili sull'inter faccia e influiscono, quindi, su ciò che l'utente può veder e. Una volta aggiunte alla

w indows for m, il lor o aspetto sar à simile a questo:

ListBox

ComboBox

Le pr opr ietà più inter essanti sono:

Solo per ListBox :

ColumnWidth : indica la lar ghezza delle colonne in una listbox in cui MultiColumn = Tr ue. Lasciar e 0 per il

valor e di default

Hor izontalEx tent : indica di quanti pix el è possibile scor r er e la listbox in or izzontale, se la scr ollbar

or izzontale è attiva

Hor izontalScr ollbar : deter mina se attivar e la scr ollbar or izzontale. Di solito, questa pr opr ietà viene

impostata a Tr ue quando la listbox dispone di più colonne

MultiColumn : deter mina se la listbox è a più colonne. In questa modalità, una volta ter minata l'altezza

della lista, gli elementi vengono posizionati di lato anzichè sotto, ed è quindi possibile visualizzar li

spostandosi a destr a o a sinistr a. Un esempio visuale:

Page 274: Guida visual basic

ListBox MultiColumn

Scr ollAlwaysVisible : deter mina se le scr ollbar vengono visualizzate sempr e, indipendentemente dal

numer o di elementi pr esenti. Infatti quando questa pr opr ietà è disabilitata, se gli elementi sono pochi e

possono esser e posizionati nell'ar ea della lista senza nasconder ne nessuno, non viene visualizzata la

scr ollbar , che appar e quando gli elementi cominciano a diventar e tr oppi. Con questa pr opr ietà attiva,

essa è sempr e visibile e, se inutilizzata, si disabilita automaticamente

SelectionMode : pr opr ietà enumer ata che deter mina in quale modo sia possibile selezionar e gli elementi.

Può assumer e quattr o valor i: None (non è possibile selezionar e niente), One (un solo elemento alla volta),

MultiSimple (più elementi selezionabili con un click), MultiEx tended (più elementi, selezionabili solo

tenendo pr emuto Ctr l e spostando il mouse sopr a di essi)

Solo per ComboBox :

AutoComplete... : tutte le pr opr ietà il cui nome inizia per "AutoComplete" sono uguali a quelle citate nella

lezione pr ecedente

Dr opDownHeight : deter mina l'altezza, in pix el, del menù a discesa

Dr opDownStyle : deter mina lo stile del menù a discesa. Può assumer e tr e valor i: Simple (il menù a discesa

è sempr e visibile, e può esser e assimilato a una listbox ), Dr opDown (stile nor male come nell'immagine di

esempio pr oposta a inizio capitolo, ma è possibile modificar e il testo dell'elemento selezionato scr ivendo

entr o la casella), Dr opDownList (stile nor male, non è possibile modificar e l'elemento selezionato in alcun

modo, se non selezionandone un altr o). Questa è un'immagine di una combobox con Dr opDownStyle =

Simple:

ComboBox Simple DropDown

FlatStyle : lo stile visuale della ComboBox . Può assumer e quattr o valor i: Flat o Popup (la combobox è

gr igia e schiacciata, senza contor ni 3D), System o Pr ofessional (la combobox è azzur r a e r ilevata, con

contor ni 3D)

Max Dr opDownItems : il numer o massimo di elementi visualizzabili nel menù a discesa

Max Length : deter mina il massimo numer o di car atter i di testo che possono esser e inser iti come input

nella casella della combobox . Questa pr opr ietà ha senso solo se Dr opDownStyle non è impostata su

Dr opDownList, poichè tale stile impedisce di modificar e il contenuto della combobox tr amite tastier a,

come già detto

Per entr ambe le liste:

Dr awMode : deter mina la modalità con cui ogni elemento viene disegnato. Può assumer e tr e valor i:

Nor mal, Owner Dr aw Fix ed e Owner Dr aw Var iable. Il pr imo è quello di default; il secondo ed il ter zo

specificano che i contr olli devono esser e disegnati da una speciale pr ocedur a definita dal pr ogr ammator e

nell'evento Dr aw Item. Per ulter ior i infor mazioni su questo pr ocedimento, veder e l'ar ticolo Font e

Page 275: Guida visual basic

diseg ni nelle liste nella sezione Appunti.

For matStr ing : dato che queste liste possono contener e anche numer i e date (e altr i oggetti, ma non è

consigliabile aggiunger e tipi diver si da quelli base), la pr opr ietà For matStr ing indica come tali valor i

debbano esser e visualizzati. Cliccando sul pulsante con i tr e puntini nella finestr a delle pr opr ietà su

questa voce, appar ir à una finestr a di dialogo con i seguenti for mati standar d: No For matting, Numer ic,

DateTime e Scientific.

For matEnabled : deter mina se è abilitata la for mattazione degli elementi tr amite For matStr ing

Integr alHeight : quando attiva, questa pr opr ietà for za la lista ad assumer e un valor e di altezza

(Size.Height) che sia un multiplo di ItemHeight, in modo tale che gli elementi siano sempr e visibili

inter amente. Se disattivata, gli elementi possono anche venir e "tagliati" fuor i dalla lista. Un esempio:

Lista con Integ ralHeig ht = False

ItemHeight : altezza, in pix el, di un elemento

Items : collezione a tipizzazione debole di tutti gli elementi. Gode di tutti i metodi consueti delle liste,

quali Add, Remove, Index Of, Inser t, ecceter a...

Sor ted : indica se gli elementi devono esser e or dinati alfabeticamente

Detto ciò, è possibile pr oceder e con un semplice esempio. Il pr ogr amma che segue per mette di aggiunger e un

qualsiasi testo ad una lista. Pr ima di iniziar e a scr iver e il codice, bisogna includer e nella w indows for m questi

contr olli:

Una listbox , di nome lstItems

Un pulsante, di nome cmdAdd, con Tex t = "Aggiungi"

Un pulsante, di nome cmdRemove, con Tex t = "Rimuovi"

Il codice:

01.02.03.04.05.06.07.08.09.10.11.12.13.14.15.16.17.18.19.20.21.22.23.

Public Class Form1Private Sub cmdAdd_Click(ByVal sender As Object, _

ByVal e As EventArgs) Handles cmdAdd.ClickDim S As String

'Inputbox(' ByVal Prompt As Object,' ByVal Title As String,' ByVal DefaultResponse As String)'Visualizza una finestra con una label esplicativa'il cui testo è racchiuso in Prompt, con un titolo'Title e una textbox con un testo di default'DeafultResponse: una volta che l'utente ha inserito'la stringa nella textbox e cliccato OK, la funzione'restituisce la stringa immessaS = InputBox("Inserisci una stringa:", "Inserimento stringa", _"[Stringa]")

'Aggiunge la stringa alla listalstItems.Items.Add(S)

End Sub

Page 276: Guida visual basic

Non solo stringheNell'esempio pr ecedente, ho mostr ato che è possibile aggiunger e agli elementi della listbox delle str inghe, ed

esse ver r anno visualizzate come testo sull'inter faccia del contr ollo. Tuttavia, la pr opr ietà Items è di tipo

ObjectCollection, quindi può contener e un qualsiasi tipo di oggetto e non necessar iamente solo str inghe. Quello

che ci pr eoccupa, in questo caso, è ciò che viene mostr ato all'utente qualor a noi inser issimo un oggetto nella

listbox : quale testo sar à visualizzato per l'elemento? Ecco un esempio (un for m con una listbox e un pulsante):

Una volta pr emuto btnDoSomething, nella lista ver r anno aggiunti due oggetti, tuttavia la GUI della listbox

visualizzer à questi due elementi:

WindowsApplication4.Form1+ItemWindowsApplication4.Form1+Item

Questo nel mio caso, poiché il pr ogetto (e quindi il namespace pr incipale) si chiama WindowsApplication4. Da ciò

si può capir e che, in assenza d'altr o, la listbox tenta di conver tir e l'oggetto in una str inga, ossia un dato

24.25.26.27.28.29.30.31.

Private Sub cmdRemove_Click(ByVal sender As Object, _ByVal e As EventArgs) Handles cmdRemove.Click'Se è selezionato un elemento...If lstItems.SelectedIndex >= 0 Then

'Lo eliminalstItems.Items.RemoveAt(lstItems.SelectedIndex)

End IfEnd Sub

End Class

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.

32.33.34.35.36.

Class Form1

Class ItemPrivate Shared IDCounter As Int32 = 0

Private _ID As Int32Private _Description As String

Public ReadOnly Property ID() As Int32

GetReturn _ID

End GetEnd Property

Public Property Description() As String

GetReturn _Description

End GetSet(ByVal value As String)

_Description = valueEnd Set

End Property

Sub New()_ID = IDCounterIDCounter += 1

End Sub

End Class

Private Sub btnDoSomething_Click(ByVal sender As Object, ByVal e As EventArgs)Handles btnDoSomething.ClicklstItems.Items.Add(New Item() With {.Description = "Asus Eee PC 900"})lstItems.Items.Add(New Item() With {.Description = "Hp Pavillion Dv6000"})

End Sub

End Class

Page 277: Guida visual basic

intellegibile all'uomo: l'unico modo per poter avviar e questa conver sione consiste nell'utilizzar e il metodo

ToStr ing, il quale, tuttavia, non è stato r idefinito dalla classe Item e pr ovoca l'uso del suo omonimo der ivante

dalla classe base Object. Quest'ultimo, infatti, r estituisce il tipo dell'oggetto, che in questo caso è pr opr io

WindowsApplication4.For m+Item. Per modificar e il compor tamento del contr ollo, dobbiamo aggiunger e alla

classe un metodo ToStr ing, ad esempio così:

Avviando di nuovo l'applicazione, gli elementi visualizzati sulla lista sar anno:

Asus Eee PC 900Hp Pavillion Dv6000

Esiste, tuttavia, un altr o modo per ottener e lo stesso r isultato senza dover r idefinir e il metodo ToStr ing.

Questa seconda alter nativa si dimostr a par ticolar mente utile quando non possiamo acceder e o modificar e il

codice della classe di cui stiamo usando istanze: ad esempio per chè si tr atta di una classe definita in un assembly

diver so. Possiamo specificar e nella pr opr ietà ListBox .DisplayMember il nome della pr opr ietà che deve ser vir e a

visualizzar e l'elemento nella lista. Nel nostr o caso, vogliamo visualizzar e la descr izione, quindi user emo questo

codice:

Ed otter r emo lo stesso r isultato.

Par allelamente, c'è anche la pr opr ietà ValueMember , che per mette di specificar e quale pr opr ietà dell'oggetto

deve esser e r estituita quando si r ichiede il valor e di un elemento selezionato mediante la pr opr ietà

SelectedValue.

1.2.3.4.5.6.7.

Class Item'...

Public Overrides Function ToString() As String

Return Me.DescriptionEnd Function

End Class

1.2.3.

lstItems.DisplayMember = "Description"lstItems.Items.Add(New Item() With {.Description = "Asus Eee PC 900"})lstItems.Items.Add(New Item() With {.Description = "Hp Pavillion Dv6000"})

Page 278: Guida visual basic

B7. CheckBox e RadioButton

Mentr e ListBox e ComboBox mir avano a r ender e visuale un insieme di elementi, questi due contr olli r appr esentano

una valor e Booleano: infatti possono esser e spuntati oppur e no.

CheckBoxLa CheckBox è la classica casella di spunta, che si può segnar e con un segno di spunta (tick). Le pr opr ietà

car atter istiche sono poche:

Appear ance : pr opr ietà enumer ata che deter mina come la checkbox viene visualizzata. Il pr imo valor e, Nor mal,

specifica che deve esser ci una casellina di spunta con il testo a fianco; il secondo valor e, Button, specifica che

deve esser e r ender izzata come un contr ollo Button. In questo secondo caso, se Checked è Tr ue il pulsante appar e

pr emuto, altr imenti no

AutoCheck : deter mina se la checkbox cambia automaticamente stato (ossia da spuntata a non spuntata) quando

viene cliccata. Se questa pr opr ietà è False, l'unico modo per cambiar e la spunta è tr amite codice

AutoEllipsis : se Appear ance = Button, questa pr opr ietà deter mina se il contr ollo si debba automaticamente

r idimensionar e sulla base del pr opr io testo

CheckAlign : se Appear ance = Nor mal, deter mina in quale posizione della checkbox si tr ovi la casellina di spunta

Checked : indica se la checkbox è spuntata oppur e no

CheckState : per le checkbox a tr e stati, indica lo stato cor r ente

FlatStyle : deter mina lo stile visuale del testo attr aver so un enumer ator e a quattr o valor i, come nelle

combobox

Tex tAlign : allineamento del testo

Tex tImageRelation : deter mina la r elazione testo-immagine, qualor a la pr opr ietà Image sia impostata. Può

assumer e diver si valor i che specificano se il testo sia sotto, sopr a, a destr a o a sinistr a dell'immagine

Thr eeState : deter mina se la checkbox suppor ta i tr e stati. In questo caso, le combinazioni possibili sono tr e,

ossia: spuntato, senza spunta e indeter minato. Può esser e utile per offr ir e una gamma di scelta più ampia o per

implementar e visualmente la logica booleana a tr e valor i

Ecco un esempio di tutte le possibili combinazioni di checkbox :

In definitiva, la CheckBox r ende visuale il legame Or tr a più condizioni.

RadioButtonA differ enza di CheckBox , RadioButton può assumer e solo due valor i, che non sono sempr e accessibili. La spiegazione di

questo sta nel fatto che solo un RadioButton può esser e spuntato allo stesso tempo in un dato contenitor e. Ad esempio,

in una finestr a che contenga tr e di questi contr olli, spuntando il pr imo, il secondo ed il ter zo sar anno depennati;

spuntando il secondo lo sar anno il pr imo ed il ter zo e così via. Tale meccanismo è del tutto automatico e aiuta

moltissimo nel caso si debbano pr opor r e all'utente scelte non sovr apponibili.

Gode di tutte le pr opr ietà di CheckBox , tr anne ovviamente Thr eeState e CheckState, e r appr esenta visualmente il

legame Xor tr a più condizioni.

Page 279: Guida visual basic

GroupBoxPar lando di contenitor i, non si può non far e menzione al Gr oupBox . Tr a tutti i contenitor i disponibili, Gr oupBox è il più

semplice dotato di inter faccia gr afica pr opr ia. La sua funzione consiste unicamente nel r aggr uppar e in uno spazio solo

più contr olli uniti da un qualche legame logico, ad esempio tutti quelli iner enti alla for mattazione del testo. Oltr e a

r ender e la str uttur a della finestr a più or dinata, dà un tocco di stile all'applicazione e, cosa più impor tante, può

condizionar e lo stato di tutti i suoi membr i (o contr olli figli). Dato che gode solamente delle pr opr ietà comuni a tutte le

classi der ivate da Contr ol, la modifica di una di esse si r iper cuoter à su tutti i contr olli in esso contenuti. Di solito si

sfr utta questa peculiar ità per disabilitar e o r ender e invisibile un gr uppo di elementi.

L'inter faccia si pr esenta in questo modo:

Page 280: Guida visual basic

B8. NumericUpDown e DomainUpDown

NumericUpDownQuesto contr ollo tor na utile quando si vuole pr opor r e all'utente una scelta di un numer o, inter o o decimale, compr eso

tr a un minimo e un massimo. Ad esempio, il semplice pr ogr amma che andr ò a illustr ar e in questo capitolo chiede di

indovinar e un numer o casuale da 0 a 100 gener ato dal computer . Con l'uso di una tex tbox , l'utente potr ebbe

commetter e un er r or e di battitur a e inser ir e in input car atter i non validi, mandando così in cr ash il pr ogr amma: la

soluzione potr ebbe esser e usar e un Tr y, ma si spr echer ebbe spazio, o un contr ollo Masked Tex tBox , ma in altr i casi

potr ebbe r isultar e limitativo o pedante, dato che r ichiede un pr eciso numer o di car atter i immessi. Usando invece una

combobox o una listbox si dovr ebber o aggiunger e manualmente tutti i numer i con un for , spr ecando spazio nel codice.

La soluzione ideale sar ebbe far e uso di Numer icUpDown. Le pr opr ietà car atter istiche:

DecimalPlaces : i posti decimali dopo la vir gola. Se impostata a 0, sar à possibile immetter e solo numer i inter i

Hex adecimal : deter mina se visualizzar e il numer o in notazione esadecimale (solo per numer i inter i positivi)

Incr ement : il fattor e di incr emento/decr emento automaticamente aggiunto/sottr atto quando l'utente clicca

sulle fr ecce del contr ollo

Inter ceptAr r owKey : deter mina se il contr ollo debba inter cettar e e inter pr etar e la pr essione delle fr ecce

dir ezionali su/giù da testier a

Max imum : massimo valor e numer ico

Minimum : minimo valor e numer ico

ThousandSepar ator : indica se visualizzar e il separ ator e delle migliaia

Value : il valor e indicato

UpDownAlign : la posizione delle fr ecce sul contr ollo

Dopo aver posizionato questi contr olli:

Una Label Label1, Tex t = "Clicca Gener a per gener ar e un numer o casuale, quindi pr ova a indovinar e!"

Un pulsante cmdGener ate, Tex t = "Gener a"

Un pulsante cmdTr y, Tex t = "Pr ova"

Un Numer icUpDown nudValue, con le pr opr ietà standar d

Una Label lblNumber , Tex t = "***", Font = Micr osoft Sans Ser if Gr assetto 16pt, AutoSize = False, Tex tAlign =

MiddleCenter

Disponeteli in modo simile a questo:

Ed ecco il codice:

01.02.03.04.05.06.07.08.09.10.11.12.13.

Class Form1'Il numero da indovinarePrivate Number As Byte'Determina se l'utente ha già dato la sua rispostaPrivate Tried As Boolean'Crea un nuovo oggetto Random in grado di generare numeri'casualiDim Rnd As New Random()

Private Sub cmdGenerate_Click(ByVal sender As System.Object, _

ByVal e As System.EventArgs) Handles cmdGenerate.Click'Genera un numero aleatorio tra 0 e 99 e lo deposita in

Page 281: Guida visual basic

DomainUpDownQuesto contr ollo è molto simile come stile gr afico a quello appena analizzato solo che, anzichè visualizzar e numer i in

successione, visualizza semplici elementi testuali come le liste dei capitoli pr ecedenti. È una specie di incr ocio fr a

questi tipi di contr ollo: gode delle pr opr ietà Minimum e Max imum, ma anche della pr opr ietà Items, che stabilisce la

lista or dinata di elementi da cui pr elevar e le str inghe.

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.

'NumberNumber = Rnd.Next(0, 100)'Imposta Tried su FalseTried = False'Nasconde la soluzionelblNumber.Text = "***"

End Sub

Private Sub cmdTry_Click(ByVal sender As System.Object, _ByVal e As System.EventArgs) Handles cmdTry.Click'Se si è già provato, esce dalla proceduraIf Tried Then

MessageBox.Show("Hai già fatto un tentativo! Premi " & _"Genera e prova con un altro numero!", "Numeri a caso", _MessageBoxButtons.OK, MessageBoxIcon.Exclamation)Exit Sub

End If

'Se NumericUpDown corrisponde al numero generato,'l'utente vinceIf nudValue.Value = Number Then

MessageBox.Show("Complimenti, hai vinto!", "Numeri a caso", _MessageBoxButtons.OK, MessageBoxIcon.Information)

ElseMessageBox.Show("Risposta sbagliata!", "Numeri a caso", _MessageBoxButtons.OK, MessageBoxIcon.Exclamation)

End If

'Ormai l'utente ha fatto la sua sceltaTried = True'Fa vedere la soluzionelblNumber.Text = Number

End SubEnd Class

Page 282: Guida visual basic

B9. PictureBox e ProgressBar

PictureBoxLa Pictur eBox è uno di quei contr olli visibili solamente nel designer , poichè i suoi contor ni, di default, sono invisibili.

L'unica car atter istica che la r ende visibile a r untime è la sua pr opr ietà fondamentale, Image. Infatti, questo contr ollo

può contener e un'immagine: di solito viene usata per posizionar e loghi, banner o scr itte all'inter no dell'inter faccia di un

pr ogr amma. Le pr opr ietà più impor tanti sono:

Er r or Image : l'immagine visualizzata qualor a non sia possibile car icar e un'immagine con la pr opr ietà Image

Image : l'immagine visualizzata

InitialImage : l'immagine visualizzata all'inizio, pr ima che sia impostata qualsiasi altr a immagine con la

pr opr ietà Image

SizeMode : modalità di r idimensionamento dell'immagine. Può assumer e cinque valor i: Nor mal (l'immagine

r imane delle dimensioni nor mali, e ignor a ogni r idimensionamento della pictur ebox : per questo può anche

venir e tagliata), Str etchImage (l'immagine si r idimensiona a seconda della pictur ebox , assumendone le stesse

dimensioni), AutoSize (la pictur ebox si r idimensiona sulla base dell'immagine contenuta), Center Image

(l'immagine viene sempr e posta al centr o della pictur ebox , ma mantiene le pr opr ie dimensioni iniziali), Zoom

(l'immagine si r idimensiona sulla base della pictur ebox , ma mantiene sempr e lo stesso r appor to tr a lar ghezza e

altezza)

Er r or Image, Image e InitialImage sono pr opr ietà di tipo Image: quest'ultima è una classe astr atta e quindi non esiste

mai ver amente, anche se espone comunque dei metodi statici per il car icamento delle immagini. La classe che

r appr esenta ver amente e mater ialmente l'immagine è System.Dr aw ing.Bitmap, o solo Bitmap per gli amici.

Nonostante il nome sugger isca diver samente, essa fa da w r apper a un numer o elevato di for mati di immagini, tr a cui

bmp, gif, jpg, png, ex if, emf, tiff e wmf. In questo capitolo user ò tale classe in modo molto par ticolar e, quindi è meglio

pr ima analizzar ne i membr i:

Classe astr atta Image

Fr omFile(File) : car ica un'immagine da File e ne r estituisce un'istanza

Fr omStr eam(Str eam) : car ica un'immagine dallo str eam Str eam e ne r estituisce un'istanza (per ulter ior i

infor mazioni sugli str eam, veder e capitolo 56)

Fr omHbitmap : car ica un'immagine a par tir e da un puntator e che punta al suo indir izzo in memor ia

Hor izontalResolution : r isoluzione sull'asse x , in pix els al pollice (=2.54cm)

Pix elsFor mat : r estituisce il for mato dell'immagine, sottofor ma di enumer ator e

RawFor mat : r estituisce il for mato dell'immagine, in un oggetto ImageFor mat

RotateFlip(F) : r uota e/o inver te l'immagine secondo il for mato F, esposto da un enumer ator e codificato

a bit

Save(File) : salva l'immagine sul file File: l'estensione del file influenzer à il metodo di scr ittur a

dell'immagine

Size : dimensione dell'immagine

Ver ticalResolution : r isoluzione sull'asse y, in pix els al pollice

Classe der ivata Bitmap

GetPix el(X, Y) : r estituisce il color e del pix el alle coor dinate (X, Y), r ifer ite al mar gine super ior e sinistr o

MakeTr anspar ent(C) : r ende il color e C tr aspar ente su tutta l'immagine

SetPix el(X, Y, C) : imposta il color e del pix el alle coor dinate (X, Y) a C

Page 283: Guida visual basic

SetResolution(x R, yR) : imposta la r isoluzione or izzontale su x R e quella ver ticale su yR, entr ambe

misur ate in punti al pollice

ProgressBarLa Pr ogr essBar è la classica bar r a di car icamento, usata per visualizzar e sull'inter faccia lo stato di un'oper azione. Le

pr opr ietà pr incipali sono poche:

Max imum : il valor e massimo r appr esentabile dal contr ollo

Minimum : il valor e minimo r appr esentabile dal contr ollo

Step : valor e che definisce il valor e di incr emento quando viene r ichiamata il metodo Per for mStep

Style : pr opr ietà enumer ata che indica lo stile della bar r a. Può assumer e tr a valor i: Block (a blocchi), Continuos

(i blocchi possono venir e tagliati, a seconda delle per centuale) e Mar quee (un blocchetto che si muove da sinistr a

a destr a, che r appr esenta quindi un'oper azione in cor so della quale non si sa lo stato)

Value : il valor e r appr esentato

Esempio: Bianco e neroL'esempio di questa lezione è un pr ogr amma capace di car icar e un'immagine, conver tir la in bianco e ner o, e poi

r isalvar la sullo stesso o su un altr o file. I contr olli da usar e sono:

Una Pictur eBox , imgPr eview, ancor ata a tutti i bor di, con SizeMode = Str ecthImage

Un Button, cmdLoad, Tex t = "Car ica", Anchor = Left Or Bottom

Un Button, cmdSave, Tex t = "Salva", Anchor = Bottom

Un Button, cmdConver t, Tex t = "Conver ti", Anchor = Right Or Bottom

Una Pr ogr essBar , pr gConver t, Style = Continuos

Disposti come in figur a:

Ecco il codice:

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.

Class Form1'Funzione che converte un colore in scala di grigioPrivate Function ToGreyScale(ByVal C As Color) As Color

'Per convertire un colore in scala di grigio è sufficiente'prendere le sue componenti di rosso, verde e blu (red,'green e blue), farne la media aritmetica e quindi'assegnare tale valore alle nuove coordinate RGB del'colore risultante

'Ottiene le componenti (coordinate RGB)Dim Red As Int32 = C.RDim Green As Int32 = C.GDim Blue As Int32 = C.B'Fa la mediaDim Grey As Int32 = (Red + Green + Blue) / 3

'Quindi crea un nuovo colore, mettendo tutte le'componenti uguali alla media ottenutaReturn Color.FromArgb(Grey, Grey, Grey)

End Function

Private Sub cmdLoad_Click(ByVal sender As System.Object, _ByVal e As System.EventArgs) Handles cmdLoad.Click'Per ulteriori informazioni sui controlli OpenFileDialog e

Page 284: Guida visual basic

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.80.81.82.83.84.85.86.87.88.89.

'SaveFileDialog vedere capitolo relativoDim Open As New OpenFileDialogOpen.Filter = "File immagine|*.jpg;*.jpeg;*.gif;*.png;*.bmp;" & _

"*.tif;*.tiff;*.emf;*.exif;*.wmf"

If Open.ShowDialog = Windows.Forms.DialogResult.OK Then'Apre l'immagine, caricandola dal file selezionato'nella finestra di dialogo tramite la funzione'statica FromFileimgPreview.Image = Image.FromFile(Open.FileName)

End IfEnd Sub

Private Sub cmdSave_Click(ByVal sender As System.Object, _

ByVal e As System.EventArgs) Handles cmdSave.Click'Se c'è un'immagine da salvare, la salvaIf imgPreview.Image IsNot Nothing Then

Dim Save As New SaveFileDialogSave.Filter = "File Jpeg|*.jpeg;*.jpg|File Bitmap|*.bmp|" & _

"File Png|*.png|File Gif|*.gif|File Tif|*.tif;" & _"*.tiff|File Wmf|*.wmf|File Emf|*.emf"

If Save.ShowDialog = Windows.Forms.DialogResult.OK Then

'Dato che la proprietà Image è di tipo Image, usa'il metodo statico Save per salvare l'immagineimgPreview.Image.Save(Save.FileName)

End IfEnd If

End Sub

Private Sub cmdConvert_Click(ByVal sender As System.Object, _ByVal e As System.EventArgs) Handles cmdConvert.Click'Prima si converte l'immagine in Bitmap, dato che Image'è una classe astrattaDim Image As Bitmap = imgPreview.Image'Variabile ausiliaria per i calcoliDim TempColor As Color

'Attenzione!'Alcuni formati non supportano SetPixel, come il formato'Gif. Controllare di passare immagini di formato adeguato

'Itera su ogni pixel, e lo cambia di colore'Scorre le righe di pixel una alla voltaFor X As Int32 = 0 To Image.Width - 1

'Quindi ogni pixel nella rigaFor Y As Int32 = 0 To Image.Height - 1

'Converte il coloreTempColor = Image.GetPixel(X, Y)TempColor = ToGreyScale(TempColor)Image.SetPixel(X, Y, TempColor)

Next'Imposta il valore della progressbar su una percentuale'che esprime il numero di righe analizzateprgConvert.Value = X * 100 / Image.Width'Evita di bloccare il programma. Per ulteriori'informazioni su Application e il namespace My,'vedere capitolo relativoApplication.DoEvents()

Next

'Reimposta l'immagine finaleimgPreview.Image = Image

End SubEnd Class

Page 285: Guida visual basic

B10. Un semplice editor di testi

Per r ealizzar e un editor di testi bisogna pr ima di tutto saper e come per metter e all'utente di sceglier e quale file

apr ir e e in quale file salvar e ciò che ver r à scr itto. Queste semplici inter azioni vengono amministr ate da due contr olli:

OpenFileDialog e SaveFileDialog.

In questo br eve capitolo esemplificher ò il caso di un semplicissimo editor di testi, con le funzionalità base di aper tur a e

salvataggio dei file *.tx t. Pr ima di pr oceder e, ecco una lista delle pr opr ietà più significative dei contr olli in questione:

AddEx tension : se il nome del file da apr ir e/salvar e non ha un estensione, il contr ollo l'aggiunge

automaticamente sulla base della pr opr ietà DefaultEx t o Filter

CheckFileEx ists : contr olla se il file selezionato esista

CheckPathEx ists : contr olla se la car tella selezionata esista

DefaultEx t : l'estenzione pr edefinita su cui si basa la pr opr ietà AddEx tension

FileName : il nome del file visualizzato di default nella tex tbox del contr ollo, e modificato dopo l'inter azione con

l'utente

Filter : la pr opr ietà più impor tante dopo FileName. Ser ve a definir e quali tipi di file siano visualizzati dal

contr ollo. Nella finestr a di dialogo, infatti, come mostr a l'immagin sopr a r ipor tata, poco sotto alla tex tbox

contenente il nome del file, c'è una combobox che per mette di selezionar e il "filtr o", per l'appunto, ossia quali

estensioni pr ender e in consider azione (nell'esempio "File di testo", con estensione *.tx t, quella che si pr ender à in

esame nell'esempio). Ci sono delle r egole standar d per la costr uzione della str inga che deve esser e passata a

questa pr opr ietà. Il for mato cor r etto è:

Se, quindi, si volesser o visualizzar e solo file multimediali, divisi in musica e video, questo sar ebbe il valor e di

Filter : "Musica|*.mp3;*.wav;*.wma;*.ogg;*.mid|Video|*.mpg;*.mp4;*.wmv;*.avi". Per i file di testo "File di

testo|*.tx t" e per tutti i file "Tutti i file|*.*"

InitialDir ector y: la car tella iniziale pr edefinita

MultiSelect: se ver o, si potr anno selezionar e più file (cr eando un r iquadr o col puntator e o selezionandoli

manualmente uno ad uno tenendo pr emuto Ctr l)

Title: il titolo della finestr a di dialogo

ValidatesName: contr olla che i nomi dei file non contengano car atter i vietati

Over Wr itePr ompt: (solo per SaveFileDialog) contr olla se il file selezionato ne sovr ascr ive un altr o e chiede se

pr oceder e o no

Esempio: Editor di testiDopo aver analizzato le pr opr ietà impor tanti, si può pr oceder e alla stesur a del codice, ma pr ima una pr ecisazione.

Non avendo inter faccia gr afica sulla finestr a, ma costituendo w indows for ms a sè stante, i contr olli OpenFileDialog e

SaveFileDialog possono esser e inser iti nel designer oppur e inizializzati da codice indiffer entemente (per quanto

r iguar da lo scopo). La diver sità nell'usar e un metodo piuttosto che un altr o sta nel fatto che il pr imo utilizza sempr e lo

stesso contr ollo, che potr ebbe dar e dei FileName er r ati in casi speciali, mentr e il secondo ne inizializza uno nuovo ad

ogni evento, costando di più in ter mini di memor ia. Nell'esempio seguente utilizzo il pr imo metodo, ma potr à capitar e

che sfr utti anche il secondo in diver se altr e occasioni.

Or a si aggiungano i contr olli necessar i:

1. Descrizione file|*.estensione1;*.estensione2|Descrizione file|...

Page 286: Guida visual basic

Button : Name = cmdOpen, Tex t = "Apr i", Anchor = Bottom Or Left

Button : Name = cmdSave, Tex t = "Salva", Anchor = Bottom Or Right

Button : Name = cmdClose, Tex t = "Chiudi", Anchor = Bottom

Tex tBox : Name = tx tFile, Multiline = Tr ue, Anchor = Top Or Right Or Bottom Or Left

OpenFileDialog : Name = FOpen, Filter = "File di testo|*.tx t", FileName = "Testo"

SaveFileDialog : Name = FSave, Filter = "File di testo|*.tx t", DefaultEx t = "tx t"

Il sor gente può esser e r eso ancor a più br eve usando i metodi IO.File.Wr iteAllTex t e IO.File.ReadAllTex t.

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.32.33.34.35.36.37.38.39.40.41.42.43.44.45.46.47.48.49.

Private Sub cmdOpen_Click(ByVal sender As Object, ByVal e As EventArgs)_Handles cmdOpen.Click'La funzione ShowDialog visualizza la finestra di dialogo e'restituisce quale pulsante è stato premuto'Se il pulsante corrisponde con OK, procediamoIf FOpen.ShowDialog = Windows.Forms.DialogResult.OK Then

'Apre un file in lettura'Usa la proprietà FileName di FOpen, che restituisce il'path del file selezionato: è sicuro che il file esista'perchè l'utente ha premuto Ok e non ha chiuso la'finestra di dialogoDim R As New IO.StreamReader(FOpen.FileName)

'Legge tutto il testo del file e lo deposita nella textboxtxtFile.Text = R.ReadToEnd

'Chiude il fileR.Close()

End IfEnd Sub Private Sub cmdSve_Click(ByVal sender As Object, ByVal e As EventArgs) _

Handles cmdSave.Click'Viene visualizzata la finestra di dialogoIf FSave.ShowDialog = Windows.Forms.DialogResult.OK Then

'Apre un file in scrittura, di ci si assicura che'l'utente acconsenta alla sovrascrittura se già esistente'mediante la proprietà OverwritePromptDim W As New IO.StreamWriter(FSave.FileName)

'Scrive tutto il contenuto della textbox nel fileW.Write(txtFile.Text)

'Chiude il fileW.Close()

End IfEnd Sub Private Sub cmdClose_Click(ByVal sender As Object, ByVal e As EventArgs) _

Handles cmdClose.ClickIf txtFile.Text <> "" And _

FSave.ShowDialog = Windows.Forms.DialogResult.OK ThenDim W As New IO.StreamWriter(FSave.FileName)

W.Write(txtFile.Text)

W.Close()

End IfEnd Sub

Page 287: Guida visual basic

B11. Scrivere un INI Reader - Parte I

I file INIDato che l'esempio di questo capitolo consiste nel r ealizzar e un lettor e di file *.ini, è bene spiegar e pr ima, per chi non

li conoscesse, come sono fatti e quale è lo scopo di questo tipo di file.

Sono file di sistema contr addistinti dalla dicitur a "Impostazioni di Configur azione", poichè tale è la lor o funzione:

ser vono a definir e il valor e delle opzioni di un pr ogr amma. Nelle applicazioni .NET ci sono altr i modo molto più

efficienti per r aggiunger e lo stesso r isultato ma li vedr emo in seguito. La str uttur a di un file ini è composta

sostanzialmente da due nuclei: campi e valor i. I campi sono r aggr uppamenti concettuali atti a divider e

funzionalmente più valor i di ambito diver so e sono delimitati da una coppia di par entesi quadr e. I valor i costituiscono

qualcosa di simile alle pr opr ietà delle classi .NET e possono esser e assegnati con l'oper ator e di assegnamento =. Un

ter zo tipo di elemento è costituito dai commenti, che, come ben si sa, non influiscono sul r isultato: questi sono

pr eceduti da un punto e vir gola e possono esser e sia su una linea inter a che sulla stessa linea di un valor e. Ecco un

esempio:

;Ipotetico INI di un gioco[General Info]Name = ProofGameVersion = 1.1.0.2Company = FG CorporationYear = 2006

[Run Info]Diffucult = easy ;difficoltàLives = 10 ;numero di viteHealth = 90 ;saluteLevel = 20 ;livello

Il pr ogr amma di esempio analizzer à il file, r appr esentando campi e valor i in un gr afico ad alber o simile a quello che

w indows usa per r appr esentar e la str uttur a ger ar chica delle car telle.

MenuStripÈ il classico menù di w indows. Una volta aggiunto al for m designer , viene cr eato uno spazio apposito sotto

all'antepr ima del for m, nel quale appar e l'icona cor r ispondente; inoltr e viene visualizzata una str iscia gr igia sul lato

super ior e della finestr a, ossia l'inter faccia gr afica che MenuStr ip pr esenter à a r un-time. Per aggiunger e una voce,

basta far e click su "Type her e" e digitar e il testo associato; è possibile cancellar ne uno pr emendo Canc o modificar lo

cliccandoci sopr a due volte lentamente. Ogni sottovoce dispone di eventuali altr i sotto-menù per sonalizzabili all'infinito.

Si può aggiunger e un separ ator e, ossia una linea or izzontale, semplicemente inser endo "-" al posto del testo. Ogni

elemento così cr eato è un oggetto ToolStr ipMenuItem, inser ito nella pr opr ietà Dr opDownItems del menù. Ecco alcune

pr opr ietà inter essanti:

MenuStr ip

Allow ItemReor der : deter mina se consentir e il r ior dinamento dei menù da par te dell'utente; quest'ultimo

potr ebbe, tenendo pr emuto ALT e tr ascinando gli header , cambiar e la posizione delle sezioni sulla bar r a

del MenuStr ip

Items : collezione di oggetti der ivati da MenuItem che costituiscono le sezioni pr incipali del menu'

Page 288: Guida visual basic

Render Mode : pr opr ietà enumer ata che definisce lo stile gr afico del contr ollo. Può assumer e tr e valor i:

System (dipende dal sistema oper ativo), Pr ofessional o Manager Render Mode (stile simile a Micr osoft

Office)

Show ItemToolTips : deter mina se visualizzar e i sugger imenti (tool tip) di ogni elemento

Tex tDir ection : dir ezione del testo, or izzontale, ver ticale a 90? o a 270?

ToolStr ipMenuItem

AutoToolTip : deter mina se usar e la pr opr ietà Tex t (Tr ue) o ToolTipTex t (False) per visualizzar e i tool tip

Checked : deter mina se il contr ollo ha la spunta

CheckOnClick : specifica sa sia possibile spuntar e il contr ollo con un click

CheckState : uno dei tr e stati di spunta

DisplayStyle : specifica cosa visualizzar e, se solo il testo, solo l'immagine, entr ambi o nessuno

Dr opDownItems : uguale alla pr opr ietà Items di MenuStr ip

Shor tcutKeyDisplayStr ing : la str inga che deter mina quale sia la scor ciatoia da tastier a per il contr ollo,

che ver r à visualizzata a destr a del testo (ad esempio "CTRL + D")

Shor tcutKeys : deter mina la combinazione di tasti usata come scor ciator ia

ShowShor tcutKeys : deter mina se visualizzar e la scor ciatoia da tastier a di fianco al testo

Tex tImageRelation : r elazione di posizione tr a immagine e testo

TooltipTex t : testo dell'eventuale tool tip

Dopo aver inser ito un MenuStr ip str MainMenu, una sezione str File e tr e sottosezioni, str Open, Separ ator e e str Ex it,

la scher mata appar ir à così:

StatusStripLa bar r a di stato, sul lato basso del for m, che indica le infor mazioni aggiuntive o lo stato dell'applicazione. È un

contenitor e che può includer e altr i contr olli, come label, pr ogr essbar , dr opdownitem, ecceter a. Per or a basta inser ir e

una label, di nome lblStatus, con testo impostato su "In attesa...". Dato che le pr opr ietà sono quasi identiche a quelle di

MenuStr ip, ecco subito un'antepr ima del for m con questi due contr olli posizionati:

Page 289: Guida visual basic

ContextMenuStripÈ il menù contestuale, ossia quel menù che appar e ogniqualvolta viene pr emuto il pulsante destr o del mouse su un

deter minato contr ollo. Per far sì che esso appaia bisogna pr ima cr ear e un legame tr a questo e il contr ollo associato,

impostando la r elativa pr opr ietà Contex tMenuStr ip, comune a tutte le classi der ivate da Contr ol. La fase di cr eazione

avviene in modo identico a quanto è già stato analizzato per MenuStr ip, e anche l'inser imento delle sottovoci è simile.

Non dovr este quindi aver e pr oblemi a cr ear ne uno e inser ir e una voce str Clear View, Tex t = "Pulisci lista".

TreeViewEcco il contr ollo clou della lezione, che per mette di visualizzar e dati in una str uttur a ad alber o. Le pr opr ietà più

impor tanti sono:

CheckBox es: deter mina se ogni elemento debba aver e alla pr opr ia sinistr a una checkbox

FullRowSelect: deter mina se, quando un elemento viene selezionato, sia evidenziato solo il nome o tutta la r iga

su cui sta il nome

ImageList: specifica quale ImageList è associata al contr ollo; un'imagelist è una lista di immagini or dinata,

ognuna delle quali è accessibile attr aver so un indice, come se fosse un ar r aylist

ImageIndex : pr opr ietà che deter mina l'indice di default di ogni elemento, da pr elevar e dall'imagelist associata;

nel caso la pr opr ietà sia r ifer ita a un elemento, indica quale immagine bisogna visualizzar e a fianco

dell'elemento

Nodes: la pr opr ietà più impor tante: al par i di Items delle listbox e delle combobox . Contiene una collezione di

Tr eeNode (ossia "nodi d'alber o"): ogni elemento Node ha molteplici pr opr ietà e costituisce un'unità dalla quale

possono dipar tir si altr e unità. Cosa impor tante, ogni nodo gode di una pr opr ietà Nodes equivalente, la quale

implementa la str uttur a suddetta

SelectedNode: r estituisce il nodo selezionato

ShowLindes: indica se visualizzar e le linee che congiungono i nodi

ShowPlusMinus: indica se visualizzar e i '+' per espander e i nodi contenuti in un elemento e i '-' per eseguir e

l'oper azione opposta

ShowRootLindes: deter mina se visualizzar e le linee che congiungono i nodi che non dipendono da niente, ossia le

unità dalle quali si dipar tono gli altr i elementi

Page 290: Guida visual basic

In una Tr eeView, ogni elemento è detto appunto nodo ed è r appr esentato dalla classe Tr eeNode: ogni nodo può a sua

volta dipar tir si in più sotto-elementi, ulter ior i nodi, in un ciclo lungo a piacer e. Gli elementi che non der ivano da nulla

se non dal contr ollo stesso sono detti r oots, radici. Allo stesso modo delle car telle e dei file del computer , ogni nodo può

esser e indicato con un per cor so di for mato simile, dove i nome dei nodi sono separ ati da "\". La pr opr ietà di Tr eeNode

non sono niente di speciale o innovativo: sono già state tutte analizzate, o der ivate da Contr ol. Ecco come appar e

l'inter faccia, dopo aver aggiunto una Tr eeView tr w Ini con Dock = Fill e un Contex tMenuStr ip cntTr eeView ad essa

associato:

Page 291: Guida visual basic

B12. Scrivere un INI Reader - Parte II

Dopo aver spiegato e posizionato i var i contr olli con le pr opr ietà adatte, si deve stender e il codice che per mette al

pr ogr amma di legger e i file e visualizar li cor r ettamente. Ecco il sor gente commentato:

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.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.

Class Form1Private Sub ReadFile(ByVal File As String)

'Lo stream da cui leggere il fileDim Reader As New IO.StreamReader(File)'Una stringa che rappresenta ogni singola riga del fileDim Line As String'L'indice associato al numero di campi letti. Dato che ogni'campo costituirà una radice del grafico, bisogna sapere da'dove far derivare i relativi valori.'Questa variabile è opzionale, in quanto è possibile usare'la proprietà trwIni.Nodes.Count-1, poichè si aggiungono'valori sempre soltanto all'ultimo campo apertoDim FieldCount As Int16 = -1

'Imposta il testo della label di statolblStatus.Text = "Apertura del file in corso..."

'Finchè non si raggiunge la fine del file si continua'a leggereWhile Not Reader.EndOfStream

'Leggiamo una linea di file (S)Line = Reader.ReadLine'Se la linea è diversa da una riga vuotaIf Line <> Nothing Then

'Se la linea inizia per "[" (significa che è'un campo)If Line.StartsWith("[") Then

'Si aumenta FieldCount, che indica quanti campi 'si sono già letti (in base 0)FieldCount += 1'Rimuove il primo carattere, ossia "["Line = Line.Remove(0, 1)'Rimuove dalla linea l'ultimo carattere,'ossia "]"Line = Line.Remove(Line.Length - 1, 1)'Aggiunge una radice alla TreeViewtrwIni.Nodes.Add(Line)

Else'Altrimenti, se la linea non inzia per ";",'ossia non è un commentoIf Not Line.StartsWith(";") Then

'Aggiunge la linea come sotto-nodo'dell'ultimo campo inserito. La linea'conterrà il valore in forma' [nome]=[contenuto]'Attenzione! Possono esserci commenti in'riga, quindi si deve prima controllare'di eliminarli'Se l'indice del carattere ";" nella riga'è positivo...If Line.IndexOf(";") > 0 Then

'Rimuove tutto quello che viene dopo'il commentoLine = Line.Remove(Line.IndexOf(";"))

End IftrwIni.Nodes(FieldCount).Nodes.Add(Line)

End IfEnd If

End IfEnd While

Page 292: Guida visual basic

Il codice degli eventi è molto semplice, mentr e più inter essante è quello della pr ocedur a ReadFile. Per aver e una

panor amica delle oper azioni sulle str inghe usate, veder e capitolo r elativo. Per quanto r iguar da la logica del sor gente,

ecco una br eve spiegazione: viene letto il file r iga per r iga e, sulla base delle condizioni che si incontr ano man mano,

vengono eseguite istr uzioni diver se:

La linea è vuota : può capitar e che si lascino linee di testo vuote per separ ar e ulter ior mente i campi o valor i

dell'inter no dello stesso campo; in questo caso, poichè non c'è niente da legger e, semplicemente si passa oltr e

La linea inizia per "[" : come già detto, in un file ini, i campi sono r acchiusi tr a par entesi quadr e, per ciò la linea

costituisce il nome di un campo. Dopo aver eliminato le par entesi con oppor tune funzioni, si usa il r isultato per

aggiunger e alla Tr eeView una r oot mediante Nodes.Add. Questo metodo accetta, tr a i var i over loads, un

par ametr o str inga che costituisce il testo del nodo

La linea inizia per ";" : è un commento e semplicemente viene omesso. Potr este comunque includer lo come nodo

ausiliar lo e color ar lo con un color e differ ente

La linea non ha nessun delle car atter istiche indicate : è un valor e. Quindi si aggiunge il suo contenuto come

sotto-nodo all'ultimo nodo r oot aggiunto, con l'accor tezza di contr ollar e pr ima se ci sono dei commenti cosiddetti

in-line e di eliminar li

Ecco uno scr eenshot di come si pr eseta il pr ogr amma finito con un file ini car icato:

Ed ecco uno scr eenshot di come potr este far lo diventar e:

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.96.

'Chiude il fileReader.Close()

lblStatus.Text = "File aperto"

End Sub

Private Sub strOpen_Click(ByVal sender As Object, _ByVal e As EventArgs) Handles strOpen.Click'Ecco un esempio di OpenFileDialog da codiceDim FOpen As New OpenFileDialogFOpen.Filter = "Impostazioni di configurazione|*.ini"If FOpen.ShowDialog = Windows.Forms.DialogResult.OK Then

ReadFile(FOpen.FileName)End If

End Sub

Private Sub strExit_Click(ByVal sender As Object, _ByVal e As EventArgs) Handles strExit.Click'Esce dal programma, chiudendo il form correnteMe.Close()

End Sub

Private Sub strClearList_Click(ByVal sender As Object, _ByVal e As EventArgs) Handles strClearList.Click'Mostra un messaggio di conferma prima di procedereIf MessageBox.Show("Eliminare tutti gli elementi dela lista?", _

"INI Reader", MessageBoxButtons.YesNo, MessageBoxIcon.Question) = _Windows.Forms.DialogResult.No Then'Se si risponde di no, esce dalla proceduraExit Sub

End If

'Elimina tutti i noditrwIni.Nodes.Clear()

End SubEnd Class

Page 293: Guida visual basic
Page 294: Guida visual basic

B13. DateTimePicker - Lavorare con le date

Il tipo di dato standar d che il .NET Fr amewor k mette a disposizione per lavor ar e cone le date e gli or ar i è Date,

facente par te del Namespace System. Per compatibilità con il vecchio Visual Basic 6, è pr esenta anche

System.DateTime, che r appr esenta la stessa identica entità. Con questo semplice tipo è possibile far e di tutto e per ciò

non è necessar io definir e manualmente alcun metodo nuovo quando si lavor a con le date. Ecco un elenco dei metodi e

delle pr opr ietà più impor tanti:

Add(t): aggiunge alla data un fattor e t di tipo TimeSpan contenente una dur ata di tempo

AddYear s, AddMonths, AddDays, AddHour s, AddMinutes, AddSeconds, AddMilliseconds: aggiungono un fattor e t di

anni, mesi, gior ni, or e, minuti, secondi, millisecondi alla data, specificata come unico par ametr o

Year , Month, Day, Hour , Minute, Second, Millisecond: r estituiscono l'anno, il mese, il gior no, l'or a, i minuti, i

secondi o i millisecondi della data contenuta nella var iabile

DayOfWeek: r estituisce un enumer ator e che r appr esenta il gior no della settimana contenuto nella data della

var iabile

DayOfYear : r estituisce un numer o che indica il numer o del gior no in tutto l'anno

DaysInMonth(y, m): r estituisce il numer o di gior ni del mese m dell'anno y

Now: pr opr ietà shar ed che r estituisce la data cor r ente (Date.Now )

Par se(s): funzione shar ed che conver te la str inga s in una data; utile per quando si deve salvar e una data su file

Subtr act(d): sottr ae alla data della var iabile la data d, r estituendo un valor e di tipo TimeSpan (ossia 'tempo

tr ascor so')

ToLongDateStr ing: conver te la data in una str inga, espandendo la data in questo for mato: [gior no della

settimana] [gior no del mese] [mese] [anno] (esempio: vener dì 30 giugno 2006)

ToLongTimeStr ing: conver te l'or a della data in una str inga, espandendola in questo for mato: [or e].[minuti].

[secondi] (esempio: 13.13.07)

ToShor tDateStr ing: conver te la data in una str inga, contr aendola in questo for mato: [gior no del mese]\[mese]

\[anno] (esempio: 30/6/2006)

ToShor tTimeStr ing: conver te l'or a della data in una str inga, contr aendola in questo for mato: [or e].[minuti]

(esempio: 13.13)

ToFileTime : funzione cur iosa, che r estituisce la data in for mato file, ossia come multiplo di inter valli di 100

nanosecondi tr ascor si dal pr imo gennaio 1601 alle or e 12.00 di mattina

Tr yPar se(s, r ): tenta di conver tir e la str inga s in una data: se ci r iesce, r assume il valor e della data (r è

passata per indir izzo) e r estituisce Tr ue; se non ci r iece, r estituisce False

Par allelamente, viene definito anche il tipo TimeSpan ("tempo tr ascor so") che r appr esenta un lasso di tempo e si

ottiene con la differ enza di due valor i Date. Ha le stesse pr opr ietà sopr a elencate, fatta eccezione per alcune che

possono r ivelar si inter essanti, come Fr omDays, Fr omHour s, Fr omSeconds, Fr omMinutes, Fr omMilliseconds: funzioni

shar ed che cr eano un valor e di tipo TimeSpan a par tir e da un ammontar e di gior ni, or e, minuti, secondi o millisecondi.

Esempio: A long, long lifeEcco un esempio molto semplice e diver tito che applica i concetti sopr a esposti. Lo scopo del pr ogr amma è di calcolar e

con una buona pr ecisione la dur ata della nostr a vita, avendo immesso pr ecedentemente la data di nascita. Il contr ollo

usato è DateTimePicker , le cui pr opr ietà sono autoesplicative. Per or a pr ender ò in analisi solo le pr opr ietà For mat e

CustomFor mat. La pr ima per mette di definir e il for mato del contr ollo: è r appr esentata da un enumer ator e che può

Page 295: Guida visual basic

assumer e quattr o valor i, Long (data in for mato esteso, come la r estituisce la funzione Date.ToLongDateStr ing), Shor t

(data in for mato br eve, come la r estituisce la funzione Date.ToShor tdateStr ing), Time (or a in for mato esteso) e

Custom (per sonalizzato). Se viene scelta l'ultima opzione, si deve impostar e la str inga CustomFor mat in modo da

r ipr odur r e il valor e in confor mità ai pr opr i bisogni. Nella str inga possono pr esenziar e queste sequenze di car atter i:

d : gior no del mese, con una o due cifr e a seconda dei casi

dd : gior no del mese, sempr e con due cifr e (vengono aggiunti zer i sulla sinistr a nel caso manchino posti)

ddd : gior no della settimana, abbr eviato a tr e car atter i secondo la cultur a cor r ente

dddd : gior no della settimana, con nome completo

M : mese, con una o due cifr e a seconda dei casi

MM : mese, sempr e con due cifr e

MMM : nome del mese, abbr eviato a tr e car atter i secondo la cultur a cor r ente

MMMM : nome completo del mese

y : anno, con una o due cifr e a seconda dei casi

yy : anno, sempr e con due cifr e

yyyy : anno, a quattr o cifr e

H : or a, in for mato 24 or e con una o due cifr e

HH : or a, in for mato 24 or e con due cifr e

h : or a, in for mato 12 or e, con una o due cifr e

hh : or a, in for mato 12 or e, con due cifr e

m : minuti, con una o due cifr e

mm : minuti, con due cifr e

s : secondi, con una o due cifr e

ss : secondi, con due cifr e

f : fr azioni di secondo (un numer o qualsiasi da uno a sette di "f" consecutive cor r isponde ad altr ettanti decimali)

Dato che il contr ollo dovr à espor r e il valor e in for mato:

[nome giorno] [giorno] [nome mese] [anno], ore [ora]:[minuti]

La str inga di for mato da inser ir e sar à:

dddd d MMMM yyyy, ore HH:mm

Gli stessi patter n valgono anche se posti come ar gomento della funzione Date.ToStr ing("For mato"). I contr olli da

aggiunger e sono un DateTimePicker (dtpBir thday), con una label di spiegazione a fianco, una label che visualizzi i

r isultati (lblAge) e un timer (tmr Refr esh) per aggior nar e il r isultato a ogni secondo che passa. Or a non r esta che

scr iver e il codice, per altr o molto semplice. Il sogente fa uso di un contr ollo Timer , che una volta abilitato

(Timer .Enabled=Tr ue o Timer .Star t()), lancia un evento Tick ogni Timer .Inter val millisecondi cir ca (il valor e è molto

var iabile, a seconda della velocità del computer su cui viene fatto cor r er e).

01.02.03.04.05.06.07.08.09.10.11.12.13.14.

Class Form1Private Sub tmrRefresh_Tick(ByVal sender As Object, _

ByVal e As EventArgs) Handles tmrRefresh.Tick'Ottiene la differenza tra le due dateDim Age As TimeSpan = (Date.Now - dtpBirthDay.Value)'La trasforma in secondiDim Seconds As Double = Age.TotalSeconds'Variabile temporanea che serve alla costruzioneDim AgeStr As New System.Text.StringBuilder

With AgeStr

.AppendLine("Hai vissuto")

Page 296: Guida visual basic

Per il mio caso, il r isultato è questo:

15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.32.

'Calcola i giorni secondo il modo già visto nelle prime'lezioni sulle classi e le proprietà.AppendFormat("{0} giorni{1}", Seconds \ (60 * 60 * 24), vbCrLf)Seconds -= (Seconds \ (60 * 60 * 24)) * (60 * 60 * 24)

'E così anche ore, minuti e secondi.AppendFormat("{0} ore{1}", Seconds \ 3600, vbCrLf)Seconds -= (Seconds \ 3600) * 3600

.AppendFormat("{0} minuti{1}", Seconds \ 60, vbCrLf)Seconds -= (Seconds \ 60) * 60

.AppendFormat("{0:n0} secondi", Seconds)

'Quindi mette il risultato come testo della labellblAge.Text = .ToString

End WithEnd Sub

End Class

Page 297: Guida visual basic

B14. ImageList

In fase di pr ogettazione, se si vogliono aggiunger e immagini a contr olli come Button, Label, SplitButton, ToolBox et

similia è sufficiente selezionar e la pr opr ietà Image (o Backgr oundImage), apr ir e la finestr a di dialogo mediante

pr essione sul pulsante che appar e, sceglier e quindi un file immagine dall'Har d Disk o dalle r isor se del pr ogetto, e

confer mar e la scelta per ottener e un effetto ottimo. Tuttavia, ciò non è sempr e possibile, ad esempio se a r un-time si

vogliono associar e deter minate icone a elementi di una lista che non è possibile pr eveder e dur ante la stesur a del

codice. In situazioni simili, il contr ollo che viene in aiuto del pr ogr ammator e si chiama ImageList. Esso costituisce una

lista, or dinata secondo indici e chiavi, che contiene immagini pr ecedentemente car icate dallo sviluppator e: tutte

queste vengono r idimensionate secondo una dimensione fissata dalle pr opr ietà del contr ollo e hanno una limitazione di

pr ofondità di color e, sempr e pr edeter minata, da 8 a 32 bit. Per ottener e effetti di gr ande impatto, è consigliabile

utilizzar e for mati ad ampio spettr o di color e e con tr aspar enza come il Por table Networ k Gr aphics (*.png), oppur e il

JPEG (*.jpg) se si vuole r ispar miar e spazio pur conser vando una discr eta qualita'; il for mato ideale è 32x 32 pix el per le

icone gr andi e 22x 22 o 16x 16 in quelle piccole come nei menù a discesa o nelle ListView a dettagli.

Il meccanismo che per mette ai contr olli di fr uir e delle r isor se messe a disposizione da un'ImageList è lo stesso usato dal

Contex tMenuStr ip. Ogni contr ollo con inter faccia che suppor ti questo pr ocesso, dispone di una pr opr ietà ImageList, che

deve esser e impostata di conseguenza a seconda della lista di immagini che si vuole quel contr ollo possa utilizzar e.

Successivamente, i singoli elementi al suo inter no sono dotati delle pr opr ietà ImageIndex e ImageKey, che per mettono

di associar vi un'immagine pr elevandola mediante l'indice o la chiave impostata. Ecco alcuni esempi di come potr ebber o

pr esentar si contr olli di questo tipo:

Imag eList su ListView

Imag eList su TreeView

Imag eList su TabControl

Reperire le iconeIndubbiamente questo contr ollo offr e moltissime possibilità di per sonalizzar e la veste gr afica dell'applicazione a piacer e

del pr ogr ammator e, tuttavia se non si dispone di mater iale adatto, il suo gr ande aiuto viene meno. Per questo

motivo, dar ò alcuni sugger imenti su come r eper ir e un buon numer o di icone fr eewar e o al limite sotto licenza lgpl (il

che le r ende disponibili per l'uso da par te di softwar e commer ciali). Come pr ima r isor sa, c'è il pr ogr amma AllEx Icon,

scr itto da Maur o Rossi in Visual Basic 6, che potete tr ovar e a questo indir izzo . Dopo aver lo avviato, basta impostar e

la dir ector y di r icer ca su C:\WINDOWS\System32 (per sistemi oper ativi Windows XP) e il filtr o su "*.dll". Ver r anno

estr atte moltissime belle icone, con la possibilità di salvar le in for mato bitmap una alla volta o in massa. Dato il lor o

for mato, anche conver tite in JPEG, r imar r à un color e di sfondo, che può venir e par zialmente eliminato impostando la

pr opr ietà ImageTr anspar ency del for m su Tr anspar ent o su White, r endendo quindi tr aspar ente il lor o sfondo. Come

seconda possibilità ci sono alcuni pacchetti di icone r eper ibili dal web. Il pr imo che consiglio è "Nuvola", lo stesso che uso

per le mie applicazioni, distr ibuito sotto licenza LGPL su questo sito; il secondo è "500.000 Icone!", una collezione di

cir ca 8000 icone, divise in *.ico e *.png, messe insieme da svar iate fonti del web: ogni r isor sa è stata r esa pubblica dal

Page 298: Guida visual basic

suo cr eator e e non ci sono limitazioni al lor o uso. Il pacchetto può esser e tr ovato solo attr aver so eMule. La ter za

possibilità consiste nel cer car e sulla r ete insiemi di immagini messe liber amente a disposizione di tutti da qualche

volenter oso designer , ad esempio su questa pagina di Wikipedia, dove, navigando tr a le var ie categor ie, è possibile

ottener e svar iate centinaia di icone.

Page 299: Guida visual basic

B15. ListView

La ListView è un contr ollo complesso e di gr ande impatto visivo. È lo stesso tipo di lista usato dall'ex plor er di w indows

per visualizzar e files e car telle. Le sue pr opr ietà per mettono di per sonalizzar ne la visualizzazione in cinque stili

diver si: i più impor tanti di questi sono Lar ge Icone (Icone gr andi), Small Icon (Icone piccole) e Details (Dettagli). Ci sono

poi anche Tile e List, ma vengono usati meno spesso. Ecco alcuni esempi:

Larg e Icon

Small Icon

Details

ListViewCome al solito, ecco la compilation delle pr opr ietà più inter essanti:

CheckBox es : indica se la listview debba visualizzar e delle CheckBox vicino ad ogni elemento

Columns : collezione delle colonne disponibili. Ogni colonna è contr addistinta da un testo (Tex t), un indice

d'immagine (ImageIndex ) e un indice di visualizzazione (DisplayIndex ) che specifica la sua posizione or dinale nella

visualizzazione. Le colonne sono visibili sono con View = Details

FullRowSelect : indica se evidenziar e tutta la r iga o solo il pr imo elemento, quando View = Details

Gr idLines : indica su visualizzar e le r ighe della gr iglia, quando View = Details

Gr oups : collezione dei gr uppi disponibili

Header Style : specifica se le intestazioni delle colonne possano esser e cliccate o meno

HideSelection : specifica se la listview debba nasconder e la selezione quando per de il Focus, ossia quando un altr o

contr ollo diventa il contr ollo attivo

HotTr acking : abilita gli elementi ad appar ir e come collegamenti iper testuali quando il mouse ci passa sopr a

Hover Selection : se impostata su Tr ue, sar à possibile selezionar e un elemento semplicemente sostandoci sopr a

con il mouse

Items : collezione degli elementi della listview

LabelEdit : specifica se sia possibile modificar e il testo dei SubItems da par te dell'utente, quando View = Details

Lar geImageList : ImageList per View = Lar ge Icon / Tile / List

MultiSelect : indica se si possano selezionar e più elementi contempor aneamente

Owner Dr aw : indica se gli elementi debbano esser e disegnati dal contr ollo o dal codice del pr ogr ammator e. Vedi

ar ticolo r elativo

ShowGr oups : deter mina se visualizzar e i gr uppi

Show ItemToolTips : deter mina se visualizzar e i ToolTips dei r ispettivi elementi

SmallIconList : ImageList per View = Small Icon / Details

Sor ting : il tipo di or dinamento, se alfabetico ascendente o discendente.

Page 300: Guida visual basic

ListViewItemOgni elemento della ListView è contr addistinto da un oggetto ListView Item, che, a differ enza di quanto avveniva cone

le nor mali liste come ListBox e ComboBox , non costituisce una semplice str inga (o un tipo base facilmente

r appr esentabile) ma un nucleo a sè stante, del quale si possono per sonalizzar e tutte le car atter istiche visive e

stilistiche. Poichè la ListView è compatibile con l'ImageList, tutti i membr i della collezione Items sono in gr ado di

impostar e l'indice d'immagine associato, come si è analizzato nella lezione scor sa. Inoltr e, sempr e manipolando le

pr opr ietà, si può attr ibuir e ad ogni elemento un testo, un font, un color e diver so a seconda delle necessità. Nella

visualizzazione a dettagli si possono impostar e tutti i valor i cor r ispettivi ad ogni colonna mediante la pr opr ietà

SubItems, la quale contiene una collezione di oggetti ListViewSubItem: di questi è possibile modificar e il font e il color e,

oltr e che il testo.

ListViewGroupUn gr uppo è un insieme di elementi r aggr uppati sotto la stessa etichetta. I gr uppi vengono aggiunti alla lista

utilizzando l'apposita pr opr ietà Gr oups e ogni elemento può esser e assegnato ad un gr uppo con la stessa pr opr ietà

(ListView Item.Gr oup). Oggetti di questo tipo godono di poche car atter istiche: solo il testo ed il nome sono modificabili.

Pr ima di pr oceder e con ogni oper azione, per ò, bisogna assicur ar si che la pr opr ietà ShowGr oups della ListView sia

impostata a Tr ue, altr imenti... beh, niente festa.

Ecco un esempio di codice:

ListView con View = DetailsLa ListView a dettagli è la ver sione più complessa di questo contr ollo, ed è contr addistinta dalla classica visualizzazione

a colonne. Ogni colonna viene deter minata in fase di sviluppo o a r un-time agendo sulla collezione Columns nelle

pr opr ietà della lista e bisogna r icor dar e l'or dine in cui le colonne vengono inser ite poichè questo influisce in manier a

significativa sul compor tamento dei SubItems. Infatti il pr imo SubItem di un ListView Item andr à sotto la pr ima colonna,

quella con indice 0, il secondo sotto la seconda e così via. Un er r or e di or dine potr ebbe pr odur r e quindi, r isultati

sgr adevoli. Nell'esempio che segue, scr iver ò un br eve pr ogr amma per calcolar e la spesa totale conoscendo i singoli

pr odotti e le singole quantità di una lista della spesa. Ecco un'antepr ima di come dovr ebbe appar ir e la finestr a:

I contr olli usati sono deducibili dal nome e dall'utilizzo. Ecco quindi il codice:

01.02.03.04.05.06.07.08.09.10.

'Nuovo gruppo 'Testo esplicativo'Dim G As New ListViewGroup("Testo esplicativo")'Nuovo elemento 'Elemento'Dim L As New ListViewItem("Elemento")'Aggiunge il gruppo alla listviewListView1.Groups.Add(G)'Setta il gruppo a cui L apparterràL.Group = G'Aggiunge l'elemento alla listaListView1.Items.Add(L)

01.02.03.04.05.06.07.08.

Class Form1Private Sub cmdAdd_Click(ByVal sender As System.Object, _

ByVal e As System.EventArgs) Handles cmdAdd.Click'Nuovo elementoDim Item As ListViewItem'Array dei valori che andranno a rappresentare i campi di'ogni singola colonna

Page 301: Guida visual basic

Per i più cur iosi, mi addentr er ò ancor a un pò di più nel par ticolar e, nella fattispecie su una car atter istica molto

appr ezzata in una ListView a dettagli, ossia la possibilità di or dinar e tutti gli elementi con un click. In questo caso,

facendo click sull'intestazione (header ) di una colonna, sar ebbe possibile or dinar e gli elementi sulla base della qualità che

quella colonna espone: per nome, per pr ezzo, per quantità. Dato che il metodo Sor t non accetta alcun over load che

consenta di usar e un Compar er , bisogner à implementar e un algor itmo che compar i tutti gli elementi e li or dini. In

questo esempio si fa r icor so a due classi che implementano l'inter faccia gener ica ICompar er (Of ListView Item): il pr imo

compar a il nome del pr odotto, mentr e il secondo il pr ezzo e la quantità. Ecco il codice da aggiunger e:

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.

Dim Values() As String = _{txtProduct.Text, nudPrice.Value, nudQuantity.Value}

'Inizializza Item sulla base dei valori datiItem = New ListViewItem(Values)'E lo aggiunge alla listalstProducts.Items.Add(Item)

End Sub

Private Sub cmdDelSelected_Click(ByVal sender As System.Object, _ByVal e As System.EventArgs) Handles cmdDelSelected.Click'Analizza tutti gli elementi selezionatiFor Each SelItem As ListViewItem In lstProducts.SelectedItems

'E li rimuove dalla listalstProducts.Items.Remove(SelItem)

NextEnd Sub

Private Sub cmdCalculate_Click(ByVal sender As System.Object, _

ByVal e As System.EventArgs) Handles cmdCalculate.Click'Totale spesaDim Total As Single = 0'Prezzo unitarioDim Price As Single'Quantit?Dim Quantity As Int32

For Each Item As ListViewItem In lstProducts.Items

'Ottiene i valori da ogni elementoPrice = CSng(Item.SubItems(1).Text)Quantity = CInt(Item.SubItems(2).Text)Total += Price * Quantity

Next

MessageBox.Show("Totale della spesa: " & Total & "€.", _"Spesa", MessageBoxButtons.OK, MessageBoxIcon.Information)

End SubEnd Class

01.02.03.04.05.06.07.08.09.10.11.12.13.14.15.16.17.18.19.20.21.22.23.

Class Form1'Queste classi saranno i comparer usati per ordinare'le righe della ListView, al pari di come si è imparato nelle'lezioni sulle interfaccePrivate Class ProductComparer

Implements IComparer(Of ListViewItem)

Public Function Compare(ByVal x As ListViewItem, _ByVal y As ListViewItem) As Integer _Implements IComparer(Of ListViewItem).Compare'Gli oggetti da comparare sono ListViewItem, mentre la proprietà'che bisogna confrontare è il nome del prodotto, ossia'il primo sotto-elementoDim Name1 As String = x.SubItems(0).TextDim Name2 As String = y.SubItems(0).TextReturn Name1.CompareTo(Name2)

End FunctionEnd Class

Private Class NumberComparer

Implements IComparer(Of ListViewItem)

Page 302: Guida visual basic

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.80.81.82.83.84.85.86.87.88.89.90.91.92.93.94.

Private Index As Int32

'Price è True se ci si riferisce al Prezzo (elemento 1)'oppure False se ci si riferisce alla quantità (elemento 2)Sub New(ByVal Price As Boolean)

If Price ThenIndex = 1

ElseIndex = 2

End IfEnd Sub

Public Function Compare(ByVal x As ListViewItem, _

ByVal y As ListViewItem) As Integer _Implements IComparer(Of ListViewItem).Compare'Qui bisogna ottenere il prezzo o la quantità: ci si basa'sul parametro passato al costruttoreDim Val1 As Single = x.SubItems(Index).TextDim Val2 As Single = y.SubItems(Index).TextReturn Val1.CompareTo(Val2)

End FunctionEnd Class

'Scambia due elementi in una lista: dato che ListViewItem sono'variabili reference, bisognerebbe clonarli per spostarne il valore,'come si faceva con i valori value, in questo modo:'Dim Temp As ListViewItem = L1.Clone()'L1 = L2.Clone()'L2 = Temp'Ma si userebbe troppa memoria. Perciò la via più facile è'usare i metodi della lista per scambiare gli elementiPrivate Sub SwapInList(ByVal List As ListView, ByVal Index As Int32)

Dim Temp As ListViewItem = List.Items(Index + 1)List.Items.RemoveAt(Index + 1)List.Items.Insert(Index, Temp)

End Sub

'Ordina gli elementi con l'algoritmo Bubble Sort già'descritto nell'ultima lezione teoricaPrivate Sub SortListViewItems(ByVal List As ListView, _

ByVal Comparer As IComparer(Of ListViewItem))Dim Occurrences As Int32 = 0

Do

Occurrences = 0For I As Int32 = 0 To List.Items.Count - 1

If I = List.Items.Count - 1 ThenContinue For

End IfIf Comparer.Compare(List.Items(I), List.Items(I + 1)) = 1 Then

SwapInList(List, I)Occurrences += 1

End IfNext

Loop Until Occurrences = 0End Sub Private Sub lstProducts_ColumnClick(ByVal sender As System.Object, _

ByVal e As ColumnClickEventArgs) Handles lstProducts.ColumnClickSelect Case e.Column

Case 0'NomeMe.SortListViewItems(lstProducts, New ProductComparer())

Case 1'PrezzoMe.SortListViewItems(lstProducts, New NumberComparer(True))

Case 2'QuantitàMe.SortListViewItems(lstProducts, New NumberComparer(False))

End SelectEnd Sub

End Class

Page 303: Guida visual basic
Page 304: Guida visual basic

B16. ToolStrip e TabControl

ToolStripTutti conoscono benissimo l'inter faccia di Micr osoft Wor d, dove sopr a lo spazio in cui si scr ive ci sono mir idai di icone,

ognuna con la pr opr ia funzione (Salva, Apr i, Nuovo, Copia, Incolla ecc...): la bar r a degli str umenti, così chiamata, dove

sono collocate quelle icone è una ToolStr ip. Ecco osser var e un esempio di toolstr ip cr eata con vb.net:

Le pr opr ietà pr incipali di una toolstr ip:

ImageScalingSize: molto impor tante, deter mina di che dimensione sar anno le immagini della toolstr ip; per

impostar le della dimensione di quelle di Wor d si lasci pur e 16;16 (16x 16), mentr e per far la appar ir e con la

stessa dimensione di quelle in immagine un 24;24 è accettabile (consiglier ei di non andar e tr oppo oltr e)

Items: l'insieme degli elementi della toolstr ip; ciò che si può metter e nella toolstr ip è un piccolo gr uppo di

contr olli nor malissimi, già analizzati, ossia: Button (un nor male pulsante: nell'immagine, Testi e Cr onologia sono

Button); Dr opDownItems (menù a discesa, identico a MenuStr ip: nell'immagine Documenti è un dr opdownitem);

SplitButton (una fusione tr a button e dr opdownitems, poichè gode di un evento click pur essendo una lista a

discesa); Label (una nor malissima etichetta di testo: nell'immagine Nome è una label); Tex tBox (casella di testo:

nell'immagine il testo "Nicolo'" contenuto in una tex tbox ); ComboBox (lista a cascata: nell'immagine è il pr imo

contr ollo della seconda r iga); Pr ogr essBar (ultimo contr ollo); Separ ator (un separ ator e, ossia una bar r a

ver ticale che separ a gli elementi: nell'immagine è pr esente fr a Documenti e Nome)

Tex tDir ection: dir ezione del testo

Per r ender e più aggr aziata la veste gr afica del pr ogr amma, una toolstr ip è molto utile.

Un'ultima cosa: facendo click col pulsante destr o sulla toolstr ip in fase di pr ogettazione, si dispor r à di var ie opzioni, fr a

cui quelle di Aggiunger e uno Str umento, Conver tir e un contr ollo in altr o tipo e Aggiunger e alla bar r a le icone standar d

di lavor o (ossia Apr i, Nuovo, Salva, Copia, Incolla e Taglia, già cor r edate di icona).

TabControlTabContr ol è un contr ollo for mato da più schede sovr apposte, ognuna delle quali contiene al pr opr io inter no una

inter faccia diver sa. Questo contr ollo, infatti, insieme a Gr oupBox , SplitPanel e pochi altr i, è un contenitor e cr eato

appositamente per or dinar e più contr olli, in questo caso c'è la possibilità di stipar e una enor me quantità di layout in

poco spazio: ogni scheda (Tab) è accessibile con un click e cambia l'inter faccia visualizzata.

A seconda della pr opr ietà Appear ance, TabContr ol può pr esentar si sotto tr e vesti gr afiche differ enti, come mostr ato in

figur a: in or dine dall'alto al basso sono Nor mal, Buttons e FlatButtons. Per inser ir e uno o più contr olli all'inter no di una

scheda è sufficiente tr ascinar li con il mouse oppur e aggiunger li da codice facendo r ifer imento alla pr opr ietà TabPages.

Ecco la lista delle pr opr ietà più r ilevanti:

Appear ance : lo stile di visualizzazione

ItemSize : la dimensione dell'intestazione delle schede

Multiline : se impostato su Tr ue, qualor a le schede fosser o tr oppe, ver r anno visualizzate diver se file di header

una sopr a all'altr a; altr imenti appar ir anno due pulsantini a mò di scr ollbar che per metter anno di scor r er le

Page 305: Guida visual basic

or izzontalmente. Nel pr imo caso, la pr opr ietà in sola lettur a RowCount r estituisce il numer o di r ighe

visualizzate

TabPages : collezione di tutte le schede disponibili sotto for ma di oggetti TabPage

Per por tar e in pr imo piano una scheda è possibile r ichiamar e da codice il metodo TabContr ol.SelectTab(Index ),

passando come unico par ametr o l'indice della scheda da r ilevar e, o, in alter nativa, tutto l'oggetto TabPage.

Page 306: Guida visual basic

B17. NotifyIcon e SplitContainer

NotifyIconLa par te infer ior e destr a della bar r a delle applicazioni di Windows è denominata System Tr ay e r aggr uppa tutte le

icone dei pr ogr ammi cor r entemente in esecuzione sul sistema oper ativo, ovviamente solo se questi ne r ichiedono una.

Ecco uno scr eenshot:

Per aggiunger e un'icona al pr ogetto, che ver r à automaticamente visualizzata in questo spazio dopo l'avvio

dell'applicazione, è sufficiente aggiunger e al designer un contr ollo NotifyIcon, che non ha inter faccia gr afica in ambiente

di sviluppo. Le pr opr ietà inter essanti sono queste:

BaloonTipIcon : deter mina l'icona da visualizzar e a sinistr a del titolo del fumetto (si può sceglier e tr a er r or , info

e war ning)

BaloonTipTex t : testo del fumetto

BaloonTipTitle : titolo del fumetto

Icon : icona visualizzata nella system tr ay (si possono sceglier e solo file icona *.ico)

ShowBaloonTip(x ) : visualizza il fumetto dell'icona per x millisecondi (2000 è una buona media)

Tex t : descr izione visualizzata quando il mouse sosta per qualche secondo sull'icona

Come molti altr i contr olli, anche questo suppor ta un menù contestuale gr azie al quale si possono eseguir e molte

oper azioni anche in assenza dell'inter faccia utente completa. Inoltr e vengono r egistr ati anche eventi come il Click o il

doppio Click del mouse sull'icona e mediante questi si può r idur r e il for m in modo che non appaia nella bar r a delle

applicazioni ma che pr esenzi solamente l'icona nella System Tr ay. Il codice da usar e in casi simili è molto semplice:

Per r ipor tar e tutto allo stato pr ecedente è sufficiente inver tir e i valor i booleani.

Fumetto

SplitContainerAnche lo SplitContainer è un contenitor e, e può r ivelar si davver o molto utile. La sua peculiar ità consiste nel poter

r idimensionar e con il mouse, spostando quello che viene chiamato splitter , le due par ti del contr ollo. Ogni par te è una

super ficie contenitor e a sè stante e viene r appr esentata da un oggetto Panel. Ecco le pr opr ietà più significative:

Bor der Style : pr opr ietà enumer ata che descr ive lo stile dei bor di: assenti (None), a linea singola (Single) o 3D

(Fix ed3D)

Fix edPanel : specifica quale dei due pannelli debba r estar e di dimensioni fisse dur ante l'atto di

r idimensionamento

IsSplitter Fix ed : deter mina se lo splitter è fisso o può muover si

1.2.3.4.5.6.

'Nasconde il form dalla barra delle applicazioniMe.ShowInTaskBar = False'Rende il form invisibileMe.Visible = False'Se l'icona non è già visibile, la rende visibileMe.nftIcon.Visible = True

Page 307: Guida visual basic

Or ientation : indica l'or ientamento dei pannelli, se ver ticale o or izzontale

Panel1 : r ifer imento al pannello 1; gli Splitter Panel non hanno alcuna pr opr ietà differ ente da Contr ol, e per ciò

non vale la pena di soffer mar si altr o tempo su questi

Panel1Collapsed : deter mina se all'inizio il Panello 1 sia collssato, ossia pr ivo di dimensione, il che implica che solo

il Pannello 2 sia visibile

Panel1MinSize : la dimensione minima del Pannello 1; si r ifer isce alla lar ghezza se Or ientation = Ver tical,

altr imenti all'altezza

Panel2... : le stesse di Panel 1

Splitter Distance : la distanza dello splitter dall'angolo super ior e sinistr o, in pix el

Splitter Incr ement : l'incr emento della posizione splitter quando viene mosso dal mouse, in pix el

Splitter Width : la lar ghezza dello splitter , in pix el

Page 308: Guida visual basic

B18. RichTextBox e Syntax Highlightning

La RichTex tBox è un contr ollo molto potente e dallo stile simile ai fogli di micr osoft wor d, che mantiene, tuttavia, un

layout w indows 98. Costituisce un potenziamento della tex tbox nor male poichè è in gr ado di visualizzar e dei testi

for mattati, ossia contenenti tag che ne definiscono lo stile: gr assetto, sottolineato, bar r ato, cor sivo, color e,

gr andezza, font ecc... Come sugger isce il nome, in questi contr olli il più delle volte viene car icato un file con estensione

.r tf (r ich tex t for mat). Un esempio gr afico di come potr ebbe appar ir e un testo in una r ichtex tbox :

La pr opr ietà e i metodi più impor tanti di una r ichtex tbox sono:

AppendTex t(t): aggiunge la str inga t al testo della r ichtex tbox

CanRedo / CanUndo: pr opr ietà che deter minano qualor a sia possibile r ifar e o annullar e dei cambiamenti

appor tati al testo

CaseSensitive: deter mina se la r ix htex tbox faccia differ enza tr a le maiuscole o le minuscole o consider i

solamente il testo (vedi Opzioni di Compilazione->Compar e)

Clear : cancella tutto il testo della r ichtex tbox

Clear Undo: cancella la lista che r ipor ta tutti i cambiamenti effettuati, così che non sia più possibile r ichiamar e la

pr ocedur a Undo

Copy / Cut / Paste: copia, taglia e incolla il testo selezionato dalla o nella clipboar d

DefaultFont / DefaultFor eColor / DefaultBackColor : deter minano r ispettivamente il font, il color e del testo e il

color e di sfondo pr eimpostati nella r ichtex tbox

DeselectAll: deseleziona tutto (equivale a por r e SelectionLength = 0)

DetectUr ls: deter mina qualor a tutti gli indir izzi ur l siano for mattati secondo il calssico stile blu sottlineato dei

collegamenti iper testuali

Find: impor tantissima funzione che per mette di tr ovar e qualsiasi str inga all'inter no del testo. Ne esistono 4

ver sioni (in r ealtà 7, ma le altr e non sono impor tanti per or a) modificate tr amite over loading: la pr ima chiede

di specificar e solo la str inga, la seconda anche le opzioni di r icer ca, la ter za anche l'indice da cui iniziar e la

r icer ca e la quar ta anche l'indice a cui ter minar e la r icer ca. Gli indici r ifer iscono una posizione nel testo

basandosi sul numer o di car atter i (r icor date, per ò, che gli indici in vb.net sono sempr e a base 0, quindi il pr imo

car atter e avr à indice uguale a 0, il secondo a 1 e così via). Le opzioni di r icer ca sono 5, deter minate da un

enumer ator e: MatchCase indica se pr ender e in consider azione anche la maiuscole e le minuscole; NoHighlight

indica di non evidenziar e il testo tr ovato; None specifica di non far niente; Rever se specifica che bisogna

tr ovar e la str inga al contr ar io; WholeWor d, invece, pr ecisa che la str inga deve esser e una par ola a sè stante,

quindi, nalla maggior par te dei casi, separ ata da spazi o da punteggiatur a dalle altr e

GetChar Fr omPosition(p) / GetChar Index Fr omPosition(p): funzioni che r estituiscono il car atter e (o il suo indice)

che si tr ova in un punto pr eciso specificato come par ametr o p

GetChar Index Fr omLine(n) / GetChar Index OfCur r entLine: funzioni che r estituiscono r ispettivamente l'indice del

pr imo car atter e della linea n e l'indice del pr imo della linea cor r ente, ossia quella su cui è fer mo il cur sor e

Lines: r estituisce un ar r ay di str inghe r appr esentanti il testo di ogni r iga della r ichtex tbox

LoadFile(f): car ica il file f nella r ix htex tbox : f può esser e anche un nor male file di testo

Rtf: r estituisce il testo della r ichtex tbox , includendo tutti i tag r tf

SaveFile(f): salva il testo for mattato in un file

Select(i, l) / SelectAll: la pr ima pr ocedur a seleziona un testo lungo l a par tir e dall'indice i, mentr e la seconda

Page 309: Guida visual basic

seleziona tutto

SelectedRtf / SelectedTex t: imposta o r estituisce il testo selezionato, sia in modo r tf (con i tag) che in modo

nor male (solo testo)

Selection...: tutte le pr opr ietà che iniziano con 'Selection' impostano o r estituiscono le opzioni del testo

selezionato, come il font, il color e, l'indentazione, l'allineamento ecc... SelectionStar t indica l'indice a cui inizia la

selezione, mentr e SelectionLength la sua lunghezza: impostar e questi due par ametr i equivale a r ichiamar e la

funzione Select

Undo / Redo: annulla l'ultima azione o la r ipete. Le pr opr ietà UndoActionName e RedoActionName r estituiscono il

nome di quell'azione

ZoomFactor : imposta o r estituisce il fattor e di ingr andimento della r ichtex tbox

Si è visto che le oper azioni che si possono eseguir e su questo contr ollo sono numer osissime, una più utile dell'altr a, ma

non è finita qui. Oltr e a esser e anche utilissima per contener e testo for mattato, la r ichtex tbox offr e anche str umenti

per modificar lo: uno di questi è il Syntax Highlighting, ossia l'evidenziator e di sintassi, pr esente in quasi ogni IDE per

linguaggi.

Syntax HighlightingQuesta tecnica consente di evidenziar e deter minate par ole chiave nel testo del contr ollo con un color e o uno stile

diver so dal r esto. È il caso delle par ole r iser vate. Sia con Visual Basic Ex pr ess che con Shar pDevelop o Visual Studio, le

keywor d vengono evidenziate con un color e differ ente, di solito in blu. È possibile r ipr odur r e lo stesso compor tamento

nella Rix hTex tBox . Ho impiegato del tempo a tr ovar e un codice già fatto r iguar do questo ar gomento e, dopo aver

cer cato molto, ci sono r iuscito: sono giunto alla conclusione che questo sia il miglior e della r ete, anche se si può

sempr e appor tar e qualche cor r ezione.

Si apr a un nuovo pr ogetto Libr er ia di Classi, e s'incolli tutto il codice nella classe Syntax RTB, dopodichè si clicchi

Build->Build [Nome pr ogetto] per gener ar e il contr ollo. Nonostante non si sia specificato che la classe r appr esenti un

contr ollo, il fatto che essa der ivi da RichTex tBox l'ha implicitamente sugger ito al compilator e. Syntax RTB non è altr o

che una RichTex tBox con dei metodi in più per il syntax highlighting. Si tr ascini il contr ollo sul for m nor malmente come

una tex tbox .

Ecco la classe commentata e r ior dinata:

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.

Public Class SyntaxRTBInherits System.Windows.Forms.RichTextBox

'La funzione SendMessage serve per inviare dati messaggi'a una finestra o un dispositivo allo scopo di ottenere'dati valori od eseguire dati compitiPrivate Declare Function SendMessage Lib "user32" Alias "SendMessageA" _

(ByVal hWnd As IntPtr, ByVal wMsg As Integer, _ByVal wParam As Integer, ByVal lParam As Integer) As Integer

'Blocca il Refresh della finestraPrivate Declare Function LockWindowUpdate Lib "user32" _

(ByVal hWnd As Integer) As Integer

'Campo privato che specifica se il meccanismo di syntax'highlighting è case sensitive oppure noPrivate _SyntaxHighlight_CaseSensitive As Boolean = False'La tabella delle parolePrivate Words As New DataTable

Public Property CaseSensitive() As Boolean

GetReturn _SyntaxHighlight_CaseSensitive

End GetSet(ByVal Value As Boolean)

_SyntaxHighlight_CaseSensitive = Value

Page 310: Guida visual basic

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.

End SetEnd Property

'Contiene costanti usate nell'inviare messaggi all'API'di windowsPrivate Enum EditMessages

LineIndex = 187LineFromChar = 201GetFirstVisibleLine = 206CharFromPos = 215PosFromChar = 1062

End Enum

'OnTextChanged è una procedura privata che ha il compito'di generare l'evento TextChanged: prima di farlo, colora il'testo, ma in questo caso l'evento non viene più generatoProtected Overrides Sub OnTextChanged(ByVal e As EventArgs)

ColorVisibleLines()End Sub

'Colora tutta la RichTextBoxPublic Sub ColorRtb()

Dim FirstVisibleChar As IntegerDim i As Integer = 0

While i < Me.Lines.Length

FirstVisibleChar = GetCharFromLineIndex(i)ColorLineNumber(i, FirstVisibleChar)i += 1

End WhileEnd Sub

'Colora solo le linee visibiliPublic Sub ColorVisibleLines()

Dim FirstLine As Integer = FirstVisibleLine()Dim LastLine As Integer = LastVisibleLine()Dim FirstVisibleChar As Integer

If (FirstLine = 0) And (LastLine = 0) Then

'Non c'è testoExit Sub

ElseWhile FirstLine < LastLine

FirstVisibleChar = GetCharFromLineIndex(FirstLine)ColorLineNumber(FirstLine, FirstVisibleChar)FirstLine += 1

End WhileEnd If

End Sub

'Colora una linea all'indice LineIndex, a partire dal carattere'lStartPublic Sub ColorLineNumber(ByVal LineIndex As Integer, _

ByVal lStart As Integer)Dim i As Integer = 0Dim SelectionAt As Integer = Me.SelectionStartDim MyRow As DataRowDim Line() As String, MyI As Integer, MyStr As String

'Blocca il refreshLockWindowUpdate(Me.Handle.ToInt32)

MyI = lStart

If CaseSensitive Then

Line = Split(Me.Lines(LineIndex).ToString, " ")Else

Line = Split(Me.Lines(LineIndex).ToLower, " ")End If

For Each MyStr In Line

Page 311: Guida visual basic

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.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.

'Seleziona i primi MyStr.Length caratteri della linea,'ossia la prima parolaMe.SelectionStart = MyIMe.SelectionLength = MyStr.Length

'Se la parola è contenuta in una delle righeIf Words.Rows.Contains(MyStr) Then

'Seleziona la rigaMyRow = Words.Rows.Find(MyStr)'Quindi colora la parola prelevando il colore da'tale rigaIf (Not CaseSensitive) Or _

(CaseSensitive And MyRow("Word") = MyStr) ThenMe.SelectionColor = Color.FromName(MyRow("Color"))

End IfElse

'Altrimenti lascia il testo in neroMe.SelectionColor = Color.Black

End If

'Aumenta l'indice di un fattore pari alla lunghezza'della parola più uno (uno spazio)MyI += MyStr.Length + 1

Next

'Ripristina la selezioneMe.SelectionStart = SelectionAtMe.SelectionLength = 0'E il coloreMe.SelectionColor = Color.Black

'Riprende il refreshLockWindowUpdate(0)

End Sub

'Ottiene il primo carattere della linea LineIndexPublic Function GetCharFromLineIndex(ByVal LineIndex As Integer) _

As IntegerReturn SendMessage(Me.Handle, EditMessages.LineIndex, LineIndex, 0)

End Function

'Ottiene la prima linea visibilePublic Function FirstVisibleLine() As Integer

Return SendMessage(Me.Handle, EditMessages.GetFirstVisibleLine, 0, 0)End Function

'Ottiene l'ultima linea visibilePublic Function LastVisibleLine() As Integer

Dim LastLine As Integer = FirstVisibleLine() + _(Me.Height / Me.Font.Height)

If LastLine > Me.Lines.Length Or LastLine = 0 Then

LastLine = Me.Lines.LengthEnd If

Return LastLine

End Function

Public Sub New()Dim MyRow As DataRowDim arrKeyWords() As String, strKW As String

Me.AcceptsTab = True

'Carica la colonna Word e ColorWords.Columns.Add("Word")Words.PrimaryKey = New DataColumn() {Words.Columns(0)}Words.Columns.Add("Color")

'Aggiunge le keywords del linguaggio SQL all'arrayarrKeyWords = New String() {"select", "insert", "delete", _

"truncate", "from", "where", "into", "inner", "update", _

Page 312: Guida visual basic

Il costr uttor e New ha il compito di inizializzar e tutte le infor mazioni iner enti alle par ole ed al lor o color e. La

str uttur a della classe utilizza una DataTable in cui ci sono due colonne: Wor d, la par ola da evidenziar e, e Color , il color e

da usar e per l'evidenziazione. Ogni r iga contiene quindi queste due infor mazioni, e ci sono tante r ighe quante sono le

keywor ds del linguaggio che si desider a. Color LineNumber è invece commentata nel sor gente.

Questi metodi, per ò, sebbene funzionino con il linguaggio di r ifer imento (SQL), per dono di ogni validità con l'HTML, dove

le par ola chiave sono attaccate le une alle altr e, ad esempio in:

<a href='http://totem.altervista.org'>Link</a>

a viene subito dopo la par entesi angolar e, mentr e hr ef pr ima di un uguale. Nonostante il modo più pr eciso in assoluto

per scovar e le keywor ds sia usar e le espr essioni r egolar i, non ancor a anlizzate, per or a si far à in altr o modo. Ecco la

classe r iscr itta da me, in modo da adeguar e il funzionamento all'HTML e miglior ando le pr estazioni:

172.173.174.175.176.177.178.179.180.181.182.183.184.185.186.187.188.189.

"outer", "on", "is", "declare", "set", "use", "values", "as", _"order", "by", "drop", "view", "go", "trigger", "cube", _"binary", "varbinary", "image", "char", "varchar", "text", _"datetime", "smalldatetime", "decimal", "numeric", "float", _"real", "bigint", "int", "smallint", "tinyint", "money", _"smallmoney", "bit", "cursor", "timestamp", "uniqueidentifier", _"sql_variant", "table", "nchar", "nvarchar", "ntext", "left", _"right", "like", "and", "all", "in", "null", "join", "not", "or"}

'Quindi le aggiunge una alla volta alla tabella con'colore rossoFor Each strKW In arrKeyWords

MyRow = Words.NewRow()MyRow("Word") = strKWMyRow("Color") = Color.LightCoral.NameWords.Rows.Add(MyRow)

NextEnd Sub

End Class

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.

Public Class SHRichTextBoxInherits System.Windows.Forms.RichTextBox

Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" _

(ByVal hWnd As IntPtr, ByVal wMsg As Integer, _ByVal wParam As Integer, ByVal lParam As Integer) As Integer

Private Declare Function LockWindowUpdate Lib "user32" _(ByVal hWnd As Integer) As Integer

Private Enum EditMessages

LineIndex = 187LineFromChar = 201GetFirstVisibleLine = 206CharFromPos = 215PosFromChar = 1062

End Enum

Protected Overrides Sub OnTextChanged(ByVal e As EventArgs)'Non colora tutte le linee visibili, bensì solo la riga'dove si trova il cursorse: in questo modo l'applicazione'risulta più veloce. L'unico caso in cui questo'approccio non funzione è quando si copia un testo'all'interno della richtextbox. In quel caso ci sarà'un pulsante appositoDim LineIndex As Int32 = Me.GetLineFromCharIndex(Me.SelectionStart)Me.ColorLineNumber(LineIndex)

End Sub

'Colora tutta la RichTextBoxPublic Sub ColorRtb()

For I As Int32 = 0 To Me.Lines.Length - 1ColorLineNumber(I)

Next

Page 313: Guida visual basic

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.106.107.

End Sub

'Colora solo le linee visibiliPublic Sub ColorVisibleLines()

Dim FirstLine As Integer = FirstVisibleLine()Dim LastLine As Integer = LastVisibleLine() If (FirstLine = 0) And (LastLine = 0) Then

'Non c'è testoExit Sub

ElseWhile FirstLine < LastLine

ColorLineNumber(FirstLine)FirstLine += 1

End WhileEnd If

End Sub

'Questa è la nuova versione: nelle stesse condizioni sopra'citate, impiega 50ms, quasi la metà! L'algoritmo vecchio'per SQL ne impiegava 10, ma non era in grado di supportare tag'vicini come quelli dell'HTMLPublic Sub ColorLineNumber(ByVal LineIndex As Int32)

TryIf Me.Lines(LineIndex).Length = 0 Then

Exit SubEnd If

Catch Ex As ExceptionExit Sub

End Try

'Indice del primo carattere della lineaDim FirstCharIndex As Int32 = _

Me.GetFirstCharIndexFromLine(LineIndex)'Tiene traccia del cursoreDim SelectionAt As Integer = Me.SelectionStart

'Blocca il refreshLockWindowUpdate(Me.Handle.ToInt32)

'Tiene traccia se ci siano tag apertiDim TagOpened As Boolean = False'Indica se il tag ha degli attributiDim Attribute As Boolean = False'Indica se un attributo è stato assegnatoDim Assigned As Boolean = False'Indica, per gli attributi come [readonly], se le parentesi'sono state aperteDim AttributeOpened As Boolean = False'Variabili locali che rappresentano Me.SelectionStart e'Me.SelectionLength: usando la variable enregistration si'guadagna qualche millisecondoDim Start, Length As Int32Dim Max As Int32 = _

(FirstCharIndex + Me.Lines(LineIndex).Length) - 1

Me.Select(FirstCharIndex, Max + 1)

For Index As Int32 = FirstCharIndex To MaxIf Char.IsLetterOrDigit(Me.Text(Index)) Then

Continue ForEnd If'Viene aperto un tag, inizia a selezionare'Es.: <aIf Me.Text(Index) = "<" Then

Start = IndexTagOpened = TrueAttribute = FalseAssigned = False

ElseIf Me.Text(Index) = ">" Then'Viene chiuso un tag: se sono stati definiti'attributi, evidenzia solo la parentesi angolare,

Page 314: Guida visual basic

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.177.178.179.

'Es.: <a href='www.example.com'>'altrimenti tutta la stringa da "<" a ">"'Es.: <div>If Not Attribute Then

Length = Index - StartMe.Select(Start, Length)Me.SelectionColor = Color.Blue

End IfMe.Select(Index, 1)Me.SelectionColor = Color.BlueMe.DeselectAll()TagOpened = FalseAttribute = FalseAssigned = False

ElseIf TagOpened AndAlso Me.Text(Index) = " " Then'Uno spazio: se un attributo è già stato impostato,'si tratta di uno spazio che separa due attributi,'quindi passa oltre, definendo solo'Assigned = False;'Es.: <div id='1' class='prova'>'altrimenti è uno spazio che precede qualsiasi'attributo, che quindi viene dopo la dichiarazione'del tag, che viene colorato in blu'Es.: <div id='1'>If Assigned Then

Assigned = FalseElse

Length = Index - StartMe.Select(Start, Length)Me.SelectionColor = Color.Blue

End IfMe.DeselectAll()Start = Index + 1

ElseIf TagOpened AndAlso Me.Text(Index) = "=" Then'Un uguale: a un attributo viene assegnato un'valore, perciò evidenzia l'attributo,'dallo spazio precedente fino a = non compreso,'e lo colore in rosso'Es.: <table width='100'>Length = Index - StartMe.Select(Start, Length)Me.SelectionColor = Color.RedMe.DeselectAll()Attribute = TrueAssigned = True

ElseIf Me.Text(Index) = "[" Then'Apre un attributoStart = IndexAttributeOpened = True

ElseIf Me.Text(Index) = "]" And AttributeOpened Then'Chiude un attributo'Es.: <input type='text' [readonly]>Length = Index - StartMe.Select(Start, Length)Me.SelectionColor = Color.RedMe.DeselectAll()AttributeOpened = False

End IfNext

'Ripristina la selezioneMe.SelectionStart = SelectionAtMe.SelectionLength = 0'E il coloreMe.SelectionColor = Color.Black

'Riprende il refreshLockWindowUpdate(0)

End Sub

'Ottiene la prima linea visibilePublic Function FirstVisibleLine() As Integer

Page 315: Guida visual basic

In questa ver sione modificate ci sono par ecchie diver genze:

Non viene utilizzata una tabella dei color i: il motivo è semplice; viene eseguito un contr ollo un car atter e alla

volta e, quale che sia il nome del tag e dell'attr ibuto specificato, viene comunque color ato. Questa car atter istica

ha dei pr egi e dei difetti. Non evidenzia gli er r or i, ma in questo caso si può sempr e r ipr istinar e la tabella

per dendo un po' di velocità. Tuttavia evidenzia anche i tag nuovi che vengono usati dai css: ad esempio, questa

pagina usava dei tag "<k>", che non esistono nell'HTML ma sono pur sempr e tag, e vengono usati per definir e le

keywor ds e per color ar e il listato. Se si consider a la pr ima ipotesi, sar ebbe meglio utilizzar e una collezione a

dizionar io a tipizzazione for te, per spr ecar e meno memor ia.

Non divide la str inga: analizza semplicemente un car atter e per volta dall'inizio alla fine. Questo pr ocedimento è

assai più r apido e ovviamente non funzioner ebbe con uno split, dato che i tag sono attaccati l'uno all'altr o

Non utilizza Color Rtb su OnTex tChanged: dato che il contr ollo è pr ogettato per aiutar e nella scr ittur a, si

suppone che chi immetta il codice stia scr ivendo, quindi color a soltanto la linea su cui si sta oper ando e non

tutte le linee visibili. Questo contr ibuisce a velocizzar e il meccanismo

Per chi avesse letto la ver sione pr ecedente della guida, si sar à cer tamente notato il cambiamento r adicale di algor itmo

utilizzato, r ispetto a quello più r udimentale:

Quest'ultimo color a solo le par ole indicate, ma esegue almeno (almeno!) un centinaio di contr olli ogni volta, ossia uno

per ogni par ola data: se poi queste appaiono nella r iga, il conto r addoppia! Questo appr occio, per far e un esempio, su

una linea di 37 car atter i con cinque o sei par ole r iser vate, impiega cir ca 90ms per color ar e, ed il tempo aumenta

ver tiginosamente di 10/20ms per ogni car atter e in più. Nel nuovo algor itmo, il tempo è r idotto a cir ca 50ms, con un

aumento di 2/3ms per ogni car atter e in più. L'algor itmo iniziale, invece, dovendo analizzar e solo il numer o di par ole

della str inga, impiegava, sempr e nelle stesse condizioni, cir ca 10ms, con un aumento di 1/2ms ogni parola in più.

(Bisogna per ò r icor dar e che il pr imo pr oposto color ava tutte le linee visibili ad ogni modifica). Si può capir e quindi

come sia vantaggioso quello iniziale in ter mini di tempo, e quanto svantaggioso in ter mini di pr estazioni.

180.181.182.183.184.185.186.187.188.189.190.191.192.193.

Return SendMessage(Me.Handle, EditMessages.GetFirstVisibleLine, 0, 0)End Function

'Ottiene l'ultima linea visibilePublic Function LastVisibleLine() As Integer

Dim LastLine As Integer = FirstVisibleLine() + _(Me.Height / Me.Font.Height)

If LastLine > Me.Lines.Length Or LastLine = 0 Then

LastLine = Me.Lines.LengthEnd If

Return LastLine

End FunctionEnd Class

01.02.03.04.05.06.07.08.09.10.11.12.13.14.

For Each Word As String In WordsI = FirstCharIndexDo

I = Me.Find(Word, I, I + Me.Lines(LineIndex).Length, _RichTextBoxFinds.None)

If I >= 0 ThenMe.SelectionStart = IMe.SelectionLength = Word.Length'Qui utilizo un dictionaryMe.SelectionColor = Words(Word)I += Word.Length

End IfLoop While I >= 0

Next

Page 316: Guida visual basic

Esempio di Syntax Hig hlig hting

Page 317: Guida visual basic

B19. PropertyGrid

Questo contr ollo è davver o molto complesso: r appr esenta una gr iglia delle pr opr ietà, esattamente la stessa che lo

sviluppator e usa per modificar e le car atter istiche dei var i contr olli nel for m designer . La sua enor me potenza sta nel

fatto che, attr aver so la r eflection, r iesce a gestir e qualsiasi oggetto con facilità. Le si può associar e un contr ollo del

for m, su cui l'utente può agir e a pr opr io piacimento, ma anche una classe, ad esempio le opzioni del pr ogr amma, con

cui sar à quindi possibile inter agir e molto semplicemente da un'unica inter faccia. Le pr opr ietà e i metodi impor tanti

sono:

CollapseAllGr idItems : r iduce al minimo tutte le categor ie

Ex pandAllGr idItems : espande al massimo tutto le categor ie

Pr oper tySor t : pr opr ietà enumer ata che indica come debbano esser e or dinati gli elementi, se alfabeticamente,

per categor ie, per categor ie e alfabeticamente oppur e senza alcun or dinamento

Pr oper tyTabs : collezione di tutte le possibili schede della Pr oper tyGr id. Una scheda, ad esempio, è costituita dal

pulsante "Or dina alfabeticamente", oppur e, nell'ambiente di sviluppo, dal pulsante "Mostr a eventi" (quello con

l'icona del fulmine). Aggiunger ne una significa aggiunger e un pulsante che possa modificar e il modo in cui il

contr ollo legge i dati dell'oggetto. Ecco un esempio pr eso da un ar ticolo sull'ar gomento r eper ibile su The Code

Project:

SelectedGr idItem : r estituisce l'elemento selezionato, un oggetto Gr idItem che gode di queste pr opr ietà:

Ex pandable : indica se l'elemento è espandibile. Sono espandibili tutte quelle pr opr ietà il cui tipo sia un

tipo r efer ence: in par ole pover e, essa deve espor r e al pr opr io inter no altr e pr opr ietà (non sono

soggetti a questo compor tamento le str uttur e, in quanto tipi value, a meno che esse non espongano a

lor o volta delle pr opr ieta'). Per i tipi definiti dal pr ogr ammator e, la Pr oper tyGr id non è in gr ado di

for nir e una r appr esentazione che possa esser e espansa a r un-time: a questo si può supplir e in modo

semplice facendo uso di cer ti attr ibuti come si vedr à fr a poco

Ex panded : indica se l'elemento è cor r entemente espanso (sono visibili tutti i suoi membr i)

Gr idItems : se Ex pandable = Tr ue, questa pr opr ietà r estituisce una collezione di oggetti Gr idItem che

r appr esentano tutte le pr opr ietà inter ne a quella cor r ente

Gr idItemType : pr opr ietà enumer ata in sola lettur a che specifica il tipo di elemento. Può assumer e

quattr o valor i: Ar r ayValue (un oggetto ar r ay o a una collezione in gener e), Categor y (una categor ia),

Pr oper ty (una qualsiasi pr opr ieta') e Root (una pr opr ietà di pr imo livello, ossia che non possiede alcun

livello ger ar chico al di sopr a di se stessa)

Label : il testo dell'elemento

Par ent : se la pr opr ietà è un membr o d'istanza di un'altr a pr opr ietà, r estituisce quest'ultima (ossia quella

che sta al livello ger ar chico super ior e)

Pr oper tyDescr iptor : r estituisce un oggetto che indica come si compor ta la pr opr ietà nella gr iglia, quale

sia il suo testo, la descr izione, se sia modificabile o meno (a r un-time o solo dur ante la scr ittur a del

pr ogr amma), se sia visualizzata nella gr iglia, quale sia il delegate da invocar e nel momento in cui questa

viene modificata e infine, il più impor tante, l'oggetto usato per conver tir e tutta la pr opr ietà in un

valor e sintetico di tipo str inga. Tutti questi attr ibuti sono specificati dur ante la scr ittur a di una

pr opr ietà che suppor ti la visualizzazione in una Pr oper tyGr id, come si vedr à in seguito

Value : r estituisce il valor e della pr opr ieta'

Page 318: Guida visual basic

SelectedObject : la pr opr ietà più impor tante. Imposta l'oggetto che Pr oper tyGr id gestisce: ogni modifica

dell'utente sul contr ollo si r iper cuoter à in manier a identica sull'oggetto, esattamente come avviene nell'ambiente

di sviluppo; vengono anche inter cettati tutti gli er r or i di casting e gestiti automaticamente

SelectedObjects : è anche possibile far sì che vengano gestiti più oggetti contempor aneamente. Se questi sono

dello stesso tipo, ogni modifica si r iper cuoter à su ognuno nella stessa manier a. Se sono di tipo diver so,

ver r anno visualizzate solo le pr opr ietà in comune

SelectedTab : r estituisce la scheda selezionata

In questo capitolo mi concentr er ò sul caso in cui si debba inter facciar e Pr oper tyGr id con un oggetto nuovo cr eato da

codice.

Binding di c lassi create dal programmatorePer far sì che Pr oper tyGr id visualizzi cor r ettamente una classe cr eata dal pr ogr ammator e, basta assegnar e un

oggetto di quel tipo alla pr opr ietà SelectedObject, poichè tutto il pr ocesso viene svolto tr amite r eflection. Tuttavia ci

sono alcune situazioni in cui questo pr ocesso ha bisogno di un aiuto ester no per funzionar e: quando le pr opr ietà sono

di tipo r efer ence (str inghe escluse), non vengono visulizzati tutti i lor o membr i, poichè il contr ollo non è in gr ado di

conver tir e un valor e adatto in str inga. Ad esempio, se si deve legger e un oggetto di tipo Per son, il nome e la data di

nascita ver r anno analizzati cor r ettamente, ma il campo Fr etello As Per son come ver r à inter pr etato? Non è possibile

far star e una classe su una sola r iga, poichè non si conosce il modo di conver tir la in un valor e r appr esentabile (in

questo caso, in una str inga). Lo str umento che Vb.Net for nisce per ar ginar e questo pr oblema è un attr ibuto, di nome

TypeConver ter , definito nel namespace System.ComponentModel (dove, tr a l'altr o, sono situati tutti gli altr i attr ibuti

usati in questo capitolo). Questo accetta come costr uttor e un par ametr o di tipo Type, che espone il tipo di una classe

con la funzione di conver titor e. Ad esempio:

Ecco un esempio di come si pr esenter à il contr ollo dopo aver for nito queste dir ettive:

La classe che implementa il conver titor e deve er editar e da Ex pandableObjectConver ter (una classe definita anch'essa in

System.ComponentModel) e deve sovr ascr iver e tr amite polimor fismo alcune funzioni: CanConver tFr om (deter mina se

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.

'Questa classe ha la funzione di convertire Person in stringaPublic Class PersonConverter

'(Per convenzione, i convertitori di questo tipo, devono'terminare con la parola "Converter"'...

End Class Public Class Person

Private _Name As StringPrivate _Birthday As DatePrivate _Brother As Person '... 'Per la proprietà Brother (fratello), si applica l'attributo'TypeConverter, specificando quale sia la classe convertitore.'Si utilizza solo il tipo perchè la classe, come vedremo'in seguito, espone solo metodi d'istanza, ma che possono'essere utilizzati da soli semplicemente fornendo i parametri'adeguati. Perciò sarà il programma stesso a creare,'a runtime, un oggetto di questo tipo e ad usarne la funzioni<TypeConverter(GetType(PersonConverter))> _Public Property Brother() As Person'...

End Class

Page 319: Guida visual basic

si può conver tir e da tipo dato), CanConver tTo (deter mina se si può conver tir e nel tipo dato), Conver tFr om (conver te,

in questo caso, da Str ing a Per son, e in gener ale al tipo di cui si sta scr ivendo il conver titor e), Conver tTo (conver te, in

questo caso, da Per son a Str ing, e in gener ale dal tipo in questione a str inga).

Questa er a la par te più difficile, di cui si avr à un buon esempio nel codice a seguir e: quello che bisogna anlizzar e or a

consente di definir e alcune piccole car atter istiche per per sonalizzar e l'aspetto di una pr opr ietà. Ecco una lista degli

attr ibuti usati e delle lor o descr izioni:

DisplayName : modifica il nome della pr opr ietà in modo che venga visualizzata a r un-time un'altr a str inga.

Accetta un solo par ametr o del costr uttor e, il nuovo nome (nell'esempio, si r impiazza la denominazione inglese

con la r ispettiva tr aduzione italiana)

Descr iption : definisce una piccola descr izione per la pr opr ieta'

Br owsable : deter mina se il valor e della pr opr ietà sia modificabile dal contr ollo: l'unico par ametr o del

costr uttor e è un valor e Boolean

[ReadOnly] : indica se la pr opr ietà è in sola lettur a oppur e no. Come Br owsable accetta un unico par ametr o

booleano

DesignOnly : specifica se la pr opr ietà si possa modificar e solo dur ante la scr ittur a del codice e non dur ante

l'esecuzione

Categor y : il nome della categor ia sotto la quale deve venir e r ipor tata la pr opr ietà: l'unico par ametr o è di tipo

Str ing

DefaultValue : il valor e di default della pr opr ietà. Accetta diver si over load, a seconda del tipo

DefaultPr oper ty : applicato alla classe che r appr esenta il tipo dell'oggetto visualizzato, indica il nome della

pr opr ietà che è selezionata di default nella Pr oper tyGr id

Pr ima di pr oceder e con il codice, ecco uno scr eenshot di come dovr ebbe appar ir e la veste gr afica in fase di

pr ogettazione:

C'è anche un'ImageList con un'immagine per gli elementi della listview lstBooks e un Contex tMenuStr ip che contiene le

voci "Aggiungi" e "Rimuovi", sempr e assegnato alla listview . Inoltr e, sia la lista che la Pr oper tyGr id sono inser ite

all'inter no di uno SplitContiner . Ecco il codice della libr er ia (nel Solution Ex plor er , cliccar e con il pulsante destr o sul

pr ogetto, quindi sceglier e Add New Item e poi Class Libr ar y):

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.

'Questo namespace contiene gli attributi necessari a'impostare le proprietà in modo che si interfaccino'correttamente con PropertyGridImports System.ComponentModel 'Quando si usa uno statementes Imports, la prima voce'si riferisce al nome del file *.dll in s?. Dato che si'vuole BooksManager sia consierato come una namespace, non'bisogna aggiungere un altro namespace BooksManager in questo file 'L'autore del libro, con eventuale biografiaPublic Class Author

'Il nome completoPrivate _Name As String'Data di nascita e mortePrivate _Birth, _Death As Date'Indica se l'autore è ancora vivoPrivate _IsStillAlive As Boolean'Una piccola biografiaPrivate _Biography As String

<DisplayName("Nome"), _

Description("Il nome dell'autore."), _Browsable(True), _

Page 320: Guida visual basic

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.084.085.086.087.088.089.090.091.092.093.094.095.096.097.

Category("Generalita'")> _Public Property Name() As String

GetReturn _Name

End GetSet(ByVal Value As String)

_Name = ValueEnd Set

End Property

<DisplayName("Piccola biografia"), _Description("Un riassunto delle parti più significative della " & _

"vita dell'autore."), _Browsable(True), _Category("Dettagli")> _

Public Property Biography() As StringGet

Return _BiographyEnd GetSet(ByVal Value As String)

_Biography = ValueEnd Set

End Property

<DisplayName("Data di nascita"), _Description("La data di nascita dell'autore."), _Browsable(True), _Category("Generalita'")> _

Public Property Birth() As DateGet

Return _BirthEnd GetSet(ByVal Value As Date)

'Nessun controllo: la data di nascita può essere'spostata a causa di uno sbaglio, che altrimenti'potrebbe produrre un'eccezione_Birth = Value

End SetEnd Property

<DisplayName("Data di morte"), _

Description("Data di morte dell'autore."), _Browsable(True), _Category("Generalita'")> _

Public Property Death() As DateGet

Return _DeathEnd GetSet(ByVal Value As Date)

'Bisogna assicurarsi che la data di morte sia'posteriore a quella di nascitaIf Value.CompareTo(Me.Birth) < 1 Then

'Genera un'eccezioneThrow New ArgumentException("La data di morte deve " & _"essere posteriore a quella di nascita!")

Else'Prosegue l'assegnazione_Death = Value'Impostando la data di morte si suppone che l'autore'non sia più in vita...Me.IsStillAlive = False

End IfEnd Set

End Property

<DisplayName("Vive"), _Description("Determina se l'autore è ancora in vita."), _Browsable(True), _Category("Generalita'")> _

Public Property IsStillAlive() As BooleanGet

Return _IsStillAlive

Page 321: Guida visual basic

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.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.

End GetSet(ByVal Value As Boolean)

_IsStillAlive = ValueEnd Set

End Property

'Un nome e una data di nascita sono obbligatoriSub New(ByVal Name As String, ByVal Birth As Date)

Me.Name = NameMe.Birth = BirthMe.IsStillAlive = True

End Sub

'Tuttavia, il controllo PropertyGrid richiede un costruttore'senza parametriSub New()

Me.Birth = Date.NowMe.IsStillAlive = True

End SubEnd Class Public Class IsbnConverter

'Facendo derivare questa classe da ExpandableObjectConverter'si comunica al compilatore che questa classe è usata per'convertire in stringa un valore rappresentabile in una'PropertyGrid. Così facendo, sarà possibile modificare'il codice agendo sulla stringa complessiva e non'obbligatoriamente sulle varie partiInherits ExpandableObjectConverter

'Determina se sia possibile convertire nel tipo datoPublic Overrides Function CanConvertTo(ByVal Context As ITypeDescriptorContext, _

ByVal DestinationType As Type) As Boolean'Si può convertire in Isbn, dato che questa classe è'scritta apposta per questoIf (DestinationType Is GetType(Isbn)) Then

Return TrueEnd IfReturn MyBase.CanConvertFrom(Context, DestinationType)

End Function

'Determina se sia possibile convertire dal tipo datoPublic Overrides Function CanConvertFrom(ByVal Context As ITypeDescriptorContext, _

ByVal SourceType As Type) As Boolean'Si può convertire da String, dato che questa classe è'scritta apposta per questoIf (SourceType Is GetType(String)) Then

Return TrueEnd IfReturn MyBase.CanConvertFrom(Context, SourceType)

End Function

'Converte da stringa a IsbnPublic Overrides Function ConvertFrom(ByVal Context As ITypeDescriptorContext, _

ByVal Culture As Globalization.CultureInfo, _ByVal Value As Object) As ObjectIf TypeOf Value Is String Then

Dim Str As String = DirectCast(Value, String)'Cerca di creare un nuovo oggetto isbnTry

Dim Obj As Isbn = Isbn.CreateNew(Str)Return Obj

Catch ex As ExceptionMessageBox.Show(ex.Message, "Books Manager", _MessageBoxButtons.OK, MessageBoxIcon.Exclamation)Return New Isbn

End TryEnd IfReturn MyBase.ConvertFrom(Context, Culture, Value)

End Function

'Converte da Isbn a stringa

Page 322: Guida visual basic

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.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.

Public Overrides Function ConvertTo(ByVal Context As ITypeDescriptorContext, _ByVal Culture As Globalization.CultureInfo, _ByVal Value As Object, ByVal DestinationType As Type) As ObjectIf DestinationType Is GetType(String) And _

TypeOf Value Is Isbn ThenDim Temp As Isbn = DirectCast(Value, Isbn)Return Temp.ToString

End IfReturn MyBase.ConvertTo(Context, Culture, Value, DestinationType)

End FunctionEnd Class 'Il codice ISBN, dal primo gennaio 2007, deve obbligatoriamente'essere a tredici cifre. Per questo motivo metterò solo'questo tipo nel sorgente'P.S.: per convenzione, gli acronimi con più di due lettere devono'essere scritti in Pascal CasePublic Class Isbn

'Un codice è formato da:'Un prefisso (3 cifre) - solitamente 978 indica un libro in generalePrivate _Prefix As Int16 = 978'Un identificativo linguistico (da 1 a 5 cifre): indica'il paese di provenienza dell'autore - in Italia è 88Private _LanguageID As Int16 = 88'Un prefisso editoriale (da 2 a 6 cifre): indica l'editorePrivate _PublisherID As Int64 = 89637'Un identificatore del titoloPrivate _TitleID As Int32 = 15'Un codice di controllo che può variare da 0 a 10. 10 viene'indicato con X, perciò lo imposto come CharPrivate _ControlChar As Char = "9"

<DisplayName("Prefisso"), _

Description("Prefisso del codice, costituito da tre cifre."), _Browsable(True), _Category("Isbn")> _

Public Property Prefix() As Int16Get

Return _PrefixEnd GetSet(ByVal Value As Int16)

If Value = 978 Or Value = 979 Then_Prefix = Value

ElseThrow New ArgumentException("Prefisso non valido!")

End IfEnd Set

End Property

<DisplayName("ID Lingua"), _Description("Identifica l'area da cui previene l'autore."), _Browsable(True), _Category("Isbn")> _

Public Property LanguageID() As Int16Get

Return _LanguageIDEnd GetSet(ByVal Value As Int16)

_LanguageID = ValueEnd Set

End Property

<DisplayName("ID Editore"), _Description("Identifica il marchio dell'editore."), _Browsable(True), _Category("Isbn")> _

Public Property PublisherID() As Int32Get

Return _PublisherIDEnd GetSet(ByVal Value As Int32)

_PublisherID = Value

Page 323: Guida visual basic

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.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.

End SetEnd Property

<DisplayName("ID Titolo"), _

Description("Identifica il titolo del libro."), _Browsable(True), _Category("Isbn")> _

Public Property TitleID() As Int32Get

Return _TitleIDEnd GetSet(ByVal Value As Int32)

_TitleID = ValueEnd Set

End Property

<DisplayName("Carattere di controllo"), _Description("Verifica la correttezza degli altri valori."), _Browsable(True), _Category("Isbn")> _

Public Property ControlChar() As CharGet

Return _ControlCharEnd GetSet(ByVal Value As Char)

_ControlChar = ValueEnd Set

End Property

Public Sub New()

End Sub

'Restituisce in forma di stringa il codicePublic Overrides Function ToString() As String

Return String.Format("{0}-{1}-{2}-{3}-{4}", _Me.Prefix, Me.LanguageID, Me.PublisherID, _Me.TitleID, Me.ControlChar)

End Function

'Metodo statico factory per costruire un nuovo codice ISBN. Se'si mettesse questo codice nel costruttore, l'oggetto verrebbe'comunque creato anche se il codice inserito fosse errato.'In questo modo, la creazione viene fermata e restituito'Nothing in caso di erroriShared Function CreateNew(ByVal StringCode As String) As Isbn

'Con le espressioni regolari, ottiene le varie partiDim Split As New System.Text.RegularExpressions.Regex( _"(?<Prefix>\d{3})\-(?<Language>\d{1,5})" & _"\-(?<Publisher>\d{2,6})\-(?<Title>\d+)\-(?<Control>\w)") Dim M As System.Text.RegularExpressions.Match = _

Split.Match(StringCode) 'Se la lunghezza del codice, senza trattini, è di'13 caratteri e il controllo tramite espressioni regolari'ha avuto successo, procedeIf StringCode.Length = 17 And M.Success Then

Dim Result As New IsbnWith Result

.Prefix = M.Groups("Prefix").Value

.LanguageID = M.Groups("Language").Value

.PublisherID = M.Groups("Publisher").Value

.TitleID = M.Groups("Title").Value

.ControlChar = M.Groups("Control").ValueEnd WithReturn Result

ElseThrow New ArgumentException("Il codice inserito è errato!")

End IfEnd Function

End Class

Page 324: Guida visual basic

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.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.

'Una classe che rappresenta un libroPublic Class Book

Private _Title As String'Si suppone che un libro abbia meno di 32767 pagine XDPrivate _Pages As Int16 = 100'Si possono anche avere più autori: in questo caso si ha'una lista a tipizzazione forte.Private _Authors As New List(Of Author)'L'eventuale serie a cui il libro appartienePrivate _Series As String'Casa editricePrivate _Publisher As String'Data di pubblicazionePrivate _PublicationDate As Date'ArgomentoPrivate _Subject As String'Costo in euroPrivate _Cost As Single = 1.0'RistampaPrivate _Reprint As Byte = 1'Codice ISBN13Private _Isbn As New Isbn

<DisplayName("Titolo"), _

Description("Il titolo del libro."), _Browsable(True), _Category("Editoria")> _

Public Property Title() As StringGet

Return _TitleEnd GetSet(ByVal Value As String)

_Title = ValueEnd Set

End Property

<DisplayName("Collana"), _Description("La collana o la serie a cui il libro appartiene."), _Browsable(True), _Category("Editoria")> _

Public Property Series() As StringGet

Return _SeriesEnd GetSet(ByVal Value As String)

_Series = ValueEnd Set

End Property

<DisplayName("Editore"), _Description("La casa editrice."), _Browsable(True), _Category("Editoria")> _

Public Property Publisher() As StringGet

Return _PublisherEnd GetSet(ByVal Value As String)

_Publisher = ValueEnd Set

End Property

<DisplayName("Pagine"), _Description("Il numero di pagine da cui il libro è composto."), _DefaultValue("100"), _Browsable(True), _Category("Dettagli")> _

Public Property Pages() As Int16Get

Return _PagesEnd Get

Page 325: Guida visual basic

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.423.424.425.426.427.428.429.430.431.432.433.434.435.436.437.438.439.440.441.442.443.444.445.446.447.448.449.450.451.452.453.454.455.456.457.

Set(ByVal Value As Int16)If Value > 0 Then

_Pages = ValueElse

Throw New ArgumentException("Numero di pagine insufficiente!")End If

End SetEnd Property

<DisplayName("Autore/i"), _

Description("L'autore o gli autori."), _Browsable(True), _Category("Editoria")> _

Public ReadOnly Property Authors() As List(Of Author)Get

Return _AuthorsEnd Get

End Property

<DisplayName("Pubblicazione"), _Description("La data di pubblicazione della prima edizione."), _Browsable(True), _Category("Dettagli")> _

Public Property PublicationDate() As DateGet

Return _PublicationDateEnd GetSet(ByVal Value As Date)

_PublicationDate = ValueEnd Set

End Property

<DisplayName("Codice ISBN"), _Description("Il codice ISBN conformato alla normativa di 13 cifre."), _Browsable(True), _Category("Editoria"), _TypeConverter(GetType(IsbnConverter))> _

Public Property Isbn() As IsbnGet

Return _IsbnEnd GetSet(ByVal Value As Isbn)

_Isbn = ValueEnd Set

End Property

<DisplayName("Ristampa"), _Description("Il numero della ristampa."), _DefaultValue(1), _Browsable(True), _Category("Dettagli")> _

Public Property Reprint() As ByteGet

Return _ReprintEnd GetSet(ByVal Value As Byte)

If Value > 0 Then_Reprint = Value

ElseThrow New ArgumentException("Ristampa: valore errato!")

End IfEnd Set

End Property

<DisplayName("Costo"), _Description("Il costo del libro, in euro."), _Browsable(True), _Category("Editoria")> _

Public Property Cost() As SingleGet

Return _CostEnd Get

Page 326: Guida visual basic

E il codice del for m:

458.459.460.461.462.463.464.465.

Set(ByVal Value As Single)If Value > 0 Then

_Cost = ValueElse

Throw New ArgumentException("Inserire prezzo positivo!")End If

End SetEnd Property

End Class

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.

Class Form1Private Sub strAddBook_Click(ByVal sender As Object, _

ByVal e As EventArgs) Handles strAddBook.ClickDim Title As String = _

InputBox("Inserire il titolo del libro:", "Books Manager")

'Controlla che la stringa non sia vuota o nullaIf Not String.IsNullOrEmpty(Title) Then

Dim Item As New ListViewItem(Title)Dim Book As New Book()Book.Title = TitleItem.ImageIndex = 0Item.Tag = BooklstBooks.Items.Add(Item)

End IfEnd Sub

Private Sub lstBooks_SelectedIndexChanged(ByVal sender As Object, _

ByVal e As EventArgs) Handles lstBooks.SelectedIndexChanged'Esce dalla procedura se non ci sono elementi selezionatiIf lstBooks.SelectedIndices.Count = 0 Then

Exit SubEnd If

'Altrimenti procedepgBook.SelectedObject = lstBooks.SelectedItems(0).Tag

End Sub

Page 327: Guida visual basic

C1. Introduzione ai database relazionali

Il modello r elazionale non è stato il pr imo in assoluto ad esser e usato per la gestione dei database, ma è stato

intr odotto più tar di, negli anni '70, gr azie alle idee di E. F. Codd. Ad oggi, è il modello più diffuso e utilizzato per la

sua semplicità.

Tale modello si basa su un unico concetto, la relazione, una tabella costituita da r ig he (o record o tuple) e colonne

(o attr ibuti). Per definir e una r elazione, basta specificar ne il nome e gli attr ibuti. Ad esempio:

Person (FirstName, LastName, BirthDay)

indica una r elazione di nome Per son, che pr esenta tr e colonne, denominate r ispettivamente Fir stName, LastName e

Bir thDay. Una volta data la definizione, per ò, è necessar io anche for nir e dei dati che ne r ispettino le r egole: dobbiamo

aggiunger e delle r ighe a questa tabella per r appr esentar e i dati che ci inter essano, ad esempio:

Relazione Per son

Fir stName LastName Bir thDay

Mar io Rossi 1/1/1965

Luigi Bianchi 13/7/1971

...

L'insieme di tutte le r ighe della r elazione si dice estensione della relazione, mentr e ogni singola tupla viene anche

chiamata istanza di relazione. Facendo un par allelismo con la pr ogr ammazione ad oggetti, quindi, avr emo queste

"somiglianze" (che si r iveler anno di vitale impor tanza nella tipizzazione for te, come vedr emo in seguito):

Database Programmazione ad oggettiRelazione -> ClasseTupla -> OggettoEstensione della relazione -> Lista di oggettiAttributo -> Proprietà dell'oggettoIstanza di relazione -> Istanza di classe (= Oggetto)

Avendo or a chiar ito questi par allelismi, vi sar à più facile entr ar e nella mentalità del modello r elazionale, dato che, se

siete ar r ivati fino a qui, si assume che sappiate già benissimo tutti gli aspetti e i concetti della pr ogr ammazione ad

oggetti.

Uno sguar do attento, tuttavia, far à notar e che, tr a i car atter i fondamentali che si possono r intr acciar e in questi

par allelismi, manca il concetto di "tipo" di un attr ibuto. Infatti, per come abbiamo pr ima definito la r elazione, sar ebbe

del tutto lecito immetter e un numer o inter o nel campo Fir stName o una str inga in Bir thDay. Per for tuna, il modello

pr evede anche che ogni colonna possegga un dominio, ossia uno specifico r ange di valor i che essa può assumer e: ciò

che noi abbiamo sempr e chiamato tipo. Il tipo di un attr ibuto può esser e scelto tr a una gamma molto limitata: inter i,

valor i a vir gola mobile, str inghe (a lunghezza limitata e non), date, car atter i, boolean e dati binar i (ar r ay di bytes). In

sostanza, questi sono i tipi pr imitivi o atomici di ogni linguaggio e pr opr io per questo motivo, si dice che il dominio di

un attr ibuto può esser e solo di tipo atomico, ossia non è possibile costr uir e tipi di dato complessi come le str uttur e o

le classi. Questa peculiar ità sembr er ebbe molto limitativa, ma in r ealtà non è così, poiché possiamo instaur ar e dei

collegamenti (o vincoli) tr a una r elazione e l'altr a, gr azie all'uso di elementi detti chiav i.

Page 328: Guida visual basic

La chiave più impor tante è la chiave pr imar ia (pr imar y key), che ser ve ad identificar e univocamente una tupla

all'inter no della r elazione. Facendo un par agone con la pr ogr ammazione, se una tupla è assimilabile ad un oggetto ed

esistono due tuple con attr ibuti identici, esse non r appr esentano comunque la stessa entità, pr opr io come due oggetti

con pr opr ietà uguali non sono lo stesso oggetto. E se per gli oggetti esiste un codice "segr eto" per distinguer li (guid), a

cui solo il pr ogr amma ha accesso, così esiste un par ticolar e valor e che ser ve per indicar e senza ombr a di dubbio se

due r ecor d sono differ enti: questo valor e è la chiave pr imar ia. Essa è solitamente un numer o inter o positivo ed è

anche la pr ima colonna definita dalla r elazione. Modificando la definizione di Per son data pr ecedente, ed intr oducendo

anche il dominio degli attr ibuti, si otter r ebbe:

'Questa sintassi è del tutto inventata!'Serve solo per esemplificare i concetti:Person (ID As Integer, FirstName As String, LastName As String, BirthDay As Date)

Relazione Per son

ID Fir stName LastName Bir thDay

1 Mar io Rossi 1/1/1965

2 Luigi Bianchi 13/7/1971

...

Per distinguer e le singole r ighe esiste, poi, un'altr a tipologia di chiave, detta chiave candidata, costituita dal più

piccolo insieme di attr ibuti per cui non esistono due tuple in cui quegli attr ibuti hanno lo stesso valor e. In gener ale,

tutte le chiavi pr imar ie sono chiavi canditate, a causa della stessa definizione data poco fa; mentr e esistono chiavi

candidate che non sono chiavi pr imar ie. Ad esempio, in questo caso, l'insieme degli attr ibuti Fir stName, LastName e

Bir thDay costituisce una chiave candidata, poichè è pr aticamente impossibile tr ovar e due per sone con lo stesso nome

nate nello stesso gior no alla stessa or a (almeno, è impossibile nella nostr a r elazione for mata da due elementi XD e

questo ci basta): quindi, questi tr e attr ibuti soddisfano le condizioni della definizione e identificano univocamente un

r ecor d. In gener e, si sceglie una fr a tutte le chiavi candidate possibili che viene assunta come chiave pr imar ia: a r igor

di logica, essa dovr à esser e la più semplice di tutte. In questo caso, il singolo numer o ID è molto più maneggevole che

non l'insieme di due str inghe e una data.

Or a, ammettiamo di aver e una r elazione così definita:

Program (ProgramID As Integer, Path As String, Description As String)

che indica un qualsiasi pr ogr amma installato su un computer ; e quest'altr a r elazione:

User (UserID As Integer, Name As String, MainFolder As String)

che indica un qualsiasi utente di quel computer . Ammettiamo anche che la macchina sulla quale sono installati i

pr ogr ammi pr esenti nell'estensione della r elazione sia condivisa da più utenti: vogliamo stabilir e, tr amite r elazioni,

quale utente possa acceder e a quale pr ogr amma. In questa cir costanza, abbiamo diver se soluzioni possibili, ma una sola

è la miglior e:

Abbiamo detto che la r elazione Pr ogr am ha una chiave pr imar ia, e la r elazione User pur e. Dato che si tr atta di

due tabelle diver se, potr ebbe venir e in mente di stabilir e questa policy di accesso: un utente può acceder e a un

pr ogr amma solo se la sua chiave pr imar ia (User ID) coincide con la chiave pr imar ia del pr ogr amma (Pr ogr amID).

In questo caso, tuttavia, le cir costanze sono molto r estr ittive, in quanto un utente può usar e uno e un solo

pr ogr amma (e vicever sa). La r elazione (in senso letter ale, ossia il collegamento) tr a le due tabelle si dice uno a

uno.

Page 329: Guida visual basic

Aggiungiamo alla r elazione Pr ogr am un altr o attr ibuto User ID, che dovr ebbe indicar ci l'utente che può usar e un

dato pr ogr amma. Tuttavia, come abbiamo visto pr ima, i valor i delle colonne devono esser e atomici e per ciò non

possiamo inser ir e in quella singola casella tutta un'altr a r iga (anche per chè non sapr emmo che tipo specificar e

come dominio). Qui ci viene in aiuto la chiave pr imar ia: sappiamo che nella r elazione User , ogni tupla è

univocamente identificata da una e una sola chiave pr imar ia chiamata User ID, quindi indicando una chiave,

indichiamo anche la r iga ad essa associata. Per cui, possiamo ben cr ear e un nuovo attr ibuto di tipo inter o (in

quanto la chiave è un numer o inter o in questo caso), nel quale specifichiamo l'User ID dell'utente che può usar e il

nostr o pr ogr amma. Ad esempio:

Program (ProgramID As Integer, Path As String, Description As String, UserID As String)

Relazione Pr ogr am

Pr ogr amID Path Descr iption User ID

1 C:\WINDOWS\notepad.ex e Editor di testo 2

2 C:\Pr ogr ammi\Fir eFox \fir efox .ex e Fir eFox web br owser 1

3 C:\Pr ogr ammi\Wor ld of War cr aft\WoW.ex e Wor ld of War cr aft 2

...

Relazione User

User ID Name MainFolder

1 Mar io Rossi C:\User s\MRossi

2 Luigi Bianchi C:\User s\Gigi

...

Come evidenziano i color i, il pr ogr amma 1 (notepad) e il pr ogr amma 3 (Wor ld of War cr aft) possono esser e usati

dall'utente 2 (Luigi Bianchi), mentr e il pr ogr amma 2 (Ffir efox ) può esser e usato dall'utente 1 (Mar io Rossi). Da un

pr ogr amma possiamo r isalir e all'utente associato, contr ollar ne l'identità e quindi consentir ne o pr oibir ne l'uso.

Questa soluzione, tuttavia, per mette l'accesso a un dato pr ogr amma da par te di un solo utente, anche se tale

utente può usar e più pr ogr ammi.

La r elazione che collega User a Pr ogr am è detta uno a molti (un utente può usar e più pr ogr ammi). Se la

guar diamo al contr ar io, ossia da Pr ogr am a User , è detta molti a uno (più pr ogr ammi possono esser e usati da

un solo utente). Entr ambr e le pr ospettive sono le due facce della stessa r elazione uno a molti, la più utilizzata.

Dato che il pr ecedente tentativo non ha funzionato, pr oviamo quindi a intr odur r e una nuova tabella:

UsersPrograms (UserID As Integer, ProgramID As Integer)

In questa tabella imponiamo che non es is ta alcuna chiave primaria. Infatti lo scopo di questa r elazione è un

altr o: ad un cer to pr ogr amma associa un utente, ma questo lo si può far e più volte. Ad esempio:

Relazione Pr ogr am

Pr ogr amID Path Descr iption

Page 330: Guida visual basic

1 C:\WINDOWS\notepad.ex e Editor di testo

2 C:\Pr ogr ammi\Fir eFox \fir efox .ex e Fir eFox web br owser

3 C:\Pr ogr ammi\Wor ld of War cr aft\WoW.ex e Wor ld of War cr aft

...

Relazione User

User ID Name MainFolder

1 Mar io Rossi C:\User s\MRossi

1 Luigi Bianchi C:\User s\Gigi

...

Relazione User sPr ogr ams

User ID Pr ogr amID

1 1

1 2

2 2

2 3

...

Nell'ultima r elazione tr oviamo un 1 (due volte) associamo pr ima ad un 1 e poi ad un 2: significa che lo stesso

utente 1 (Mar io Rossi) può acceder e sia al pr ogr amma 1 (notepad) sia al pr ogr amma 2 (fir efox ). Allo stesso

modo, l'utente 2 può acceder e sia al pr ogr amma 2 (fir efox ) sia al pr ogr amma 3 (Wor ld of War cr aft). Con

l'aggiunta di un'altr a tabella siamo r iusciti a legar e più utenti a più pr ogr ammi. Relazioni tr a tabelle di questo

tipo si dicono molti a molti.

In ognuno di questi esempi, l'inter o con cui ci si r ifer isce ad un'altr a tupla viene detto chiave esterna.

Page 331: Guida visual basic
Page 332: Guida visual basic

C2. Descrizione dei componenti principali

Dettagli tecnic iPr ima di iniziar e, qualche dettaglio tecnico. Per i pr ossimi esempi user ò MySql. Tr ovate una guida su come scar icar lo,

configur ar lo e gestir lo nel capitolo A1 del tutor ial dedicato a LINQ. Oltr e a ciò che viene descr itto in quella sezione,

avr emo bisogno di alcune classi per inter facciar ci con MySql, e che non sono pr esenti nell'installazione standar d del

fr amewor k. Potete scar icar e gli assemblies necessar i da qui. Essi ver r anno automaticamente installati nella GAC e

sar anno accessibili sucessivamente assieme a tutti gli altr i assemblies fondamentali nella scheda ".NET" della finestr a di

dialogo "Add Refer ence", già spiegata pr ecedentemente. Per usar li, impor tate i r ifer imenti e aggiungete le dir ettive

Impor ts all'inizio del sor gente.

ConnessioneLa pr ima cosa da far e per iniziar e a smanettar e su un database consiste pr incipalmente nel collegar si alla macchina

sulla quale esso esiste, che possiamo definir e ser ver . L'applicazione è quindi un client (vedi capitolo sui Socket), che

instaur a un collegamento non solo fisico (tr amite Inter net), ma anche logico, con il pr ogr amma che for nisce il ser vizio

di gestione dei database; nel nostr o caso si tr atta di MySql. Per gli esempi che user ò, l'host, ossia l'elabor ator e che

ospita il ser vizio, coincider à con il vostr o stesso computer , ossia ci connetter emo a localhost (127.0.0.1). Se avete

installato tutto senza pr oblemi e avviato MySql cor r ettamente, possiamo iniziar e ad analizzar e la pr ima classe

impor tante: MySqlConnection. Essa for nisce le funzionalità di connessione sopr acitate mediante due semplici metodi:

Open (apr e la connessione) e Close (la chiude). Il suo costr uttor e pr incipale accetta come ar gomento una str inga detta

connection str ing , la quale definisce dove e come eseguir e il collegamento. Tipicamente, è for mata da var ie par ti,

separ ate da punti e vir gola, ciascuna delle quali imposta una data pr opr ietà. Eccone un esempio:

Esiste anche un'altr a var iante della connection str ing, che si pr esenta come segue:

"Data Source=localhost; UserId=root; PWD=root;"

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.

Imports MySql.Data.MySqlClient '... 'Crea una nuova connessione all'host locale,'accedendo al servizio come utente "root" e con'password "root". Se non avete modificato le'impostazioni di sicurezza, questa coppia di username'e password è quella predefinita.'Naturalmente è un'idiozia mantenere queste'credenziali così ovvie e dovrebbero essere cambiate'subito, ma per i miei esempi userò sempre root.Dim Conn As New MySqlConnection("Server=localhost; Uid=root; Pwd=root;") Try

'Avvia la connessioneConn.Close()

Catch Ex As Exception'...

Finally'E la chiude. Ricordatevi che è sempre bene'chiudere la connessione anche quando si verifichino'errori (anzi, soprattutto in queste situazioni).Conn.Close()

End Try

Page 333: Guida visual basic

Una volta connessi, è possibile effettuar e oper azioni var ie sui database, anche se, a dir la ver ità, noi non abbiamo

ancor a nessun database su cui oper ar e...

Esecuzione di una queryIl ter mine quer y indica una str inga mediante la quale si inter r oga un database per ottener ne infor mazioni, o per

cr ear e/modificar e quelle già contenutevi. Le quer y pr esentano una lor o sintassi par ticolar e, la quale, pur var iando

legger mente da un gestor e all'altr o (MySql, Sql Ser ver , Or acle, Access, ecceter a...), ader isce ad uno standar d univer sale:

l'SQL, appunto (Str uctur ed Quer y Language). In questo capitolo e nei pr ossimi analizzer ò solo qualche semplice esempio

di quer y, poiché la tr attazione del linguaggio inter o r ichieder ebbe svar iati capitoli suppletivi che esulano dalle

intenzioni di questa guida. Vi invito, comunque, a sceglier e una guida a questo linguaggio, o almeno un r efer ence

manual, da legger e in par allelo con i pr ossimi capitoli. Alcuni link inter essanti: World Wide Web Consortium ,

Morpheus Web, HTML.it (SQL) HTML.it (MySQL).

Per iniziar e, vediamo quale classe gestisca le quer y. Si tr atta di MySqlCommand. Essa espone alcuni costr uttor i, tr a cui

uno senza par ametr i, ma tutti gli ar gomenti specificabili sono anche accessibili tr amite le sue pr opr ietà. I membr i

significativi sono:

Cancel() : cancella l'esecuzione di una quer y in cor so;

CommandTex t : indica la quer y stessa;

CommandTimeout : il tempo massimo, in millisecondi, oltr e il quale l'esecuzione della quer y viene annullata;

Connection : deter mina l'oggetto MySqlConnecction mediante il quale si è connessi al database. Senza

connessione, ovviamente, non si potr ebbe far e un bel niente;

Ex ecuteNonQuer y() : esegue la quer y specificata e r estituisce il numer o di r ecor d da essa affetti, ossia

selezionati, cr eati, cancellati o modificati. Restituisce -1 in caso di fallimento;

Ex ecuteReader () : esegue la quer y e r estituisce un oggetto MySqlDataReader che per mette di scor r er e una alla

volta le r ighe che sono state selezionate. Il r eader , come sugger isce il nome, "legge" i dati, ma questi sono

vir tualmente posti su un flusso immaginar io, poiché la quer y non viene eseguita tutto in un colpo, ma di volta in

volta. Vedr emo successivamente, più in dettaglio come usar e un oggetto di questo tipo e quali limitazioni

compor ta;

Ex ecuteScalar () : esegue la quer y e r estituisce il contenuto della pr ima colonna della pr ima r iga di tutti i

r isultati. Restituisce Nothing se la quer y fallisce;

Or a, per pr ima cosa, dobbiamo cr ear e un nuovo database: potete far lo manualmente da SQL Yog o da qualsiasi altr o

pr ogr amma di gestione, ma io user ò solo codice, per evitar e di impor r e vincoli inutili:

Quando il nuovo database è cr eato, possiamo modificar e la connection str ing in modo da apr ir e quello desider ato: la

sintassi per indicar e il database di default è "Database=[nome db];" oppur e "Initial Catalog=[nome db];". Per

01.02.03.04.05.06.07.08.09.10.11.12.13.14.15.16.17.

'Potete porre questo sorgente sia'in una Windows Application sia in una Console Application,'anche perchè lo useremo solo una volta.Dim Conn As New MySqlConnection("Server=localhost; Uid=root; Pwd=root;")Dim Cmd As New MySqlCommand() Try

Conn.Open()Cmd.Connection = Conn'Crea un nuovo database di nome "appdata"Cmd.CommandText = "CREATE DATABASE appdata;"Cmd.ExecuteNonQuery()

Catch Ex As Exception Finally

Conn.Close()End Try

Page 334: Guida visual basic

esemplificar e questa nuova aggiunta alla str inga di connessione, utilizziamo anche un codice per cr ear e la tabella su cui

lavor er emo:

Con lo stesso pr ocedimento, è anche possibile inser ir e tuple nella tabella mediante il "comando" inser t into:

INSERT INTO Customers VALUES(1, 'Mario', 'Rossi', 'Via Roma 89, Milano', '50 288 41 971');

Qui potete tr ovar e una cinquantina di quer ies cr eate a r andom da eseguir e sulla tabella per inser ir e qualche valor e,

giusto per aver e un po' di dati su cui lavor ar e.

Enumerazione di recordCome accennato, pr ecedentemente, quando si esegue Ex ecuteReader , viene r estituito un oggetto MySqlDataReader che

per mette di scor r er e i r isultati di una quer y. Ecco una br eve descr izione dei suoi membr i:

Close() : chiude il r eader . Dato che mentr e il r eader è aper to, nessuna oper azione può esser e eseguita sul

database, è sempr e obbligator io chiuder e l'oggetto dopo l'uso;

FieldCount : indica il numer o di attr ibuti della r iga cor r ente;

Get...(i) : tutte le funzioni il cui nome inizia per "Get" ser vono per ottener e il valor e della i-esima colonna

sottofor ma di un par ticolar e tipo;

HasRows : deter mina se il r eader contenga almeno un r ecor d da legger e;

IsClosed : indica se l'oggetto è stato chiuso;

IsDbNull(i) : r estituisce Tr ue se il campo i-esimo del r ecor d cor r ente non contiene un valor e (r appr esentato dalla

costante DBNull.Value);

Read() : legge una nuova r iga e r estituisce Tr ue se l'oper azione è r iuscita. False significa che non c'è più nulla da

legger e;

Ecco un esempio di come usar e il Reader , in un'applicazione Windows For m con una listview e un pulsante:

01.02.03.04.05.06.07.08.09.10.11.12.13.14.15.16.17.

18.19.20.21.22.23.

Dim Conn As New MySqlConnection("Server=localhost; Database=appdata; Uid=root; Pwd=root;")Dim Cmd As New MySqlCommand() Try

Conn.Open()Cmd.Connection = Conn'Crea una nuova tabella di nome Customers nel database'appdata. I suoi attributi (colonne) sono:' - ID : identificativo numerico del record; non può essere' vuoto, viene autoincrementato quando si aggiunge una' nuova riga ed è la chiave primaria della' relazione Customers' - FirstName : nome del cliente (max 150 caratteri)' - LastName : cognome del cliente (max 150 caratteri)' - Address : indirizzo (max 255 caratteri)' - PhoneNumber : numero telefonico (max 30 caratteri)Cmd.CommandText = "CREATE TABLE Customers (ID int NOT NULL AUTO_INCREMENT, FirstName

char(150), LastName char(150), Address char(255), PhoneNumber char(30), PRIMARY KEY(ID));"

Cmd.ExecuteNonQuery()Catch Ex As Exception Finally

Conn.Close()End Try

01.02.03.04.05.

Imports MySql.Data.MySqlClient Class Form1

Private Sub btnLoad_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)

Page 335: Guida visual basic

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.

Handles btnLoad.ClickDim Conn As New MySqlConnection("Server=localhost; Database=appdata; Uid=root;

Pwd=root;")Dim Command As New MySqlCommand

Try

'Seleziona tutti i record della tabella Customers,'includendovi tutti gli attributi. Questa query è'equivalente a:' SELECT ID, FirstName, LastName, Address, PhoneNumber FROM CustomersCommand.CommandText = "SELECT * FROM Customers;"Command.Connection = ConnConn.Open()

'Esegue la query e restituisce il readerDim Reader As MySqlDataReader = Command.ExecuteReader()

lstRecords.Columns.Clear()'Aggiunge tante colonne alla listview quanti sono'gli attributi dei record selezionati. È possibile'ottenere il nome di ogni colonna con la funzione'GetName di MySqlDataReaderFor I As Int32 = 0 To Reader.FieldCount - 1

lstRecords.Columns.Add(Reader.GetName(I))Next

Dim L As ListViewItemDim S(Reader.FieldCount - 1) As String

'Fintanto che c'è qualche record da leggere,'lo aggiunge alla listviewDo While Reader.Read()

'Riempie l'array S con i valori degli attributi'della tupla corrente. Se una cella non contiene'valori, mette una stringa vuota al suo postoFor I As Int32 = 0 To S.Length - 1

If Not Reader.IsDBNull(I) Then'GetString(I) ottiene il valore della cella'I sottoforma di stringaS(I) = Reader.GetString(I)

ElseS(I) = ""

End IfNextL = New ListViewItem(S)lstRecords.Items.Add(L)

Loop

'Chiude il ReaderReader.Close()

Catch ex As ExceptionMessageBox.Show(ex.Message, Me.Text, MessageBoxButtons.OK,

MessageBoxIcon.Exclamation)Finally

'Chiude la connessioneConn.Clone()

End TryEnd Sub

End Class

Page 336: Guida visual basic
Page 337: Guida visual basic

C3. Un esempio pratico

Applicando i concetti del capitolo scor so, ho scr itto un piccolo esempio pr atico di applicazione basata su database, ossia

un semplicissimo gestionale per or ganizzar e la tabella Customer s. L'inter faccia è questa:

Il contr ollo vuoto è una ListView con View =Details e HideSelection=False. Le tr e tex tbox hanno un tag associato: la

pr ima ha tag "Fir stName", la seconda "LastName", la ter za "Addr ess" e la quar ta "PhoneNumber ". Ecco il codice:

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.

Imports MySql.Data.MySqlClient Class Form1

Private Conn As MySqlConnection

'Esegue una query sul database, quindi carica i'risultati nella listviewPrivate Sub LoadData(ByVal Query As String)

Dim Command As New MySqlCommand

Command.CommandText = QueryCommand.Connection = Conn

Dim Reader As MySqlDataReader = Command.ExecuteReader()

If lstRecords.Columns.Count = 0 Then

For I As Int32 = 0 To Reader.FieldCount - 1lstRecords.Columns.Add(Reader.GetName(I))

NextEnd If

Dim L As ListViewItemDim S(Reader.FieldCount - 1) As String

lstRecords.Items.Clear()Do While Reader.Read()

For I As Int32 = 0 To S.Length - 1If Not Reader.IsDBNull(I) Then

Page 338: Guida visual basic

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.

S(I) = Reader.GetString(I)Else

S(I) = ""End If

NextL = New ListViewItem(S)lstRecords.Items.Add(L)

Loop

Reader.Close()Command.Dispose()Command = Nothing

End Sub

Private Sub LoadData()Me.LoadData("SELECT * FROM Customers")

End Sub

'Scorciatoia per eseguire una query velocementePrivate Function ExecuteQuery(ByVal Query As String) As Int32

Dim Command As New MySqlCommand(Query, Conn)Dim Result As Int32 = Command.ExecuteNonQuery()

Command.Dispose()Command = Nothing

Return Result

End Function

Private Sub Form_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) HandlesMyBase.LoadConn = New MySqlConnection("Server=localhost; Database=appdata; Uid=root; Pwd=root;")

Try

Conn.Open()Catch ex As Exception

Conn.Close()MessageBox.Show(ex.Message, Me.Text, MessageBoxButtons.OK,

MessageBoxIcon.Exclamation)Me.Close()

End Try

LoadData()End Sub

Private Sub btnAdd_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)

Handles btnAdd.ClickIf ExecuteQuery(String.Format("INSERT INTO Customers VALUES(null, '{0}', '{1}', '{2}',

'{3}');", txtFirstName.Text, txtLastName.Text, txtAddress.Text,txtPhoneNumber.Text)) ThenMessageBox.Show("Cliente aggiunto!", Me.Text, MessageBoxButtons.OK,

MessageBoxIcon.Information)LoadData()

End IfEnd Sub

Private Sub btnEdit_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)

Handles btnEdit.ClickIf lstRecords.SelectedIndices.Count = 0 Then

MessageBox.Show("Nessun record selezionato!", Me.Text, MessageBoxButtons.OK,MessageBoxIcon.Exclamation)

Exit SubEnd If

Dim ID As Int32 = CType(lstRecords.SelectedItems(0).SubItems(0).Text, Int32)Dim Query As New System.Text.StringBuilder()

'L'istruzione UPDATE aggiorna i campi della tabella'specificata usando i valori posti dopo la clausola'SET. Solo i record che rispettano i vincoli imposti'dalla clausola WHERE vengono modificatiQuery.Append("UPDATE Customers SET")

Page 339: Guida visual basic

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.144.145.146.

147.148.149.150.151.152.153.154.155.156.157.

For Each T As TextBox In New TextBox() {txtFirstName, txtLastName, txtAddress,txtPhoneNumber}Query.AppendFormat(" {0} = '{1}',", T.Tag.ToString(), T.Text)

Next'Rimuove l'ultima virgola...Query.Remove(Query.Length - 1, 1)

Query.AppendFormat(" WHERE ID = {0};", ID)

ExecuteQuery(Query.ToString())Query = NothingLoadData()

End Sub

Private Sub btnFilter_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)Handles btnFilter.ClickDim Query As New System.Text.StringBuilder()Dim Conditions As New List(Of String)

Query.Append("SELECT * FROM Customers")

'La scrittura:' Field LIKE '%Something%''equivarrebbe teoricamente a:' Field.Contains(Something)For Each T As TextBox In New TextBox() {txtFirstName, txtLastName, txtAddress,

txtPhoneNumber}If Not String.IsNullOrEmpty(T.Text) Then

Conditions.Add(String.Format("WHERE {0} LIKE '%{1}%'", T.Tag.ToString(),T.Text))

End IfNext

If Conditions.Count >= 1 Then

Query.AppendFormat(" {0}", Conditions(0))If Conditions.Count > 1 Then

For I As Int32 = 1 To Conditions.Count - 1Query.AppendFormat(" AND {0}", Conditions(I))

NextEnd If

End IfQuery.Append(";")

LoadData(Query.ToString())Query = Nothing

End Sub

Private Sub Form1_FormClosing(ByVal sender As System.Object, ByVal e AsSystem.Windows.Forms.FormClosingEventArgs) Handles MyBase.FormClosingIf Conn.State <> ConnectionState.Closed Then

Conn.Close()End If

End Sub

Private Sub btnReload_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)Handles btnReload.ClickLoadData()

End Sub

Private Sub lstRecords_SelectedIndexChanged(ByVal sender As System.Object, ByVal e AsSystem.EventArgs) Handles lstRecords.SelectedIndexChangedIf lstRecords.SelectedItems.Count = 0 Then

Exit SubEnd If

Dim Selected As ListViewItem = lstRecords.SelectedItems(0)txtFirstName.Text = Selected.SubItems(1).TexttxtLastName.Text = Selected.SubItems(2).TexttxtAddress.Text = Selected.SubItems(3).TexttxtPhoneNumber.Text = Selected.SubItems(4).Text

End SubEnd Class

Page 340: Guida visual basic
Page 341: Guida visual basic

C4. Dalle relazioni agli oggetti - Parte I

Usar e quer ies per manipolar e il database è un mezzo molto efficacie, anche se il pr ocesso per cr ear e una quer y

sottofor ma di str inga può r isultar e alquanto macchinoso in alcuni casi. A questo pr oposito, vor r ei invitar vi a legger e le

pr ime lezioni del tutor ial che ho scr itto r iguar do a LINQ, il linguaggio di quer ying integr ato disponibile dalla ver sione

2008 del linguaggio (fr amewor k v3.5).

In questo capitlo, invece, inizier emo a passar e dalle r elazioni, ossia dalle tabelle del database nel lor o ambiente, agli

oggetti, tr asponendo, quindi, tutte le oper azioni a costr utti che già conosciamo. Possiamo r appr esentar e un database

e le sue tabelle in due modi:

Mediante l'appr occio standar d, con le classi DataSet e DataTable, che r appr esentano esattamente il database, con

tutte le sue pr opr ietà e car atter istiche. Queste classi astr aggono tutta la str uttur a r elazione e la tr aspor tano

nel linguaggio ad oggetti;

Mediante l'appr occio LINQ, con nor mali classi scr itte dal pr ogr ammator e, ar tificalmente collegate tr amite

attr ibuti e metadati, alle r elazioni pr esenti nel database;

Vedr emo or a solo il pr imo appr occio, poiché il secondo è tr attato già nel tutor ial menzionato pr ima.

DataSetLa classe DataSet ha lo scopo di r appr esentar e un database. Mediante un apposito oggetto, detto Adapter (che

analizzer emo in seguito), è possibile tr avasar e tutti i dati del database in un oggetto DataSet e quindi oper ar e su

questo senza bisogno di quer y. Una volta ter minate le elabor azioni, si esegue il pr ocesso inver so aggior nando il

database con le nuove modifiche appor tate al DataSet. Questo è il pr incipio di base con cui si affr onta il passaggio dalle

r elazioni agli oggetti.

Questa classe espone una gr an quantità di membr i, tr a cui menzioniamo i più impor tanti:

AcceptChanges() : confer ma tutte le modifiche appor tate al DataSet; questo metodo deve esser e r ichiamato

obbligator iamente pr ima di iniziar e la pr ocedur a di aggior namento del database a cui è collegato;

CaseSensitive : indica se la compar azione di campi di tipo str ing avviene in modalità case-sensitive;

Clear () : elimina tutti i dati pr esenti nel DataSet;

Clone() : esegue una clonazione deep dell'oggetto DataSet e r estituisce la nuova istanza;

DataSetName : nome del DataSet;

GetChanges() : r estituisce una copia del DataSet in cui sono pr esenti tutti i dati modificati (come se si fosse

r ichiamato AcceptChanges());

GetXml() : r estituisce una str inga contenente la r appr esentazione x ml di tutti i dati pr esenti nel DataSet;

HasChanges : deter mina se il DataSet sia stato modificato dall'ultimo salvataggio o car icamento;

IsInitialized : indica se è inizializzato;

Mer ge(D As DataSet) : unisce D al DataSet cor r ente. Le tabelle e i dati di D vengono aggiunti all'oggetto

cor r ente;

RejectChanges() : annulla tutte le modifiche appor tate dall'ultimo salvataggio o car icamento;

ReadXml(R) : legge un file XML mediante l'oggetto R (di tipo XmlReader ) e tr asfer isce i dati ivi contenuti nel

DataSet;

Reset() : annulla ogni modifica appor tata e r ipor ta il DataSet allo stato iniziale (ossia come er a dopo il

car icamento);

Page 342: Guida visual basic

Tables : r estituisce una collezione di oggetti DataTable, che r appr esentano le tabelle pr esenti nel DataSet (e

quindi nel database);

DataTableSe un DataSet r appr esenta un database, allor a un oggetto di tipo DataTable r appr esenta un singola tabella, composta di

r ighe e colonne. Nessun nuovo concetto da intr odur r e, quindi: si tr atta solo di una classe che r appr esenta ciò che

abbiano visto fin or a per una r elazione. I suoi membr i sono pr essoché simili a quelli di DataSet, con l'aggiunta delle

pr opr ietà Columns, Rows, Pr imar yKey e del metodo AddRow (ho menzionato solo quelli di uso più fr equente).

Caricamento e bindingOr a possiamo car icar e i dati in un DataSet ed eseguir e un binding ver so un contr ollo. Abbiamo già il concetto di binding

nei capitoli sulla r eflection, in r ifer imento alla possibilità di legar e un identificator e a un valor e. In questo caso, pur

essendo fondamentalmente la stessa cosa, il concetto è legger mente differ ente. Noi vogliamo legar e un cer to insieme di

valor i ad un contr ollo, in modo che esso li visualizzi senza dover scr iver e un codice par ticolar e per inser ir li. Il contr ollo

che, per eccellenza, r ende gr aficamente nel modo miglior e le tabelle è DataGr idView . Assumendo, quindi, di aver e nel

for m solo un contr ollo DataGr idView1, possiamo scr iver e questo codice:

DataGr idView1 ver r à r iempito con tutti i dati pr esenti nella pr ima (e unica) tabella del dataset.

DataSet tipizzatiNel pr ossimo esempio vedr emo di accennar e alla costr uzione di una semplice applicazione per gestir e br ani musicali

con un database, ed eventualmente intr odur r ò un po' di codice per la r ipr oduzione audio. In questo esempio, per ò, non

user emo un gener ico database, ma uno specifico database di cui conosciamo le pr opr ietà e della cui esistenza siamo

cer ti. Quindi far emo a meno di usar e un gener ico DataSet, ma ne cr eer emo uno specifico per quel database, che sia

più semplice da manipolar e. User emo, quindi, un DataSet tipizzato. Non dovr emo scr iver e alcuna r iga di codice per far

questo, poiché baster à "disegnar e" la str uttur a del database con uno specifico str umento che il nostr o IDE mette a

disposizioni, e che si chiama Table Designer . Mediante quest'ultimo, possiamo delinar e lo schema di un database e delle

sue tabelle, e l'ambiente di sviluppo pr ovveder à a scr iver e un codice adeguato per cr ear e un dataset tipizzato specifico

per quel database. A livello pr atico, un dataset tipizzato non è altr o che una classe der ivata da DataSet che definisce

alcune pr opr ietà e metodi atti a semplificar e la scr ittur a di codice. Ad esempio, invece di r efer enziar e la pr ima cella

01.02.03.04.05.

06.

07.08.09.10.11.12.13.14.15.16.17.18.19.

Imports MySql.Data.MySqlClientClass Form1

Private Data As New DataSet

Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) HandlesMyBase.LoadDim Conn As New MySqlConnection("Server=localhost; Database=appdata; Uid=root;

Pwd=root;")Dim Adapter As New MySqlDataAdapter

Conn.Open()

Adapter.SelectCommand = New MySqlCommand("SELECT * FROM Customers;", Conn)Adapter.Fill(Data)

Conn.Clone()

DataGridView1.DataSource = Data.Tables(0)

End Sub

End Class

Page 343: Guida visual basic

della pr ima r iga di una tabella, ottener ne il valor e e conver tir lo in inter o, la ver sione tipizzata del dataset espone

dir ettamente una pr opr ietà ID (ad esempio) che fa tutto questo. C'è solo un difetto nel codice autogener ato, ma lo

illustr er ò in seguito.

Pr ima di iniziar e, bisogna cr ear e effettivamente le tabelle che user emo nel database AppData. Per questo pr ogr amma,

ho ideato tr e tabelle: author s, songs e albums, costr uite come segue:

CREATE TABLE `albums` ( `ID` int(11) NOT NULL AUTO_INCREMENT, `Name` char(255) NOT NULL, `Year` int(11) DEFAULT NULL, `Description` text, `Image` text, PRIMARY KEY (`ID`) );

CREATE TABLE `authors` ( `ID` int(11) NOT NULL AUTO_INCREMENT, `Name` char(255) NOT NULL, `Nickname` char(255) DEFAULT NULL, `Description` text, `Image` text, PRIMARY KEY (`ID`) );

CREATE TABLE `songs` ( `ID` int(11) NOT NULL AUTO_INCREMENT, `Path` char(255) NOT NULL, `Title` char(255) DEFAULT NULL, `Author` int(11) DEFAULT NULL, `Album` int(11) DEFAULT NULL, PRIMARY KEY (`ID`) );

[Gli accenti tonici sono stati aggiunti da SQLyog, e sono obbligator i solo se il nome della colonna o della tabella contiene

degli spazi.]

Pr ima di pr oceder e, potr ebbe esser e utile mostr ar e la toolbar di gestione delle basi di dati: per far questo, cliccate

con il pulsante destr o su uno spazio vuoto della toolbar e spuntate Data Design per far appar ir e le r elative icone:

Page 344: Guida visual basic

Per aggiunger e un nuovo dataset tipizzato, invece, cliccate sempr e col destr o sul nome del pr ogetto nel solution

ex plor er , scegliete Add Item e quindi DataSet. Dovr ebbe appar ir vi un nuovo spazio vuoto simile a questo:

Spostando il mouse sulla toolbox a fianco, potr ete notar e che è possibile aggiunger e tabelle, r elazioni, quer ies e alcune

altr e cose. Dato che dobbiamo r icr ear e la stessa str uttur a del database AppData, è necessar io cr ear e tr e tabelle:

Albums, Author s e Songs, ciascuna con le stesse colonne di quelle sopr a menzionate. Tr ascinate un componente

DataTable all'inter no della scher mata e r inominatelo in Songs, quindi fate click col destr o sull'header della tabella e

scegliete Add > Column:

Aggiungete quindi tante colonne quante sono quelle del codice SQL. Or a selezionate la pr ima colonna (ID) e por tate in

Page 345: Guida visual basic

pr imo piano la finestr a della pr opr ietà (la stessa usata per i contr olli). Essa visualizzer à alcune infor mazioni sulla

colonna ID. Per r ispettar e il vincolo con il database r eale, essa deve esser e dichiar ata di tipo inter o, deve suppor tar e

l'autoincr emento e non può esser e NULL:

Or a fate lo stesso con tutte le altr e colonne, tenendo conto che char (255) e tex t sono entr ambi contenibili dallo stesso

tipo (Str ing). Pr ima di passar e alla compilazione delle altr e tabelle, r icor datevi di impostar e ID come chiave pr imar ia:

cliccate ancor a sull'header della tabella, scegliendo Add > Key:

Bene. Come avr ete sicur amente notate, i campi Author e Album di Songs non sono str inghe, bensì inter i. Infatti, come

abbiamo visto qualche capitolo fa, è possibile collegar e logicamente due tabelle tr amite una r elazione (uno-a-uno,

uno-a-molti o molti-a-molti). In questo caso, vogliamo collegar e al campo Author di una canzone, la tupla che

r appr esenta quell'autor e nella tabella Author s. Questa è una r elazione uno-a-molti (in questo pr ogr amma semplificato,

assumiamo che tutti color o che hanno par tecipato alla r ealizzazione siano consider ati "autor i", senza le var ie

Page 346: Guida visual basic

distinzioni tr a autor e dei testi, ar tist, compositor i ecceter a...). Mediante l'editor integr ato nell'ambiente di sviluppo

possiamo anche aggiunger e questa r ealzione (che andr à a popolar e la pr opr ietà Relations del DataSet). Basta

aggiunger e un oggetto Relation e compilar e i campi r elativi:

Una volta completati tutti i passaggi, è possibile iniziar e a scr iver e qualche r iga di codice (non dimenticatevi di

r iempir e il database con qualche tupla di esempio pr ima di iniziar e il debug).

Musica, maestro!Ecco l'inter faccia del pr ogr amma:

Page 347: Guida visual basic

Oltr e ai contr olli che si vedono nell'immagine, ho aggiunto anche il dataset tipizzato cr eato pr ima dall'editor ,

AppDataSet. Dato che nella listbox sulla sinistr a visualizzer emo i titoli delle canzoni, possiamo eseguir e un binging di

tali dati sulla listbox . Dopo aver la selezionata, andate nella pr opr ietà DataSour ce e scegliete la tabella Songs:

Dopodiché, impostate il campo DisplayMember su Title e ValueMember su ID: come avevo spiegato nel capitolo sulla

listbox , queste pr opr ietà ci per mettono di modificar e cosa viene visualizzato coer entemente con gli oggetti

immagazzinati nella lista. Se avete fatto tutto questo, l'IDE cr eer à automaticamente un nuovo oggetto di tipo

BindingSour ce (il SongsBindingSour ce dell'immagine pr ecedente). Esso ha il compito di mediar e tr a la sor gente dati e

l'inter faccia utente, e ci sar à utile in seguito per la r icer ca.

Ecco il codice:

001.002.003.

004.

005.006.007.008.009.010.011.012.013.014.015.016.017.018.019.

Public Class Form1

Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) HandlesMyBase.LoadDim Conn As New MySqlConnection("Server=localhost; Database=appdata; Uid=root;

Pwd=root;")Dim Adapter As New MySqlDataAdapter

Conn.Open()

'Carica le tabelle nel dataset. Le tabelle sono ora'accessibili mediante omonime proprietà dal'dataset tipizzato Adapter.SelectCommand = New MySqlCommand("SELECT * FROM Songs;", Conn)Adapter.Fill(AppDataSet.Songs)

Adapter.SelectCommand = New MySqlCommand("SELECT * FROM Authors;", Conn)Adapter.Fill(AppDataSet.Authors)

Page 348: Guida visual basic

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.084.085.086.087.088.089.090.

Adapter.SelectCommand = New MySqlCommand("SELECT * FROM Albums;", Conn)Adapter.Fill(AppDataSet.Albums)

Conn.Clone()

End Sub

Private Sub lstSongs_SelectedIndexChanged(ByVal sender As System.Object, ByVal e AsSystem.EventArgs) Handles lstSongs.SelectedIndexChangedIf lstSongs.SelectedIndex < 0 Then

Exit SubEnd If

'Trova la riga con ID specificato. Il tipo SongsRow è stato'definito dall'IDE durante la creazione del dataset tipizzato.Dim S As AppDataSet.SongsRow = AppDataSet.Songs.FindByID(lstSongs.SelectedValue)

lblName.Text = S.Title

tabAuthor.Tag = Nothing'Anche il metodo IsAuthorNull è stato definito dall'IDE.'In generale, per ogni proprietà per cui non è stata'specificata la clausola NOT NULL, l'IDE crea un metodo per'verificare se quel dato attributo contiene un valore'nullo. Equivale a chiamare S.IsNull(3).If Not S.IsAuthorNull() Then

'Ottiene un array che contiene tutte le righe della'tabella Authors che soddisfano la relazione definita'tra Songs e Authors. Dato che la chiave esterna della'tabella figlio era un ID, la realzione, pur essendo'teoricamente uno-a-molti, è in realtà'uno-a-uno. Perciò, se questo array ha almeno'un elemento, ne avrà solo uno.Dim Authors() As AppDataSet.AuthorsRow = S.GetAuthorsRows()'Come IsNull, questo metodo equivale a chiamare'S.GetChildRows("Songs_Authors") If Authors.Length > 0 Then

Dim Author As AppDataSet.AuthorsRow = Authors(0)

lblAuthorName.Text = Author.NameIf Not Author.IsNicknameNull() Then

lblAuthorName.Text &= vbCrLf & Chr(34) & Author.Nickname & Chr(34)End IfIf Not Author.IsImageNull() Then

imgAuthor.Image = Image.FromFile(Author.Image)Else

imgAuthor.Image = NothingEnd If

If Not Author.IsDescriptionNull() Then

txtAuthorDescription.Text = Author.DescriptionElse

txtAuthorDescription.Text = ""End IftabAuthor.Tag = Author.ID

End IfEnd If

tabAlbum.Tag = NothingIf Not S.IsAlbumNull() Then

Dim Albums() As AppDataSet.AlbumsRow = S.GetAlbumsRows()

If Albums.Length > 0 ThenDim Album As AppDataSet.AlbumsRow = Albums(0)

lblAlbumName.Text = Album.NameIf Not Album.IsYearNull() Then

lblAlbumYear.Text = Album.YearElse

lblAlbumYear.Text = ""End IfIf Not Album.IsImageNull() Then

Page 349: Guida visual basic

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.

imgAlbum.Image = Image.FromFile(Album.Image)Else

imgAlbum.Image = NothingEnd IfIf Not Album.IsDescriptionNull() Then

txtAlbumDescription.Text = Album.DescriptionElse

txtAlbumDescription.Text = ""End IftabAlbum.Tag = Album.ID

End IfEnd If

End Sub

Private Sub btnSearch_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)Handles btnSearch.ClickIf Not String.IsNullOrEmpty(txtSearch.Text) Then

'La proprietà Filter di un binding source è come'una condizione SQL. In questo caso cerchiamo tutte le'canzoni il cui titolo contenga la stringa specificata.SongsBindingSource.Filter = String.Format("title like '%{0}%'", txtSearch.Text)

ElseSongsBindingSource.Filter = ""

End IfEnd Sub

End Class

Page 350: Guida visual basic

C5. Dalle relazioni agli oggetti - Parte II

Aggiungere, eliminare, modificareL'ultimo esempio di codice per metteva solo di scor r er e elementi già pr esenti nel database, ma questo è davver o poco

utile all'utente. Vediamo, allor a, di aggiunger e un po' di dinamismo all'applicazione. Volendo gestir e tutto in manier a

or dinata, sar ebbe bello che ci fosse un contr ollo dedicato a visualizzar e, modificar e e salvar e le infor mazioni sull'autor e

e uno identico per l'album. A questo scopo, possiamo scr iver e dei nuovi contr olli utente. Io ho scr itto solo il pr imo,

poiché il codice per il secondo è pr essoché identico:

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.32.33.34.35.36.37.38.39.40.41.42.43.44.45.46.

47.48.49.50.51.52.53.54.

Public Class AuthorViewerPrivate _Author As AppDataSet.AuthorsRow

'Evento generato quando un autore viene aggiunto. Questo'evento si verifica se l'utente salva dei cambiamenti'quando la proprietà Author è Nothing.'Non potendo modificare una riga esistente, quindi, ne'viene creata una nuova. Poich´, tuttavia, questo'autore deve essere associato alla canzone, bisogna che'qualcuno ponga l'ID della nuova riga nel campo'Author della canzone opportuna e, dato che questo'controllo non può n´ logicamente né'praticamente arrivare a fare ciò, bisogna che'qualcun altro se ne occupi.Public Event AuthorAdded As EventHandler

Public Property Author() As AppDataSet.AuthorsRow

GetReturn _Author

End GetSet(ByVal value As AppDataSet.AuthorsRow)

If value IsNot Nothing Then_Author = valueWith value

lblName.Text = .NameIf Not .IsImageNull() Then

imgAuthor.ImageLocation = .ImageElse

imgAuthor.ImageLocation = NothingEnd IfIf Not .IsDescriptionNull() Then

txtDescription.Text = .DescriptionElse

txtDescription.Text = ""End If

End WithElse

lblName.Text = "Nessun nome"imgAuthor.ImageLocation = NothingtxtDescription.Text = ""

End IfimgSave.Visible = False

End SetEnd Property

Private Sub imgAuthor_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)

Handles imgAuthor.Click'FOpen è un OpenFileDialog dichiarato nel designer.'Questo codice serve per caricare un'immagine da disco'fissoIf FOpen.ShowDialog = DialogResult.OK Then

imgAuthor.ImageLocation = FOpen.FileNameimgSave.Visible = True

End If

Page 351: Guida visual basic

E questa è l'inter faccia:

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.

End Sub

'L'immagine del floppy diventa visibile solo quando c'è stata'una modifica, ossia è stato cambiato uno di questi'parametri: nome, immagine, descrizione.Private Sub txtDescription_TextChanged(ByVal sender As System.Object, ByVal e As

System.EventArgs) Handles txtDescription.TextChangedimgSave.Visible = True

End Sub

Private Sub lblName_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)Handles lblName.ClickDim NewName As String = InputBox("Inserire nome:")

If Not String.IsNullOrEmpty(NewName) Then

lblName.Text = NewNameimgSave.Visible = True

End IfEnd Sub

Private Sub imgSave_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)

Handles imgSave.ClickIf _Author Is Nothing Then

'Crea la nuova riga e la inserisce nel dataset'principale. Notare che questo approccio non è'il migliore possibile, poich´ è sempre'consigliabile rendere il codice il più generale'possibile, e limitare i riferimenti agli altri form.'Sarebbe stato più utile rendere AppDataSet'visibile all'intero progetto mediante un'modulo pubblico._Author = My.Forms.Form1.AppDataSet.Authors.AddAuthorsRow(lblName.Text, "",

txtDescription.Text, imgAuthor.ImageLocation)'Genera l'evento AuthorAddedRaiseEvent AuthorAdded(Me, EventArgs.Empty)

Else_Author.Name = lblName.Text_Author.Description = txtDescription.Text_Author.Image = imgAuthor.ImageLocation

End IfimgSave.Visible = False

End SubEnd Class

Page 352: Guida visual basic

È pr esente uno split container , in cui nella par te sinistr a c'è la pictur ebox (con dock=fill) e nella par te destr a la

tex tbox . L'immagine del floppy ser ve per avviar e il salvataggio dei dati nell'oggetto Author sRow sotteso (ma non nel

database).

E questo è il codice dell'applicazione, modificato in modo da suppor tar e il nuovo contr ollo (solo per l'autor e):

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.

Imports MySql.Data.MySqlClient Public Class Form1

Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) HandlesMyBase.LoadDim Conn As New MySqlConnection("Server=localhost; Database=appdata; Uid=root;

Pwd=root;")Dim Adapter As New MySqlDataAdapter

Conn.Open()

Adapter.SelectCommand = New MySqlCommand("SELECT * FROM Songs;", Conn)Adapter.Fill(AppDataSet.Songs)

Adapter.SelectCommand = New MySqlCommand("SELECT * FROM Authors;", Conn)Adapter.Fill(AppDataSet.Authors)

Adapter.SelectCommand = New MySqlCommand("SELECT * FROM Albums;", Conn)Adapter.Fill(AppDataSet.Albums)

Conn.Clone()

End Sub

Private Sub lstSongs_SelectedIndexChanged(ByVal sender As System.Object, ByVal e AsSystem.EventArgs) Handles lstSongs.SelectedIndexChangedIf lstSongs.SelectedIndex < 0 Then

Exit SubEnd If

Dim S As AppDataSet.SongsRow = AppDataSet.Songs.FindByID(lstSongs.SelectedValue)

lblName.Text = S.Title

tabAuthor.Tag = NothingIf Not S.IsAuthorNull() Then

Dim Authors() As AppDataSet.AuthorsRow = S.GetAuthorsRows()

'Imposta la proprietà Author del controllo avAuthor,'che non è altro che un'istanza di AuthorViewer,'il controllo utente creato poco faIf Authors.Length > 0 Then

Dim Author As AppDataSet.AuthorsRow = Authors(0)avAuthor.Author = Author

ElseavAuthor.Author = Nothing

End IfEnd If

tabAlbum.Tag = NothingIf Not S.IsAlbumNull() Then

Dim Albums() As AppDataSet.AlbumsRow = S.GetAlbumsRows()

If Albums.Length > 0 ThenDim Album As AppDataSet.AlbumsRow = Albums(0)

lblAlbumName.Text = Album.NameIf Not Album.IsYearNull() Then

lblAlbumYear.Text = Album.YearElse

lblAlbumYear.Text = ""

Page 353: Guida visual basic

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.

End IfIf Not Album.IsImageNull() Then

imgAlbum.Image = Image.FromFile(Album.Image)Else

imgAlbum.Image = NothingEnd IfIf Not Album.IsDescriptionNull() Then

txtAlbumDescription.Text = Album.DescriptionElse

txtAlbumDescription.Text = ""End IftabAlbum.Tag = Album.ID

End IfEnd If

End Sub

Private Sub btnSearch_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)Handles btnSearch.ClickIf Not String.IsNullOrEmpty(txtSearch.Text) Then

SongsBindingSource.Filter = String.Format("title like '%{0}%'", txtSearch.Text)Else

SongsBindingSource.Filter = ""End If

End Sub

Private Sub avAuthor_AuthorAdded(ByVal sender As System.Object, ByVal e AsSystem.EventArgs) Handles avAuthor.AuthorAddedIf lstSongs.SelectedIndex < 0 Then

Exit SubEnd If

Dim S As AppDataSet.SongsRow = AppDataSet.Songs.FindByID(lstSongs.SelectedValue)'Imposta il campo Author della canzone correnteS.Author = avAuthor.Author.ID

End Sub

Private Sub Form1_FormClosing(ByVal sender As System.Object, ByVal e AsSystem.Windows.Forms.FormClosingEventArgs) Handles MyBase.FormClosingDim Conn As New MySqlConnection("Server=localhost; Database=appdata; Uid=root;

Pwd=root;")Dim Adapter As New MySqlDataAdapter 'Un oggetto di tipo CommandBuilder genera automaticamente'tutti le istruzioni update, insert e delete che servono'a un adapter per funzionare. Tali istruzioni vengono'generate relativamente alla tabella dalla quale si stanno'caricando i datiDim Builder As MySqlCommandBuilder

Conn.Open()

Adapter.SelectCommand = New MySqlCommand("SELECT * FROM Songs;", Conn)Builder = New MySqlCommandBuilder(Adapter)Adapter.Update(AppDataSet.Songs)

Adapter.SelectCommand = New MySqlCommand("SELECT * FROM Authors;", Conn)Builder = New MySqlCommandBuilder(Adapter)Adapter.Update(AppDataSet.Authors)

Adapter.SelectCommand = New MySqlCommand("SELECT * FROM Albums;", Conn)Builder = New MySqlCommandBuilder(Adapter)Adapter.Update(AppDataSet.Albums)

Conn.Clone()

End SubEnd Class

Page 354: Guida visual basic

C6. Il controllo BindingNavigator

FunzionamentoQuesto contr ollo per mette di navigar e attr aver so insiemi di dati, siano essi tabelle di database o semplici liste di

oggetti non fa differ enza, per mettendo di visualizzar e o modificar e una qualsiasi delle lor o pr opr ietà e di aggiunger e

od eliminar e uno qualsiasi dei suoi elementi. La par ticolar ità che lo distingue da qualsiasi altr o contr ollo del gener e

(come potr ebber o esser e ListView o DataGr idView) consiste nel fatto che la sua inter faccia non è una tabella: anzi, è a

pr ior i indefinita. Se si consider a poi il fatto che aggiunger lo semplicemente al for m non por ter à alcun r isultato, si

potr ebbe pensar e che BindingNavigator è pr opr io una fr egatur a XD

In effetti, per veder lo funzionar e cor r ettamente bisogna aggiunger e un po' di altr i contr olli e scr iver e qualche r iga di

codice. Infatti, ho appena detto che esso per mette di navigar e attr aver so un insieme di dati e visualizzar e tali dati su

una cer ta inter faccia gr afica che per or a non conosciamo: le incognite, quindi, sono due, ossia da dove attinger e i dati

e come visualizzar li. Per questo motivo, sono necessar i almeno altr i due componenti. Il pr imo di questi è un contr ollo

BindingSour ce, il quale, come già visto nel capitolo pr ecedente, si pr eoccupa di gestir e e mediar e l'inter azione con una

cer ta r isor sa di infor mazioni. Il secondo (e gli altr i eventuali) è ar bitr ar io e dipende dalla natur a dei dati da

visualizzar e: per delle str inghe, ad esempio, avr emo bisogno di una Tex tBox .

Autori illustri...Per esemplificar e il compor tamento di BindingNavigator , ecco una semplice applicazione che per mette di visualizzar e

una ser ie di gr andi nomi e le lor o oper e pr incipali. La nostr a fonte di dati sar à una lista di oggetti di tipo Author ,

classe così definita:

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.32.33.

Public Class Form1

Public Class AuthorPrivate _Name As StringPrivate _Works As List(Of String)

Public Property Name() As String

GetReturn _Name

End GetSet(ByVal value As String)

_Name = valueEnd Set

End Property

Public ReadOnly Property Works() As List(Of String)Get

Return _WorksEnd Get

End Property

Public Sub New()_Works = New List(Of String)

End Sub

Public Sub New(ByVal Name As String, ByVal ParamArray WorksNames() As String)Me.New()Me.Name = NameMe.Works.AddRange(WorksNames)

End SubEnd Class

Page 355: Guida visual basic

Or a aggiungiamo al for m un BindingNavigator di nome bnMain:

All'aspetto sembr a solo una toolstr ip con qualche button, due label e una tex tbox . È questo, e anche di più.

Aggiungiamo or a un BindingSour ce di nome bsData e impostiamo la pr opr ietà bsMain.BindingSour ce su bsData.

Aggiungete altr i contr olli in modo che l'inter faccia sia la seguente:

Vor r emo usar e il pulsanti fr eccia del binding navigator per spostar ci avanti e indietr o nella lista, e i r ispettivi pulsanti

per aggiunger e o eliminar e un elemento. Il codice:

34.35.

Public Authors As New List(Of Author) End Class

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.32.33.34.35.

36.37.

38.

39.40.41.42.43.44.45.46.47.48.49.50.

Public Class Form1

Public Class AuthorPrivate _Name As StringPrivate _Works As List(Of String)

Public Property Name() As String

GetReturn _Name

End GetSet(ByVal value As String)

_Name = valueEnd Set

End Property

Public ReadOnly Property Works() As List(Of String)Get

Return _WorksEnd Get

End Property

Public Sub New()_Works = New List(Of String)

End Sub

Public Sub New(ByVal Name As String, ByVal ParamArray WorksNames() As String)Me.New()Me.Name = NameMe.Works.AddRange(WorksNames)

End SubEnd Class

Public Authors As New List(Of Author)

Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles

MyBase.Load'Aggiungie alcuni elementi iniziali alla listaAuthors.Add(New Author("Dante Alighieri", "Comedìa", "Vita Nova", "De vulgari

eloquentia", "De Monarchia"))Authors.Add(New Author("Luigi Pirandello", "Il fu Mattia Pascal", "Uno, nessuno,

centomila", "Il gioco delle parti"))'Imposta la sorgente di dati del bindingsourcebsAuthors.DataSource = Authors

'Aggiunge un binding alla textbox. Ciò significa'che la proprietà Text di txtName sarà da'ora in poi sempre legata alla proprietà Name'dell'elemento corrente della sorgente di dati di'bsAuthors. Dato che quest'ultima è una lista di'Author, ogni suo elemento espone la proprietà'Name.txtName.DataBindings.Add("Text", bsAuthors, "Name")

Page 356: Guida visual basic

Come vedete, il codice è molto r idotto anche se l'applicazione suppor ta un numer o più elevato di funzionalità: tutto ciò

che non abbiamo scr itto viene automaticamente gestito dal BindingNavigator .

La pr opr ietà DataBindings, per inciso, non appar tiene solo a Tex tBox , ma a tutti i contr olli e non è necessar io

specificar e come sor gente di dati un binding sour ce, ma un qualsiasi oggetto, poiché tutto viene gestito tr amite

r eflection. È possibile associar e una qualsiasi pr opr ietà di un contr ollo ad un campo di un qualsiasi altr o oggetto.

Allo stesso modo, è possibile associar e alla pr opr ietà DataSour ce di BindingSour ce una tabella di un database, o un

dataset (e associate un dataset, dovr ebe usar e la pr opr ietà DataMember per specificar e quale tabella).

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.

'Non possiamo fare la stessa cosa con lstWorks.Items,'poiché Items è una proprietà readonly.'Questo capita abbastanza spesso: si ha bisogno di'visualizzare una lista per ogni elemento dell'insieme.'La soluzione consiste nel caricare gli items della'lista quando viene caricato un nuovo elemento

End Sub

Private Sub bsAuthors_CurrentChanged(ByVal sender As System.Object, ByVal e AsSystem.EventArgs) Handles bsAuthors.CurrentChanged'L'evento CurrentChanged si verifica quando la proprietà'Current del binding source viene modificata. Ciò significa'che l'utente si è spostato tramite il binding'navigator. Ottenendo l'oggetto Current, possiamo risalire alla'lista di stringhe che esso contieneDim Author As Author = bsAuthors.CurrentlstWorks.Items.Clear()lstWorks.Items.AddRange(Author.Works.ToArray())

End Sub

Private Sub btnAdd_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)Handles btnAdd.Click'Aggiunge alla listbox e alla lista Works un nuovo'titolo aggiunto dall'utenteIf Not String.IsNullOrEmpty(txtAdd.Text) Then

lstWorks.Items.Add(txtAdd.Text)DirectCast(bsAuthors.Current, Author).Works.Add(txtAdd.Text)txtAdd.Text = ""

End IfEnd Sub

Private Sub btnDelete_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)

Handles btnDelete.Click'Elimina una delle opere visualizzateIf lstWorks.SelectedIndex >= 0 Then

DirectCast(bsAuthors.Current, Author).Works.RemoveAt(lstWorks.SelectedIndex)lstWorks.Items.RemoveAt(lstWorks.SelectedIndex)

End IfEnd Sub

End Class

Page 357: Guida visual basic

C7. DataGridView - Parte I

IntroduzioneDataGr idView è uno dei contr olli più potenti e gr andi del Fr amewor k .NET. Consente la visualizzazione di dati in una

tabella ed è per questo motivo for temente cor r elato all'uso dei database, anche se natur almente suppor ta, tr amite la

pr opr ietà DataSour ce, il binding di qualsiasi oggetto:

Ecco un elenco delle pr opr ietà inter essanti:

Ar eAllCellsSelected(includeInvisible As Boolean) : r estituisce Tr ue se tutte le celle sono selezionate. Se

includeInvisible è Tr ue, include nel contr ollo anche quelle colonne che nor malmente sono nascoste (è possibile

nasconder e colonne indesider ate, come ad esempio l'ID);

AllowUser To AddRows, DeleteRows, Or der Columns, ResizeColumns, ResizeRows: una ser ie di pr opr ietà booleane

che deter minano se l'utente sia o meno in gr ado di aggiunger e o r imuover e r ighe, or dinar e le colonne o

r idimensionar e sia le r ighe che le colonne;

Alter natingRowsDefaultCellStyle : specifica il CellStyle (un insieme di pr opr ietà che definiscono l'aspetto estetico

di una cella) per le r ighe di posto dispar i. Se questo valor e è diver so da DefaultCellStyle, avr emo le r ighe di stile

alter nato (ad esempio una di un color e e una di un altr o), da cui il nome di questo membr o;

AutoResize... : tutti i metodi che iniziano con "AutoResize" ser vono per r idimensionar e r ighe o colonne;

AutoSizeColumnsMode : pr opr ietà enumer ata che deter mina in che modo le colonne vengano r idimensionate

quando del testo va oltr e i confini visibili. I valor i che può assumer e sono:

None : le colonne r imangono sempr e della stessa lar ghezza, a meno che l'utente non le r idimensioni

manualmente;

AllCells : la colonna viene allar gata affinché il testo di tutte le celle sottostanti e della stessa intestazione

sia visibile;

AllCellsEx ceptHeader : la colonna viene allar gata in modo che solo il testo di tutte le celle sottostanti sia

visibile;

ColumnHeader : la colonna viene allar gata in modo che il testo dell'header sia inter amente visibile;

DisplayedCells : come AllCells, ma solo per le celle visibili nei mar gini del contr ollo;

DisplayedCellsEx ceptHeader : come AllCellsEx ceptHeader , ma solo per le celle visibili nei mar gini del

contr ollo;

Fill : le colonne vengono r idimensionate affinché la lor o lar ghezza totale sia quanto più possibile vicina

all'ar ea effettivamente visibile a scher mo, nei limiti imposti dalla pr opr ietà MinimumWidth di ciascuna

colonna.

AutoSizeRowsMode : come sopr a, ma per le r ighe;

CancelEdit() : ter mina l'editing di una cella e annulla tutte le modifiche ad essa appor tate;

CellBor der Style : pr opr ietà enumer ata che definisce come sia visualizzato il bor do delle celle. Inutile descr iver ne

tutti i valor i: basta pr ovar li tutti per veder e come appaiono;

Clear Selection: deseleziona tutte le celle selezionate

ColumnCount / RowCount : deter mina il numer o iniziale di colonne o r ighe visualizzate sul contr ollo

ColumnHeader s / Row Header s : imposta lo stile di visualizzazione, i bor di, le dimensioni e la visibilità delle

intestazioni

Columns : insieme di tutte le colonne del contr ollo. Tr amite questa pr opr ietà è possibile deter minar e quali siano

Page 358: Guida visual basic

i tipi di valor i che si possono immetter e in una cella. Nella finestr a di dialogo dur ante la scr ittur a del

pr ogr amma, infatti, quando si aggiunge una colonna non a r untime, viene anche chiesto quale debba esser e il

suo tipo, pr oponendo una gamma abbastanza ampia di possibilità (tex tbox , combobox , checkbox , image, button,

linklabel)

Cur r entCell : imposta o r estituisce la cella selezionata (un oggetto di tipo DataGr idViewCell)

Cur r entCellAddr ess : r estituisce due coor dinate sotto for ma di Point che indicano la colonna e la r iga della cella

selezionata

Cur r entRow : indica la r iga contenente la cella selezionata (o la r iga selezionata se tutte le sue celle sono

selezionate);

DefaultCellStyle : specifica lo stile pr edefinito per una cella; ogni cella, poi, può modificar e il pr opr io aspetto

mediante la pr opr ietà Style;

DisplayedPar tialColumns/Rows(includePar tial As Boolean) : r estituisce il numer o di colonne / r ighe visibili nel

contr ollo. Se includePar tial è Tr ue, include nel conteggio anche quelle che si vedono solo par zialmente;

EditMode : pr opr ietà enumer ata che indica in che modo sia possibile iniziar e a modificar e il contenuto di una

cella. I valor i che può assumer e sono:

EditOnEnter : inizia la modifica quando la cella viene selezionata, quando r iceve il focus oppur e quando

viene pr emuto invio su di essa;

EditOnF2 : inizia la modifica quando l'utente pr eme F2 sulla cella selezionata;

EditOnKeystr oke : inizia la modifica quando viene pr emuto un qualsiasi tasto (alfanumer ico) mentr e la

cella ha il focus;

EditOnKeystr okeOr F2 : è palese...

EditPr ogr ammatically : inizia la modifica solo quando viene esplicitamente r ichiamato da codice il

metodo BeginEdit;

Fir stDisplayedCell : ottiene o imposta un r ifer imento alla pr ima cella visualizzata;

GetCellCount(Filter ) : r estituisce il numer o di celle che soddisfano il filtr o impostato. Filter non à altr o che un

valor e enumer ato codificato a bit che contempla questi valor i: Displayed (celle visualizzate), Fr ozen (celle che è

impossibile scr ollar e), None (celle che sono in stato di default), ReadOnly (celle a sola lettur a), Resizable and

ResizableSet (se specificati entr ambi, indicano le celle r idimensionabili), Selected (celle selezionate), Visible (celle

visibili, nel senso che è possibile veder le, non che sono effettivamente visualizzate);

HitTest(x , y) : r estituisce un valor e str uttur ato contenente r iga e colonna della cella che si tr ova alle coor dinate

r elative x e y (in pix el);

IsCur r entCellDir ty : indica se la cella cor r ente contiene modifiche non salvate;

IsCur r entCellInEditMode : indica se la cella cor r ente è in modalità edit (modifica);

IsCur r entRowDir ty : indica se la r iga cor r ente contiene modifiche non salvate;

Item(x ,y): r estituisce la cella alle coor dinate x e y (colonna e r iga). La sua pr opr ietà più impor tante è Value, che

r estituisce o imposta il valor e contenuto nella cella, che può esser e un testo, un valor e booleano, una combobox

ecceter a

MultiSelect: deter mina qualor a sia possibile selezionar e più celle, colonne o r ighe insieme;

Row... : tutte le pr opr ietà che iniziano con "Row " sono analoghe a quelle spiegate per Column;

ReadOnly : deter mina se l'utente possa o meno modificar e i contenuto delle celle

SelectedCells/Columns/Rows : r estituisce un insieme delle celle/colonne/r ighe cor r entemente selezionate;

SelectionMode : pr opr ietà enumer ata che indica come debba avvenir e la selezione. Può assumer e 5 valor i:

CellSelect (solo la cella), FullRowSelect (tutta la r iga), FullColumnSelect (tutta la colonna), RowHeader Select (solo

l'intestazione della r iga) o ColumnHeader Select (solo l'intestazione della colonna);

ShowCellToolTip : indica se visualizzar e il tooltip della cella;

ShowEditingIcon : indica se visualizzar e l'icona di modifica;

Selected Cells, Columns, Row: tr e collezioni che r estituiscono un insieme di tutte le celle, colonne o r ighe

selezionate;

Page 359: Guida visual basic

Sor t(a, b): utilissima pr ocedur a che or dina la colonna a della datagr idview secondo un or dine ascendente o

discendente definito da un enumer ator e di tipo ComponentModel.ListSor tDir ection;

Standar dTab : indica se il pulsante Tab viene usato per ciclar e i contr olli (tr ue) o le celle all'inter no del

datagr idview (false);

Come avete visto c'è una mar ea di membr i, e un numer o consistente di questi sono dedicati ad impostar e l'"estetica"

del contr ollo. Ma tutto questo non è nulla se confr ontato alla quantità di eventi che DataGr idView espone (e al numer o

di distur bi mentali che è solita causar e nei pr ogr ammator i sani).

Un c lassico esempio di gestionaleLa DataGr idView è un contr ollo usatissimo sopr attutto in quei noiosissimi pr ogr ammi che qualcuno chiama gestionali,

e che un gr an numer o di pover i pr ogr ammator i è costr etto a scr iver e per guadagnar si il pane quotidiano. Per questo

motivo, il pr ossimo esempio sar à par ticolar mente r ealistico. Andr emo a scr iver e un'applicazione per gestir e clienti e

or dini.

Pr ima di iniziar e, cr eiamo le tabelle che ci ser vir anno per il pr ogr amma (sì, user emo un database):

CREATE TABLE `customers` ( `ID` int(11) NOT NULL AUTO_INCREMENT, `FirstName` char(150) DEFAULT NULL, `LastName` char(150) DEFAULT NULL, `Address` char(255) DEFAULT NULL, `PhoneNumber` char(30) DEFAULT NULL, `RegistrationDate` date NOT NULL, `AccountType` int(5) DEFAULT '0', PRIMARY KEY (`ID`) ); CREATE TABLE `orders` ( `ID` int(11) NOT NULL AUTO_INCREMENT, `CustomerID` int(11) NOT NULL, `ItemName` char(255) NOT NULL, `ItemPrice` float unsigned NOT NULL, `ItemCount` int(10) unsigned DEFAULT '1', `CreationDate` date NOT NULL, `Settled` tinyint(1) NOT NULL, PRIMARY KEY (`ID`) );

E, per completezza, bisogna aggiunger e anche le r ispettive r appr esentazioni tabular i in un nuovo dataset (si tr atta

solo di r icopiar e).

Lo scopo del pr ogr amma consiste nel gestir e una ser ie di clienti e di or dini associati, indicando lo stato di ciascuno e le

sue condizioni di pagamento. Avr emo, quindi, una datagr idview per contener e i clienti, una per conter e gli or dini e

una listview per visualizzar e un r iepilogo delle infor mzioni. Inoltr e, iniziamo subito con una casistica un po' complicata:

pr ima di tutto vogliamo che il tipo dell'account di ciascun cliente sia visualizzato sottofor ma di icona; poi vogliamo

anche che la seconda datagr idview visualizzi solo gli or dini associati al cliente cor r entemente selezionato. La pr ima

r ichiesta ver r à gestita completamente da codice, ma è oppor tuno che si aggiunga un'ImageList contenente almeno tr e

piccole immagini (16x 16 vanno bene), mentr e per la seconda avr emo bisogno un po' di aiuto da par te di BindingSour ce.

Nei capitoli pr ecedenti, infatti, si è visto che questo componente espone una pr opr ietà Filter mediante la quale

possiamo r estr inger e l'insieme di dati visualizzati sotto cer ti par ametr i (aggiungete quindi anche un BindingSour ce di

nome bsOr der s, ed impostate il suo DataSour ce sul dataset pr incipale, e il suo DataMember su Or der s). Ecco il codice:

001.002.

Imports MySql.Data.MySqlClient

Page 360: Guida visual basic

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.

'Prima di iniziare:' - MainDatabase è un'istanza di AppDataSet, ossia il' dataset tipizzato creato mediante l'editor che contiene le' due tabelle.' - bsOrders è il BindingSource che useremo come sorgente' dati per dgvOrders. Ricordatevi che:' bsOrders.DataSource = MainDatabase' bsOrders.DataMember = "Orders" o MainDatabase.Orders' - AllowUserToAddRows = False per entrambi i datagridview Public Class Form1

Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) HandlesMyBase.LoadDim Connection As New MySqlConnection("Server=localhost; Database=appdata; Uid=root;

Pwd=root;")Dim Adapter As New MySqlDataAdapter()

Try

Connection.Open()Adapter.SelectCommand = New MySqlCommand("SELECT * FROM Customers;", Connection)Adapter.Fill(MainDatabase.Customers)Adapter.SelectCommand = New MySqlCommand("SELECT * FROM Orders;", Connection)Adapter.Fill(MainDatabase.Orders)

Catch Ex As ExceptionMessageBox.Show("Impossibile connettersi al database!", Me.Text,

MessageBoxButtons.OK, MessageBoxIcon.Error)Finally

Connection.Close()End Try

'Imposta la sorgente dati di dgvCustomers. Quando questa'proprietà viene impostata, crea automaticamente tutte'le colonne necessarie, ne imposta il tipo e'l'intestazione. Per questo motivo, tutte le colonne che'andremo ad usare iniziano ad esistere solo dopo questa'riga di codice. Tutte quelle che erano state definite'prima vengono eliminate.dgvCustomers.DataSource = MainDatabase.Customers'Nasconde la prima e la settima colonna, ossia l'ID e'l'AccountType. La prima non deve essere visibile poiché'contiene dati che non riguardano l'utente, mentre'l'ultima la nascondiamo perchè avevamo detto di'voler visualizzare il tipo di account con un'iconadgvCustomers.Columns(0).Visible = FalsedgvCustomers.Columns(6).Visible = False

'Crea una nuova colonna di datagridview adatta a contenere'immagini. Esistono molti tipi di colonna (button, combobox,'linklabel, image, eccetera...), di cui la più comune'è una che contiene solo testo. Il tipo di una'colonna indica il tipo di dati che tutte le celle ad essa'sottostanti devono contenere. In questo caso vogliamo'che l'ultima colonna contenga una piccola immagine'indicante il livello dell'account (ci saranno tre livelli)Dim Img As New DataGridViewImageColumn'Imposta l'immagine di defaultImg.Image = imgAccountTypes.Images(0)'E l'intestazione della colonnaImg.HeaderText = "AccountLevel"'Quindi la aggiunge a quelle esistentidgvCustomers.Columns.Add(Img)

'Poi cicla attraverso tutte le righe, controllando il'contenuto della settima cella di ogni riga (ossia il'valore corrdispondente ad AccountType, un numero intero),'e imposta il contenuto dell'ottava prelevando la'rispettiva immagine dall'imagelist.'Ricordate che la colonna di indice 6, pur essendo'nascosta, esiste comunqueFor Each Row As DataGridViewRow In dgvCustomers.Rows

Page 361: Guida visual basic

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.

Row.Cells(7).Value = imgAccountTypes.Images(CInt(Row.Cells(6).Value))Next

'Imposta come sorgente di dati di dgvOrders il binding'source bsOrders, specificato in precedenzadgvOrders.DataSource = bsOrders'E nasconde le prime due colonne, ossia CustomerID e IDdgvOrders.Columns(0).Visible = FalsedgvOrders.Columns(1).Visible = False'Impone di visualizzare solo le tuple di ID pari a -1,'ossia nessunabsOrders.Filter = "ID=-1"

End Sub

Private Sub dgvCustomers_KeyDown(ByVal sender As System.Object, ByVal e AsSystem.Windows.Forms.KeyEventArgs) Handles dgvCustomers.KeyDown'Intercettiamo la pressione di un pulsante quando l'utente'si trova nell'ultima colonna (quella dell'icona)If dgvCustomers.CurrentCell.ColumnIndex = 7 Then

'Ottiene il valore di AccountTypeDim CurValue As Int32 = CInt(dgvCustomers.CurrentRow.Cells(6).Value)

'Se è stato premuto +, aumenta il valore di 1'Se è stato premuto -, lo decrementa di 1If e.KeyCode = Keys.Oemplus Then

CurValue += 1ElseIf e.KeyCode = Keys.OemMinus Then

CurValue -= 1End If

'Fa in modo di non andare oltre i limit impostiIf CurValue < 0 Then

CurValue = 0ElseIf CurValue > 2 Then

CurValue = 2End If

'Quindi imposta il nuovo valore di AccountTypedgvCustomers.CurrentRow.Cells(6).Value = CurValue'E la corrispondente nuova immaginedgvCustomers.CurrentCell.Value = imgAccountTypes.Images(CurValue)

End IfEnd Sub

'L'evento DataError viene generato ogniqualvolta l'utente'non rispetti i vincoli imposti dal database. Ad esempio,'viene generato se un campo marcato come NOT NULL viene'lasciato vuoto, o se una data non è valida, o'se un numero è troppo grande o troppo piccolo,'oppure ancora se non viene soddisfatto il vincolo di'unicità, eccetera...'In questi casi, se il programmatore non gestisce l'evento,'appare una finestra di default che riporta tutto il testo'dell'eccezione e vi assicuro che non è una bella cosaPrivate Sub dgvCustomers_DataError(ByVal sender As System.Object, ByVal e As

System.Windows.Forms.DataGridViewDataErrorEventArgs) Handles dgvCustomers.DataErrorDim Result As DialogResult

'Riporta l'errore all'utente, e lascia scegliere se'modificare i dati incompatibili oppure annullare'le modifiche e cancellare la rigaResult = MessageBox.Show("Si è verificato un errore di compatibilità dei dati immessi.

Messaggio:" & _Environment.NewLine & e.Exception.Message & Environment.NewLine & _"E' possibile che dei dati mancanti compromettano il database. Premere Sì per

modificare opportunamente " & _"tali valori, o No per cancellare la riga.", Me.Text, MessageBoxButtons.YesNo,

MessageBoxIcon.Exclamation)

If Result = Windows.Forms.DialogResult.Yes Then'Annulla questo evento: non viene generata la'finestra di errore

Page 362: Guida visual basic

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.203.204.205.206.207.208.209.210.

e.Cancel = True'Pone il cursore sulla casella corrente e obbliga'ad iniziare l'edit mode. Il valore booleano tra'parentesi indica di selezionare l'intero contenuto'della cella correntedgvCustomers.BeginEdit(True)

Else'Annulla l'eccezione e l'evento, quindi cancella'la riga correntee.ThrowException = Falsee.Cancel = True'Le righe "nuove", ossia quelle in cui non è'stato salvato ancora nessun dato, non possono essere'eliminate (così dice il datagridview...)If Not dgvCustomers.CurrentRow.IsNewRow Then

dgvCustomers.Rows.Remove(dgvCustomers.CurrentRow)End If

End IfEnd Sub

'Questa procedura aggiorna la ListView con alcuni dettagli'sullo stati dei pagamentiPrivate Sub RefreshPreview(ByVal CustomerID As Int32)

lstPreview.Items.Clear()

'Cerca il clienteDim Customer As AppDataSet.CustomersRow = _

MainDatabase.Customers.FindByID(CustomerID)

'Se non esiste, esceIf Customer Is Nothing Then

Exit SubEnd If Dim TotalPaid, TotalUnpaid As SingleDim Delay As TimeSpan

'Notate che qui agiamo direttamente sul dataset,'perchè contiene campi tipizzati, e ci consente di'utilizzare meno operatori di castFor Each Order As AppDataSet.OrdersRow In MainDatabase.Orders

'Conta solo gli ordini associati a un clienteIf Order.CustomerID <> CustomerID Then

Continue ForEnd If

'Se l'ordine è stato pagato, aggiunge il'totale alla variabile TotalPaid, altrimenti a'TotalUnpaid.'Se l'ordine non è stato pagato, inoltre,'calcola il ritardo maggiore nel pagamentoIf Order.Settled Then

TotalPaid += Order.ItemPrice * Order.ItemCountElse

TotalUnpaid += Order.ItemPrice * Order.ItemCountIf Date.Now - Order.CreationDate > Delay Then

Delay = Date.Now - Order.CreationDateEnd If

End IfNext

Dim Item1 As New ListViewItemItem1.Text = "Ammontare pagato"Item1.SubItems.Add(String.Format("{0:N2}€", TotalPaid))

Dim Item2 As New ListViewItemItem2.Text = "Oneri futuri"Item2.SubItems.Add(String.Format("{0:N2}€", TotalUnpaid))

Dim Item3 As New ListViewItemItem3.Text = "Ritardo pagamento"Item3.SubItems.Add(CInt(Delay.TotalDays) & " giorni")

Page 363: Guida visual basic

Ed ecco come potr ebbe pr esentar si:

Come avr ete cer tamente notato, fatta eccezione per l'unica pr ocedur a Refr eshPr eview, abbiamo agito solo sul

datagr idview e non sul dataset. Questo accade per chè l'applicazione cr eata è "str atificata": può esser e consider ata

come un par ticolar e caso di applicazione 3-tier . L'ar chitettur a thr ee-tier indica una par ticolar e str uttur azione del

softwar e in cui ci sono tr e layer (str ati) pr incipali: data layer , buisness layer e gui layer . Il pr imo si dedica alla

gestione dei dati per sistenti (nel nostr o caso, il database), il secondo si occupa di for nir e delle logiche funzionali, ossia

metodi che gestiscono le infor mazioni in manier a da r ispecchiar e il funzionamento dell'applicazione e che per mettono

di inter facciar si più facilmente con il data layer (il nostr o buisness layer è il dataset, r appr esentazione oggettiva e

funzionale dei dati per sistenti) mentr e il ter zo ha il compito di mediar e l'inter azione con l'utente attr aver so

l'inter faccia gr afica. Sentir ete par lar e molto spesso di questo tipo di ar chitettur a nel campo dei gestionali.

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.

Dim DaysLimit As Int32'Un diverso tipo di account permette un maggior ritardo'nei pagamenti...Select Case Customer.AccountType

Case 0DaysLimit = 60

Case 1DaysLimit = 90

Case 2DaysLimit = 120

Case ElseDaysLimit = 60

End Select

'Se il cliente ha superato il limite con almeno uno'dei suoi ordini, la riga viene colorata in rossoIf Delay.TotalDays > DaysLimit Then

Item3.ForeColor = Color.RedEnd If

lstPreview.Items.Add(Item1)lstPreview.Items.Add(Item2)lstPreview.Items.Add(Item3)

End Sub

'Evento generato quando l'utente si posizione su una rigaPrivate Sub dgvCustomers_RowEnter(ByVal sender As System.Object, ByVal e As

System.Windows.Forms.DataGridViewCellEventArgs) Handles dgvCustomers.RowEnter'Se si tratta di una riga valida...If e.RowIndex < dgvCustomers.Rows.Count And e.RowIndex >= 0 Then

Dim CurID As Int32 = CInt(dgvCustomers.Rows(e.RowIndex).Cells(0).Value)'Aggiorna il filtro di bsOrders, per visualizzare solo gli'ordini di quel dato clientebsOrders.Filter = "CustomerID=" & CurID'E aggiorna la listviewRefreshPreview(CurID)

End IfEnd Sub

'Quando una cella di dgvOrders viene modificata, aggiorna'la listview...Private Sub dgvOrders_CellEndEdit(ByVal sender As System.Object, ByVal e As

System.Windows.Forms.DataGridViewCellEventArgs) Handles dgvOrders.CellEndEditTry

RefreshPreview(CInt(dgvCustomers.CurrentRow.Cells(0).Value))Catch Ex As Exception

End Try

End SubEnd Class

Page 364: Guida visual basic

C8. DataGridView - Parte II

Manipolazione dei datiNell'esempio pr ecedente, l'utente poteva modificar e ed eventualmente cancellar e dati esistenti, ma, ancor a una volta,

ho tr alasciato di implementar e l'aggiunta. In questo caso, per ò, aver lasciato la possibilità di agir e liber amente sui dati

aggiunti avr ebbe causato non pochi danni, poiché gli ID sono tutti nascosti e il contr ollo datagr idview non implementa

nessun tipo di autoincr emento per i valor i numer ici: aggiungendo nuove r ighe, l'utente non avr ebbe potuto influir e

sulle celle ID, che sar ebber o r imaste vuote e avr ebber o causato sempr e lo stesso er r or e (avendolo noi gestito nel modo

che sapete, l'unica scelta possibile sar ebbe stata quella di cancellar e l'ultima r iga e per ciò non si sar ebbe potuto

aggiunger e nulla in ogni caso). In poche par ole, bisogna inter venir e a livello di codice.

Possiamo cor r egger e in modo elegante aggiungendo due Contex tMenu con un solo elemento "Aggiungi cliente" o

"Aggiungi or dine" ed associar e ciascuno dei due a uno dei datagr idview. Per aggiunger e un nuovo cliente basta agir e

dir ettamente sulla tabella customer , r ichiamando AddCustomer sRow e lasciando tutti i par ametr i vuoti (con la data di

default), poiché nessuno di essi è specificato come NOT NULL nella str uttur a della tabella. Per l'or dine, invece, non è

possibile seguir e la stessa str ada, poiché quasi tutti gli attr ibuti non possono esser e null. Per questo cr eer emo una

nuova finestr a di dialogo di nome Cr eateOr der Dialog con quest'aspetto:

e con questo semplice codice:

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.

32.33.34.35.

Public Class CreateOrderDialog

Private CustomerID As Int32Private _NewOrder As AppDataSet.OrdersRow 'Restituisce una nuova riga con gli attributi impostati'nel dialogPublic ReadOnly Property NewOrder() As AppDataSet.OrdersRow

GetReturn _NewOrder

End GetEnd Property

'Per creare un nuovo ordine ci serve l'ID del cliente ad'esso associato, perciò dobbiamo costringere il chiamante'(ossia noi stessi XD) a passarci questo dato in qualche'modo. In questo caso, sovrascriviamo il metodo ShowDialog'mediante shadowing:Public Shadows Function ShowDialog(ByVal CustomerID As Int32) As DialogResult

Me.CustomerID = CustomerIDReturn MyBase.ShowDialog()

End Function

Private Sub OK_Button_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)Handles OK_Button.ClickIf String.IsNullOrEmpty(txtItemName.Text) Then

MessageBox.Show("Specificare una descrizione valida del prodotto!", Me.Text,MessageBoxButtons.OK, MessageBoxIcon.Exclamation)

Exit SubEnd If

If nudItemPrice.Value = 0.0F Then

MessageBox.Show("Prezzo non valido!", Me.Text, MessageBoxButtons.OK,MessageBoxIcon.Exclamation)

Exit SubEnd If

Page 365: Guida visual basic

Tenendo conto del nuovo dialog appena scr itto, il codice del for m diventer ebbe:

Manca ancor a un'ultima cosa per ter minar e il pr ogr amma, ossia il salvataggio. Possiamo, ad esempio, salvar e tutto alla

chiusur a del for m:

36.37.38.39.40.41.42.43.44.45.46.47.48.49.

50.51.52.53.54.

_NewOrder = My.Forms.Form1.MainDatabase.Orders.NewOrdersRow()With _NewOrder

.CustomerID = Me.CustomerID

.ItemName = txtItemName.Text

.ItemPrice = nudItemPrice.Value

.ItemCount = nudItemCount.Value

.CreationDate = dtpCreationDate.Value

.Settled = chbSettled.CheckedEnd With Me.DialogResult = System.Windows.Forms.DialogResult.OKMe.Close()

End Sub

Private Sub Cancel_Button_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)Handles Cancel_Button.ClickMe.DialogResult = System.Windows.Forms.DialogResult.CancelMe.Close()

End Sub End Class

01.02.03.04.05.06.

07.08.09.10.

11.12.13.14.15.16.17.18.19.20.21.22.23.24.

Imports MySql.Data.MySqlClientPublic Class Form1

'... Private Sub strAddCustomer_Click(ByVal sender As System.Object, ByVal e As

System.EventArgs) Handles strAddCustomer.ClickMainDatabase.Customers.AddCustomersRow("", "", "", "", Date.Now, 0)

End Sub

Private Sub strAddOrder_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)Handles strAddOrder.ClickIf dgvCustomers.CurrentRow Is Nothing Then

Exit SubEnd If

Dim CID As Int32 = CInt(dgvCustomers.CurrentRow.Cells(0).Value)Dim OrderDialog As New CreateOrderDialog()

If OrderDialog.ShowDialog(CID) = Windows.Forms.DialogResult.OK Then

MainDatabase.Orders.AddOrdersRow(OrderDialog.NewOrder)RefreshPreview(CID)

End IfEnd Sub

End Class

01.02.03.04.

05.

06.07.08.09.10.11.12.13.14.15.16.

Public Class Form1'... Private Sub Form1_FormClosing(ByVal sender As System.Object, ByVal e As

System.Windows.Forms.FormClosingEventArgs) Handles MyBase.FormClosingDim Connection As New MySqlConnection("Server=localhost; Database=appdata; Uid=root;

Pwd=root;")Dim Adapter As New MySqlDataAdapter()Dim Builder As MySqlCommandBuilder

Try

Connection.Open()Adapter.SelectCommand = New MySqlCommand("SELECT * FROM Customers;", Connection)Builder = New MySqlCommandBuilder(Adapter)Adapter.Update(MainDatabase.Customers)

Adapter.SelectCommand = New MySqlCommand("SELECT * FROM Orders;", Connection)

Page 366: Guida visual basic

P.S.: r icor datevi di r iassegnar e le immagini all'ultima cella dopo che le r ighe sono state r ior dinate (è possibile or dinar e

automaticamente i dati cliccando sull'intestazione di una colonna).

StampaIl passo successivo di un gestionale consiste, di nor ma, nel voler stampar e i dati che si gestiscono. Esistono par ecchi

componenti già pr onti per stampar e un datagr idview, nonché molti str umenti integr ati nell'IDE (r epor ts), acquistabili

e non, e anche un discr eto numer o di "scor ciatoie", che non fanno altr o che disegnar e il contr ollo su un qualche suppor to

e delegar ne la stampa ad un'altr a applicazione. Potete sceglier e liber amente di usar e uno dei metodi sopr acitati senza

spr ecar vi più di tanto nel codice. In ogni caso, la soluzione che pr opongo potr ebbe esser e utile per r ipassar e l'uso di

Gr aphics:

17.18.19.20.

21.22.23.24.25.26.27.28.

Builder = New MySqlCommandBuilder(Adapter)Adapter.Update(MainDatabase.Orders)

Catch Ex As Exception

If MessageBox.Show("Impossibile connettersi al database per il salvataggio.Proseguire nella chiusura? Tutti i dati non salvati andranno persi.", Me.Text,MessageBoxButtons.YesNo, MessageBoxIcon.Error) = Windows.Forms.DialogResult.NoThene.Cancel = True

End IfFinally

Connection.Close()End Try

End Sub

End Class

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.

Public Class Form1

'... 'Indici dei campi da stamparePrivate PrintingFields() As Int32 = {1, 2, 3, 4, 5, 7}

Private Sub PrintDoc_PrintPage(ByVal sender As System.Object, ByVal e As

System.Drawing.Printing.PrintPageEventArgs) Handles PrintDoc.PrintPage'Indice della prima riga della prima paginaStatic RowIndex As Int32 = 0'Indica se si tratta della prima paginaStatic FirstPage As Boolean = True

'Offset orizzontaleDim CellOffset As Int32 = e.MarginBounds.X'Offset verticaleDim Y As Int32 = e.MarginBounds.Y'Larghezza totale colonneDim TotalWidth As Int32 = 0

'Se è la prima pagina, stampa le intestazioniIf FirstPage Then

For Each Column As DataGridViewColumn In dgvCustomers.Columns'Stampa solo gli header dei campi contemplatiIf Array.IndexOf(PrintingFields, Column.Index) < 0 Then

Continue ForEnd If

e.Graphics.DrawString(Column.HeaderText,

dgvCustomers.ColumnHeadersDefaultCellStyle.Font, NewSolidBrush(dgvCustomers.ColumnHeadersDefaultCellStyle.ForeColor),CellOffset, Y)

CellOffset += Column.WidthTotalWidth += Column.Width

NextY += dgvCustomers.ColumnHeadersHeight

Page 367: Guida visual basic

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.

End If

'Stampa le righeFor I As Int32 = RowIndex To dgvCustomers.RowCount - 1

Dim Row As DataGridViewRow = dgvCustomers.Rows(I)

CellOffset = e.MarginBounds.X'Ottiene lo stile delle celleDim Style As DataGridViewCellStyle'Distingue fra quelle pari e dispariIf Row.Index Mod 2 = 0 Then

Style = dgvCustomers.DefaultCellStyleElse

Style = dgvCustomers.AlternatingRowsDefaultCellStyleEnd If

'Se nessun font particolare è specificato, prende'quello del controlloIf Style.Font Is Nothing Then

Style.Font = dgvCustomers.FontEnd If'Se nessun colore particolare è specificato (di'default valore argb=(0,0,0,0)) prende quello del'controlloIf Style.ForeColor.A = 0 Then

Style.ForeColor = dgvCustomers.ForeColorEnd If

'Disegna una striscia del colore di sfondo della rigae.Graphics.FillRectangle(New SolidBrush(Style.BackColor), CellOffset, Y,

TotalWidth, dgvCustomers.RowTemplate.Height)'E la contorna con un tratto neroe.Graphics.DrawRectangle(Pens.Black, CellOffset, Y, TotalWidth,

dgvCustomers.RowTemplate.Height)

For Each Cell As DataGridViewCell In Row.Cells'Stampa solo gli attributi contemplatiIf Array.IndexOf(PrintingFields, Cell.ColumnIndex) < 0 Then

Continue ForEnd If

'Se la cella contiene un'immagime, la stampaIf Cell.ValueType Is GetType(Image) Then

Dim Img As Image = Cell.Valuee.Graphics.DrawImage(Img, CellOffset +

dgvCustomers.Columns(Cell.ColumnIndex).Width \ 2 - Cell.Value.Width \2, Y + dgvCustomers.CurrentRow.Height \ 2 - Img.Height \ 2)

ElseDim Height As Int32Dim StrVal As String

'Formatta la data in forma compattaIf Cell.ValueType Is GetType(Date) Then

StrVal = CType(Cell.Value, Date).ToShortDateString()Else

StrVal = Cell.Value.ToString()End If'Calcola l'altezza del testo per centrarloHeight = Cell.MeasureTextHeight(e.Graphics, StrVal, Style.Font, 500,

TextFormatFlags.Default)

'Stampa il testoe.Graphics.DrawString(StrVal, Style.Font, New SolidBrush(Style.ForeColor),

CellOffset, Y + dgvCustomers.RowTemplate.Height \ 2 - Height \ 2)End If

'Si sposta alla prossima ascissaCellOffset += dgvCustomers.Columns(Cell.ColumnIndex).Width

'Per tutte le celle tranne l'ultima, disegna una linea'verticale di separazione dalla cella successivaIf Array.IndexOf(PrintingFields, Cell.ColumnIndex) < PrintingFields.Length - 1

Page 368: Guida visual basic

Risultato:

Validazione dell'inputE' possibile convalidar e l'input dell'utente o indicar e che alcuni dati sono er r onei utilizzando l'evento CellValidating o

RowValidating:

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.

Thene.Graphics.DrawLine(Pens.Black, CellOffset - 4, Y, CellOffset - 4, Y +

dgvCustomers.RowTemplate.Height)End If

Next

'Aumenta l'ordinataY += dgvCustomers.RowTemplate.Height

Next

If e.HasMorePages ThenFirstPage = False

ElseFirstPage = TrueRowIndex = 0

End IfEnd Sub

'strPrint è un sottoelemento del context menuPrivate Sub strPrint_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)

Handles strPrint.ClickDim PDialog As New PrintDialog

PDialog.Document = PrintDocIf PDialog.ShowDialog = Windows.Forms.DialogResult.OK Then

PrintDoc.PrinterSettings = PDialog.PrinterSettings'Ridimensiona le colonne per far stare i testi in modo'corretto. N.B.: ho impostato la larghezza minima della'colonna Address a 190 pixeldgvCustomers.AutoResizeColumns()PrintDoc.Print()

End IfEnd Sub

End Class

01.

02.03.04.05.06.07.08.09.10.11.

Private Sub dgvCustomers_CellValidating(ByVal sender As System.Object, ByVal e AsSystem.Windows.Forms.DataGridViewCellValidatingEventArgs) HandlesdgvCustomers.CellValidatingIf e.ColumnIndex > 0 And e.ColumnIndex < 5 Then

With dgvCustomers.Item(e.ColumnIndex, e.RowIndex)If String.IsNullOrEmpty(e.FormattedValue.ToString()) Then

.ErrorText = "Il campo non dovrebbe essere lasciato vuoto"Else

.ErrorText = NothingEnd If

End WithEnd If

End Sub

Page 369: Guida visual basic

D1. Il controllo WebBrowser

WebBr owser è uno dei contr olli standar d for niti dal Fr amewor k .NET fin dalla ver sione 1.0, e le sue potenzialità sono

abbastanza elevate da per metter ci di "cr ear e" (o quanto meno, simular e) un nostr o per sonale web br owser , come

Mozilla Fir eFox , Oper a o Google Chr ome. Non a caso ho messo tr a vir golette il ver bo creare, poiché il contr ollo che

andr emo ad analizzar e tr a poco assolve un'unica funzione, che costituisce, per ò, il fulcr o di tutta la navigazione.

WebBr owser per mette di car icar e al pr opr io inter no una pagina web e di visualizzar la senza pr aticamente scr iver e

alcun codice. Nei pr ossimi par agr afi illustr er ò come scr iver e un semplicissimo pr ogr amma di navigazione basato su

questa classe.

La fisionomia di WebBrowserDopo aver cr eato un nuovo pr ogetto Windows For ms, tr ascinate sulla super ficie del designer un nuovo contr ollo

WebBr owser . Una volta posizionato dovr ebbe mostr ar si come un'ar ea totalmente bianca: per or a, infatti, non contiene

ancor a nessuna pagina. Pr ima di pr oceder e, ecco uno sguar do alla lista dei suoi membr i più impor tanti:

CanGoBack : deter mina se sia possibile tor nar e indietr o nella cr onologia

CanGoFor war d : deter mina se sia possibile andar e avanti nella cr onologia

Document : un oggetto di tipo HtmlDocument contenente tutte le infor mazioni sulla pagina. Tr a le sue

pr opr ietà, inoltr e, ci sono molti modi per ottener e una vasta gamma di tag, ma illustr er ò in dettaglio questi

meccanismi nel pr ossimo capitolo

DocumentStr eam : per mette di legger e la pagina web come da un file. Restituisce un oggetto System.IO.Str eam

DocumentTex t : r estituisce o imposta il codice della pagina. Dopo aver car icato una pagina, contiene il suo

codice HTML. Modificando questa pr opr ietà, anche la pagina visualizzata ver r à r ielabor ata (e r icar icata) di

conseguenza

DocumentTitle : il titolo del documento

DocumentType : il tipo del documento

GoBack : tor na indietr o alla pagina pr ecedente

GoFor war d : pr ocede alla pagina successiva

GoHome : r itor na all'Home Page. Per ottener e l'indir izzo di quest'ultima, r icer ca nel r egistr o di sistema le

pr efer enze che l'utente ha impostato per il br owser Inter net Ex plor er

GoSear ch : si r eca alla pagina di r icer ca pr edefinita. Esegue lo stesso pr ocedimento di GoHome

IsBusy : indica se il contr ollo sta car icando un nuovo documento

IsOffline : indica se il contr ollo è in modalità offline (sta pr ocessando pagine web su disco fisso)

IsWebBr owser Contex tMenuEnabled : deter mina se sia attivo il menù contestuale pr edefinito per il Web Br ow ser

Naviagate(S) : apr e la pagina r efer enziata dall'indir izzo ur l S

Pr int : stampa il documento aper to con i settaggi impostati della stampante cor r ente

ReadyState : r estituisce lo stato del contr ollo. L'enumer ator e può assumer e quattr o valor i: Complete (pagina

completa), Inter active (le par ti della pagina car icate sono sufficienti a gar antir e un minimo di inter azione con

l'utente, ad esempio con dei click sui link pr esenti), Loaded (il documento è car icato e inizializzato, ma non tutti

i dati sono ancor a stati r icevuti), Loading (il documento è in car icamento) e Uninitialized (nessun documento è

stato aper to)

ShowPageSetupDialog : visualizza le impostazioni pagina con una finestr a di dialogo Inter net Ex plor er

ShowPr intDialog : visualizza la finestr a di stampa di Inter net Ex plor er

Page 370: Guida visual basic

ShowPr intPr eviewDialog : visualizza l'antepr ima di stampa in una finestr a Inter net Ex plor er

ShowPr oper tiesDialog : visualizza la finestr a delle pr opr ietà pagina come Inter net Ex plor er

ShowSaveAsDialog : visualizza la finestr a di dialogo di salvataggio di Inter net Ex plor er

Ur l : r estituisce un oggetto Ur i r appr esentante l'indir izzo della pagina car icata

Ver sion : la ver sione di Inter net Ex plor er installata

Alcune delle funzionalità esposte da questi membr i si r eggono pesantemente su Inter net Ex plor er , come ad esempio la

visualizzazione dell'antepr ima o la r icer ca della home page (che potete cambiar e solo dal menù opzioni di IE).

Nonostante tali pesanti impedimenti, è possibile usar e il contr ollo con semplicità.

Nel nostr o pr ogetto possiamo quindi aggiunger e qualche altr o contr ollo:

btnBack per andar e indietr o;

btnFor war d per andar e avanti;

btnRefr esh per aggior nar e la pagina;

tx tUr l per contener e l'indir izzo a cui r ecar si;

Come vedete ho inser ito tutti i contr olli sopr a menzionati in un ToolStr ip, e tutti i pulsanti sono di default disattivati

(Enabled = False), poiché all'inizio non è car icata nessuna pagina e di conseguenza non si può effettuar e alcuna

oper azione. Con questo semplice codice potr emo iniziar e a navigar e un po':

01.02.03.

04.05.06.07.08.09.10.11.

Public Class Form1

Private Sub txtUrl_KeyDown(ByVal sender As System.Object, ByVal e AsSystem.Windows.Forms.KeyEventArgs) Handles txtUrl.KeyDown'Quando si preme invio durante la digitazione, naviga'alla pagina indicataIf e.KeyCode = Keys.Enter Then

wbBrowser.Navigate(txtUrl.Text)'Poiché si inizia a navigare, è lecito fermare'il caricamento, quindi attiva btnCancelbtnCancel.Enabled = True

Page 371: Guida visual basic

Come alter nativa a DocumentCompleted, si può utilizzar e Navigated:

Possiamo or a aggiunger e una bar r a di stato in basso per comunicar e lo stato della navigazione:

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 IfEnd Sub

Private Sub btnCancel_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)

Handles btnCancel.Click'Ferma l'attività del WebBrowserwbBrowser.Stop()btnCancel.Enabled = FalsebtnRefresh.Enabled = True

End Sub

Private Sub wbBrowser_Navigating(ByVal sender As System.Object, ByVal e AsSystem.Windows.Forms.WebBrowserNavigatingEventArgs) Handles wbBrowser.Navigating'L'evento Navigating si genera prima della navigazionebtnCancel.Enabled = True

End Sub

Private Sub wbBrowser_DocumentCompleted(ByVal sender As System.Object, ByVal e AsSystem.Windows.Forms.WebBrowserDocumentCompletedEventArgs) HandleswbBrowser.DocumentCompleted'L'evento DocumentCompleted si verifica quando una pagina'è stata completamente caricata. Se anche una sola'delle parti della pagina non è completa, l'evento'non viene generato. Per evitare brutte soprese, potete'utilizzare l'evento Navigated, che si verifica dopo la'navigazione (indipendentemente dal successo o meno'dell'operazione)btnCancel.Enabled = FalsebtnBack.Enabled = wbBrowser.CanGoBackbtnForward.Enabled = wbBrowser.CanGoForwardbtnRefresh.Enabled = True

End Sub

Private Sub btnRefresh_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)Handles btnRefresh.ClickwbBrowser.Refresh()

End Sub Private Sub btnBack_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)

Handles btnBack.ClickwbBrowser.GoBack()

End Sub

Private Sub btnForward_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)Handles btnForward.ClickwbBrowser.GoForward()

End Sub

End Class

1.

2.3.4.5.6.

Private Sub wbBrowser_Navigated(ByVal sender As System.Object, ByVal e AsSystem.Windows.Forms.WebBrowserNavigatedEventArgs) Handles wbBrowser.NavigatedbtnCancel.Enabled = FalsebtnBack.Enabled = wbBrowser.CanGoBackbtnForward.Enabled = wbBrowser.CanGoForwardbtnRefresh.Enabled = True

End Sub

01.02.03.

04.05.06.07.08.09.

Public Class Form1

Private Sub txtUrl_KeyDown(ByVal sender As System.Object, ByVal e AsSystem.Windows.Forms.KeyEventArgs) Handles txtUrl.KeyDownIf e.KeyCode = Keys.Enter Then

wbBrowser.Navigate(txtUrl.Text)btnCancel.Enabled = True

End IfEnd Sub

Page 372: Guida visual basic

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.

Private Sub btnCancel_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)

Handles btnCancel.ClickwbBrowser.Stop()btnCancel.Enabled = FalsebtnRefresh.Enabled = True

End Sub

Private Sub wbBrowser_Navigating(ByVal sender As System.Object, ByVal e AsSystem.Windows.Forms.WebBrowserNavigatingEventArgs) Handles wbBrowser.NavigatingbtnCancel.Enabled = True'La proprietà StatusText contiene in forma leggibile'un resoconto dell'operazione che il controllo sta svolgendolblStatus.Text = wbBrowser.StatusText

End Sub

Private Sub wbBrowser_Navigated(ByVal sender As System.Object, ByVal e AsSystem.Windows.Forms.WebBrowserNavigatedEventArgs) Handles wbBrowser.NavigatedbtnCancel.Enabled = FalsebtnBack.Enabled = wbBrowser.CanGoBackbtnForward.Enabled = wbBrowser.CanGoForwardbtnRefresh.Enabled = TruelblStatus.Text = "Pagina caricata"

End Sub

Private Sub btnRefresh_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)Handles btnRefresh.ClickwbBrowser.Refresh()

End Sub

Private Sub btnBack_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)Handles btnBack.ClickwbBrowser.GoBack()

End Sub

Private Sub btnForward_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)Handles btnForward.ClickwbBrowser.GoForward()

End Sub

Private Sub wbBrowser_ProgressChanged(ByVal sender As System.Object, ByVal e AsSystem.Windows.Forms.WebBrowserProgressChangedEventArgs) HandleswbBrowser.ProgressChangedprgProgress.Value = e.CurrentProgress / e.MaximumProgress * 100lblStatus.Text = wbBrowser.StatusText

End Sub End Class

Page 373: Guida visual basic

Dato che questo non vuole esser e un tutor ial su come cr ear e un br owser , ma solo un abstr act per mostr ar e le

funzionalità del contr ollo, non mi dilungher ò oltr e nella modifica e nella r affinazione dell'applicazione pr oposta in

esempio, anche per chè sono sicur o che qualche lettor e lo star à già facendo e non vor r ei toglier gli il diver timento XD

Page 374: Guida visual basic

D2. Parsing di codice HTML

È possibile scar icar e pagine web in molti modi diver si, di cui WebBr owser è solo il pr imo che ho intr odotto. Nel

capitolo pr ecedente non ci siamo posti alcun pr oblema sull'analsi del codice di una pagina, poiché l'impor tante er a

r iuscir e a visualizzar la ed a navigar e da essa ad alr e pagine pr esenti in r ete. Tuttavia, pr esto o tar di incor r er ete nel

bisogno di ottener e infor mazioni sui tag html pr esenti in un data pagina, ad esempio per effettuar e un login

automatico senza esser e per sonalmente al computer , o per aggiunger e alcune funzionalità al br owser che state

scr ivendo di nascosto.

Per nostr a for tuna esistono un paio di classi, HtmlDocument e HtmlElement, che eseguono autonomamente il par sing

del sor gente html e ci per mettono di agir e su di esso mediante oggetti di alto livello.

Uno sguardo alle c lassiHtmlDocument è la classe di par tenza, che ci per mette di iniziar e ad ispezionar e il codice. Essa non espone costr uttor i,

né metodi statici, e quindi non esiste alcun modo di inizializzar la o di applicar la ad un file html. L'unico modo in cui

possiamo ottener ne un'istanza è attr aver so la pr opr ietà Document del contr ollo WebBr owser . HtmlDocument espone

alcuni membr i inter essanti:

ActiveElement : r estituisce un oggetto HtmlElement che r appr esenta l'elemento che possiede il focus al momento.

Può indicar e, ad esempio, un tag tex tar ea se l'utente sta digitando del testo, od un div se è stato selezionata

una par te di par agr afo;

All : r estituisce una collezione di tutti i tag pr esenti nel documento, sempr e sottofor ma di HtmlElement;

Body : r estituisce l'elemento body della pagina;

Cr eateElement(tagName) : cr ea un nuovo HtmlElement con tagName dato. Questo è l'unico modo in cui possiamo

cr ear e nuovi oggetti da aggiunger e alla pagina (tr anne ovviamente r icopiar e il codice, modificar lo, e poi

impostar e di nuovo la pr opr ietà DocumentTex t);

For ms : r estituisce una collezione di tutti i tag form pr esenti nel documento;

GetElementById(id As Str ing) : r estituisce un r ifer imento all'elemento con specifico id;

GetElementFr omPoint(p As Point) : r estituisce un r ifer imento all'elemento che contiene il punto p; le coor dinate

del punto sono r elative all'estr emo super ior e sinistr o della pagina;

GetElementsByTagName(tagName As Str ing) : r estituisce una collezione di tutti i tag con dato tagName.

GetElementsByTagName("div"), ad esempio, r estituisce l'insieme di tutti i div della pagina;

Images : r estituisce una collezione di tutti i tag image;

InvokeScr ipt(scr iptName As Str ing, ar gs() As Object) : esegue il metodo di nome scr iptName passandogli gli

ar gomenti specificati in ar gs. Il metodo deve esser e definito all'inter no di un tag s cript nella pagina (non è

impor tante il linguaggio, ma per or a ho ver ificato che funzioni solo con javascr ipt e actionscr ipt);

Links : r estituisce una collezione di tutti i tag a;

Title : indica il titolo della pagina;

Ur l : l'indir izzo della pagina car icata;

Window : r estituisce un oggetto HtmlWindow associato alla finestr a che visualizza la pagina. Questo oggetto

espone alcuni membr i molto inter essanti, tr a cui:

Aler t(S) : visualizza il messaggio S in una finestr a di dialogo;

Confir m(S) : visualizza il messaggio S in una finestr a di dialogo e per mette di sceglier e tr a OK e Annulla;

r estituisce Tr ue se è stato pr emuto OK, altr imenti False;

Page 375: Guida visual basic

Pr ompt(S, D) : visualizza il messaggio S in una finestr a di dialogo e chiede di inser ir e un valor e in una

casella di testo (il valor e pr edefinito è D). Restituisce il valor e che l'utente ha immesso.

Gli oggetti HtmlElement contengono più o meno gli stessi membr i, con l'aggiunta di GetAttr ibute e SetAttr ibute per

modificar e gli attr ibuti di un tag.

Nei pr ossimi par agr afi far ò alcuni esempi di come utilizzar e tali classi.

Login automaticoEcco un modo con cui potr este automatizzar e il login in una pagina salvando le infor mazioni e compilando i campi con

un solo pulsante.

Ipotizziamo di aver e un dizionar io LoginInfo in cui sono contenute delle coppie indir izzo-dizionar io. I valor i sono a lor o

volta altr i dizionar i che contengono le infor mazioni per il login. Potr emmo utilizzar e il codice seguente per

automatizzar e il tutto:

Nonostante possa sempr ar e inutile, questo appr occio potr ebbe diventar e molto più intr igante, ad esempio, se l'utente

immettesse semplicemente una tesser a o una chiavetta in un dispositivo collegato al computer e, usando il vostr o

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.32.33.34.35.36.37.38.39.40.41.42.43.44.45.

Class Form1

'Qui c'è il codice del capitolo precedente 'Ecco il dizionario che contiene tuttoDim LoginInfo As New Dictionary(Of String, Dictionary(Of String, String))

Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles

MyBase.Load'Per semplicità, in questo esempio carichiamo dei'dati di prova al caricamento del form Dim TInfo As New Dictionary(Of String, String)With TInfo

'ID del form di login.Add("form-id", "totemlogin")'ID della textbox per l'username.Add("username-field", "lname")'ID della textbox per la password.Add("password-field", "lpassw")'Username e password.Add("username", "prova").Add("password", "prova")

End With

'Associa alla pagina il suo loginLoginInfo.Add("http://totem.altervista.org/guida/versione3/login.php", TInfo)

End Sub

Private Sub btnAction_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)Handles btnAction.Click'Quando viene premuto il pulsante, ricava il dizionario'dei dati dall'url della paginaDim Info As Dictionary(Of String, String) = LoginInfo(wbBrowser.Url.ToString())'Quindi compila i campi e invia la richiesta di loginWith wbBrowser.Document

.GetElementById(Info("username-field")).SetAttribute("value", Info("username"))

.GetElementById(Info("password-field")).SetAttribute("value", Info("password"))'InvokeMember invoca un metodo usabile da un'certo elemento. I metodi sono gli stessi che si'usano in javascript.GetElementById(Info("form-id")).InvokeMember("submit")

End WithEnd Sub

End Class

Page 376: Guida visual basic

br owser , potesse inter facciar si con tale dispositivo per automatizzar e e per sonalizzar e tutti i login a seconda

dell'utente. Oppur e potr este sfr uttar e il r iconoscimento vocale offer to dalle libr er ie del fr amewor k 3.5 per confer mar e

l'accesso mediante una par ola detta a voce.

TrasformazioniNel par agr afo pr ecedente ho mostr ato come modificar e degli elementi. In questo mostr er ò come aggiunger e nuovi

elementi alla pagina dinamicamente e come gestir ne gli eventi.

Sempr e tenendo come guida il codice pr oposto nel par agr afo pr ecedente, cambiamo la funzione del pulsante btnAction

con la seguente: dopo il click sul pulsante, cliccando su qualsiasi immagine nella pagina, questa viene tr asfor mata in un

link all'immagine. Ecco il codice:

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.32.

33.34.35.36.37.38.39.40.41.42.43.44.45.46.47.48.49.50.51.52.53.

Class Form1

'... Private Sub btnAction_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)

Handles btnAction.ClickWith wbBrowser.Document

'Scorre tutte le immagini nella pagina e ad ognuna'aggiunge un nuovo gestore d'evento per l'evento OnClick.'Il metodo AttachEventHandler può essere usato'da qualsiasi HtmlElement, ed accetta come primo'parametro il nome dell'evento da gestire (vedere la'documentazione ufficiale W3C) e come secondo un'delegate che punta al sottoscrittore.For Each img As HtmlElement In .Images

.AttachEventHandler("onclick", AddressOf ImageToLink)Next

End WithEnd Sub 'Questo è il nuovo gestore d'evento. Nonostante i'parametri, sender è sempre NothingPrivate Sub ImageToLink(ByVal sender As Object, ByVal e As EventArgs)

'Ottiene un riferimento all'immagine con il metodo'GetElementFromPoint, sfruttando il fatto che questo'codice viene eseguito subito dopo un click.'MousePosition indica la posizione del mouse sullo schermo,'Me.Location determina la posizione del form sullo schermo'e wbBrowser.Location la posizione del browser sul form.'La differenza tra questi punti è la posizione'del mouse rispetto al browser. Anche se un po' grezzo,'questo metodo dovrebbe funzionare abbastanzaDim Img As HtmlElement = wbBrowser.Document.GetElementFromPoint(MousePosition -

Me.Location - wbBrowser.Location)'Crea un nuovo link mediante il metodo CreateElement'di HtmlDocumentDim Link As HtmlElement = wbBrowser.Document.CreateElement("a")

'Imposta l'attributo href dell'immagineLink.SetAttribute("href", Img.GetAttribute("src"))'Imposta il testo del linkIf Not String.IsNullOrEmpty(Img.GetAttribute("longdesc")) Then

Link.InnerText = Img.GetAttribute("longdesc")ElseIf Not String.IsNullOrEmpty(Img.GetAttribute("alt")) Then

Link.InnerText = Img.GetAttribute("alt")Else

Link.InnerText = "Immagine"End If

'Aggiunge il link prima dell'immagineImg.InsertAdjacentElement(HtmlElementInsertionOrientation.BeforeBegin, Link)'Dato che non è possibile eliminare elementi,'impone all'immagine larghezza 0Img.SetAttribute("width", "0")

Page 377: Guida visual basic

54.55.

End Sub End Class

Page 378: Guida visual basic

D3. Scaricare file dalla rete

Oltr e al WebBr owser , ci sono altr i tr e modi di scar icar e file da inter net. In questo par agr afo li analizzer ò uno per uno.

Download sincrono gestitoIl pr imo e più semplice dei suddetti modi consiste nell'utilizzar e una classe messa a disposizione dal Fr amewor k, ossia

WebClient (del namespace System.Net). Una volta istanziato un oggetto di questo tipo, è possibile r ichiamar e da esso

molti metodi diver si per scar icar e pr aticamente qualsiasi cosa. Qui espongo i metodi sincr oni:

DownloadData(ur i) : scar ica il file con dato ur i (Unifor m Resour ce Identifier , una for ma più gener ale dell'ur l) e

r estituisce tutti i dati scar icati sottofor ma di un ar r ay di bytes. Par ticolar mente indicato per scar icar e piccoli

file binar i ad uso tempor aneo;

DownloadFile(ur l, path) : scar ica il file indicato dall'indir izzo ur l e lo salva nel per cor so path su disco fisso;

DownloadStr ing(ur i) : molto simile a Dow nloadData, ma anziché r estituir e un ar r ay di bytes, r estituisce una

str inga.

Ci sono, poi, altr i membr i che è inter essante conoscer e:

Cr edentials : indica le cr edenziali usate per acceder e alla data r isor sa. È utile impostar e questa pr opr ietà

quando si accede a ser ver che r ichiedono un'autenticazione tr amite nome utente e passwor d, come ad esempio

si usa far e quando si utilizza il pr otocollo ftp per il tr asfer imento di file. Ad esempio:

Header s : espone una collezione degli header posti all'inizio della r ichiesta per il file. Quando un metodo di

download viene invocato, la classe WebClient si pr eoccupa di inviar e una r ichiesta oppor tuna al ser ver . Ad essa

può aggiunger e alcune metainfor mazioni note come header s, definite dallo standar d del pr otocollo HTTP (di cui

potete tr ovar e una descr izione appr ofondita qui). Nei pr ossimi esempi user ò un solo tipo di header , Range, che

per mette di ottener e solo una data par te del file;

Pr ox y : imposta il proxy che la classe attr aver sa per inoltr ar e la r ichiesta;

Quer yStr ing : indica un insieme di chiavi e valor i che costituiscono la quer y applicata alla pagina r ichiesta. Una

query str ing può esser e accodata alla fine dell'ur l intr oducendola con un "?", definendo una coppia come

nome=valor e e separ ando tutte le copie da un car atter e "&". Ser ve per ottener e r isultati diver si da una stessa

pagina, specificando cosa si sta cer cando.

Alcuni semplici esempi:

1.2.

Dim W As New Net.WebClientW.Credentials = New Net.NetworkCredential("username", "password")

01.02.03.04.05.06.07.08.09.10.11.12.13.14.

Dim W As New Net.WebClient'Scarica l'home page del sito e la salva in C:W.DownloadFile("http://totem.altervista.org/index.php", "C:\index.php") Dim S As String'Scarica il contenuto del file Capitoli.txt e lo salva'nella stringa SS = W.DownloadString("http://totem.altervista.org/guida/versione3/Capitoli.txt") 'Aggiunge una coppia nome-valore alla queryW.QueryString.Add("name", "twaveeditor")'La prossima richiesta sarà quindi equivalente a:' http://totem.altervista.org/download/details.php?name=twaveeditor

Page 379: Guida visual basic

La pecca di questi metodi è che sono sincr oni, ossia bloccano il funzionamento dell'applicazione fino a quando il download

non è ter minato. Questo compor tamento può r ivelar si utile in cer ti casi e r ender e più maneggevole il codice per

scar icar e file di piccole dimensioni, ma è tutt'altr o che accettabile per gr andi quantità di dati.

Download asincrono gestitoPer file molto gr andi, invece, ci vengono in aiuto le ver sioni asincr one dei metodi sopr a esposti: sono r iconoscibili dal

suffisso "Async" dopo il nome del metodo. Questi eseguono il dow nload in un thr ead separ ato, per ciò non inter fer iscono

con le nor mali oper azioni del pr ogr amma. In compenso, sono un po' più difficili da gestir e, ma nulla di par ticolar mente

complicato.

I metodi asincr oni si r ichiamano usando esattamente gli stessi par ametr i delle ver sioni sincr one, ma per saper e come

stanno andando le cose, dobbiamo far e uso di due eventi della classe WebClient: DownloadPr ogr essChanged, che notifica

il pr ogr esso del download, e DownloadFileCompleted (o DownloadDataCompleted o Dow nloadStr ingCompleted, a seconda

dei casi). Ecco un semplice esempio:

15.16.

'Ossia scaricherà la pagina di download di TWave Editor.'Il contenuto del file verrà salvato in BDim B() As Byte = W.DownloadData("http://totem.altervista.org/download/details.php")

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.32.33.34.35.36.

37.38.39.

Class Form1'WithEvents permette di gestire gli eventi di WPrivate WithEvents W As New Net.WebClient()

Private Sub btnDownload_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)

Handles btnDownload.Click'Inizia il download asincronoW.DownloadFileAsync(New Uri(txtUrl.Text), txtFile.Text)btnCancel.Enabled = TruebtnDownload.Enabled = False

End Sub

Private Sub W_DownloadProgressChanged(ByVal sender As Object, ByVal e AsNet.DownloadProgressChangedEventArgs) Handles W.DownloadProgressChanged'Il parametro e contiene alcune informazioni'sul progresso del downloadlblStatus.Text = _

String.Format("Bytes ricevuti: {0} B{3}Dimensione file: {1} B{3}Progresso:{2:N0}%", _

e.BytesReceived, e.TotalBytesToReceive, _e.ProgressPercentage, Environment.NewLine)

End Sub

Private Sub W_DownloadFileCompleted(ByVal sender As Object, ByVal e AsSystem.ComponentModel.AsyncCompletedEventArgs) Handles W.DownloadFileCompleted'e.Cancelled vale True se il download è stato annullato.'e.Error è di tipo Exception e contiene l'eccezione' generata nel caso si sia verificato un errore.If e.Cancelled Then

MessageBox.Show("Il download è stato cancellato!", Me.Text, MessageBoxButtons.OK,MessageBoxIcon.Exclamation)

ElseIf e.Error IsNot Nothing ThenMessageBox.Show("Si è verificato un errore: " & e.Error.Message, Me.Text,

MessageBoxButtons.OK, MessageBoxIcon.Exclamation)Else

MessageBox.Show("Download completato con successo!", Me.Text,MessageBoxButtons.OK, MessageBoxIcon.Information)

End IfbtnDownload.Enabled = TruebtnCancel.Enabled = False

End Sub

Private Sub btnCancel_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)Handles btnCancel.Click'Il metodo CancelAsync cancella il download asincronoW.CancelAsync()

Page 380: Guida visual basic

Download sincrono/asincrono non gestitoCome ho illustr ato nei par agr afi pr ecedenti, WebClient si occupa di eseguir e molte istr uzioni r iguar do al download:

connetter si al ser ver indicato, cr ear e una r ichiesta valida secondo il pr otocollo usato (HTTP o FTP o altr i), inoltr ar e la

r ichiesta, aspettar e una r isposta, legger e dallo str eam di r ete i dati for niti dal ser ver e copiar li nel file indicato,

quindi chiuder e la connessione. Insomma, non lascia nulla al contr ollo del pr ogr ammator e. Con il pr ossimo metodo che

andr ò ad intr odur r e, potr emmo manipolar e alcuni di questi passaggi a nostr o piacimento.

Le classi che ci inter essano or a sono WebRequest e WebResponse, del namespace System.Net. Esse sono classi astr atte,

poiché ogni pr otocollo implementa le pr opr ie r ichieste e r isposte secondo deter minati standar d. Nel nostr o esempio,

user emo HttpWebRequest per cr ear e ed inviar e una r ichiesta http ad un ser ver e HttpWebResponse per inter pr etar ne

la r isposta. Sappiate, per ò, che esistono anche le r ispettive ver sioni per il pr otocollo FTP, ossia FtpWebRequest e

FtpWebResponse. Ecco una pr ima semplice ver sione del codice:

Pr ima di pr oceder e, vor r ei far e alcuni chiar imenti sullo str eam di r ete. Esso r appr esenta un flusso di dati che

pr oviene dal ser ver a cui si è inviata la r ichiesta, ed è un flusso a senso unico, per ciò non suppor ta oper azioni di

r icer ca (invocando il metodo Seek o modificando la pr opr ietà Position otter r ete degli er r or i). Non è neppur e possibile

saper ne la dimensione complessiva, poiché anche la pr opr ietà Length gener a eccezioni. E, infine, non è possibile

scr iver vi sopr a. Esiste un modo per saper e le dimensioni dei dati, ossia r ichiamar e la pr opr ietà

Reponse.ContentLength, che, tuttavia, potr ebbe contener e valor i pr ivi di senso (ad esempio -1). Questo succede per chè

essa si limita ad espor r e il valor e di un header posto nella r isposta http: tuttavia, il ser ver non è obbligato ad inser ir e

questo header , e se non lo fa, non c'è modo di legger lo.

40.41.42.43.

btnDownload.Enabled = TruebtnCancel.Enabled = False

End Sub

End Class

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.32.

Public Sub DownloadFile(ByVal Address As String, ByVal Path As String)'Crea una richiesta http per l'indirizzo Address.'Address può anche contenere una query stringDim Request As Net.HttpWebRequest = Net.HttpWebRequest.Create(Address)'Invia la richieste a ottiene la rispostaDim Response As Net.HttpWebResponse = Request.GetResponse()'Ottiene da Response uno stream di rete dal quale si'potrà leggere il file richiesto.Dim Reader As IO.Stream = Response.GetResponseStream()'Crea un nuovo file in localeDim Writer As New IO.FileStream(Path, IO.FileMode.Create)'Un buffer di byte che contiene i blocchi letti'dallo stream. La lettura a blocchi è più'conveniente che trasferire in massa tutto il contenuto,'poiché altrimenti si dovrebbe usare un buffer'gigantesco (almeno le dimensioni del file)Dim Buffer(8127) As ByteDim BytesRead As Int32

'La funzione Read, vi ricordo, restituisce come risultato'il numero di bytes effettivamente letti dallo streamBytesRead = Reader.Read(Buffer, 0, Buffer.Length)Do While BytesRead > 0

Writer.Write(Buffer, 0, BytesRead)BytesRead = Reader.Read(Buffer, 0, Buffer.Length)

Loop

Reader.Close()Writer.Close()Response = NothingRequest = Nothing

End Sub

Page 381: Guida visual basic

Osser viamo or a che tutte le oper azioni svolte sono sincr one, ma, come il titolo sugger isce, è possibile r ender e tutto il

metodo asincr ono, facendo uso dei thr ead. Infatti, è sufficiente eseguir e la pr ocedur a in un thr ead differ ente: per

ulter ior i infor mazioni sul multithr eading, veder e capitolo r elativo.

In ultimo, è possibile ottener e solo una par te del file aggiungendo l'header Range alla r ichiesta, come anticipato nei

par agr afi pr ecedenti. Dato che la pr opr ietà Header s di WebClient vieta l'uso di questo header (non è ben chiar a la

r agione), l'unico modo per usar lo consiste nell'impiegar e quest'ultimo metodo di download. Basta r ichiamar e AddRange

pr ima dell'invio della r ichiesta:

Non ser ve eseguir e altr e oper azioni par ticolar i per la lettur a. Lo str eam ottenuto consentir à di legger e esattamente

ciò che si è r ichiesto come se fosse un unico flusso di dati.

01.02.03.04.05.06.07.08.09.10.

'...'Indica al server che vogliamo iniziare la lettura'dall'offset nRequest.AddRange(n) 'oppure 'Indica al server che vogliamo iniziare la lettura dalla'posizione n, ma solo fino alla posizione qRequest.AddRange(n, q)

Page 382: Guida visual basic

D4. I Socket - Parte I

I socket sono uno str umento che per mette di inviar e e r icever e dati tr a due applicazioni che cor r ono su macchine

collegate da una r ete, la quale, nel caso più fr equente, coincide con Inter net. Le classi che espongono i metodi necessar i

sono contenute nel namespace System.Net.Sockets, di cui la classe Socket costituisce il membr o più eminente. In questo

capitolo e nel successivo, tuttavia, non user emo dir ettamente tale classe, poiché è poco pr atica da gestir e e fa

massiccio uso di tecniche di pr ogr ammazione un po' complesse, quali il multithr eading, che non ho ancor a spiegato.

Intr odur r ò, invece, al suo posto, due classi più semplici che fanno da wr apper ad alcune funzioni basilar i del socket:

TcpListener e TcpClient.

ServerIl ser ver , nel nostr o caso, è il computer sul quale r isiede l'applicazione pr incipale deputata alla gestione delle

connessioni e dei ser vizi dei client ester ni. Più in gener ale è una componente infor matica che for nisce ser vizi ad altr e

componenti attr aver so una r ete. Per implementar e un'applicazione Ser ver da codice dobbiamo far sì che essa possa

accettar e connessioni da par te di altr i. Per far questo è necessar io usar e la classe TcpListener , che si mette in ascolto

su di una por ta, e r ifer isce quando ci sono r ichieste di connessioni in attesa su di essa. I suoi membr i di spicco sono:

AcceptTcpClient() : accetta una connessione in attesa e r estituisce un oggetto TcpClient collegato al client che ha

inviato la r ichiesta. Usando tale oggetto sar à possibile inviar e o r icever e dati, poiché il Listener , di per sé, non

fa altr o che attender e e accettar e connessioni, ma l'azione ver a viene intr apr esa da oggetti TcpClient;

Pending() : r estituisce Tr ue se si cono connessioni in attesa;

Ser ver : r estituisce l'oggetto socket che TcpListener sfr utta. Come avevo detto nel par agr afo intr oduttivo,

queste due classi fanno uso inter namente di Socket, ma espongono metodi di più semplice gestione;

Star t() : inizia l'oper azione di listening su una por ta data;

Stop() : inter r ompe il listening;

Il costr uttor e di TcpListener che user emo r ichiede come unico par ametr o la por ta su cui metter si in ascolto.

ClientLa classe fondamentalmente usata per un client è TcpClient. I suoi membr i più significativi sono:

Available: r estituisce il numer o di bytes r icevuti e pr onti per la lettur a

Close(): chiude la connessione

Connect(IP, P): tenta una connessione ver so il ser ver identificato da IP sulla por ta P. IP può esser e sia un

indir izzo IP che DNS

Connected: r estituisce Tr ue se è connesso, altr imento False

GetStr eam(): funzione impor tantissima che r estituisce un oggetto di tipo Sockets.Netw or kStr eam su cui e da cui

si scr ivono e leggono tutti i dati scambiati tr a client e ser ver

ReceiveBuffer Size: imposta la gr andezza del buffer di bytes r icevuti

SendBuffer Size: imposta la gr andezza del buffer di bytes inviati

Il client tenta la connessione al ser ver e, se accettato, può dialogar e con esso scambiando messaggi. I dati vengono

inviati e r icevuti attr aver so uno str eam di r ete bidir ezionale, che è possibile ottener e r ichiamando GetStr eam().

Page 383: Guida visual basic

Quando il client scr ive su questo str eam, il ser ver r iceve i dati e li può legger e, e vicever sa.

Un semplice scambio di messaggiPer iniziar e scr iver emo un semplice pr ogr amma per scambiar e messaggi (chiamar lo "chat" sar ebbe a dir poco

inoppor tuno). Per semplicità d'uso, la stessa applicazione potr à far e sia da ser ver che da client, così un utente potr à

sia collegar si ad un altr o che attender e connessioni (ma non far e le due cose contempor aneamente). L'inter faccia che ho

pr epar ato è questa:

Ci sono anche due timer , tmr Connections e tmr Data. Ecco il codice:

001.002.003.004.005.006.007.008.009.010.011.012.013.014.015.016.017.018.019.020.021.022.

Imports System.Net.SocketsImports System.Text.UTF8Encoding Public Class Form1

Private Listener As TcpListenerPrivate Client As TcpClientPrivate NetStream As NetworkStream

'Questa procedura serve per attivare o disattivare i'controlli a seconda che si sia connessi oppure no. Serve'per impedire che si tenti di inviare un messaggio quando'non si è connessi, ad esempioPrivate Sub EnableControls(ByVal Connected As Boolean)

btnConnect.Enabled = Not ConnectedbtnListen.Enabled = Not ConnectedtxtIP.Enabled = Not Connected

btnSend.Enabled = ConnectedtxtMessage.Enabled = ConnectedbtnDisconnect.Enabled = Connected

Page 384: Guida visual basic

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.084.085.086.087.088.

If Connected Then

tmrData.Start()Else

tmrData.Stop()End If

End Sub

Private Sub btnListen_Click(ByVal sender As Object, ByVal e As EventArgs) HandlesbtnListen.Click'Inizializza il listener e inizia l'ascolto sulla porta'5000. Inoltre, attiva il timer per controllare se ci'sono connessioni in arrivo. Il timer scatta ogni 100msListener = New TcpListener(5000)Listener.Start()tmrConnections.Start()btnListen.Enabled = FalsebtnConnect.Enabled = FalsetxtLog.AppendText("Server - in ascolto..." & Environment.NewLine)

End Sub

Private Sub tmrConnections_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs)Handles tmrConnections.Tick'Se ci sono connessioni...If Listener.Pending() Then

'Ferma un attimo il timertmrConnections.Stop()

'Chiede all'utente se confermare la connessioneIf MessageBox.Show("Rilevato un tentativo di connessione. Accettare?", Me.Text,

MessageBoxButtons.YesNo, MessageBoxIcon.Question) =Windows.Forms.DialogResult.Yes Then'Ottiene l'oggetto TcpClient collegato al clientClient = Listener.AcceptTcpClient()'Ferma il listenerListener.Stop()'Ottiene il network streamNetStream = Client.GetStream()'E attiva/disattiva i controlli per quando si è connessiEnableControls(True)

ElseListener.Stop()Listener.Start()tmrConnections.Start()

End IfEnd If

End Sub

Private Sub btnConnect_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)Handles btnConnect.ClickDim IP As Net.IPAddress

'Prima esegue un controllo sull'indirizzo IP per'controllare che sia validoIf Not Net.IPAddress.TryParse(txtIP.Text, IP) Then

MessageBox.Show("IP non valido!", Me.Text, MessageBoxButtons.OK,MessageBoxIcon.Exclamation)

Exit SubEnd If

'Quindi inizializza un client e tenta la connessione'al dato IP sulla porta 5000Client = New TcpClient()txtLog.AppendText("Client - tentativo di connessione..." & vbCrLf)Try

Application.DoEvents()Client.Connect(IP, 5000)

Catch Ex As Exception

End Try

'Se la connessione ha avuto successo, ottiene il network

Page 385: Guida visual basic

Come avete visto dal codice non c'è nulla di par ticolar mente complicato da capir e. Tuttavia, questo pr ogr amma è molto

semplice e per mette di gestir e solo una connessione (in ar r ivo o in uscita). Il Listener , anche se r iavviabile, continuer à

a dar e Pending = Tr ue almeno fino a che tutti i client r elativi alla connessione siano stati cor r ettamente chiusi, e

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.144.145.146.147.148.149.150.

'stream e agisce sui controlli come nel codice precedenteIf Client.Connected Then

txtLog.AppendText("Tentativo di connessione riuscito!" & vbCrLf)NetStream = Client.GetStream()EnableControls(True)

ElsetxtLog.AppendText("Tentativo di connessione fallito..." & vbCrLf)

End IfEnd Sub

Private Sub tmrData_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs)

Handles tmrData.Tick'Se ci sono dati disponibiliIf Client.Available > 0 Then

'Li legge dallo streamDim Buffer(Client.Available - 1) As ByteNetStream.Read(Buffer, 0, Buffer.Length)

'Li trasforma in una stringaDim Msg As String = UTF8.GetString(Buffer)

'Se il messaggio inizia con questa stringa'particolare signifia che l'altro utente ha chiuso'la connessione, quindi disconnette anche questoIf Msg.StartsWith("\\\close\\\") Then

btnDisconnect_Click(Me, EventArgs.Empty)Exit Sub

End If

'Altrimenti lo accoda alla textbox grandetxtLog.AppendText("Ricevuto: ")txtLog.AppendText(Msg)txtLog.AppendText(Environment.NewLine)

End IfEnd Sub

Private Sub btnSend_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)

Handles btnSend.ClickIf Not String.IsNullOrEmpty(txtMessage.Text) Then

Dim Buffer() As Byte = UTF8.GetBytes(txtMessage.Text)txtLog.AppendText("Inviato: " & txtMessage.Text & Environment.NewLine)NetStream.Write(Buffer, 0, Buffer.Length)txtMessage.Text = ""

End IfEnd Sub

Private Sub btnDisconnect_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)

Handles btnDisconnect.ClicktmrData.Stop()

Dim Buffer() As Byte = UTF8.GetBytes("\\\close\\\")NetStream.Write(Buffer, 0, Buffer.Length)

Client.Client.Close()Client.Close()Client = Nothing

If Listener IsNot Nothing Then

Listener.Server.Close()Listener = Nothing

End If

EnableControls(False)txtLog.AppendText("Disconnesso" & Environment.NewLine)

End SubEnd Class

Page 386: Guida visual basic

questo non si ver ifica se non alla fine del pr ogr amma, ossia quando uno dei due si disconnette. Per far la br eve, è

impossibile cr ear e un'applicazione di scambio messaggi multiutente con questi oggetti e queste modalità.

Page 387: Guida visual basic

D5. I Socket - Parte II

Esempio: File SenderFino ad or a si è par lato di inviar e semplici messaggi sotto for ma di str inghe, ma come ci si dovr ebbe compor tar e nel

caso il contenuto da inviar e sia un file inter o o, per chè no?, molti files? Il pr ocedimento è lo stesso e con questo esempio

for nir ò una pr ova di come sia altr ettanto semplice questo compito. L'applicazione File Sender si basa su un semplice

scambio di inter r ogazioni tr a i due computer , al ter mine delle quali si inizia l'invio effettivo del file. Per pr ima cosa il

client comunica al ser ver che sta per cominciar e il flusso di dati; il ser ver deve per ciò r isponder e in caso affer mativo

se l'utente è disposto al tr asfer imento: in questo caso, r imanda indietr o un messaggio di confer ma, e apr e una nuova

por ta per i dati in ar r ivo; par allelamente, il client si connette alla por ta aper ta e inizia il tr asfer imento.

File Sender: serverHo str uttur ato l'inter faccia del ser ver in questo modo:

Label1 : una label esplicativo con il testo "Pr ogr esso:"

pr gPr ogr ess : la bar r a del pr ogr esso

cmdListen : il pulsante "Ascolta"

str Status : la status str ip sul lato basso del for m

lblStatus : la label contenuta in str Status, con il compito di infor mar e l'utente sullo stato dell'applicazione

tmr Contr olConnection : timer con Inter val = 100 che ha il compito di contr ollar e se ci sono r ichieste in attesa

tmr Contr olFile : timer con Inter val = 100 con il compito di contr ollar e se ci sono r ichieste in attesa sulla por ta

1001, deputata in questo caso alla r icezione del file dal client

tmr GetData : timer con Inter val = 100 con il compito di ottener e i messaggi inviati dal client e di r isponder vi

bgReceiveFile : Backgr oundWr oker con Wr oker Repor tPr ogr ess = Tr ue che ha il compito di r icever e il file dal

client

E si pr esenta gr aficamente così:

Ed ecco il codice:

001.

Page 388: Guida visual basic

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.

Imports System.Net.SocketsImports System.Text.ASCIIEncoding Imports System.ComponentModelPublic Class Form1

'Listener: attende una connessione sulla porta 25'FileListener: attende una connessione sulla porta 1001. Questa' ha il compito di trasferire i bytes del file

Private Listener, FileListener As TcpListener'Client: l'oggetto che ha il compito di dialogare con' il client e confermarne le operazioni'FileReceiver: l'oggetto che ha il compito di ricevere le

' informazioni contenute nel file e scriverle sulla macchina' in forma di file concretoPrivate Client, FileReceiver As TcpClient'NetStream: lo stream su cui si scrivono i dati di comunicazione

'NetFile: lo stream da cui si leggono i dati del filePrivate NetStream, NetFile As NetworkStream'Percorso su cui salvare il filePrivate FileName As String

'Dimensione del filePrivate FileSize As Int64

'I seguenti metodi semplificano le operazioni di invio e'ricezione di stringhe

'Invia un messaggio su uno stream di retePrivate Sub Send(ByVal Msg As String, ByVal Stream As NetworkStream)

'Se si può scrivere

If Stream.CanWrite Then'Converte il messaggio in binarioDim Bytes() As Byte = ASCII.GetBytes(Msg)'E lo scrive sul network stream

Stream.Write(Bytes, 0, Bytes.Length)

End IfEnd Sub

'Ottiene un messaggio dallo stream di retePrivate Function GetMessage(ByVal Stream As NetworkStream) As String

'Se si può leggereIf Stream.CanRead Then

Dim Bytes(Client.ReceiveBufferSize) As Byte

Dim Msg As String'Legge i bytes arrivatiStream.Read(Bytes, 0, Bytes.Length)'Li converte in una stringa leggibileMsg = ASCII.GetString(Bytes)'E restituisce la stringa

Return Msg.Normalize

ElseReturn Nothing

End IfEnd Function

Private Sub cmdListen_Click(ByVal sender As Object, _

ByVal e As EventArgs) Handles cmdListen.ClickIf cmdListen.Text = "Ascolta" Then

'Inizia ad ascoltare sulla porta 25Listener = New TcpListener(25)Listener.Start()'Attiva il timer per controllare le richieste di connesionetmrControlConnection.Start()

Page 389: Guida visual basic

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.144.145.

'Cambia il testo e la funzione del pulsantecmdListen.Text = "Stop"

Else

'Ferma l'operazione di ascoltoListener.Stop()'Ripristina il testocmdListen.Text = "Ascolta"

End IfEnd Sub

Private Sub tmrControlConnection_Tick(ByVal sender As Object, _

ByVal e As EventArgs) Handles tmrControlConnection.Tick'Se ci sono connessioni in attesa...

If Listener.Pending Then

'Ferma il timer per eseguire le operazionitmrControlConnection.Stop()lblStatus.Text = "È stata ricevuta una richiesta"'Richiede all'utente se accettare la connessioneIf MessageBox.Show("È stata ricevuta una richiesta di connessione. Accettare?", _

Me.Text, MessageBoxButtons.YesNo, MessageBoxIcon.Question) = _Windows.Forms.DialogResult.Yes Then

'Acceta la connessioneClient = Listener.AcceptTcpClient'Apre lo stream di rete condivisoNetStream = Client.GetStream'Termina l'ascoltoListener.Stop()'Rende il pulsante cmdListen inutilizzabile, poiché'una connessione è già stata aperta

cmdListen.Enabled = False'Inizia la ricezione di messaggitmrGetData.Start()lblStatus.Text = "Connessione riuscita!"

Else'Altrimenti si rimette in attesa per altre connessionitmrControlConnection.Start()lblStatus.Text = "In attesa di connessioni..."

End If

End IfEnd Sub

Private Sub tmrControlFile_Tick(ByVal sender As Object, _

ByVal e As EventArgs) Handles tmrControlFile.Tick'Se c'è una richiesta, l'accetta subito

If FileListener.Pending Then

tmrControlFile.Stop()FileReceiver = FileListener.AcceptTcpClientNetFile = FileReceiver.GetStream'Ferma il listenerFileListener.Stop()lblStatus.Text = "Flusso di informazioni aperto"'Attiva la ricezione di dati attraverso un background workerbgReceiveFile.RunWorkerAsync()

End If

End Sub

Private Sub tmrGetData_Tick(ByVal sender As Object, _ByVal e As EventArgs) Handles tmrGetData.TickIf Client.Connected And Client.Available Then

'Ferma il timer mentre si eseguono le operazionitmrGetData.Stop()'Legge il messaggioDim Msg As String = GetMessage(NetStream)

Page 390: Guida visual basic

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.216.

If Msg.StartsWith("ConfirmTransfer") Then

'Divide il messagio in parti in base al carattere pipeDim Parts() As String = Msg.Split("|")'La prima parte è "ConfirmTransfer"

'La seconda è il percorso del file sull'altro computerDim File As String = Parts(1)'La terza è la dimensione

Dim Size As Int64 = CType(Parts(2), Int64)'Ottiene solo il nome del file, senza percorsoFile = IO.Path.GetFileName(File)'Costruisce il percorso del file su questo computer,'salvandolo nella cartella del progetto (bin\Debug)

FileName = Application.StartupPath & "\" & File'Imposta Size come variabile globaleFileSize = Size'Richiede se accettare il trasferimentoIf MessageBox.Show(String.Format( _"È stata ricevuta una richiesta di trasferimento di {0} ({1} bytes).

Acettare?", _File, Size), Me.Text, MessageBoxButtons.YesNo, _MessageBoxIcon.Question) = Windows.Forms.DialogResult.Yes Then

'Manda OK al clientSend("OK", NetStream)'Intanto si mette in attesa sulla porta 1001 per'l'invio dei bytes del fileFileListener = New TcpListener(1001)FileListener.Start()'E attiva il timer di controllo

tmrControlFile.Start()

Else'Altrimenti, risponde di noSend("NO", NetStream)

End IfEnd If

'Riprende il controllotmrGetData.Start()

End IfEnd Sub

Private Sub bgReceiveFile_DoWork(ByVal sender As Object, _

ByVal e As DoWorkEventArgs) Handles bgReceiveFile.DoWork'Apre un nuovo stream in base al percorso costruito

'nella procedura precedenteDim Stream As New IO.FileStream(FileName, IO.FileMode.Create)'Crea un indice che indica il progressoDim Index As Int64 = 0

lblStatus.Text = "In ricezione..."Do

If FileReceiver.Available Then

'Riceve i bytes necessariDim Bytes(4096) As Byte

Dim Msg As String = ASCII.GetString(Bytes)'Se i bytes sono un messaggio stringa e contengono'"END", oppure la dimensione giusta è già stata

'raggiunta, allora si fermaIf Msg.Contains("END") Or Index >= FileSize Then

Exit Do

End If'Preleva i bytes dallo stream di rete

Page 391: Guida visual basic

File Sender: c lientHo str uttur a l'inter faccia del client in questo modo:

gr pTr asnfer : un Gr oupBox con Tex t = "Tr asfer imento" che contiene tutti i contr olli sul tr asfer imento del file

tx tFile : una Tex tBox che contiene il per cor so del file da inviar e

cmdBr owse : un pulsante con Tex t = "Sfoglia" per per metter e all'utente di selezionar e un file in manier a

semplice

cmdSend : un pulsante con Tex t = "Invia" che ha il compito di inoltr ar e la r ichiesta al ser ver

pr gPr ogr ess : una bar r a di pr ogr esso

cmdConnect : un pulsante con Tex t = "Connetti" con il compito di connetter si al ser ver

str Status : una StatusStr ip nel lato infer ior e del for m

lblStatus : la label con il compito di tener e l'utente al cor r ente dello stato dell'applicazione

tmr GetData : un timer con Inter val = 100 per r icever e e inviar e messaggi al ser ver

bgSendFile : un Backgr oundWr oker con Wr oker Repor tPr ogr ess = Tr ue che ha il compito di inviar e il file

L'inter faccia si pr esenta così:

217.218.219.220.221.222.223.224.225.226.227.228.229.230.231.232.233.234.235.236.237.238.239.

NetFile.Read(Bytes, 0, 4096)'E li scrive sul file fisicoStream.Write(Bytes, 0, 4096)'Incrementa l'indice di 4096

Index += 4096'E notifica il progressobgReceiveFile.ReportProgress(Index * 100 / FileSize)

End IfLoop

lblStatus.Text = "File ricevuto!"Stream.Close()MessageBox.Show("File ricevuto con successo!", Me.Text, _

MessageBoxButtons.OK, MessageBoxIcon.Information)End Sub

Private Sub bgReceiveFile_ProgressChanged(ByVal sender As Object, _

ByVal e As ProgressChangedEventArgs) _Handles bgReceiveFile.ProgressChangedprgProgress.Value = e.ProgressPercentage

End Sub End Class

Page 392: Guida visual basic

E questo è il codice:

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.

Imports System.Net.SocketsImports System.Text.ASCIIEncodingImports System.ComponentModel Public Class Form1

'Client: il client che si dovrà connettere al server'FileSender: il client che ha il compito di trasferire i' pacchetti di informazioni al server

Private Client, FileSender As TcpClient'NetStream: lo stream su cui scrivere i dati di comunicazione'NetFile: lo stream per inviare i dati da scrivere sul filePrivate NetStream, NetFile As NetworkStream'L'IP del server a cui connettersi

Private IP As String

'I seguenti metodi semplificano le operazioni di invio e'ricezione di stringhe

'Invia un messaggio su uno stream di retePrivate Sub Send(ByVal Msg As String, ByVal Stream As NetworkStream)

'Se si può scrivere

If Stream.CanWrite Then'Converte il messaggio in binarioDim Bytes() As Byte = ASCII.GetBytes(Msg)'E lo scrive sul network stream

Stream.Write(Bytes, 0, Bytes.Length)

End IfEnd Sub

'Ottiene un messaggio dallo stream di retePrivate Function GetMessage(ByVal Stream As NetworkStream) As String

'Se si può leggereIf Stream.CanRead Then

Dim Bytes(Client.ReceiveBufferSize) As Byte

Dim Msg As String'Legge i bytes arrivatiStream.Read(Bytes, 0, Bytes.Length)'Li converte in una stringa leggibileMsg = ASCII.GetString(Bytes)'E restituisce la stringa

Return Msg.Normalize

ElseReturn Nothing

End IfEnd Function

Private Sub cmdConnect_Click(ByVal sender As Object, _

ByVal e As EventArgs) Handles cmdConnect.Click'Ottiene l'IP del server

IP = InputBox("Inserire l'IP del server:", Me.Text)

'Controlla che l'IP non sia nullo o vuotoIf String.IsNullOrEmpty(IP) Then

MessageBox.Show("Connessiona annullata!", Me.Text, _MessageBoxButtons.OK, MessageBoxIcon.Exclamation)

Exit Sub

End If

'Inizializza un nuovo client

Page 393: Guida visual basic

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.140.141.

Client = New TcpClient'E tenta la connessione all'IP dato, sulla porta 25

lblStatus.Text = "Connessione in corso..."Try

Client.Connect(IP, 25)Catch SE As SocketException

MessageBox.Show("Impossibile stabilire una connessione!", _Me.Text, MessageBoxButtons.OK, MessageBoxIcon.Exclamation)Exit Sub

End Try'Se la connessione è riuscita, ottiene lo'stream condiviso di rete direttamente collegato con'il networkstream del server

If Client.Connected Then

'Ora si è sicuri di essere connessi:'sblocca i comandi per il trasferimentoNetStream = Client.GetStreamgrpTransfer.Enabled = TruelblStatus.Text = "Connessione riuscita!"

End If

End Sub

Private Sub cmdBrowse_Click(ByVal sender As Object, _ ByVal e As EventArgs) Handles cmdBrowse.ClickDim Open As New OpenFileDialogOpen.Filter = "Tutti i file|*.*"If Open.ShowDialog = Windows.Forms.DialogResult.OK Then

txtFile.Text = Open.FileName

End IfEnd Sub

Private Sub cmdSend_Click(ByVal sender As Object, _

ByVal e As EventArgs) Handles cmdSend.Click'Controlla che il file esista

If Not IO.File.Exists(txtFile.Text) Then

MessageBox.Show("Il file non esiste!", Me.Text, _MessageBoxButtons.OK, MessageBoxIcon.Exclamation)Exit Sub

End If

'Se si è connessi e si può scrivere'sullo stream di rete...If Client.Connected AndAlso NetStream.CanWrite Then

'Manda un messaggio al server, chiedendo'conferma del trasferimento. Nel messaggio immette anche'alcune informazioni riguardo il nome e la'dimensione del fileDim Msg As String = _

String.Format("ConfirmTransfer|{0}|{1}", txtFile.Text, _FileLen(txtFile.Text))

'Invia il messaggio con la procedura scritta sopra

Send(Msg, NetStream)

'Attiva il timer per controllare i dati arrivatitmrGetData.Start()'Disattiva il pulsante per evitare più azioni'contemporanee indesideratecmdSend.Enabled = FalselblStatus.Text = "In attesa di conferma dal server..."

End If

End Sub

Private Sub tmrGetData_Tick(ByVal sender As Object, _

Page 394: Guida visual basic

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.212.213.

ByVal e As EventArgs) Handles tmrGetData.TickIf Client.Connected AndAlso Client.Available Then

'Ferma il timer mentre si eseguono le operazionitmrGetData.Stop()'Legge il messaggioDim Msg As String = GetMessage(NetStream)

'Uso Contains per un semplice motivo. Quando si converte

'un array di bytes in una stringa, ci possono essere'caratteri speciali successivi a questa, come ad esempio'il NULL terminator (carattere 00), che ne compromettono'la struttura.If Msg.Contains("OK") Then

'Termina questa connessione e si connette'alla porta deputata alla ricezione dei fileFileSender = New TcpClientFileSender.Connect(IP, 1001)If FileSender.Connected Then

'Ottiene lo stream associato a questa operaizoneNetFile = FileSender.GetStream'E inizia la trasmissione dei datibgSendFile.RunWorkerAsync(txtFile.Text)

End IfElseIf Msg.Contains("NO") Then

MessageBox.Show("Il server ha rifiutato il trasferimento!", _Me.Text, MessageBoxButtons.OK, MessageBoxIcon.Exclamation)cmdSend.Enabled = True

End If

'Riprende il controllo dei datitmrGetData.Start()

End If

End Sub

Private Sub bgSendFile_DoWork(ByVal sender As Object, _ByVal e As DoWorkEventArgs) Handles bgSendFile.DoWork'Ottiene il nome del file dall'argomento passato al metodo

'RunWorkerAsync nella procedura precedenteDim FileName As String = e.Argument'Crea un nuovo lettore del file a basso livello, così'da poter ottenere bytes di informazione anziché caratteri

'come nello StreamReaderDim Reader As New IO.FileStream(FileName, IO.FileMode.Open)'Calcola la grandezza del file, per poter poi tenere'l'utente al corrente della percentuale di completamento

Dim Size As Int64 = FileLen(FileName)'Un blocco di bytes da 4096 posti. Il file viene spedito in'"pacchettini" per evitare di sovraccaricare la connessioneDim Bytes(4095) As Byte

'Se il file è più grande di 4KiB, lo divide'in blocchi di dati da 4096 bytesIf Size > 4096 Then

For Block As Int64 = 0 To Size Step 4096

'Se i bytes rimanenti sono più di 4096,

'ne legge un blocco interoIf Size - Block >= 4096 Then

Reader.Read(Bytes, 0, 4096)Else

'Altrimenti un blocco più piccolo

Page 395: Guida visual basic

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.

Reader.Read(Bytes, 0, Size - Block)End If'Scrive i dati prelevati sullo stream di rete,'inviandoli così al serverNetFile.Write(Bytes, 0, 4096)'Riporta la percentuale all'utente

bgSendFile.ReportProgress(Block * 100 / Size)'Smette per 30ms, così da dare tempo dal'server di poter processare i pacchetti uno per'uno, evitando confusioneThreading.Thread.Sleep(30)

Next

Else'Se il file è minore di 4KiB, lo invia tutto'direttamente dal serverReader.Read(Bytes, 0, Size)NetFile.Write(Bytes, 0, Size)

End If

Reader.Close()

'Percentuale massima: lavoro terminatobgSendFile.ReportProgress(100)Threading.Thread.Sleep(100)'Comunica la fine delle operazioniNetFile.Write(ASCII.GetBytes("END"), 0, 3)MessageBox.Show("File inviato con successo!", Me.Text, _

MessageBoxButtons.OK, MessageBoxIcon.Information)cmdSend.Enabled = True

End Sub

Private Sub bgSendFile_ProgressChanged(ByVal sender As Object, _ByVal e As ProgressChangedEventArgs) _Handles bgSendFile.ProgressChanged'Aggiorna la progressbar

prgProgress.Value = e.ProgressPercentage

End SubEnd Class

Page 396: Guida visual basic

E1. Il Filesystem - Gestire files e cartelle

Il .NET Fr amewor k offr e una vastissima gamma di classi e metodi per oper ar e qualsiasi oper azioni di Input/Output e

analisi di file e car telle. Pr opr io per il suo scopo, il namespace che contiene tutto questo è chiamato System.IO. Sar ebbe

un'impr esa immane quella di documentar e il funzionamento di ogni membr o di ogni classe del namespace in questione,

quindi scr iver ò solamente delle dir ettive gener ali sui tipi da utilizzar e in ciascuna situazione.

IO.Directory : espone solo metodi statici che per mettono la modifica, l'analisi e la cr eazione di car telle sul

computer . I metodi più fr equentemente utilizzati sono Cr eateDir ector y (for nito un per cor so come unico

par ametr o, cr ea tutte le car telle ancor a inesistenti), Delete (come pr imo par ametr o accetta il per cor so

dell'unica car tella da eliminar e: se questa non è vuota, bisogna specificar e Tr ue come secondo par ametr o per

indicar e di eseguir e una pulizia r icor siva), Ex ists (contr olla l'esistenza di una car tella), GetDir ector ies

(r estituisce un ar r ay di str inghe contenente tutte le sottocar telle di pr imo livello) e GetFiles (r estituisce un

ar r ay di str inghe contenente tutti i files pr esenti nella dir ector y data; opzionalmente si può specificar e un

patter n di r icer ca di for mato simile a quello della pr opr ietà Filter di OpenFileDialog; ad esempio "*.dll;*.ex e").

IO.DirectoryIndo : oggetto che espone metodi d'istanza simili a quelli sopr a citati. Il costr uttor e accetta come

par ametr o il per cor so della car tella

IO.Dr iveInfo : oggetto che espone metodi d'istanza per ottener e infor mazioni su un par ticolar e dr ive. Il

costr uttor e accetta come par ametr o il nome del dr ive nella for ma "[Letter a]:\" (faccio pr esente che è possibile

ottener e una lista dei dr ivder logici con la funzione IO.Dir ector y.GetLogicalDr iver s). I membr i esposti sono per

lo più pr opr ietà, come AvaiableFr eeSpace (lo spazio liber o), Dr iveFor mat (il for mato del dr ivder , ad esempio

FAT32), Dr iveType (il tipo di dr iver , ad esempio se r appr esenta un lettor e CD o un disco fisso o r emovibile),

TotalSize (la dimensione totale) o VolumeLabel (l'etichetta associata).

IO.File : espone solo metodi statici che per mettono la modifica, l'analisi, la cr eazione e l'eliminazione di file sul

computer . I metodi più fr equentemente utilizzati sono Copy, Cr eate, Delete, Ex ists, Move, Replace

(r ispettivamente copia, cr ea, elimina, contr olla l'esistenza, sposta o sovr ascr ive un file: in tutti il pr imo

par ametr o è il per cor so del file). Molto utili sono anche i metodi ReadAllTex t, che legge e r estituisce tutto il

testo di un file, Wr iteAllTex t, che scr ive un dato testo in un dato file, e AppendAllTex t, che aggiunge una dato

testo a un dato file; di questi metodi esistono anche le var ianti Bytes e Lines, che oper ano su ar r ay di bytes o di

str inghe (linee di testo) anzichè su una str inga unica. Degne di nota sono infine anche le pr ocedur e Encr ypt e

Decr ypt, che cr iptano e decr iptano un file cosicchè solo l'utente che li ha codificati li possa legger e, e la funzione

GetAttr ibutes insieme con SetAttr ibute, che per mettono di modificar e gli attr ibuti di un file. Da r icor dar e che

gli attr ibuti sono posti in un enumer ator e codificato a bit.

IO.FileInfo : oggetto che espone metodi d'istanza simili a quelli sopr a citati. Il costr uttor e accetta come

par ametr o il per cor so del file.

IO.Path : espone solo metodi statici per lavor ar e con nomi di files e dir ector y e inter agir e cone i files

tempor anei. Molto usati sono GetFileName e GetFileNameWithoutEx tension, che r estituiscono r ispettivamente il

nome di file con o senza estensione di un per cor so completo dato: alla stessa str egua lavor ano i metodi

GetDir ector yName e GetEx tension che estr aggono l'estensione o la dir ector y da un per cor so di file completo. Ad

esempio:

1.2.3.4.5.

S = C:\Cartella\Sottocartella\file.txtGetFileName(S) = file.txtGetFileNameWithoutExtension(S) = fileGetDirectoryName = C:\Cartella\Sottocartella\GetExtension = .txt

Page 397: Guida visual basic

GetTempFileName, invece, cr ea e r estituisce un file tempor aneo con un nome stabilito dal sistema oper ativo,

mentr e GetTempPath r estituisce il nome della car tella tempor anea usata cor r entemente. Una featur e

inter essante è GetRandomFileName, che r estituisce un nome del tutto casuale adatto per file e car telle.

Esempio: File BrowserQuesto pr ogr amma di esempio per mette di navigar e nelle car telle del computer e di ottener e infor mazioni sui files,

usando un sistema di liste simile (ma non uguale) a quello del vecchio Visual Basic 6. L'inter faccia del pr ogr amma sar à

più o meno così:

E questo il codice:

001.002.003.004.005.006.007.008.009.010.011.012.013.014.015.016.017.018.019.

Class Form1'Tiene traccia del drive e della cartella correntePrivate CurrentDrive, CurrentDir As String

'Questa funzione permetterà di formattare le date'in poco spazioPrivate Function FormatDate(ByVal D As Date) As String

'Ad esempio'"lunedì 26 novembre 2007, ore 19:97"Return D.ToString("dddd dd MMMM yyyy, ore HH:mm")

End Function

Private Sub lstDrive_SelectedIndexChanged(ByVal sender As Object, _ByVal e As EventArgs) Handles lstDrive.SelectedIndexChangedIf lstDrive.SelectedIndex = -1 Then

Exit SubEnd If

Page 398: Guida visual basic

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.084.085.086.087.088.089.090.091.

'Procede solo se la periferica è prontaIf Not (New IO.DriveInfo(lstDrive.SelectedItem).IsReady) Then

MessageBox.Show("La periferica non è pronta!", "File Browser", _MessageBoxButtons.OK, MessageBoxIcon.Exclamation)Exit Sub

End If

'Memorizza il drive selezionatoCurrentDrive = lstDrive.SelectedItem'Quando si cambia driver, si resetta la lista delle'cartelle quindi la cartella iniziale è uguale'al driveCurrentDir = CurrentDrive.Clone'Pulisce la lista delle cartellelstDir.Items.Clear()'Quando si seleziona un drive, carica le cartelle'al suo internoFor Each Dir As String In _

IO.Directory.GetDirectories(lstDrive.SelectedItem)lstDir.Items.Add(Dir)

NextEnd Sub

Private Sub lstDir_SelectedIndexChanged(ByVal sender As Object, _

ByVal e As EventArgs) Handles lstDir.SelectedIndexChanged'Solo se è eramente selezionato un elemento procedeIf lstDir.SelectedIndex = -1 Then

Exit SubEnd If

'Memorizza la cartella selezionataCurrentDir = IO.Path.Combine(CurrentDir, lstDir.SelectedItem)

'Pulisce la lista delle cartelle e dei fileslstDir.Items.Clear()lstFiles.Items.Clear()

'Carica le sottocartelle, solo con il nomeFor Each SubDir As String In IO.Directory.GetDirectories(CurrentDir)

'Si può fare anche con le cartelle, poichè le funzione'considera solamente il formato della stringalstDir.Items.Add(IO.Path.GetFileName(SubDir))

Next'Carica i files interni alla cartella, solo con il nomeFor Each File As String In IO.Directory.GetFiles(CurrentDir)

lstFiles.Items.Add(IO.Path.GetFileName(File))Next

End Sub

Private Sub lstFiles_SelectedIndexChanged(ByVal sender As Object, _ByVal e As EventArgs) Handles lstFiles.SelectedIndexChangedIf lstFiles.SelectedIndex = -1 Then

Exit SubEnd If

'Ottiene le informazioni relative al file'Path.Combine combina due directory o un file e'una directory'per ottenere un percorso completoDim Info As New IO.FileInfo( _

IO.Path.Combine(CurrentDir, lstFiles.SelectedItem))

lblInfo.Text = String.Format( _"Nome: {1}{0}Data creazione: {2}{0}Ultimo accesso: {3}{0}" & _"Ultima modifica: {4}{0}Dimensione totale: {5:N0} bytes", vbCrLf, _Info.Name, FormatDate(Info.CreationTime), _FormatDate(Info.LastAccessTime), FormatDate(Info.LastWriteTime), _Info.Length)

End Sub

Private Sub cmdParent_Click(ByVal sender As Object, _ByVal e As EventArgs) Handles cmdParent.Click

Page 399: Guida visual basic

092.093.094.095.096.097.098.099.100.101.102.103.104.105.106.107.

Try'Si reca alla directory precedente nell'ordine'gerarchicoCurrentDir = IO.Directory.GetParent(CurrentDir).FullName

lstDir.Items.Clear()For Each SubDir As String In IO.Directory.GetDirectories(CurrentDir)

lstDir.Items.Add(IO.Path.GetFileName(SubDir))Next

Catch Ex As Exception'Se le directory sono le prime in ordine, si'genera un erroreMessageBox.Show("Non è possibile risalire più indietro!", _"File Browser", MessageBoxButtons.OK, MessageBoxIcon.Exclamation)

End TryEnd Sub

End Class

Page 400: Guida visual basic

E2. Manipolare il registro di sistema

Il r egistr o di sistema è un po' il totum continens delle infor mazioni: navigando fr a le sue chiavi e i suoi valor i, ci si può

tr ovar e qualunque cosa, ed è infatti uno dei ber sagli più agognati dagli spywar e. Il sistema oper ativo lo usa per

immagazzinar e qualsiasi dato utile al suo funzionamento, dai pr ogr ammi associati ai tipi di file, agli assembly

installati, alle estensioni del pannello di contr ollo, fino ai tempi che l'inter faccia impiega per aggior nar si. Con un

par agone un pò iper bolico, si potr ebbe par agonar e il r egistr o di sistema all'insieme delle var iabili di Windows stesso.

Saper lo modificar e oppor tunamente por ta gr andi vantaggi alle applicazioni che si scr ivono. Di contr o, un'azione

azzar data potr ebbe causar e molti danni. Se non avete una minima conoscenza di come agir e in questo contesto,

saltate il capitolo (oppur e andate a documentar vi e tor nate più tar di), altr imenti, pr oseguite senza indugio.

Microsoft.Win32.RegistryKeyPer iniziar e a utilizzar e le classi che agiscono sul r egistr o, bisogna pr ima impor tar e il namespace Micr osoft.Win32.

Una volta fatto ciò diventa disponibile il tipo Registr yKey, i cui membr i per mettono di eseguir e ogni azione possibile e

immaginabile. Una var iabile di questo tipo contiene le infor mazioni r elative alla chiave su cui è impostata, e i metodi

che apr ono altr e chiavi si r ifer iscono s olo alle sottochiavi di quella consider ata. In vir tù di ciò, esistono due modi per

apr ir e una chiave par tendo da una r oot. Il pr imo consiste nell'utilizzar e i campi pubblici già impostati della classe

Registr y:

Il secondo consiste nell'usar e la funzione OpenRemoteBaseKey. Dopo aver aper to una chiave, si possono usar e i suoi

metodi. Eccone un elenco:

Close : chiude la chiave e attua le modifiche appor tate

Cr eateSubKey(S) : cr ea una sottochiave di nome S. S può anche esser e un per cor so, come ad esempio

"Pr ova\command\shell": in questo caso ver r anno cr eate tutte le chiavi non ancor a esistenti

DeleteSubKey(S) : r imuove la sottochiave di nome S

DeleteValue(V) : elimina un valor e di nome V all'inter no della chiave

Flush : attua tutte le modifiche

GetSubKeyNames : r estituisce un ar r ay di str inghe contenente il nome di tutte le sottochiavi. Molto utile per le

enumer azioni

GetValue(V, D) : ottiene i dati contenuti nel valor e di nome V. Opzionalmente si può specificar e un secondo

par ametr o che costituisce il r isultato dell'oper azione nel caso non esista alcun valor e V nella chiave

GetValueKind(V) : r estituisce il tipo di dati contenuto nel valor e V

GetValueNames : ottiene un ar r ay di str inghe contenente il nome di tutti i valor i della chiave

Name : il nome della chiave

OpenRemoteBaseKey(Base, M) : apr e la chiave r oot specificata dall'enumer ator e Base sulla macchina M

OpenSubKey(S, W) : apr e la sottochiave S. W è un valor e booleano che specifica se si possano eseguir e modifiche

sulla sottochiave aper ta. Molte volte è causa di er r or i a r untime l'esser si dimenticati di impostar e il secondo

par ametr o a Tr ue. La funzione r estituisce Nothing nel caso non sia stata tr ovata la data sottochiave

1.2.3.4.5.6.

'Da notare che il tipo non espone costruttoriDim RegKey As RegistryKey'Apre la chiave HKEY_CLASSES_ROOTRegKey = Registry.ClassesRoot'Apre la sottochiave .zipRegKey = RegKey.OpenSubKey(".zip")

Page 401: Guida visual basic

SetValue(V, A) : imposta ad A il contenuto della chiave V

SubKeyCount : il numer o delle sottochiavi

SubValueCount : il numer o dei valor i

Con il codice pr oposto nel pr ossimo esempio è possibile r isalir e all'applicazione usata per apr ir e un cer to for mato di

file:

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.32.

33.34.35.36.37.38.39.40.41.42.43.

Imports Microsoft.Win32 Module Module1

Sub Main()Dim RegKey As RegistryKeyDim Extension As String

Console.Write("Inserire un'estesione valida: ")Extension = Console.ReadLine()

RegKey = Registry.ClassesRoot.OpenSubKey(Extension)If RegKey Is Nothing Then

Console.WriteLine("Questa estensione non è associata a nessuna applicazione!")Console.ReadKey()Exit Sub

End If

Dim AppKey As String = RegKey.GetValue("")

RegKey = Registry.ClassesRoot.OpenSubKey(AppKey)

If RegKey Is Nothing ThenConsole.WriteLine("Non è possibile risalire all'applicazione. Dati mancanti.")Console.ReadKey()Exit Sub

End If

RegKey = RegKey.OpenSubKey("shell\open\command")

If RegKey Is Nothing ThenConsole.WriteLine("Non è possibile aprire il file direttamente con

un'applicazione.")Console.ReadKey()Exit Sub

End If

Console.WriteLine("Applicazione usata:")Console.WriteLine(RegKey.GetValue(""))

Console.ReadKey()

End Sub End Module

Page 402: Guida visual basic

E3. Lavorare con i processi

Pr emessa: Se non vi r icor date, o se non sapete, cos'è un pr ocesso, vi r imando alla pr ima lezione sulla Reflection.

La c lasse ProcessLa classe che contiene tutte le infor mazioni su un pr ocesso è Pr ocess. Ecco una lista dei suoi membr i più significiativi:

BasePr ior ity : r estituisce un numer o inter o che identifica la pr ior ità di un pr ocesso. I valor i che può assumer e

sono: 4 (solo per il ciclo Idle del sistema), 8 (pr ior ità nor male), 13 (pr ior ità alta) e 24 (molto alta, usata per le

applicazioni in tempo r eale). La pr ior ità di ciuascun pr ocesso influenza la quantità di tempo macchina che il

pr ocessor e gli concede

Close() : chiude il pr ocesso e r ilascia le r isor se ad esso associate

CloseMainWindow() : chiude il pr ocesso comunicando alla sua finestr a pr incipale di chiuder si. Funziona solo se

tale pr ocesso dispone di un'inter faccia gr afica qualsiasi

EnableRaisingEvent : deter mina se l'oggetto Pr ocess debba gener ar e un evento quando viene chiuso. In questo

modo si può monitor ar e un pr ocesso e visualizzar e infor mazioni e messaggi alla sua chiusur a

Ex itCode : r estituisce il codice di chiusur a del pr ocesso (questa infor mazione, ad esempio, può esser e chiamata

quando il pr ocesso gener a l'evento Ex ited per saper e se ha dato buoni r isultati oppur e no). Si può pensar e al

pr ocesso anche come a una funzione: il sistema oper ativo gli passa degli ar gomenti (da linea di comando) e il

pr ocesso r estituisce un codice inter o che deter mina il r isultato delle sue oper azioni. Questo codice viene

deter minato dallo stesso pr ogr ammator e che ha scr itto il pr ogr amma, ed gener almente è 0 quando il pr ocesso

ha dato esiti positivi (chi sa il C conosce bene il r etur n 0 finale di Main). Se si tenta di acceder e a questa

pr opr ietà pr ima della chiusur a, ver r à gener ato un er r or e

Ex itTime : r estituisce la data e l'or a esatta della chiusur a del pr ocesso. Anche questa pr opr ietà è molto utile per

monitor ar e i pr ocessi

Handle : r estituisce l'indir izzo di memor ia del pr ocesso, sottofor ma di puntator e a inter o (IntPtr )

HandleCount : ottiene il numer o di handles aper ti dal pr ocesso (questo numer o potr ebbe anche cor r isponder e al

numer o di finestr e aper te, dato che ogni finestr a ha un pr opr io handle

HasEx ited : r estituisce Tr ue se il pr ocesso è stato chiuso, altr imenti False

Get... : analizzer ò le funzioni che iniziano per "Get" a par te

Id : ottiene l'identificativo univoco del pr ocesso, che non è altr o che un Int32. Questo numer o può esser e utile

nell'uso di altr e funzioni, come ad esempio GetPr ocessesById

Kill() : fer ma immediatamente il pr ocesso. È più br utale di Close

MachineName : nome della macchina sulla quale il pr ocesso è in esecuzione. Gener almente r estituisce il nome del

computer stesso, ma può cambiar e ad esempio se si usa una macchina vir tuale

MainModule : r estituisce un oggetto System.Diagnostics.Pr ocessModule che identifica il modulo pr incipale del

pr ocesso. Con modulo si intende l'assembly dal quale il pr ogr amma è stato fatto par tir e. I membr i che questo

oggetto espone sono:

BaseAddr ess : r estituisce l'indir izzo di memor ia (IntPtr ) all'inizio del quale il modulo è stato car icato. Ad

esempio, in un'applicazione console, questa pr opr ietà r estituisce l'indir izzo di memor ia di Module1

Entr yPointAddr ess : r estituisce l'indir izzo di memor ia in cui è collocato l'entr y point, ossia il metodo

pr incipale, da cui il pr ogr amma è stato avviato. Ad esempio, in un'applicazione console, questa pr opr ietà

r estituisce l'indir izzo della pr ocedur a Main

Page 403: Guida visual basic

FileName : r estituisce il nome del file dal quale è stato car icato il modulo, ossia il per cor so su disco del

pr ogr amma

FileVer sionInfo : r estituisce una cater va di infor mazioni sulla ver sione dell'eseguibile. Non sto neanche ad

analizzar e tutti i suoi membr i

ModuleMemor ySize : r estituisce la quantità di memor ia, in bytes, che il modulo occupa

ModuleName : nome del modulo

MainWindowHandle : handle della finestr a pr incipale. Si tr atta dello stesso handle ottenuto con

EnumDesktopWindow s nel capitolo pr ecedente

MainWindowTitle : titolo della finestr a pr incipale. Si tr atta dello stesso titolo ottenuto con GetWindowTex t nel

capitolo pr ecedente

Modules : r estituisce una collezione di tutti i moduli car icati dal pr ocesso

Pr ior ityClass : come BasePr ior ity r estituisce la pr ior ità del pr ocesso, ma attr aver so un enumer ator e. Può

assumer e i seguenti valor i: Idle (ciclo Idle del sistema), BelowNor mal (bassa), Nor mal (nor male), AboveNor mal

(alta), High (altissima), RealTime (r appr esenta il maggior valor e di pr ior ità possibile: un pr ocesso con pr ior ità

RealTime ha quasi il 100% di tempo macchina)

Pr ivilegedPr ocessor Time : r estituisce un oggetto TimeSpan che indica quanto tempo il pr ocesso ha passato ad

eseguir e del codice nel sistema oper ativo

Pr ocessName : nome del pr ocesso

Responding : r estituisce False se il pr ocesso non r isponde ai comandi, altr imenti Tr ue

Star t / Star tInfo : analizzer ò questi due membr i in seguito

Thr eads : r estituisce una collezione di Pr ocessThr ead che r appr esentano tutti i thr ead aper ti dal pr ocesso. Ogni

oggetto della collezione espone i seguenti membr i:

BasePr ior ity : pr ior ità di base del thr ead, espr essa tr amite un numer o

Cur r entPr ior ity : pr ior ità cor r ente del thr ead. Questa pr opr ietà esiste poiché il thr ead può cambiar e la

sua pr ior ità

Id : r estituisce l'identificator e univoco del thr ead (simile a Pr ocess.Id)

IdealPr ocess : deter mina l'indice del pr ocessor e sul quale il thr ead ver r ebbe eseguito in manier a

ottimale. Vale solo per computer multipr ocessor e

Pr ior ityBoostEnabled : deter mina se il thr ead può r icever e un aumento di pr ior ità quanto la sua finestr a

r iceve il focus, ossia viene selezionata dall'utente

Pr ivilegedPr ocessor Time : r estituisce un oggetto TimeSpan che indica quanto tempo il thr ead ha passato

ad eseguir e del codice nel sistema oper ativo

Star tAddr ess : r estituisce l'indir izzo di membr ia del metodo pr incipale di questo thr ead

Star tTime : r estituisce la data e l'or a esatta in cui il thr ead è stato avviato

Thr eadState : stato del thr ead

TotalPr ocessor Time : r estituisce un oggetto TimeSpan che indica da quanto tempo il thr ead è attivo

User Pr ocessor Time : r estituisce un oggetto TimeSpan che indica quanto tempo il thr ead ha passato ad

eseguir e del codice all'inter no dell'applicazione

TotalPr ocessor Time : r estituisce un oggetto TimeSpan che indica da quanto tempo il pr ocesso è attivo

User Pr ocessor Time : r estituisce un oggetto TimeSpan che indica quanto tempo il pr ocesso ha passato ad

eseguir e del codice all'inter no dell'applicazione

Ottenere i processi in esecuzioneLa classe pr ocess espone anche quattr o funzioni statiche che per mettono di ottener e un ar r ay dei pr ocessi in

esecuzione, basandosi su deter minati par ametr i. Ad esempio, Pr ocess.GetCur r entPr ocess r estituisce il pr ocesso

attualmente in esecuzione, e per ciò quello associato dir ettamente all'applicazione. Pr ocess.GetPr ocess ottiene invece un

Page 404: Guida visual basic

ar r ay contenente tutti i pr ocessi attivi sul computer . Invece, Pr ocess.GetPr ocessesByName("nome") r estituisce un

ar r ay di tutti i pr ocessi con dato nome, dove tale nome non è alter o che il nome dell'eseguibile dal quale sono par titi,

ma senza l'estensione. Ad esempio Pr ocess.GetPr ocessesByName("ex plor er ") r estituisce un solo pr ocesso,

"ex plor er .ex e", che costituisce il pr ogr amma pr incipale dell'inter faccia di w indows. In ultimo,

Pr ocess.GetPr ocessById(id) ottiene un pr ocesso con dato id. Nell'esempio che segue, si chiede all'utente di immetter e il

nome di un pr ocesso e, se ne esiste un con quel nome, il pr ogr amma visualizza tutte le infor mazioni possibili su di

esso, usando le pr opr ietà spiegate nel par agr afo pr ecedente:

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.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.

Module Module1'Ottiene tutte le informazioni possibili su un moduloPublic Sub ScanModule(ByVal M As ProcessModule)

Console.WriteLine(" Nome modulo: {0}", M.ModuleName)Console.WriteLine(" Handle: {0:X8}", M.BaseAddress.ToInt32)Console.WriteLine(" Handle del metodo principale: {0:X8}", _

M.EntryPointAddress.ToInt32)Console.WriteLine(" Memoria occupata: {0:N0} bytes", _

M.ModuleMemorySize)Console.WriteLine(" Informazioni versione del programma:")With M.FileVersionInfo

Console.WriteLine(" Nome file: {0}", .FileName)Console.WriteLine(" Versione file: {0}", .FileVersion)Console.WriteLine(" Descrizione file: {0}", _

.FileDescription)Console.WriteLine(" Versione prodotto: {0}", _

.ProductVersion)Dim Rel As String = "Nessuna"If .IsDebug Then

Rel = "Debug"ElseIf .IsPatched Then

Rel = "Patch"ElseIf .IsPreRelease Then

Rel = "Beta"ElseIf .IsPrivateBuild Then

Rel = "Release privata"ElseIf .IsSpecialBuild Then

Rel = "Release speciale"End IfConsole.WriteLine(" Caratteristiche: {0}", Rel)Console.WriteLine(" Copyright: {0}", .LegalCopyright)Console.WriteLine(" Trademark: {0}", .LegalTrademarks)Console.WriteLine(" Commenti: {0}", .Comments)Console.WriteLine(" Nome compagnia: {0}", .CompanyName)

End WithEnd Sub

'Formatta un valore timespanPublic Function FormatTime(ByVal T As TimeSpan) As String

Return String.Format("{0}h {1}m {2}s", T.Hours, T.Minutes, T.Seconds)End Function

'Ottiene tutte le informazioni possibili su un processoPublic Sub ScanProcess(ByVal P As Process)

Console.WriteLine("Nome processo: " & P.ProcessName)Console.WriteLine(" Handle: {0:X8}", P.Handle.ToInt32)Console.WriteLine(" Handles usati: {0}", P.HandleCount)Console.WriteLine(" Id: {0}", P.Id)Console.WriteLine(" Nome macchina: {0}", P.MachineName)Console.WriteLine(" Moduli:")For Each M As ProcessModule In P.Modules

Console.WriteLine()ScanModule(M)

NextConsole.WriteLine()Console.WriteLine(" Handle finestra principale: {0:X8}", _

P.MainWindowHandle.ToInt32)Console.WriteLine(" Titolo finestra principale: {0}", _

P.MainWindowTitle)Console.WriteLine(" Threads: {0}", P.Threads.Count)Console.WriteLine(" Tempo di esecuzione su OS: {0}", _

Page 405: Guida visual basic

Avviare nuovi processiPer avviar e un nuovo pr ocesso, è possibile sceglier e due str ade diver se. La pr ima compor ta l'uso di un nuovo oggetto

Pr ocess e della sua pr opr ietà Star tInfo, mentr e la seconda usa solamente il metodo statico Pr ocess.Star t.

Cominciamo a descr iver e la pr ima. Dopo aver inizializzato un nuovo oggetto Pr ocess:

Si accede alla pr opr ietà Star tInfo e tr amite questa si specificano tutte le infor mazioni necessar ie all'avvio. I membr i di

Star tInfo sono:

Ar guments : deter mina gli ar gomenti passati a linea di comando. È una semplice str inga. Per saper ne di più sui

par ametr i passati da linea di comando, veder e il tutor ial associato nella sezione Appunti

Cr eateNoWindow : deter mina se il pr ocesso debba esser e avviato in una nuova finestr a

Envir onmentVar iables : è una collezione di str inghe che r appr esenta tutte le var iabili d'ambiente. Queste ultime

sono speciali tipi di var iabili globali, che non vengono definite dall'applicazione ma dal sistema oper ativo (o dal

pr ogr amma che r ichiama l'applicazione stessa) e possono esser e utilizzate in qualsiasi punto del codice. È

possibile ottener e una var iabile d'ambiente pr ecedentemente impostata con la funzione

Envir onment.GetEnvir onmentVar iable("nome var iabile"). Di queste ne esistono alcune pr edefinite, che sono

valide per ogni pr ocesso avviato sul computer : per saper le, possiamo usar e questo br eve codice:

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.

FormatTime(P.PrivilegedProcessorTime))Console.WriteLine(" Tempo di esecuzione user: {0}", _

FormatTime(P.UserProcessorTime))Console.WriteLine(" Tempo di esecuzione totale: {0}", _

FormatTime(P.TotalProcessorTime))End Sub

Sub Main()

Dim Name As StringDim Processes() As Process

'Fa inserire il nome del processoConsole.WriteLine("Inserire il nome di un processo:")Name = Console.ReadLine

'Inizializza la collezioneProcesses = Process.GetProcessesByName(Name)'Se l'array è vuoto, non c'è nessun processo'aperto con quel nomeIf Processes.Length = 0 Then

Console.WriteLine("Non esiste alcun processo con questo nome!")Else

'Altrimenti enumera tutti i processi apertiFor Each P As Process In Processes

Console.WriteLine()ScanProcess(P)

NextEnd If

Console.ReadKey()

End SubEnd Module

1. Dim P As New Process

01.02.03.04.05.06.07.08.09.10.

Module Module1Sub Main()

Console.WriteLine("Variabili d'ambiente:")Console.WriteLine()

For Each Name As String In Environment.GetEnvironmentVariables.Keys

Console.WriteLine("{0} = {1}", Name, _Environment.GetEnvironmentVariable(Name))

Next

Page 406: Guida visual basic

Sul mio computer , questo è il r isultato:

P.S.: io non mi chiamo Nicola, eh: quando il tecnico ha fatto backup e for mattazione ha capito male il mio nome,

e non mi sono ancor a pr eso la br iga di cambiar e le impostazioni

FileName : nome del file da avviar e. Se si tr atta di un eseguibile, ver r à avviato il pr ogr amma r elativo. Se si

tr atta, invece, di un qualsiasi altr o tipo di file, questo ver r à aper to con il pr ogr amma associato, se esiste (ad

esempio, un'immagine sar à aper ta con Paint). Se l'estensione del file non è associata a nessun applicativo, ver r à

r estituito un er r or e

User Name / Passwor d : se il pr ocesso deve esser e avviato in un cer to account, potr ebber o esser e r ichieste

delle cr edenziali. Passwor d è di tipo Secur eStr ing (vedi capitolo "Lavor ar e con le str inghe")

Ver b : se il file non è eseguibile, questa str inga deter mina quale azione si debba usar e per apr ir lo. Ad esempio,

se FileName è un file *.tx t, si potr ebbe usar e "pr int" in questa pr opr ietà per stampar lo. Le azioni disponibili

per un cer to tipo di file var iano sulla base dei pr ogr ammi associati e sono r eper ibili nel r egistr o di sistema

WindowStyle : deter mina come visualizzar e la finestr a aper ta dal pr ocesso, se Massimizzata, Minimizzata,

Nor male con focus o Nor male senza focus

Wor kingDir ector y : deter mina la dir ector y di lavor o del pr ocesso

Dopo aver impostato le adeguate pr opr ietà, si r ichiama semplicemente Star t:

Questo codice equivale ad apr ir e il pr ompt dei comandi di w indows e scr iver e:

11.12.13.

Console.ReadKey()

End SubEnd Module

1.2.3.4.5.6.7.8.

Dim P As New ProcessWith P.StartInfo

.FileName = "C:\programma.exe"

.Arguments = "-t"

.WorkingDirectory = "C:\cartella"

.WindowStyle = ProcessWindowStyle.MaximizedEnd WithP.Start()

1.2.

CD "C:\cartella"

Page 407: Guida visual basic

La seconda possibilità consiste nell'usar e la ver sione statica di Star t:

Essa accetta anche altr i par ametr i, che per mettono di impostar e i dati come si far ebbe con Star tInfo.

C:\programma.exe -t

1. Process.Start("nome file")

Page 408: Guida visual basic

E4. Multithreading - Parte I

I thr ead sono le ver e unità dinamiche di esecuzione: il computer assegna, infatti, il tempo macchina (noto anche con il

nome di tempo di CPU o timeslice) a ogni singolo thr ead per volta anzichè a un inter o pr ocesso. Ognuno di essi è in

gr ado di eseguir e un codice pr opr io indipendentemente dagli altr i, la cui esecuzione appar e all'occhio dell'utente

simultanea. La macchina, infatti, passa così velocemente da un thr ead all'altr o che i sensi umani non r iescono a

distinguer li. Il par ticolar e tipo di meccanismo usato su Window s è detto multitasking preem ptive, che consente la

sospensione di un thr ead in qualsiasi momento: in ver sioni pr ecedenti del sistema oper ativo, er a invece necessar io

r ichieder ne esplicitamente la chiusur a (e ciò può ben far intuir e come il cr ash di un solo thr ead causasse la sospesione

dell'inter o sistema).

Ciascun thr ead conser va una pr opr ia autonomia, pr opr ie var iabili, pr opr i gestor i d'eccezioni, ecceter a... È possibile

anche assegnar vi una diver sa pr ior ita', a seconda di quanto sia impor tante il compito che esso svolge: thr ead con

pr ior ità più alta godr anno di un timeslice maggior e e quindi di maggior tempo e spazio per completar e le pr opr ie

oper azioni. Inoltr e, dato che tutti i thr ead consumano memor ia e r ichiedono un cer to tempo di CPU, maggior e è la

quantità di thr ead aper ti, maggior e sar à l'utilizzo di memor ia e il tempo impiegato. Per questo motivo, pr ima di

pr ogettar e un'applicazione che implementi questa car atter istica sar ebbe oppor tuno valutar e se non ci siano altr e

possibilità o alter native meno complesse. Di solito, il multitasking viene impiegato in oper azioni che r ichiedono un

lungo per iodo di esecuzione e che impiegano r isor se complesse come file o connessioni. Poichè le r isor se possono esser e

condivise tr a più thr ead, è necessar io monitor ar ne l'uso e contr ollar e che non ci siano due o più tentativi di accesso

simultanei, il che potr ebbe condur r e a un loop e di conseguenza a un cr ash dell'applicazione. Ma or a veniamo alla

pr atica.

Uso dei ThreadTutti i metodi e i tipi utilizzati nel multithr eading vengono r aggr uppati sono un unico namespace di nome

System.Thr eading. L'oper azione più r udimentale che si possa eseguir e è Star t, che fa par tir e un nuovo thr ead con un

cer to metodo. Il costr uttor e accetta un delegate di tipo Thr eadStar t senza par ametr i: questo delegate punta al metodo

che dovr à esser e eseguito dal thr ead. Un thr ead ter mina quando ha finito il pr opr io compito, quando viene r ichiamato

il metodo Stop oppur e quando viene abor tito da se stesso o da un'altr a par te del pr ogr amma con Abor t. Altr a

pr ocedur a molto comune è Sleep(X), che attende X millisecondi pr ima di eseguir e altr o. Ecco un esempio:

01.02.03.04.05.06.07.08.09.10.11.12.13.14.15.16.17.18.19.20.21.

Module Module1'Il metodo da far eseguire al Thread:Sub WriteNumbers()

'Scrive 100 volte il numero 0 sullo schermoFor I As Byte = 1 To 100

Console.Write("0")'Aspetta 0.1 secondi prima di continuare. La classe'Thread espone anche metodi statici come questo, che'vengono eseguiti dal thread chiamante, in questo'caso quello che eseguirà questa proceduraThreading.Thread.Sleep(100)

NextEnd Sub

Sub Main()

'Un nuovo threadDim T As New Threading.Thread(AddressOf WriteNumbers)

'Fa partire il thread'Una volta avviato, il programma passa alle istruzioni

Page 409: Guida visual basic

Sullo scher mo appar e una sequenza gr osso modo r egolar e di 0 e 1: questi numer i vengono alter nati quasi

per fettamente, ma ci sono delle r ipetizioni ogni tanto. Questo mostr a come il thr ead pr incipale che esegue il ciclo degli

1 sia indipendente da quello secondar io che fa cor r er e il ciclo degli 0 (e vicever sa); il timeslice di ognuno viene

alter nato così che eseguano oper azioni quasi contempor anee, ma legger mente sfasate. I metodi del tipo di Star t, ossia

che por tano a ter mine una r outine in un thr ead separ ato, vengono detti asincroni: un esempio è il metodo

WebClient.DownloadFileAsync (scar ica un file da inter net), che si è già analizzato.

Or a sar ebbe quanto meno utile poter usar e i meccanismi impar ati in modo un pò più ver satile: bisogna tr ovar e il

modo di passar e degli ar gomenti a una pr ocedur a delegate del costr uttor e, poichè così facendo si acquisisce più

multifor mità e il codice è meno r igido. Per nostr a for tuna, il costr uttor e suppor ta un over load in cui l'unico par ametr o

deve esser e un delegate la cui signatur e accetta un ar gomento di tipo object. Mediante il tipo Object, infatti, è

possibile tr asmetter e quasliasi tipo di dato. Non è da consider ar e limitante il fatto dell'aver e oper azioni di

box ing/unbox ing: pr imo per chè non c'è altr o modo, secondo per chè, definendo nuove classi, è possibile passar e dati

anche complessi attr aver so un solo par ametr o. Ecco un esempio:

22.23.24.25.26.27.28.29.30.31.32.33.34.35.36.37.38.39.40.41.

'successive, poichè, come già detto, il thread è in grado'di gestirsi da soloT.Start()

'A prova di ciò, esegue questa routine nel thread'principale, ossia in Sub Main:For I As Byte = 1 To 100

Console.Write("1")Threading.Thread.Sleep(100)

Next 'Curiosità -'Se a Thread.Sleep viene passato il valore 0, il thread'associato cederà il proprio tempo di CPU al'thread successivo, mentre il valore -1 indica di attendere'all'infinito (o almento finchè non verrà abortito)

'Cosa appare alla fine?Console.ReadKey()

End SubEnd Module

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.

Module Module2'Il metodo da far eseguire al Thread:Sub WriteNumbers(ByVal Data As Object)

'Scrive Times volte il numero Number sullo schermoFor I As Byte = 1 To Data.Times

Console.Write(Data.Number)Threading.Thread.Sleep(100)

NextEnd Sub

Sub Main()

'Un nuovo threadDim T As New Threading.Thread(AddressOf WriteNumbers)

'Fa partire il thread'Questo overload si Start accetta un parametro Object, che,'prima dell'avvio verrà passato come argomento'della procedura delegate dichiarata nel costruttore,'in questo caso WriteNumbersT.Start(New ThreadData(8, 120))

For I As Byte = 1 To 100

Console.Write("1")Threading.Thread.Sleep(100)

Next

Console.ReadKey()End Sub

End Module

Page 410: Guida visual basic

È da r icor dar e che l'applicazione ter mina solo quando vengono por tati a ter mine tutti i suoi thr ead.

Un'altr a funzionalità dei thr ead è, come già accennato in pr ecedenza, la pr ocedur a Abor t. Essa è speciale in quanto

costituisce un modo "sicur o" (per quanto possa esser e sicur o ter minar e br utalmente un thr ead) per metter e fine

all'esecuzione di un thr ead. Tuttavia pr esenta alcune par ticolar ità: non fer ma immediatamente il codice in eseuzione,

ma attende finchè non si sia r aggiunto un safe point, un punto sicur o nel quale possa esser e lanciata senza pr oblemi

un'oper azione di Gar bage Collection (un esempio di safe point è lo statement Retur n o End all'inter no di un metodo).

Ogniqualvolta viene invocato Abor t, il thr ead da abor tir e lancia un'eccezione speciale, di tipo Thr eadAbor tEx ception,

che non può esser e inter cettata dall'applicazione; nonostante ciò, se tale thr ead è stato fatto par tir e all'inter o di un

blocco Tr y, il codice associato alla calusola Finally ver r à comunque eseguito. In occasioni eccezionali, esso potr ebbe

anche inter venir e per evitar e la pr opr ia "mor te".

Altr i metodi meno usati sono:

BeginCr iticalRegion : notifica al gestor e di thr ead che il codice sta per intr odur si in una oper azione di vitale

impor tanza per il pr ogr amma, nella quale un Abor t o anche una semplice eccezione potr ebber o compr ometter e

tutta l'applicazione. In questi casi l'abor t viene posticipato come sopr a descr itto

AllocateDataSlot : alloca una slot di memor ia per tutti i thr ead in modo da poter passar e facilmente dati tr a un

thr ead e l'altr o. Restituisce un oggetto LocalDataStor eSlot che contiene le infor mazioni necessar ie a r ichiamar e

le infor mazioni salvate, pur non esponendo alcun membr o

AllocateNamedDataSlot : come sopr a, ma assegna allo slot anche un nome

Cur r entCultur e : la cultur a del thr ead

Cur r entThr ead : il thr ead che è cor r entemente in esecuzione

EndCr iticalRegion : notifica al gestor e di thr ead che la zona a r ischio elevato è ter minata

Fr eeNamedDataSlot : liber a la memor ia associata a uno slot nominale, il cui nome viene passato come pr imo

ar gomento del metodo

GetData(D) : ottiene i dati associati allo slot di memor ia D

GetDomain : r estituisce un oggetto AppDomain che r appr esenta il dominio applicativo nel quale viene eseguito il

thr ead

GetDomainID : r estituisce l'ID dell'AppDomain in cui viene eseguito il thr ead

GetNamedDataSlot(N) : passando il nome N, r estituisce l'oggetto LocalDataStor eSlot associato

IsAlive : deter mina se il thr ead è in esecuzione

IsBackgr ound : deter mina se il thr ead è in backgr ound, oppur e lo imposta come tale. Si dicono "in backgr ound"

thr ead con bassa pr ior ita'

Join : blocca il thr ead chiamante fino a quanto non ter mina il thr ead dal quale è stata invocata la pr ocedur a.

Bisogna far e attenzione a distinguer e bene i r uoli che inter cor r ono in questo meccanismo, poichè se un thr ead

r ichiama Join su se stesso, l'applicazione andr à in loop. Per questo motivo sono assolutamente da ev itare

istr uzioni come queste:

Mentr e codici simili a questo sono del tutto cor r etti:

I due over load del metodo pr evedono la possibilità di specificar e un timeout super ato il quale non è più

necessar io attender e oltr e: il pr imo accetta un par ametr o di tipo Int32 che r appr esenta il numer o di

1.2.3.

Thread.Join()'OppureThread.CurrentThread.Join

1.2.3.4.5.6.

Dim T As New Thread(AddressOf Something)T.Start()'...'Il thread chiamante (ossia quello principale) attende che'T termini. T è il bersaglio della chiamataT.Join()

Page 411: Guida visual basic

millisecondi di attesa massimo; il secondo r ichiede solo un par ametr o di tipo TimeSpan

ManagedThr eadID : r estituisce un identificativo univoco per il thr ead managed

Name : il nome (opzionale) del thr ead

Pr ior ity : pr opr ietà enumar ata che deter mina quale sia la pr ior ità del thr ead. Può assumer e cinque valor i:

Nor mal, BelowNor mal (infer ior e alla nor ma), AboveNor mal (super ior e alla nor ma), Highest (massimo) e Lowest

(minimo)

ResetAbor t : annulla l'Abor t di un thr ead

SetData(D, O) : imposta il contenuto dello slot di memor ia D sull'oggetto O

Sleep : già analizzato

SpinWait(N) : simile a Sleep, solo che N indica il numer o di iter azioni di attesa pr ima di continuar e. Ogni volta

che il thr ead pr osegue le sue oper azioni dopo aver r icevuto il timeslice oppor tuno dal gestor e dei thr ead,

l'indice di iter azioni aumenta di 1

Thr eadState : definisce lo stato del thr ead. La pr opr ietà enumer ata può assumer e questi valor i: Abor ted

(abor tito), Abor tRequested (è in cor so la r icezione della r ichiesta di abor to), Backgr ound (come IsBackgr ound),

Running (come IsAlive), Stopped (inter r otto: un thr ead in questo stato non può mai r ipr ender e), StopRequested

(si sta per inter r omper e il thr ead), Suspended (sospeso), SuspendRequested (si sta per sospeder e il thr ead),

Unstar ted (non ancor a avviato) e WaitSleepJoin (in attesa a causa del metodo Join o Wait)

Condivisione di datiDi default, tutti i possibili tipi di var iabili vengono condivisi tr a i thr ead in esecuzione. L'unica eccezione a questa

r egola è costituita dalle var iabili locali dinamiche, ossia quelle pr esenti all'inter no di metodi o pr opr ietà (compr esa

anche Sub Main): questo si ver ifica sempr e, anche nel caso in cui i thr ead siano in esecuzione all'inter no del suddetto

metodo.

Per quanto r iguar da le var iabili Shar ed, ogni thr ead condivide la stessa copia del valor e associato. Se si volesse far e in

modo di r ender e tali var iabili r elative al thr ead , ossia thread-relative, per cui il lor o valor e si conser vi solo

all'inter o di ciascuno, si dovr ebbe usar e l'attr ibuto Thr eadStatic:

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.

Module Module3Public Class StaticVar

'La variabile è accessibile da ogni membro e da ogni'istanza della classe, ma assume valori differenti a'seconda che sia eseguita in un thread piuttosto che'in un altro'Una dimostrazione? continuate a leggere<ThreadStatic()> _Public Shared Value As Int32

End Class

'Aumenta la variabileSub Test(ByVal Increment As Object)

'StaticVar.Value è statica, perciò ogni thread la dovrebbe'vedere con lo stesso valore, ma in questo caso ciò non'accade a causa dell'attribut ThreadStaticFor I As Byte = 1 To 10

Console.WriteLine("Thread {0} -> Value = {1}", _Thread.CurrentThread.ManagedThreadId, StaticVar.Value)Thread.Sleep(100)StaticVar.Value += CInt(Increment)

NextEnd Sub

Sub Main()

Dim T(2) As Thread

'Inizia 3 thread diversi con un diverso incrementoFor I As Byte = 0 To 2

Page 412: Guida visual basic

Nell'output si vedr à che il thr ead con ID minor e visualizzer à tutti i numer i da 0 a 9, quello con ID inter medio solo i par i

da 0 a 18, mentr e quello con ID massimo solo i multipli di 3 da 0 a 27.

Altr a cir costanza possibile è quella in cui il computer su cui gir a l'applicazione sia multipr ocessor e. In questo caso, i

r egistr i CPU non sono condivisi e ogni r egistr o associato a un diver so pr ocessor e mantiene una pr opr ia autonomia: i

valor i delle var iabili, per ciò, se sono modificati da un thr ead che lavor a su un dato pr ocessor e non vengono letti da uno

che sia in esecuzione su un pr ocessor e differ ente. Per evitar e er r or i di sor ta, il .Net Fr amewor k mette a disposizione

i metodi VolatileWr ite e VolatileRead che per mettono di scr iver e valor i di var iabili in un r egistr o condiviso; inoltr e

Memor yBar r ier aggior na tutti i r egistr i al valor e più r ecente.

31.32.33.34.35.36.

T(I) = New Thread(AddressOf Test)T(I).Start(I + 1)

Next

Console.ReadKey()End Sub

End Module

Page 413: Guida visual basic

E5. Multithreading - Parte II

Avendo a che far e con i thr ead, diventa difficoltoso sincr onizzar e l'accesso alle r isor se. Mi spiego meglio. Si ponga di

aver e questo codice:

Or a, per ipotesi Str è una var iabile condivisa fr a thr ead, così come anche I; sempr e per ipotesi, Str ha assunto il

valor e "Ciao" pr ima di entr ar e nel blocco If. Il thr ead A contr olla la var iabile e tr ova, giustamente, che Str è uguale a

"Ciao": nessun pr oblema, pr osegue all'inter no della str uttur a e incr ementa I di uno. Pr opr io dopo il ter mine di

quest'ultima oper azione, scade il suo timeslice, e il gestor e dei thr ead concede al thr ead B la sua par te di tempo

macchina. Quest'ultimo thr ead vede che Str è ancor a uguale alla costante str inga specificata dal pr ogr ammator e, in

quanto A si er a inter r otto subito pr ima di passar e all'istr uzione successiva, ossia Str = Nothing: per logica, a sua volta

incr ementa I di un'altr a unità e poi pr osegue nor malmente annullando Str . Al ter mine del blocco si ha che I è stato

incr ementato di due anzichè di uno. Pr oblemi del gener e sono in gener e r ar i, ma si possono comunque ver ificar e e la

pr obabilità di incontr ar li aumenta par allelamente all'impiego del meccanismo di thr eading. Per r isolver e er r or i come

questi si deve sincronizzare l'accesso alle r isor se e si fa uso dello statement SyncLock. Esso ha il compito di

r acchiuder e un'ar ea di codice in un blocco unico, in modo che il thr ead che lo sta eseguendo finisca tutte le oper azioni

ivi contenute senza esser e distur bato da altr i thr ead, i quali a lor o volta attender anno di poter vi acceder e. La sintassi

usata per dichiar ar e SyncLock è:

L'og g etto di lock può esser e un qualsiasi oggetto r efer ence non nullo condiviso tr a i thr ead (ad esempio una var iabile

di modulo o di classe o una var iabile statica a cui non sia stato applicato l'attr ibuto Thr eadStatic): una volta entr ati nel

blocco SyncLock, l'oggetto viene, per così dir e, "segnato", in modo che qualsiasi altr o thr ead che cer chi di acceder vi

sapr à che è attualmente in uso e attender à il pr opr io tur no. Non è impor tante quale sia l'oggetto di lock, nè lo è il suo

tipo: basta che soddisfi i r equisiti sopr a esposti. In una classe si può benissimo usar e Me al suo posto. Ad esempio:

Tuttavia, la sincr onizzazione mediata dal costr utto SyncLock è da utilizzar si solo se ver amente indispensabile, poichè

r acchiuder e tutti i campi o tutti i metodi in un blocco del gener e r ischia di r ender e il codice sia illeggibile sia più lento

e meno economico. Un'altr a par ticolar ità di SyncLock è che, dietr o le quinte, il compilator e lo implementa inser endovi

all'inter no uno statement Tr y, per evitar e di non poter r ilasciar e il lock qualor a si ver ifichino eccezioni, r agion per cui

1.2.3.4.

If Str = "Ciao" ThenI += 1Str = Nothing

End If

1.2.3.

SyncLock [Oggetto di lock]'Istruzioni sincronizzate

End SyncLock

01.02.03.04.05.06.07.08.09.10.11.12.13.14.

'Volendo riprendere l'esempio di prima:'LockObject è condivisa (campo di classe, in più shared), non nulla'(viene usato il costruttore New) e reference (ovviamente,'Object è reference)Private Shared LockObject As New Object() '... 'Questo blocco è ora correttamente sincronizzatoSyncLock LockObject

If Str = "Ciao" ThenI += 1

End IfEnd SyncLock

Page 414: Guida visual basic

non si può saltar vi all'inter no con l'utilizzo di GoTo (vedi capitolo r elativo).

Altri metodi di sincronizzazioneSe un inter o oggetto viene esposto alla possibilità di poter venir e manipolato da più thr ead contempor aneamente,

sar ebbe utile applicar vi un attr ibuto speciale che sincr onizza automaticamente l'accesso a tutti i membr i d'istanza: tale

attr ibuto si chiama Synchr onization, non espone alcun costr uttor e usato fr equentemente e appar tiene al namespace

System.Runtime.Remoting.Contex ts:

Come alter nativa a SyncLock, esiste l'oggetto Monitor , che espone metodi statici per la sincr onizzazione. Enter accetta

un ar gomento, che costituisce l'oggetto di lock, e incr ementa il contator e di lock di 1, cosicchè gli altr i thr ead che

tentino di acceder e al codice successivo a Enter debbano attender e (esattamente come accade con SyncLock). Ex it esce

dal blocco sincr onizzato, mentr e Tr yEnter cer ca di entr ar e e r estituisce False se non è possibile acceder e al blocco

monitor ato entr o un timeout specificato come pr imo ar gomento. Dato che è essenziale r ilasciar e sempr e il lock, se il

sor gente ha la possibilità di lanciar e un'eccezione, bisogna necessar iamente usar e un costr utto Tr y nella cui clausola

Finally si r ichiama Ex it. Ad esempio:

Il tipo Mutex , invece, è più ver satile: intanto può esser e istanziato, e inoltr e espone metodi d'istanza in gr ado di

gestir e più lock contempor aneamente. Eccone un elenco:

WaitOne : attende di poter entr ar e nella sezione sincr onizzata e, una volta entr ato, ottiene il lock per il thr ead

cor r ente

WaitAny(M()) : accetta un ar r ay di Mutex M() e attende di poter acquisir e il lock di almeno uno di essi. Questo

metodo può esser e usato ad esempio quando si dispone di un numer o limitato di r isor se (file, connessioni,

database...) , ognuna manipolata da un thr ead differ ente

WaitAll(M()) : accetta un ar r ay di Mutex M() e attende di poter acquisir e il lock di tutti. Questo metodo può

esser e usato ad esempio quando si devono compier e più oper azioni contempor aneamente e aspettar e che tutte

siano state por tate a ter mine

ReleaseMutex : r ilascia il lock

SignalAndWait(M1, M2) : tenta di acquisir e il lock di M1 e, una volta acquisito, aspetta che anche M2 venga

lockato

È possibile r ichiamar e WaitOne più volte, a patto che si r ichiamai lo stesso numer o di volte ReleaseMutex .

1.2.3.4.5.6.

<System.Runtime.Remoting.Contexts.Synchronization()> _Public Class AnObject

Inherits ContextBoundObject'Altro dettaglio: l'oggetto deve ereditare da ContextBoundObject'...

End Class

01.02.03.04.05.06.07.08.09.10.11.12.

Private Shared LockObject As New Object()'...Try

'Entra nel codice sincronizzatoMonitor.Enter(LockObject)'...

Catch Ex As Exception'Cattura eccezioni se ce ne sono

Finally'Ma rilascia sempre il lockMonitor.Exit()

End Try

Page 415: Guida visual basic

Il tipo Semaphor e, invece, contr olla che un deter minato numer o di thr ead possa eseguir e un dato blocco di codice

sincr onizzato. Il suo costr uttor e accetta come pr imo par ametr o un inter o che indica il valor e di default dei thr ead che

lo stanno eseguendo e come secondo par ametr o il conteggio massimo. Al suo inter no, ogni volta che un thr ead ottiene il

lock della sezione contr ollata, il contator e viene decrementato di 1, fino al r aggiungimento del valor e di default; ogni

volta che si r ilascia il lock, esso viene incrementato di 1, fino al r aggiungimaneto del valor e massimo. WaitOne()

ser ve per acquisir e il lock e Release per r ilasciar lo.

N.B.: Tutti i tipi fin'or a esposti (Monitor , Mutex e Semaphor e) devono sempr e esser e inclusi in un blocco Tr y, per

assicur ar si che anche se si ver ificasser o delle eccezioni, il lock venga comunque r ilasciato.

Delegate asincroniAltr a car atter istica che r ende ancor più ver satili i delegate è costituita dalla possibilità di invocar e metodi asincor ni.

In questi casi, il metodo puntato dal delegate viene eseguito in un thr ead differ ente, senza quindi bloccar e il nor male

cor so di istr uzioni del pr ogr amma, come d'altr onde, sono solite far e tutte le dir ettive asincr one. Il pr imo passo da

effettuar e per cr ear e una pr ocedur a del gener e è, ovviamente, dichiar ar e il delegate cor r ispondente, ad esempio:

Il compilator e cr ea automaticamente due metodi speciali per ogni nuovo delegate dichiar ato dal pr ogr ammator e: essi

sono BeginInvoke ed EndInvoke. Il pr imo accetta come ar gomenti gli stessi definiti nella signatur e del delegate (in

questo caso Dir As Str ing e Patter n As Str ing); inoltr e, la lista dei par ametr i pr osegue con altr i due slot che spiegher ò

in seguito e che per or a imposter ò semplicemente a Nothing. Bisogna poi specificar e che è una funzione, quindi

r estituisce un valor e: tale valor e non è il r isultato dell'oper azione, ma un oggetto di tipo IAsyncResult (ossia che

implementa l'inter faccia IAsyncResult) che ser ve a for nir e infor mazioni sul pr ogr esso del metodo. Tr a le sue quattr o

pr opr ietà, una in par ticolar e, IsCompleted, deter mina quando il thr ead che esegue l'oper azione ha por tato a ter mine il

suo compito. Il secondo accetta semplicemente lo stesso oggetto IAsyncResult ottenuto in pr ecedenza e, una volta sicur i

di aver ter minato il tutto, r estituisce il ver o r isultato della funzione (se c'e'). Ecco un esempio:

1.2.3.4.5.

'Questo delegate accetta i parametri adatti a svolgere una ricerca'di files in più sottodirectory, esempio già citato in molte'lezioni precedentiPublic Delegate Function GetFileRecursive(ByVal Dir As String, _

ByVal Pattern As String) As List(Of String)

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.

Module Module1Public Delegate Function GetFileRecursive(ByVal Dir As String, _

ByVal Pattern As String) As List(Of String)

Public Function FindFiles(ByVal Dir As String, _ByVal Pattern As String) As List(Of String)Dim Result As New List(Of String)

'Aggiunge in un solo colpo tutti i files trovati con'GetFilesResult.AddRange(IO.Directory.GetFiles(Dir, Pattern))'Analizza le altre directoryFor Each SubDir As String In IO.Directory.GetDirectories(Dir)

Result.AddRange(FindFiles(SubDir, Pattern))Next

Return Result

End Function

Sub Main()'Nuovo oggetto di tipo delegate GetFileRecursiveDim Find As New GetFileRecursive(AddressOf FindFiles)'Con questo oggetto, monitoreremo lo stato del metodo, per'sapere se è stata completato o se è ancora'in esecuzione.'Si cercano tutti i files *.dll in una cartella di sistemaDim AsyncRes As IAsyncResult = _

Find.BeginInvoke("C:\WINDOWS\system32", "*.dll", _

Page 416: Guida visual basic

All'inter o di IAsyncResult è definita anche un'altr a pr opr ietà, AsyncWaitHandle, che r estituisce un oggetto WaitHandle:

dato che da questo der iva Mutex , lo si può tr attar e come un comunissimo Mutex , appunto, usando i metodi WaitOne,

WaitAny o WaitAll sopr a esposti.

Analizziamo or a il penultimo par ametr o di BeginInvoke. È un delegate di tipo System.AsyncCallback e costituisce il

metodo di callback . Questi tipi di metodi vengono automaticamente r ichiamati dal pr ogr amma alla fine delle

oper azioni nel thr ead separ ato: così facendo non si deve continuamente contr ollar ne il completamento con

IAsyncResult.IsCompleted. La sua signatur e deve r ispecchiar e quella di AsyncCallback, ossia deve accettar e un unico

par ametr o di tipo IAsyncResult. Ecco lo stesso esempio di pr ima r iscr itto usando questa tecnica:

30.31.32.33.34.35.36.37.38.39.40.41.42.43.44.45.46.47.48.49.50.51.52.

Nothing, Nothing)'Risultato della ricercaDim Files As List(Of String)

Console.WriteLine("Ricerca di tutti i files *.dll in System32")'Finchè non si è completato, scrive a schermo'"Ricerca in corso..."Do Until AsyncRes.IsCompleted

Console.WriteLine("Ricerca in corso...")Thread.Sleep(2000)

Loop

'Ottiene il risultatoFiles = Find.EndInvoke(AsyncRes)'Usa il metodo ForEach di Array per eseguire una stessa'operazione per ogni elemento di un array. Dato che'Files è una lista tipizzata, la converte in array'di stringhe, quindi richiama su ogni elemento il metodo'Console.WriteLine per scriverlo a schermoArray.ForEach(Files.ToArray, AddressOf Console.WriteLine)

Console.ReadKey()

End SubEnd Module

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.32.33.34.

Module Module1Public Delegate Function GetFileRecursive(ByVal Dir As String, _

ByVal Pattern As String) As List(Of String)

Public Function FindFiles(ByVal Dir As String, _ByVal Pattern As String) As List(Of String)Dim Result As New List(Of String)

Result.AddRange(IO.Directory.GetFiles(Dir, Pattern))For Each SubDir As String In IO.Directory.GetDirectories(Dir)

Result.AddRange(FindFiles(SubDir, Pattern))Next

Return Result

End Function

Public Sub DisplayFiles(ByVal AsyncRes As IAsyncResult)Dim Files As List(Of String) = Find.EndInvoke(AsyncRes)Array.ForEach(Files.ToArray, AddressOf Console.WriteLine)

End Sub

'Nuovo oggetto di tipo delegate GetFileRecursive'Notare che è dichiarato come variabile globale di modulo per essere'accessibile anche alla procedura callbackDim Find As New GetFileRecursive(AddressOf FindFiles)

Sub Main()

'Il terzo argomento è l'indirizzo del metodo callbackDim AsyncRes As IAsyncResult = _

Find.BeginInvoke("C:\WINDOWS\system32", _"*.dll", AddressOf DisplayFiles, Nothing)

Console.WriteLine("Ricerca di tutti i files *.dll in System32")

Page 417: Guida visual basic

L'ultimo par ametr o specifica solamente delle infor mazioni aggiuntive r ichiamabili con IAsyncResult.AsyncState.

In gener ale, tutti i metodi che vengono r esi asincr oni, dispongono di due ver sioni, una che inizia per "Begin", l'altr a che

inizia per "End", con le stesse car atter istiche sopr a esposte. Anche i metodi BeginWr ite e EndWr ite di IO.FileStr eam

sono ottimi esempi di metodi asincr oni.

35.36.37.38.39.40.41.

Do Until AsyncRes.IsCompletedConsole.WriteLine("Ricerca in corso...")Thread.Sleep(2000)

Loop

Console.ReadKey()End Sub

End Module

Page 418: Guida visual basic

E6. BackgroundWorker e FileSystemWatcher

BackgroundWorkerDopo aver analizzato nel dettaglio la str uttur a e il funzionamento del sistema di thr eading, veniamo or a a veder e

alcuni contr olli che implementano questo meccanismo "dietr o le quinte". Il pr imo di questi è Backgr oundWor ker , un

contr ollo senza inter faccia gr afica, che nelle Windows Application r ende molto più facile l'utilizzo di thr ead separ ati per

compiti diver si: infatti for nisce metodi e pr opr ietà che fanno da w r apper a un thr ead separ ato. Ecco una lista dei

membr i più usati:

CancelAsync : annulla le oper azioni che il contr ollo sta svolgendo. Al par i di Thr ead.Abor t, l'azione non è

immediata e possono anche esser e eseguite istr uzioni che evitino l'abor to del thr ead

CancellationPending : deter mina se è stato r ichiesto l'annullamento delle oper azioni con CancelAsync

IsBusy : deter mina se il contr ollo è in fase di esecuzione

Repor tPr ogr ess(I) : se r ichiamato della pr ocedur a pr incipale del thr ead separ ato, gener a un evento

Pr ogr essChanged contenente infor mazioni sullo stato dell'oper azione. I è la per centuale di completamento del

lavor o

RunWor ker Async : dà inizio alle oper azioni tr amite il contr ollo Backgr oundWor ker . Il suo over load accetta un

solo par ametr o di tipo Object contenente i par ametr i che opzionalmente si devono passar e alla pr ocedur a

pr incipale del thr ead separ ato

Wor ker Repor tPr ogr ess : deter mina se il contr ollo possa gener ar e eventi Pr ogr essChanged

Wor ker Suppor tCancellation : deter mina se il contr ollo suppor ta la cancellazione delle oper azioni

Or a, il Backgr oundWor ker lavor a come descr itto di seguito. La pr ocedur a pr incipale che deve esser e eseguita nel

thr ead separ ato va posta in un evento speciale del contr ollo, chiamato DoWor k: attr aver so il par ametr o "e" è possibile

anche ottener e altr i dati necessar i alle oper azioni da svolger e. Una volta che tutto il codice in DoWor k è stato

completato, viene lanciato l'evento RunWor ker Completed. Tale evento viene comunque gener ato anche nel caso in cui il

sor gente abbia dato esito negativo (ad esempio a causa del ver ificar si di eccezioni gestite e non), oppur e si sia

r ichiamata la pr ocedur a CancelAsync. Sempr e all'inter no di DoWor k si può usar e il metodo Repor tPr ogr ess per

comunicar e all'applicazione pr incipale un avanzamento del livello di completamento del lavor o.

Chi ha familiar ità con i thr ead, sapr à che se si tenta di acceder e a qualsiasi contr ollo dell'applicazione pr incipale da un

thr ead separ ato, viene gener ata un'eccezione di tipo Cr ossThr eadEx ception. Anche sotto questo punto di vista

Backgr oundWor ker for nisce un aiuto non di poco conto poichè i suoi eventi sono pr edisposti in modo tale da evitar e

er r or i di questo tipo. Infatti DoWor k viene effettivamente eseguito in un diver so contesto, ma gli eventi sono pr odotti

nel thr ead pr incipale, in modo da poter acceder e a qualsiasi contr ollo senza pr oblemi. Ecco un esempio commentato.

L'inter faccia dovr ebbe pr esentar si come quella che segue. I nomi sono facilmente intuibili dal sor gente. Bisogna invece

aggiunger e, ovviamente, il contr ollo Backgr oundWor ker (che non ha inter faccia), con Wor ker Repor tPr ogr ess = Tr ue e

Wor ker Suppor tCancellation = Tr ue. Io l'ho chiamato bgwScan.

Page 419: Guida visual basic

E il codice:

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.

Imports System.ComponentModel'In System.ComponentModelClass Form1

Private Sub txtDir_Click(ByVal sender As Object, _ByVal e As EventArgs) Handles txtDir.ClickDim Open As New FolderBrowserDialogOpen.Description = "Scegliere la cartella da analizzare:"If Open.ShowDialog = Windows.Forms.DialogResult.OK Then

txtDir.Text = Open.SelectedPathEnd If

End Sub

Private Sub cmdAnalyze_Click(ByVal sender As Object, _ByVal e As EventArgs) Handles cmdAnalyze.ClickIf cmdAnalyze.Text = "Analizza" Then

'Controlla che la cartella esistaIf Not IO.Directory.Exists(txtDir.Text) Then

MessageBox.Show("Cartella inesistente!", "Sizing", _MessageBoxButtons.OK, MessageBoxIcon.Exclamation)

End If

'Fa partire il BackgroundWorker, passandogli come unico'argomento il percorso della cartella da analizzarebgwScan.RunWorkerAsync(txtDir.Text)'Modifica il testo del pulsante, così da potergli'assegnare anche un altro compitocmdAnalyze.Text = "Ferma"

Else'Il testo non è "Analizza". Deve per forza essere'"Ferma", quindi termina l'operazione forzatamentebgwScan.CancelAsync()cmdAnalyze.Text = "Analizza"

End IfEnd Sub

Private Sub bgwScan_DoWork(ByVal sender As Object, _

ByVal e As DoWorkEventArgs) Handles bgwScan.DoWork'Ottiene tutti i files presenti nella cartella:'- e.Argument ottiene lo stesso valore passato a' RunWorkerAsync: in questo caso contiene una stringa'- il pattern *.* specifica di cercare files di ogni' estensione'- l'ultimo argomento comunica di eseguire una ricerca' ricorsiva analizzando anche tutte le sottocartelleDim Files() As String = _

IO.Directory.GetFiles(e.Argument, "*.*", _IO.SearchOption.AllDirectories)

'Dimensione totaleDim Size As Double = 0'Files analizzatiDim Index As Int32 = 0

'Calcola la dimensiona totale della cartella sommando tutte'le dimensioni parzialiFor Each File As String In Files

'FileLen è una funzione di VB6, ma il VB.NET'implicherebbe di creare un nuovo oggetto FileInfo e'quindi richiamarne la proprietà Length. In'questo modo è molto più comodo, anche'se non proprio conforme alle direttive .NETSize += FileLen(File)

Page 420: Guida visual basic

FileSystemWatcherL'oggetto FileSystemWatcher ser ve per monitor ar e files o car telle in modo da saper e in tempo r eale quando vengono

modificati, cancellati, aper ti o spostati, ed eseguir e azioni di conseguenza. Si potr ebbe, ad esempio, contr ollar e un file

speciale e visualizzar e un messaggio di war ning se l'utente cer ca di apr ir lo, o chieder e una passw or d, oppur e

addir ittur a spegner e il computer (!!). Questo contr ollo, come si pu&ogr av; intuir e, non ha inter faccia gr afica. Le sue

pr opr ietà inter essanti sono:

EnableRisingEvent : deter mina se il contr ollo gener i gli eventi; dato che il suo utilizzo è basato su questi,

impostar e a Tr ue False tale valor e equivale ad "accender e" o "spegner e" il contr ollo

Filter : specifica il filtr o di monitor aggio e si stanno contr ollando dei files. Non è altr o che l'estensione dei files

IncludeSubdir ector ies : deter mina se includer e nel monitor aggio anche le sottocar telle

NotifyFilter : pr opr ietà enumer ata codificata a bit che descr ive cosa monitor ar e. Può assumer e questi valor i:

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.

Index += 1'Riporta la percentuale e genera un evento'ProgressChangedbgwScan.ReportProgress(Index * 100 / Files.Length)'Controlla se ci sono richieste di cancellazione.'Se ce ne sono, termina qui la proceduraIf bgwScan.CancellationPending Then

e.Cancel = TrueExit Sub

End IfNext

'Il valore Result di e rappresenta il valore da restituire.'In questo caso è come se DoWork fosse una funzione.'Dato che si può passare solo un valore Object,'mettiamo in quel valore un array di Double contenente'il numero di files trovati e la loro dimensione complessivae.Result = New Double() {Files.Length, Size}

End Sub

Private Sub bgwScan_ProgressChanged(ByVal sender As Object, _ByVal e As ProgressChangedEventArgs) Handles bgwScan.ProgressChanged'Visualizza la percentuale sulla barraprgProgress.Value = e.ProgressPercentage

End Sub

Private Sub bgwScan_RunWorkerCompleted(ByVal sender As Object, _ByVal e As RunWorkerCompletedEventArgs) Handles bgwScan.RunWorkerCompleted'Controlla la causa che ha scatenato questo evento

If e.Cancelled Then

'Una cancellazione?MessageBox.Show("Operazione annullata!", "Sizing", _MessageBoxButtons.OK, MessageBoxIcon.Exclamation)

ElseIf e.Error IsNot Nothing Then'Un'eccezione?MessageBox.Show("Si è verificato un errore!", "Sizing", _MessageBoxButtons.OK, MessageBoxIcon.Error)

Else'O semplicemente la fine delle operazioni'COnverte il risultato ancora in un array di DoubleDim Values() As Double = e.ResultlblInfo.Text = String.Format("Sono stati trovati {0} files." & _"{1}La dimensione totale della cartella è {2:N0} bytes.", _Values(0), vbCrLf, Values(1))

End If

'Per sicurezza, reimposta il testo del pulsantecmdAnalyze.Text = "Analizza"

End SubEnd Class

Page 421: Guida visual basic

Dir ector yName (cambiamenti nel nome della/delle car tella/e), FileName (cambiamenti nel nome dei files),

Attr ibutes (cambiamenti degli attr ibuti di un file), Size (dimensione di file o car telle), LastAccess (ultimo

accesso), LastWr ite (ultima modifica), Cr eationTime (data di cr eazione) e Secur ity (par ametr i di sicur ezza). I

valor i possono esser e sommati con l'oper ator e su bit Or

Path : il per cor so del file/car tella da monitor ar e

Bisogna anche dir e, per ò, che nel 99% dei casi questo contr ollo fa cilecca... infatti non gener a nessun evento anche

quando dovr ebbe. Mister i del .NET Fr amew or k!

Page 422: Guida visual basic

E7. Il Platform Invoke

Il platfor m invoke è una tecnica che per mette all'applicazione di usar e metodi definiti in libr er ie ester ne. Ovviamente,

queste libr er ie non sono scr itte in linguaggi .NET, altr imenti sar ebbe bastato impor tar le come r ifer imento nel

pr ogetto. L'utilità più dir etta che consegue dall'uso del platfor m invoke è la possibilità di inter agir e con l'Application

Pr ogr amming Inter face (API) di Windows, ossia l'insieme delle libr er ie di sistema. Facendo questo è possibile

inter venir e a basso livello nel sistema oper ativo e acceder e e manipolar e infor mazioni che non sar ebbe nor malmente

consentito conoscer e.

Estrarre un metodo dalle librerieUna cosa impor tante è il fatto che non si può utilizzar e tutta la libr er ia nel suo insieme, ma si può solo estr ar ne un

metodo alla volta - e nella maggior anza dei casi basta quello. Con i ter mini "estr ar r e un metodo" voglio dir e che nel

nostr o pr ogr amma dichiar iamo un metodo nor malmente (con nome, par ametr i, ecceter a...) ma non ne specifichiamo il

cor po: quando tale metodo ver r à r ichiamato, sar à invece usato il metodo scr itto nella libr er ia.

Per impor tar e un metodo da una dll di sistema si possono usar e due differ enti modalità. La pr ima - quella miglior e per

.NET - consiste nell'utilizzar e l'attr ibuto DllImpor t:

L'attr ibuto DllImpor t pr esenta anche moltissime altr e pr opr ietà, che per or a non ci ser vono. Se si usa l'attr ibuto

DllImpor t, il nome del metodo deve coincider e esattamente con il nome del metodo che si sta impor tando dalla libr er ia

(altr imenti il pr ogr amma non sapr ebbe quale sceglier e).

Il secondo modo è esattamente r ipr eso tale e quale dal Visual Basic 6:

In questo caso, non occor r e che il nome del metodo coincida con quello della libr er ia, per ché si può specificar e il ver o

nome nella clausola Alias. La keywor d Auto, invece, è opzionale e indica quale set di car atter ei usar e per il passaggio di

str inghe: in questo caso, si usa quello pr edefinito. Le due alter native sono Ansi (ascii) e Unicode. Ma questi sono solo

dettagli.

Nell'esempio che segue user ò l'attr ibuto DllImpor t, per chè è la scelta più cor r etto in ambito .NET. Questo semplice

pr ogr amma tr ova tutte le finestr e aper te sullo scher mo e ne comunica all'utente titolo e indir izzo di memor ia

(handle).

1.2.3.4.

<System.Runtime.InteropServices.DllImport("NomeLibreria.dll")> _Public Sub/Function [Nome]([Parametri]) End Sub/Function

1. Declare Auto Sub [Nome] Lib "NomeLibreria.dll" Alias "VeroNome" ([Parametri])

001.002.003.004.005.006.007.008.009.010.011.012.013.014.015.

Imports System.Runtime.InteropServices Module Module1

'Le funzioni che risiedono nelle librerie di sistema lavorano'a basso livello, come già detto, perciò i tipi'più frequentemente incontrati sono IntPtr (puntatore'intero) e Int32 (intero a 32 bit). Nonostante ciò, esse'possono anche richiedere tipi di dato molto più complessi,'come in questo caso. La funzione che useremo necessita di'un delegate come parametro.Public Delegate Function EnumCallback(ByVal Handle As IntPtr, _

ByVal lParam As Int32) As Boolean

'La funzione EnumDesktopWindows è definita nella libreria

Page 423: Guida visual basic

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.084.085.086.087.

'C:\WINDOWS\system\system32\user32.dll. Dato che si tratta di una'libreria di sistema, possiamo omettere il percorso e scrivere solo'il nome (provvisto di estensione). Come vedete, il nome con cui'è dichiarata è lo stesso dl metodo definito'in user32.dll. Per importarla correttamente, però, anche'i parametri usati devono essere identici, almeno per tipo.'Infatti, per identificare univocamente un metodo che potrebbe'essere provvisto di overloads, è necessario sapere solo'il nome del metodo e la quantità e il tipo di parametri.'Anche cambiando il nome a un parametro, la signature non'cambia, quindi mi sono preso la libertà di scrivere'dei parametri più "amichevoli", poiché la'dichiarazione originale - come è tipico del C -'prevede dei nomi assurdi e difficili da ricordare.<DllImport("user32.dll")> _Public Function EnumDesktopWindows(ByVal DesktopIndex As IntPtr, _

ByVal Callback As EnumCallback, ByVal lParam As Int32) As Boolean'Notare che il corpo non viene definito

End Function

'Questa funzione ha il compito di ottenere il titolo di'una finestra provvisto in input il suo indirizzo (handle).'Notare che non restituisce una stringa, ma un Int32.'Infatti, la maggiore parte dei metodi definiti nelle librerie'di sistema sono funzioni che restituiscono interi, ma questi'interi sono inutili. Anche se sono funzioni, quindi, le si'può trattare come banali procedure. In questo caso,'tuttavia, l'intero restituito ha uno scopo, ed equivale'alla lunghezza del titolo della finestra.'GetWindowText, dopo aver identificato la finestra,'ne deposita il titolo nello StringBuilder, che, essendo'un tipo reference, viene sempre passato per indirizzo.'Capacity indica invece il massimo numero di caratteri'accettabili.<DllImport("user32.dll")> _Public Function GetWindowText(ByVal Handle As IntPtr, _

ByVal Builder As StringBuilder, ByVal Capacity As Int32) As Int32End Function

Public Function GetWindowTitle(ByVal Handle As IntPtr)

'Crea un nuovo string builder, con una capacità'di 255 caratteriDim Builder As New System.Text.StringBuilder(255)'Richiama la funzione di sistema per ottenere il'titolo della finestraGetWindowText(Handle, Builder, Builder.Capacity)'Dopo la chiamata a GetWindowText, Builder conterrà'il titolo: lo restituisce alla funzioneReturn Builder.ToString

End Function

Public Function FoundWindow(ByVal Handle As IntPtr, _ByVal lParam As Int32) As Boolean'Ottiene il titolo della finestraDim Title As String = GetWindowTitle(Handle)'Scrive a schermo le informazioni sulla finestraConsole.WriteLine("Handle {0:X8} - Titolo: {1}", _

Handle.ToInt32, Title)'Restituisce sempre True: come già detto, i'risultati delle funzioni di sistema non sono sempre'utili, se non al sistema operativo stessoReturn True

End Function

'Enumera tutte le finestraPublic Sub EnumerateWindows()

'Inizializza il metodo callbackDim Callback As New EnumCallback(AddressOf FoundWindow)Dim Success As Boolean

'Richiama la funzione di sistema. IntPtr.Zero come primo'parametro indica che il desktop da considerare è

Page 424: Guida visual basic

Il meccanismo di fondo non è difficile. Quando si r ichiama la funzione EnumDesktopWindows, questa par te e cer ca tutte

le finestr e attive sul desktop: ogni volta che ne tr ova una r ichiama il delegate Callback passato come par ametr o,

passandogli l'indir izzo della finestr a e un ar gomento aggiuntivo che non c'inter essa. Il delegate, quindi, che nel nostr o

caso è FoundWindow , esegue le azioni necssar ie, ossia la stampa a video delle infor mazioni. Facendo cor r er e il

pr ogr amma dovr este veder e una lista assai numer osa, molto di più di quanto ci si sar ebbe aspettato: questo accade

per chè non vengono consider ate finestr e solo le w indows for ms, ma ci sono anche oggetti di sistema nascosti (che poi

non sono altr o che pr ocessi) ed altr i di natur a ingannevole (ad esempio, la bar r a delle applicazioni è essa stessa una

finestr a). Per r idur r e un po' il numer o di r isultati, si potr ebbe intr odur r e un contr ollo sul titolo, e impor r e che non sia

Nothing. Tuttavia, l'impor tante è che abbiate capito come funziona il meccanismo del platfor m invoke (PInvoke).

DocumentazioneIl platfor m invoke è facile da usar e, ma molto più difficile è saper e quali funzioni usar e e dove tr ovar le. Se in un

pr ogr amma intuite di aver bisogno di funzioni di sistema, la pr ima cosa da far e è chieder e in un for um: ci sar à

sicur amente qualcuno che conosce il metodo adatto da usar e. Successivamente, potete far e una r icer ca su MSDN, il sito

documentativo ufficiale della Micr osoft, e ottener e una spiegazione esatta di quello che la funzione fa e dei par ametr i

r ichiesti. Alla fine, potete anche consultar e PInvoke.NET, che espone una lista enor me di tutte le libr er ie di sistema e

dei lor o metodi, cor r edate di codice dichiar ativo e talvolta anche di esempi. Quest'ultimo sito, inoltr e, r ipor ta sempr e

il numer o e il tipo esatto di par ametr i da usar e: vi r icor do, infatti, che in Visual Basic 6 i tipi numer ici sono differ enti

da Visual Basic .NET e copiar e un codice vecchio potr ebbe causar e un er r or e di sbilanciamento dello stack.

088.089.090.091.092.093.094.095.096.097.098.099.100.101.102.103.104.105.106.107.108.109.

'quello correntemente aperto. Il secondo parametro'fornisce l'indirizzo del metodo callback, che verrà'chiamato ogni volta che sia stata trovata una nuova finestraSuccess = EnumDesktopWindows(IntPtr.Zero, Callback, 0)

'Se la funzione non ha successo, restituisce un erroreIf Success = False Then

Console.WriteLine("Si è verificato un errore nell'applicazione!")End If

End Sub

Sub Main()Console.Clear()Console.WriteLine("Questo programma enumera tutte le finestre aperte.")Console.WriteLine("Premere un tasto qualsiasi per iniziare.")

Console.ReadKey()

EnumerateWindows()

Console.ReadKey()

End SubEnd Module

Page 425: Guida visual basic

E8. La clase Marshal e i puntatori

I puntatoriI puntator i sono speciali var iabili che non contengono un valor e, bensì un indir izzo ad un'altr a ar ea di memor ia. I

puntator i sono una car atter istica peculiar e del C e dei suoi immediati discendenti e per mettono di gestir e la memor ia

a basso livello. Il .NET non suppor ta ufficialmente i puntator i, sebbene in C# sia possibile usar li in cer ti blocchi di codice

non gestito. Nonostante ciò, è definito nel namespace System un tipo str uttur ato di nome IntPtr che r appr esenta un

puntator e, anche se molto diver so da quelli tipici del C. Nell'ambito di quest'ultimo linguaggio, infatti, si definisce un

puntator e specificando a quali tipi di dato esso punti: un puntator e a Integer , significa, ad esempio, che l'ar ea di

memor ia da esso puntata contiene un numer o inter o a 32 bit; allo stesso modo, un puntator e a Point indica che l'ar ea

di memor ia puntata contiene una str uttur a di quel tipo, che viene r appr esentata in binar io come sequenza dei suoi

membr i, ossia due Int32 (X e Y). Questo consente di eseguir e oper azioni ar itmetiche sui puntator i tenendo conto della

dimensione occupata dai dati puntati. Non mi dilungo oltr e nella spiegazione, pur inter essante, poiché in .NET tutto

questo non è possibile. Esiste solo IntPtr , che r appr esenta un gener ico puntator e.

La c lasse MarshalIl marshalling (da cui il nome della classe) consiste nel passar e da dati gestiti a dati non gestiti e vicever sa.

Tipicamente, i "dati gestiti" sono le entità che noi utilizziamo nella pr ogr ammazione ad oggetti: tipi value, str uttur e,

oggetti, delegate, ecceter a... Par allelamente, i "dati non gestiti" cor r ispondono alla r appr esentazione gr ezza dei dati

in memor ia, ossia semplici sequenze di bytes. Per indicar e dati non gestiti si utilizzano i puntator i, che r ifer iscono

dove, nella memor ia di lavor o, sono state allocate le infor mazioni che ci ser vono.

Quando si lavor a a basso livello sulla memor ia, tuttavia, bisgna r icor dar si di eseguir e sempr e cer te oper azioni di cui

nor malmente non ci pr eoccupiamo, poiché vengono amministr ate dal CLR e dal Gar bage Collector :

Allocar e la memor ia pr ima dell'uso. Nel caso si debba conver tir e un oggetto nella sua r appr esentazione

unmanaged, è pr ima necessar io r ichieder e al gestor e della memor ia un cer to spazio da poter utilizzar e per

scr iver ci sopr a. Tale spazio deve esser e delle dimensioni più piccole possibili, per evitar e spr echi;

Liber ar e la memor ia dopo l'uso. Quando si è finito di elabor ar e, tutta la memor ia esplicitamente allocata va

liber ata. In caso ciò non venga effettuato, i dati r esidui r imar r anno ad occupar e spazio fino al ter mine

dell'esecuzione. Il Gar bage Collector non pr ovveder à al r ilascio della memor ia, poiché esso oper a solo in

ambiente managed.

Ecco un semplice esempio:

01.02.03.04.05.06.07.08.09.10.11.12.13.14.15.16.

Imports System.Runtime.InteropServices Module Module1

Sub Main()'Un nuovo Guid. Il Guid è un tipo di identificativo'usato in gran quantità dal Framework.'È un tipo strutturato semplice del namespace'System, perciò l'ho scelto come esempioDim G As Guid = Guid.NewGuid()'Calcola la dimensione in bytes di GDim GuidSize As Int32 = Marshal.SizeOf(G)'Un puntatoreDim Pointer As IntPtr

Page 426: Guida visual basic

Gli altr i metodi di Mar shal hanno un utilizzo altamente specifico e molto tecnico, per ciò non è utile analizzar li tutti. I

membr i più comuni sono stati esposti nell'esempio pr ecedente, ed altr e funzioni ver r ano tr attate in seguito par lando

di sicur ezza.

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.

'La funzione AllocHGlobal (dove H sta per Handle) alloca'un certo numero di bytes, passato come parametro, nella'memoria di lavoro e restituisce un puntatore'all'area allocata. In questo caso allochiamo un'numero di bytes pari alla dimensione di G:Pointer = Marshal.AllocHGlobal(GuidSize)'Copia la struttura G nell'area di memoria puntata'da Pointer, eventualmente eliminando una vecchia'struttura se esiste (True)Marshal.StructureToPtr(G, Pointer, True)

'Ora leggiamo un byte alla volta dall'area di memoria'allocata, ed avremo la rappresentazione binaria'della variabile G.'La funzione ReadByte legge e restituisce un byte di'informazione all'indirizzo puntato da Pointer. Come'secondo parametro accetta un intero che indica l'offset'di cui spostarsi rispetto all'indirizzo baseFor I As Int32 = 0 To GuidSize - 1

Console.Write("{0:X2} ", Marshal.ReadByte(Pointer, I))Next

'Libera la memoria puntata da Pointer. Questa funzione'è un po' più sofisticata, poiché'non solo libera la memoria strettamente indicata da'Pointer, ma elimina anche tutti i sottoriferimenti'contenuti nella struttura. Ad esempio, se la struttura'contenesse una stringa (che, come sappiamo è un'tipo reference), la rappresentazione binaria'indicherebbe solo un intero al posto della stringa,'ossia un puntatore all'oggetto stringa che risiede'in un'altra parte della memoria. DestroyStructure'elimina anche riferimenti di questo tipo, assicurando'che non rimanga alcuna area di memoria inutilizzataMarshal.DestroyStructure(Pointer, GetType(Guid))

Console.ReadKey()

End Sub

End Module

Page 427: Guida visual basic

F1. Magie con le stringhe

Di tutti i tipi disponibili nel Fr amewor k .Net, sicur amente le str inghe costituiscono quello più potente, flessibile,

ver satile e utile. Saper lavor ar e cone le str inghe, massimizzar e il pr ogr amma, r idur r e i tempi di elabor azione,

pr odur r e r isultati miglior i è davver o un'"ar te", se così si può dir e. Non sempr e i pr ogr ammator i scelgono la via più

giusta: ecco una panor amica delle oper azioni su str inga.

System.StringIl tipo che espone le str inghe è System.Str ing ed essendo un tipo r efer ence, dispone di tr e costr uttor i in over load: il

pr imo accetta un car atter e e un inter o X e gener a di conseguenza una str inga for mata da quel car atter e r ipetuto X

volte; la seconda accetta un ar r ay di valor i Char , che vengono conver titi in un unico dato str ing; la ter za è un

ar r icchimento della seconda che per mette di specificar e anche l'indice da cui par tir e e il numer o di car atter i da

pr elevar e. Non sempr e il pr ogr ammator e ne è a conoscenza, poichè la classica dichiar azione di una var iabile di questo

tipo è "Dim S As Str ing", dalla quale non si evince con evidenza la natur a di oggetto della str inga. Per esser e pr ecisi

esse sono oggetti immutabili, checchè se ne pensi: infatti una volta assegnatovi un valor e non c'è modo di modificar lo.

Questo potr ebbe sembr ar e str ano, dato che le assegnazioni non hanno mai pr odotto pr oblemi di sor ta, ma in r ealtà

non lo è. La spiegazione è semplice: quando si assegna un valor e a un oggetto str inga, si cr ea implicitamente un nuovo

oggetto str inga e si passa il puntator e ad esso alla var iabile in assegnazione:

E lo stesso avviene quando si usa l'oper ator e di concatenazione &: tutti i valor i str inga inter calati dall'oper ator e sono

nuovi oggetti cr eati al momento dal pr ogr amma. Per questo motivo l'appr occio della cr eazione di str inghe con l'uso

dell'oper ator e è sconsigliabile, a favor e invece della funzione For mat, come si vedr à da questo elenco di metodi e

pr opr ietà:

Char s(I) : collezione in sola lettur a che contiene tutti i car atter i della str inga, accessibili tr amite l'indice I. Char s

è la pr opr ietà di default della classe, quindi scr iver e S.Char s(0) e S(0) è esattamente la stessa cosa

Clone : r estituisce un nuovo oggetto il cui valor e è identico alla str inga da cui viene chiamata la funzione

Compar e : funzione statica con moltissimi over load che per mette di confr ontar e due str inghe. Implementa

ICompar aer .Compar e e r estitusce valor i definiti analizzati nei capitoli sulle inter facce

Compar eTo : funzione che implementa l'inter faccia ICompar able.Compar eTo

Concat : concatena tutti gli ar gomenti passati

Contains(S) : r estituisce Tr ue se S è contenuta nella str inga, altr imenti False

Copy(S) : come Clone, ma statica

Empty : costante che r appr esenta una str inga vuota

EndsWith(S) : r estituisce Tr ue se la str inga ter mina con S, altr imenti False

Equals(S) : deter mina se la str inga e S contengano lo stesso valor e (è la stessa cosa che utilizzar e l'oper ator e =)

For mat(S, ...) : for matta la str inga secondo la str inga di for mato S, utilizzando i par ametr i passati dopo

Index Of(C) : r estituisce l'indice di C nella str inga. -1 se la r icer ca non ha pr odotto r isultati. Ha molti over load,

che per mettono di cer car e una sottostr inga e di specificar e l'indice a cui iniziar e la r icer ca e la lunghezza del

testo da contr ollar e

1.2.3.4.

'Oggetto stringa inizialmente uguale a NothingDim S As String'Crea un nuovo oggetto stringa "Ciao" e lo assegna a SS = "Ciao"

Page 428: Guida visual basic

Index OfAny(C()) : r estituisce l'indice di uno qualsiasi dei car atter i passati come ar r ay in C

Inser t(S, I) : inser isce nella str inga un valor e S all'indice I

IsNullOr Empty : deter mina se la str inga sia Nothing oppur e vuota

Join(S, V()) : concatena i valor i nell'ar r ay V, separ andoli con un separ ator e str inga S

LastIndex / LastIndex OfAny : come Index Of, solo l'ultima occor r enza e non la pr ima

Length : la lunghezza della str inga

PadLeft / PadRight (C, N) : allinea a sinistr a o a destr a la str inga fino alla lunghezza N, r iempiendo gli eventuali

vuoti col car atter e C

Refer enceEquals(S1, S2) : deter mina se S1 e S2 puntino allo stesso oggetto (è la stessa cosa che utilizzar e

l'oper ator e Is)

Remove(I, L) : r imuove dalla str inga tutti i car atter i a par tir e dall'indice I, opzionalmente specificandone anche il

numer o L

Replace(O, N) : r impiazza tutte le occor r enze di O nella str inga con nuovi valor i N

Split(C) : spezza la str ingha in più sottostr inghe utilizzando come separ ator e il car atter e o l'ar r ay di car atter i

passato

Star tsWith(S) : deter mina se la str inga inizi per S

Substr ing(I, L) : ottiene una sottostr inga r icavata pr endendo solo i car atter i dall'indice I in poi, opzionalmente

specificandone anche il numer o L

ToChar Ar r ay : r estituisce la str inga sotto for ma di ar r ay di car atter i

ToLower : conver te la str inga tutta in minuscolo

ToUpper : conver te la str inga tutta in maiuscolo

Tr im / Tr imEnd / Tr imStar t : cancella tutti gli spazi bianchi dalla str inga. A seconda della funzione r ichiamata

esegue tale pr ocesso sia all'inizio che alla fine, solo alla fine o solo all'inizio. L'unico over load di tutte queste

funzioni per mette di specificar e una cer ta gamma di car atter i da eliminar e in luogo degli spazi bianchi

Tutti i metodi non statici elencati, bisogna r icor dar lo, sono funzioni d'istanza, quindi r estituiscono un valor e, ossia

una str inga nuova ottenuta modificando quella esistente. Risulta infatti evidente che, siccome le str inghe sono oggetti

immutabili, non possano esser e modificate. Per ciò il seguente codice non pr odur r à alcuna modificazione:

Mentr e questo otter r à il r isultato:

Nei fr ammenti di codice in cui si gener ano str inghe, tuttavia, è molto meglio utilizzar e uno Str ingBuilder .

System.Text.StringBuilderQuesto oggetto ha il compito ci costr uir e pezzo per pezzo una str inga: è pr ogettato in modo da lavor ar e con singoli

car atter i alla volta, così da non cr ear e alcun str inga tempor anea in memor ia, r ispar miando molto spazio e molto

tempo. Espone alcuni metodi pr esi da Str ing, come Inser t e Remove. Ha una pr opr ietà car atter istica denominata

Capacity, che indica il numer o massimo di car atter i contenibili nell'oggetto, cor r elata di funzione Ensur eCapacity che si

1.2.3.4.5.6.7.

Dim S As String = "Ciao"'Rimuove il primo carattere dalla stringa, ma il valore'restituito viene perso in quanto non c'è'alcuna assegnazioneS.Remove(0, 1)Console.WriteLine(S)'> Ciao

1.2.3.4.5.6.

Dim S As String = "Ciao"'Rimuove il primo carattere dalla stringa, e pone il'riferimento alla nuova stringa creata in SS = S.Remove(0, 1)Console.WriteLine(S)'> iao

Page 429: Guida visual basic

assicur a che questa condizione venga r ispettata: dur ante il lavor o, se la lunghezza della str inga in elabor azione r isulta

maggior e della capacità, comunque, quest'ultima viene aumentata in modo da ader ir e alle nuove necessità di spazio,

eliminando quindi ogni pr oblema. Le pr ocedur e impor tanti sono Append, che accoda una str inga al tutto, AppendLine,

che inoltr e manda anche a capo e AppendFor mat, che aggiunge un valor e for mattato con una str inga di for mato, come

al solito. Per pr ovar e l'efficacia di Str ingBuilder , ecco un test a pr ova di bomba che calcola le pr estazioni di entr ambi

gli utilizzi:

Sul mio computer , la pr ima ver sione impiega 14'678ms, mentr e la seconda 4ms... Sembr a sbalor ditivo, ma

Str ingBuilder , in questo caso è quasi 4000 volte più veloce. Guar dando il consumo di RAM, si noter à inoltr e che la pr ima

ver sione spr eca in media 2000 bytes in più di memor ia.

System.Security.SecureStringQuando si memor izzano passwor d o numer i impor tanti come quelli della car ta di cr edito, le str inghe or dinar ie

per dono una quantità esager ata di punti in sicur ezza. Infatti, oltr e a esser e r eper ibili nello spazio di memor ia che il

pr ogr amma utilizza (anche se ottener e tali indir izzi è davver o assai difficile), spesso vengono salvate in file di sistema

tempor anei, più accessibili, oppur e per mangono in diver se copie nella memor ia poichè, essendo immutabili, non se ne

può ver amente azzer ar e il valor e. Secur eStr ing è un tipo sicur o che consente di evitar e questi r ischi: non pr esenta

alcun costr uttor e, non è deducibile da una str inga or dinar ia (altr imenti non avr ebbe senso), costr uisce la str inga

sicur a un car atter e alla volta usando un algor itmo di cr iptazione per mascher ar e il car atter e. Pr esenta metodi simili

a Str ingBuilder , ma che lavor ano solo con un car atter e alla volta. Implementar e una tex tbox che legga una passwor d ad

esempio, può esser e un compito molto semplice: la str inga non viene memor izzata nel contr ollo, che espone un testo

omogeneo, ma nell'oggetto Secur eStr ing: l'unico modo per costr uir e una str inga in questo modo è un car atter e per

volta utilizzando l'evento keypr ess.

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.

Module Module1Sub Main()

Dim T As New StopwatchDim S As String = ""Dim B As New StringBuilder

'Cronometra quando si impiega a concatenare 100'000'caratteri con l'operatore &T.Start()For I As Int32 = 1 To 100000

S &= "c"NextT.Stop()Console.WriteLine("Operatore & : {0}ms", T.ElapsedMilliseconds)

'Cronometra l'operazione con StringBuilderT.Reset()T.Start()For I As Int32 = 1 To 100000

B.Append("c")NextT.Stop()Console.WriteLine("StringBuiler : {0}ms", T.ElapsedMilliseconds)

Console.ReadKey()

End SubEnd Module

01.02.03.04.05.06.07.08.

Dim Password As New SecureString Private Sub TextBox1_KeyPress(ByVal sender As Object, _

ByVal e As KeyPressEventArgs) Handles TextBox1.ClickSelect Case Asc(e.KeyChar)

Case 8'Il carattere 8 corrisponde al backspace: cancella

Page 430: Guida visual basic

Un'alter nativa a questo metodo non è data dall'uso della pr opr ietà Passwor dChar della tex tbox , poichè essa si limita a

mascher ar e ester ior mente il contenuto, che invece per mane immutato nella memor ia. L'unico modo per r ipr ender e i

dati così immessi nell'oggetto Secur eStr ing è utilizzar e questo codice, che conver te la passwor d in un puntator e a

str inga binar ia e successivamente der efer enzia tale puntator e per ottener e il valor e or iginar io:

Mar shal è una classe appar tenente al Namespace System.Runtime.Inter opSer vices e ser ve per copiar e e manipolar e

blocchi di memor ia a livello molto basso, senza r iguar do nè per il tipo che per la compatbilità, ma solo per indir izzi e

dimensioni. È il w r apper managed del cor r ispondente metodo unmanaged CopyMemor y della libr er ia ker nel32.dll di

Windows. Neppur e questo ar gomento ver r à tr attato oltr e in questo capitolo, ma vi r imando alla lezione

cor r ispondente della sezione E.

09.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.

'l'ultimo carattereIf TextBox1.SelectionStart > 0 Then

Password.RemoveAt(TextBox1.SelectionStart - 1)End If

'32 rappresenta uno spazio, ed è il primo carattere'intelleggibile nella tabella ASCII. In caso sia maggiore'o uguale a 32, quindi, il carattere non è di controlloCase Is >= 32

'Se il cursore non si trova alla fine della stringa,'inserisce il carattere all'indice datoIf TextBox1.SelectionStart < TextBox1.Text.Length - 1 Then

Password.InsertAt(TextBox1.SelectionStart, e.KeyChar)Else

Password.AppendChar(e.KeyChar)End If'Nella textbox, visualizza *e.KeyChar = "*"

End SelectEnd Sub

01.02.03.04.05.06.07.08.09.10.11.12.13.14.15.16.17.

'Ottiene il puntatore a stringa BSTR. La sigla indica una Basic'STRing alias Binary STRing. Le caratteristiche di questa'stringa risiedono nelle modalità di memorizzazione'sottoforma di dati nella memoria. È composta da un prefisso'che ne specifica la lunghezza, un contenuto e un terminatore,'noto come NULL terminator ai programmatori C.'Non procedo oltre nell'argomento, poichè non è scopo'del capitolo trattare questo tipo di stringheDim Ptr As IntPtr = Marshal.SecureStringToBSTR(Password)'Dereferenzia il puntatore e ottiene la password veraDim Pass As String = Marshal.PtrToStringBSTR(Ptr) 'In questa parte viene usato il valore della stringa 'Quindi il suo spazio di memoria viene istantaneamente liberato per'evitare che possa essere rintracciata in qualche modoMarshal.ZeroFreeBSTR(Ptr)

Page 431: Guida visual basic

F2. Espressioni regolari

Cos'è un'espressione regolare?Mi sembr a palese che un'espr essione r egolar e sia... un'espr essione r egolar e! Niente di più di quello che si intende in

italiano con questo ter mine, solo con una sfumatur a di str inga: una por zione di testo che, pur non r ipetendosi

esattamente uguale, è possibile r icondur r e ad uno schema specifico. Ad esempio, un indir izzo email può esser e

r icondotto a questo schema:

una sequenza di due o più car atter i alfanumer ici o under scor e o punti;

il simbolo @

un'altr a sequenza di car atter i alfanumer ici;

un punto;

una sequenza limitata scelta tr a un insieme finito di possibilità.

Poiché possiamo r iconoscer e un indir izzo e-mail da queste car atter istiche, possiamo anche cr ear e una espr essione

r egolar e che lo r appr esenti. Esiste un linguaggio a par te per le espr essioni r egolar i, che è uno standar da e viene

implementato allo stesso modo in tutti i linguaggi di pr ogr ammazione e di scr ipting esistenti. Per questo motivo, vale

la pena spender e qualche capitolo per intr odur r e tale linguaggio.

Ed or a andiamo un pò più nello specifico...

Descrizione del linguaggio Regular ExpressionLe espr essioni r egolar i vengono definite attr aver so deter minati patter n, scr itti usando delle specifiche r egole di

sintassi e deter minati car atter i "speciali", che svolgono funzioni dispar ate e inter essanti. Si può consider ar e questo

come un linguaggio a par te, che deve anch'esso esser e appr eso al fine di sfr uttar e fino in fondo le potenzialità offer te

al pr ogr ammator e. Ecco un piccolo esempio di patter n:

Questo r appr esenta una qualsiasi delle vocali: impiegandolo in un metodo di sostituzione, si potr ebber o quindi

sostituir e tutte le vocali non accentate di un testo con qualcos'altr o. In questo caso par ticolar e si è notato come le

par entesi quadr e svolgano una funzione di r aggr uppamento di altr i car atter i: sono dei car atter i "jolly", che vengono

inter pr etati dal par ser come dir ettive di compor tamento. Allo stesso modo, si possono r aggr uppar e tali jolly sotto

alcune classificazioni:

Caratter i di escape : vengono utilizzati per indicar e singoli car atter i e r efer enziar e car atter i non stampabili,

ossia di contr ollo. Inoltr e possono for nir e ver sioni "nor mali" dei jolly che assumono par ticolar e significato nelle

espr essioni. Esempio: \t (tabulazione), \n (a capo), \[ (una par entesi quadr a, che non viene consider ata come

jolly, ma come semplice car atter e);

Classi di caratter i : r appr esentano insiemi di car atter i. Nel caso delle par entesi quadr e, non è necessar io

utilizzar e sequenze di escape per i jolly tr anne che per il car atter e ] e -. Esempio: [aei()ou\-] (uno qualsiasi tr a i

car atter i: a, e, i, o, u, (, ) e -)

Asserzioni atomiche di ampiezza zero : specificano dove debba tr ovar si l'espr essione da cer car e/sostituir e.

Esempio: ^ac (la str inga "ac" all'inizio di una r iga)

Qualificator i : specificano quante volte una deter minata espr essione debba appar ir e all'inter no della str inga.

Sono distinti in due gr uppi: gr eedy e lazy. I membr i del pr imo confr ontano sempr e quanti più car atter i

1. [aeiou]

Page 432: Guida visual basic

possibili; quelli del secondo invece quanti meno possibili. Esempio: \w * (zer o o più letter e vicine)

Costruttor i di rag g ruppamento : ser vono per r aggr uppar e una o più espr essioni assieme; assumono

par ticolar e utilità in combinazione con i qualificator i, ma anche nei metodi di r icer ca, in quanto possono

"mar car e" una deter minata sottostr inga con una chiave che potr à esser e usata in seguito per r ecuper ar e

quell'espr essione. Esempio: (?<inizio>^\w+)

Sostituzioni : indicano di r ipr ender e un dato gr uppo di espr essioni mar cato nel patter n di sostituzione con un

costr uttor e di r aggr uppamento. Esempio: se il patter n di sostituzione indica di sostituir e "(?<inizio>^\w+)" con

"Linea: ${inizio}", tutte le par ole di almeno una letter e a inizio r iga sar anno sostituite con la str inga "Linea: "

seguita dalla par ola

Costruttor i di r ifer imento all'indietro : per mettono di r efer enziar e un par ticolar e gr uppo

pr ecedentemente definito nell'espr essione. Esempio: (?<gr p>\s+\w +\s+)ciao\k<gr p> (una par ola separ ata da spazi,

seguita da "ciao", seguita dalla stessa par ola di pr ima)

Costruttor i di alternanza : for niscono un modo per specificar e alter native. Esempio : (Ciao|Buongior no)

Totem! (cer ca una di queste possibilità "Ciao Totem!" e "Buongior no Totem!")

Ecco una lista di tutte le espr essioni jolly più impor tanti:

Caratter i di escape

\a : campanello

\b : tr a par entesi quadr e e nelle sostituzioni, r appr esenta il backspace, altr imenti il limite di una par ola

\t : tabulazione

\r : r itor no car r ello

\v : tabulazione ver ticale

\f : avanzamento pagina

\n : a capo

\e : escape

\000 : un car atter e ASCII espr esso in notazione ottale. Deve sempr e aver e tr e cifr e. Ad esempio \040

r appr esenta uno spazio (32 in decimale)

\x 00 : un car atter e ASCII espr esso in notazione esadecimale. Lo spazio, ad esempio, è \x 20

\* : quando il backslash è seguito da un car atter e che sar ebbe un jolly, r appr esenta quel car atter e nor malmente.

\* r appr esenta un aster isco (e non ha più quindi la funzione di qualificator e)

Classi di caratter i

. : qualsiasi car atter e eccetto l'a capo

[abcde] : uno qualsiasi dei car atter i specificati all'inter no delle par entesi

[a-z] : il tr attino r appr esenta una ser ie di car atter i consecutivi. In questo caso, tutte le letter e minuscole da a a

z

[a-z-[aeiuo]] : quando una classe di car atter i appar e nidificata all'inter no di un'altr a e pr eceduta da un segno

meno, allor a si consider a la ser ie pr ima espr essa escludendo i car atter i specificati nella seconda coppia di

par entesi. In questo caso, tutte le consonanti minuscole

\w : un car atter e alfanumer ico o un under scor e (compr ende anche car atter i accentati)

\W : negazione di \w , ossia tutti i car atter i non alfanumer ici, inclusi quelli accentati

\s : uno spazio bianco. Rappr esenta contempor aneamente uno qualsiasi tr a uno spazio nor male, \t, \r , \v, \f e \n

\S : negazione di \s, ossia tutti i car atter i che non sono uno spazio bianco

\d : una cifr a decimale

\D : negazione di \d

Asserzioni atomiche di ampiezza zero

Page 433: Guida visual basic

^ : inizio di una r iga

$ : fine di una r iga (se l'opzione Multiline è attiva, come si vedr à in seguito) o fine della str inga

\A : inizio della str inga (sempr e, anche se Multiline è attivo)

\Z : fine della str inga (sempr e, anche se Multiline è attivo); nel caso ci sia un car atter e di a capo, r appr esenta la

posizione immediatamente pr ecedente

\z : come \Z, solo che compr ende anche l'a capo

\b : limite di una str inga, ossia il pr imo o l'ultimo car atter e di una par ola delimitata da spazi bianchi o altr i

car atter i di punteggiatur a

\B : negazione di \b

Qualificator i

* : zer o o più cor r ispondenze. Ad esempio \s*Public indica la par ola Public pr eceduta da zer o o più car atter i di

spazio

+ : una o più cor r ispondenze

? : zer o o una cor r ispondenza

{n} : esattamente n cor r ispondenze. Ad esempio (\b\w+\b){6} indica sei par ole consecutive di almeno un

car atter e

{n,} : almeno n cor r ispondenze. Ad esempio \*{1,} è uguale a \*+ e indica una ser ie di aster ischi

{n,m} : da n a m cor r ispondenze

*? : la pr ima cor r ispondenza con il minor numer o di r ipetizioni

+? : la pr ima cor r isponde con il minor numer o di r ipetizioni, ma almeno una

?? : se possibile zer o, altr imenti una cor r ispondenza

{n}? {n,}? {n,m}? : come sopr a

Costruttor i di rag g ruppamento

(str inga) : una str inga o un'espr essione. Le par entesi vengono numer ate: la pr ima ha indice 1. Ci si può r ifer ir e

ad esse anche con l'indice

(?<nome>ex pr ) : cattur a l'espr essione "ex pr " e le assegna il nome "nome"

(?=ex pr ) : continua il confr onto solo se l'espr essione a destr a cor r isponde a quella data. Ad esempio \w +(?=,)

indica una par ola seguita da una vir gola, ma senza compr ender e la vir gola

(?!ex pr ) : continua il confr onto solo se l'espr essione a destr a non cor r isponde a quella data

(?<=ex pr ) : continua il confr onto solo se l'espr essione a sinistr a cor r isponde a quella data. Ad esempio (?<=,)\w+\b

r appr esenta una par ola che segue una vir gola, ma senza compr ender e la vir gola

(?<!ex pr ) : continua il confr onto solo se l'espr esisone a sinistr a non cor r isponde a quella data

Sostituzioni

$n : sostituisce la sottostr inga r appr esentata dall'espr essione n-esima (si contano solo quelle tr a par entesi

tonde). $0 indica tutta la str inga

${nome} : sostituisce la sottostr inga r appr esentata da un'espr essione (?<nome>)

$& : analogo a $0

$$ : simbolo del dollar o (solo nelle sostituzioni)

Costruttor i di r ifer imento all'indietro

\n : si r ifer isce all'n-esimo gr uppo di car atter i all'indietr o a par tir e da \n. Ad esempio (\w )\1 r appr esenta un

car atter e r ipetuto (è come se fosse \w \w)

\k<nome> : si r ifer isce a un gr uppo denominato "nome"

Costruttor i di alternanza

Page 434: Guida visual basic

| : indica un'alter nativa tr a due o più espr essioni. Ad esempio (\.net|\.com|\.it) r appr esenta una qualsiasi delle

str inghe ".net", ".com" e ".it"

(?(ex pr )s|n) : r appr esenta la par te s se l'espr essione cor r isponde a ex pr , altr imenti la par te n

La c lasse RegexLa classe che r appr esenta un'espr essione r egolar e è Regex , facente par te del namespace

System.Tex t.Regular Ex pr essions. Ha due costr uttor i ed entr ambi hanno come pr imo par ametr o un patter n. Il secondo

par emetr o consiste di un enumer ator e codificato a bit che indica le opzioni con le quali debba esser e eseguita la

r icer ca; i valor i più utili sono: Compiled (compila l'espr essione r egolar e, r endendola più veloce, ma impiega più

memor ia), Ignor eCase (disattiva il case sensitive), Ignor ePatter nWithSpace (ignor a gli spazi bianchi epliciti, ossia quelli

non mar cati da un car atter e di escape \s, e abilita i commenti intr odotti da # nel testo da anlizzar e), Multiline (la

r icer ca è svolta su un testo di più r ighe: i car atter i speciali $ e ^ cambiano il lor o significato), Singleline (la r icer ca è

svolta su un testo di una sola r iga) e RightToLeft (il testo viene analizzato da destr a a sinistr a). Le funzioni più

impor tanti in assoluto sono IsMatch, che contr olla la validità di un'espr essione r egolar e nella str inga data e r estituisce

Tr ue o False, Match, che esegue la stessa cosa e r estituisce un oggetto Match, e finine Matches, che r estituisce una

collezione a tipizzazione for te MatchCollection. Gli altr i metodi utili di Regex :

Escape(S) : sostituisce tutti i car atter i speciali nella str inga con car atter i di escape, quindi r estituisce la nuova

str inga

GetGr oupNames / GetGr oupNumber s : r estituisce un ar r ay di str inghe o inter i che deter minano i var i gr uppi

definiti nel patter n

GetGr oupNameFr omNumber / GetGr oupNumber Fr omName : r estituisce il nome di un gr uppo a par tir e

dall'indice o vicever sa

Replace(T, S) : analizza il testo T con le opzioni definite in pr ecedenza e sostituisce tutte le occor r enze

dell'espr essione r egolar e inser tia come patter n nel costr uttor e con la str inga S, che opzionalmente può

contener e Sostituzioni. Alla fine dell'oper azione viene r estituita la str inga r isultante

Split(S) : lavor a come la funzione Str ing.Split, solo che il separ ator e è costituito dall'espr essione r egolar e

Unescape(S) : esegue l'opzione inver sa a Escape, ossia sostituisce tutti i car atter i di escape con car atter i

nor mali

Le c lassi Match e MatchCollectionUn oggetto di tipo Match è il r isultato della funzione Regex .Match, mentr e lo stesso avviene per Regex .Matches con

MatchCollection. Un Match è una cor r ispondenza dell'espr essione tr ovata all'inter no della str inga da anlizzar e. Se non

viene tr ovata nessuna cor r ispondenza, viene r estituito un oggetto Match la cui pr opr ietà Success è impostata a False.

Ecco una lista dei membr i più usati:

Gr oups(N) : r estituisce un oggetto di tipo Gr oupCollection, del quale ogni elemento r appr esenta un singolo

gr uppo r acchiuso da par entesi tonde. È possibile pr elevar e un gr uppo usando un ar gomento N che può esser e un

indice inter o o una str inga nel caso si siano usati dei costr uttor i di r aggr uppamento. Ogni oggetto Gr oup ha

alcune pr opr ietà: Index r estituisce l'indice della sottostr inga nella str inga inter a, Length la sua lunghezza, Value

il suo valor e e Success se quel gr uppo è pr esente oppur e no

Index : l'indice del pr imo car atter e che inizia la sottoespr essione tr ovata all'inter no della str inga inter a

Length : la lunghezza della sottostr inga tr ovata

Success : indica se la r icer ca ha avuto successo oppur e no

Value : r estituisce tutta la sottostr inga

Page 435: Guida visual basic

Un esempio praticoEcco un esempio:

E un esempio più complesso:

01.02.03.04.05.06.07.08.09.10.11.12.13.14.15.16.17.18.19.20.21.22.

Module Module1Sub Main()

Dim Text As String = _"Questo è un testo intervallato da alcuni spazi e " & vbCrLf & _"un a capo. Inoltre, viene supportata anche la punteggiatura."'Questa espressione ricerca tutti gli insiemi di almeno un'carattere separati dal resto del testo da spazi bianchi o'segni di punteggiatura.'Ergo: cerca tutte le singole paroleDim R As New Regex("\b\w+\b")Dim Matches As MatchCollection = R.Matches(Text)

For Each M As Match In Matches

'chr(34) rappresenta il carattere 34 della tabella ASCII,'ossia le virgoletteConsole.WriteLine("All'indice {0}, la sottostringa {1}{2}{1}", _

M.Index, Chr(34), M.Value)Next

Console.ReadKey()

End SubEnd Module

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.32.33.34.35.36.37.38.39.40.41.

Module Module2Sub Main()

Dim Text As String = String.Format( _"Sub Prova(){0}" & _" Dim Int As Int32 = 4{0}" & _" Dim Str As String = {1}Ciao {1}{0}" & _" For I As Int32 = Int To 48{0}" & _" Dim Str2 As String = Str & I{0}" & _" Console.WriteLine(Str2){0}" & _" Next{0}" & _"End Sub", vbCrLf, Chr(34))

'Questa espressione ricerca tutte le dichiarazioni di'variabili nel codice sopraDim R As New Regex( _"\s*Dim\s+(?<Name>\w+)\s+As\s+(?<Type>\w+)(\s+=\s+(?<Value>[\w"" ]+))?", _RegexOptions.Multiline)'Ecco la spiegazione di ogni parte del codice:'\s* : le dichiarazioni possono essere a inizio riga o precedute' da tabulazioni o spazi. Perciò si deve usare *, che' indica zero o più ripetizioni''Dim : ovviamente deve essere presenta la keyword Dim''(?<Name>\w+) : dopo Dim viene il nome della variabile,' rappresentato con \w+, ossia almeno un carattere o' underscore. Questo gruppo è chiamato Name, cosicchè' lo potremo riprendere in seguito''As : la clausola As, separata da almeno uno spazio (\s+)' del nome e dal tipo della variabile''(?<Type>\w+) : come Name''(...)? : tutto quello che viene ora è posto in una coppia di' parentesi tonde per poter usare il qualificatore ?. Quindi' tutta questa espressione può apparire 0 o una volta,' ossia è opzionale. Si tratta dell'inizializzazione' della variabile in-line'

Page 436: Guida visual basic

42.43.44.45.46.47.48.49.50.51.52.53.54.55.56.57.58.59.60.61.62.63.64.65.

'\s+=\s+ : un segno uguale, separato da spazi dal resto''(?<Value>[\w" ]+) : il valore della variabile può' contenere lettere, underscore, spazi bianchi singoli' o virgolette.Nell'esempio ci sono due virgolette' poichè ci si trova in una stringa e una' sola sarebbe interpretata come fine della stringa.' Due di seguito vengono lette invece come una' virgoletta nel testoDim Matches As MatchCollection

Matches = R.Matches(Text)

For Each M As Match In Matches

Console.Write("- Nome: {1}{0} Tipo: {2}{0}", _vbCrLf, M.Groups("Name").Value, M.Groups("Type").Value)

If M.Groups("Value").Success Then

Console.WriteLine(" Valore: {0}", M.Groups("Value").Value)End If

Next

Console.ReadKey()End Sub

End Module

Page 437: Guida visual basic

F3. Espressioni regolari in azione

Ricerca di paroleLa funzione pr incipale delle espr essioni r egolar i è la r icer ca di deter minate sottostr inghe in un testo. Per oper ar e una

r icer ca si usa solitamente la funzione Regex .Matches, che r estituisce tutte le occor r enze, oppur e un ciclo nel quale, ad

ogni iter azione, si ottiene il match successivo con la funzione Match.Nex tMatch. Entr ambi i metodi eseguono

l'oper azione nella stessa manier a, poichè anche Matches non viene r iempito tutto subito, ma ad ogni iter azione del

ciclo For a cui viene sottoposto, cer ca e inser isce nel r isultato una nuova cor r ispondenza. Ad esempio, questo codice

r icer ca nel sor gente di questa pagina tutte le keywor ds usate per l'indicizzazione dei motor i di r icer ca:

Quest'altr o esempio, invece, è molto più diver tente e... cattivello. Cer ca in un testo copiato negli appunti tutti gli

indir izzi e-mail e li r aggr uppa in una lista, con la possibilità di salvar li in un file di testo. L'inter faccia è semplice:

compr ende una listbox e due pulsanti. Ed ecco il codice:

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.

Module Module1Sub Main()

Dim Text As String = IO.File.ReadAllText("74.php")

'Cerca la definizione del tag, ottenendo l'elenco delle'paroleDim Keywords As New Regex("\<meta name=""keywords"" content='(?<Keywords>[\w, ]+)'\>",

RegexOptions.Multiline)Dim Match As MatchDim Matches As MatchCollection

Match = Keywords.Match(Text)

'Se la ricerca ha avuto successo, scandisce ogni parolaIf Match.Success Then

Dim Word As New Regex("\b\w+\b")Dim Index As Int32 = 1'Dal gruppo Keywords, preleva ogni singola parolaMatches = Word.Matches(Match.Groups("Keywords").Value)Console.WriteLine("Parole chiave:")For Each M As Match In Matches

'E le scrive a schermo numerandoleConsole.WriteLine("{0} - {1}", Index, M.Value)Index += 1

NextEnd If

Console.ReadKey()

End SubEnd Module

01.02.03.04.05.06.07.08.09.10.11.12.13.14.15.16.17.

Class Form1Private Sub cmdSearch_Click(ByVal sender As Object, _

ByVal e As EventArgs) Handles cmdSearch.Click'Clipboard è una proprietà di My.Computer, di tipo'Microsoft.VisualBasic.MyService.ClipboardProxy: è un'oggetto singleton che rappresenta gli appunti. Questo'codice ottiene il testo copiato nella clipboard con'la funzione CopiaDim Text As String = Clipboard.GetText'Questa espressione deve ricercare tutti gli indirizzi'e-mail presenti nel testo, quindi aggiungerli alla lista:'\b(\w+) : la prima serie di caratteri costituisce' l'username dell'utente. Ad esempio in' [email protected], è gianni90''\s* : zero o più spazi. Può capitare che per non rendere

Page 438: Guida visual basic

Per pr ovar e il pr ogr amma potete copiar e questo testo:

Salve, ragazzi! Il mio indirizzo è [email protected], scrivetemi presto, attendo risposte per la mia domanda.

Ciao gianni90: ti devo ricordare che sarebbe meglio se non mettessi il tuo indirizzo in chiaro, poichè potrebbe essere più facilmente rintracciato. Prova invece ad usare questa forma: bartolo[at]prov[dot]com.

Ok grazie!

Anzi, se invece vuoi, puoi usare anche questo: caio at miap dot net.

Vedrò di provare anche questo. Ma potrei anche fare delle combinazioni, ad esempio ciack[at]email.fr oppure mark at prov[dot]it... O no?

Certo, ottima idea.

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.

' l'indirizzo reperibile, si usi questa sintassi:' "gianni90 at provider dot it"''(@|at|\[at\]) : uno qualsiasi tra @, at e [at], a seconda' di come viene scritto l'indirizzo''\s*(\w+)\s* : spazi per lo stesso discorso di prima, poi' una serie di caratteri che indica il provider.''(\.|dot|\[dot\]) : uno qualsiasi tra ., dot e [dot], a' seconda di come viene scritto l'indirizzo''(\w+) : l'ultima serie di caratteri è il dominioDim Email As New Regex( _"\b(\w+)\s*(@|at|\[at\])\s*(\w+)\s*(\.|dot|\[dot\])(\w+)", _RegexOptions.Multiline)

For Each M As Match In Email.Matches(Text)

'Aggiungi un elemento alla lista e lo spunta'Attenzione! Bisogna tenere in conto che:'- il gruppo 0 rappresenta sempre tutta la sottostringa' catturata e non uno dei raggrupamenti presenti'- anche i costruttori di alternanze sono gruppi,' poichè racchiusi entro parentesi tonde'Perciò il risultato sarebbe'0 1 2 3 4 5' gianni90[at]provider[dot]it'Quindi 1 è l'username, 3 il provider e 5 il dominiolstEmail.Items.Add(String.Format( _"{0}@{1}.{2}", M.Groups(1).Value, M.Groups(3).Value, _M.Groups(5).Value), True)

NextEnd Sub

Private Sub cmdSave_Click(ByVal sender As Object, _

ByVal e As EventArgs) Handles cmdSave.Click'Salva gli indirizzi spuntatiDim Save As New SaveFileDialogSave.Filter = "File di testo|*.txt"If Save.ShowDialog = Windows.Forms.DialogResult.OK Then

Dim Writer As New IO.StreamWriter(Save.FileName)'CheckedItems è una collezione in sola lettura che'restituisce tutti gli elementi spuntatiFor Each Item As String In lstEmail.CheckedItems

Writer.WriteLine(Item)NextWriter.Close()

End IfEnd Sub

End Class

Page 439: Guida visual basic

Validazione di espressioniLe espr essioni r egolar i tor nano utili anche nella validazione di dati immessi dall'utente: è possibile contr ollar e che i

valor i abbiano un par ticolar e for mato pr ima di pr oceder e in oper azioni che potr ebber o pr odur r e degli er r or i. In

questi casi non si usa Matches e di solito si analizza solamente una linea di testo: vengono per lo più usate le funzioni

IsMatch e Match. Ad esempio è possibile contr ollar e che un indir izzo e-mail immesso abbia la giusta for mattazione:

Oppur e contr ollar e che numer i e date siano immessi cor r ettamente:

01.02.03.04.05.06.07.08.09.10.11.12.13.14.15.

Module Module2Sub Main()

'Controlla la formattazione dell'indirizzoDim R As New Regex("(\w+)@(\w+)\.(\w+)")Dim Input As String

Do

Console.Write("Inserire il proprio indirizzo e-mail: ")Input = Console.ReadLine

Loop Until R.IsMatch(Input)Console.WriteLine("Indirizzo accettato!")

Console.ReadKey()

End SubEnd Module

01.02.03.04.05.06.07.08.09.10.11.12.13.14.15.16.17.18.

Module Module3Sub Main()

'Controlla la formattazione del numero. È uso frequente'nelle validazioni usare i caratteri ^ e $, che indicano'inizio e fine della stringa, per controllare che all'interno'ci sia solo il valore che si vuole e non altre coseDim R As New Regex("^\d{4}$")Dim Input As String

Do

Console.Write("Inserire un numero intero tra 1000 e 9999: ")Input = Console.ReadLine

Loop Until R.IsMatch(Input)Console.WriteLine("Numero accettato!")

Console.ReadKey()

End SubEnd Module

01.02.03.04.05.06.07.08.09.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.

Module Module4Sub Main()

'Controlla la formattazione del numero.'\d+ : almeno una cifra''(...)? : tutto quello che viene dopo è messo tra parentesi' per poter usare il qualificatore ?. Il numero può ' infatti anche non essere decimale''(\.|,) : virgola o punto''\d+ : le cifre decimali, almeno unaDim R As New Regex("^\d+((\.|,)\d+)?$")Dim Input As String

Do

Console.Write("Inserire un numero anche decimale: ")Input = Console.ReadLine

Loop Until R.IsMatch(Input)Console.WriteLine("Numero accettato!")

Console.ReadKey()

End Sub

Page 440: Guida visual basic

Parsing di file di datiQuando l'applicazione non utilizza nè x ml nè database nè altr i tipi par ticolar i di file per il salvataggio di dati, può

impiegar e un file di dati, con estensione *.dat (cer te volte, il softwar e usa un'estensione pr opr ietar ia). All'inter no di

taluni tipi di file si possono immagazzinar e valor i for mattati a piacer e, senza sottostar e a nessuna r egola di sintassi

pr edefinita. In questi casi è il pr ogr ammator e che cr ea da solo le r egole per scr iver e le infor mazioni sul suppor to. Una

gr ammatica che ha usato in passato per molti pr ogr ammi è questa:

Dove ogni campo è separ ato dagli altr i da un car atter e pipe, e ogni r iga r appr esenta un oggetto diver so. Ecco un

esempio:

End Module

01.02.03.04.05.06.07.08.09.10.11.12.13.14.15.16.17.18.

Module Module5Sub Main()

'Controlla la formattazione della data'Una o due cifre, seguite da /, - o |, seguite dalla stessa'cosa e da due o quattro cifre indicanti l'anno'Ovviamente non viene controllata la coerenza della dataDim R As New Regex("^\d{1,2}[/\-\|]\d{1,2}[/\-\|](\d{2}|\d{4})$")Dim Input As String

Do

Console.Write("Inserire una data: ")Input = Console.ReadLine

Loop Until R.IsMatch(Input)Console.WriteLine("Data accettata!")

Console.ReadKey()

End SubEnd Module

1. Campo1|Campo2|Campo3|...

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.32.33.

Module Module6Sub Main()

'Scandisce una riga del file, ottenendo i vari valoriDim R As New Regex( _"^(?<FirstName>[\w\s]+)\|(?<LastName>[\w\s]+)\|(?<BirthDay>.+)$", _RegexOptions.Multiline)Dim File As String

Console.WriteLine("Inserire il nome del file da caricare:")File = Console.ReadLine

If Not IO.File.Exists(File) Then

Console.WriteLine("File inesistente!")Exit Sub

End If

'Classe creata nelle prime lezioni sulle classiDim P As Person'Come sopraDim List As New PersonCollectionDim M As MatchDim Line As StringDim Reader As New IO.StreamReader(File)

While Not Reader.EndOfStream

'Legge una linea di testoLine = Reader.ReadLine'La confronta con l'espressione regolareM = R.Match(Line)'E se ha successo...If M.Success Then

Page 441: Guida visual basic

Un file di esempio:

Parsing di codiceÈ possibile anche analizzar e del codice per ottener e infor mazioni sulle sue var ie par ti. Ad esempio, si possono contar e

e r eper ir e tutte le pr ocedur e o tutte le var iabili, con codice annesso, ottener e le linee di codice e di commenti e così

via. Nel pr ogr amma Sour ce Scanner , ho usato le espr essioni r egolar i per scansionar e uno o più sor genti ed individuar e

tutte le occor r enze di ogni membr o di classe. Questo esempio mostr a come far e la stessa cosa con le pr ocedur e:

34.35.36.37.38.39.40.41.42.43.44.45.46.47.48.49.50.

'Crea un nuovo oggetto PersonP = New Person(M.Groups("FirstName").Value, _

M.Groups("LastName").Value, _Date.Parse(M.Groups("BirthDay").Value))

'Lo visualizza a schermoConsole.WriteLine("{0}, nato il {1}", P.CompleteName, _

P.BirthDay.ToShortDateString)'E lo aggiunge alla listaList.Persons.Add(P)

End IfEnd While

Reader.Close()Console.WriteLine("L'età media è {0} ", List.AverageAge)

Console.ReadKey()

End SubEnd Module

1.2.3.4.

Tizio Caio|Sempronio|21/12/2006Pinco|Pallino|13/01/2000Chissoio|Nonso|06/07/1990Mario|Rossi|19/06/1994

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.32.33.34.35.

Module Module7Sub Main()

'Scandisce una riga del file, ottenendo i vari valoriDim Argument As String = "(ByVal|ByRef) (?<Arg>\w+) As (?<Type>\w+)"Dim R As New Regex( _"\s*[\w\s]*\s*Sub\s+(?<Name>\w+)\((" & Argument & "\W*)*\)\s*$", _RegexOptions.Multiline)Dim File As String

Console.WriteLine("Inserire il nome del file da caricare:")File = Console.ReadLine

If Not IO.File.Exists(File) Then

Console.WriteLine("File inesistente!")Exit Sub

End If

For Each M As Match In R.Matches(IO.File.ReadAllText(File))Console.Write(M.Groups("Name").Value)Console.Write("(")'Dato che ci possono essere più argomenti, ogni gruppo Arg'e Type può venire catturato più volte. In questo caso'la proprietà Captures restituisce ogni singola'istanza del gruppoFor I As Int32 = 0 To M.Groups("Arg").Captures.Count - 1

Console.Write("{0} As {1}", M.Groups("Arg").Captures(I), _M.Groups("Type").Captures(I))If I < M.Groups("Arg").Captures.Count - 1 Then

Console.Write(", ")End If

NextConsole.WriteLine(")")

Next

Page 442: Guida visual basic

36.37.

Console.ReadKey()End Sub

End Module

Page 443: Guida visual basic

F4. Drag and Drop

Con il ter mine "Dr ag and Dr op" si indica una tecnica visuale che per mette di tr ascinar e dati da un contr ollo su un altr o

contr ollo con il solo ausilio del mouse. È assai utile poichè per mette all'utente di ottener e il massimo gr ado di

inter azione con il pr ogr amma con il minimo sfor zo. Per far sì che un contr ollo possa r ecepir e dati spostati mediante

Dr ag and Dr op, la sua pr opr ietà AllowDr op deve esser e impostata a Tr ue. L'oper azione di tr ascinamento inizia quando

viene pr emuto il pulsante sinistr o del mouse sul contr ollo, per ciò nell'evento MouseDown. Si cr ei ad esempio un for m

con due tex tbox vuote, e AllowDr op di una su Tr ue:

DoDr agDr op è un metodo appar tenente alla classe Contr ol e per ciò viene er editato da tutti i contr olli. Il pr imo

par ametr o è costituito dall'insieme dei dati da passar e nell'oper azione, mentr e il secondo è un enumer ator e che

definisce le modalità di spostamento. Queste non influiscono sul compor tamento del meccanismo a meno che non lo

voglia il pr ogr ammator e: infatti tutto il codice per il tr avaso e la manipolazione dei dati viene scr itto manualmente.

Or a che si possono iniziar e oper azioni di Dr ag&Dr op, non è tuttavia ancor a possibile por tar le a ter mine: manca infatti

il codice che gestisce il meccanismo sul contr ollo r icevente. Per pr ima cosa bisogna contr ollar e in entr ata, che ci siano

dati e, in questo caso, che siano coer enti con il contenuto del contr ollo. Per far questo si utilizza l'evento Dr agEnter ,

che notifica quando il mouse entr a nell'ar ea specificata.

Il ter zo passo è il più impor tante e per mette di scr iver e il pezzo di codice per la gestione effettiva dei dati. Quando il

mouse viene r ilasciato, si gener e l'evento Dr agDr op, nel quale si deve oper ar e:

In questo esempio si è cr eato un meccanismo molto semplice che per mette di tr ascinar e del testo da una tex tbox ad

un'altr a, ma nulla vieta di far lo con ar gomenti assai più complessi, come ad esempio il Dr ag&Dr op di file. Quest'ultimo si

può effettuar e dall'ex plor er di w indows sui pr ogr ammi .net semplicemente contr ollando che i dati siano coer enti a

DataFor mat.FileDr op: in questo caso i dati sono un ar r ay di str inghe contenenti i per cor si completi dei file.

1.2.3.4.5.6.7.

Private Sub TextBox1_MouseDown(ByVal sender As Object, _ByVal e As EventArgs) Handles TextBox1.MouseDown'Inizia l'operazione di Drag e Drop dalla textbox numero 1,'usando come dati da trasportare il suo testo. L'effetto'del mouse, invece, deve essere quello usato per la copiaTextBox1.DoDragDrop(TextBox1.Text, DragDropEffects.Copy)

End Sub

01.02.03.04.05.06.07.08.09.10.11.

Private Sub TextBox2_DragEnter(ByVal sender As Object, _ByVal e As DragEventArgs) Handles TextBox2.DragEnter'Se contiene i dati giusti di tipo StringIf e.Data.GetDataPresent(GetType(String)) Then

'Continua a copiaree.Effect = DragDropEffects.Copy

Else'Altrimenti annulla l'azionee.Effect = DragDropEffects.None

End IfEnd Sub

1.2.3.4.5.6.7.8.

Private Sub TextBox2_DragDrop(ByVal sender As Object, _ByVal e As DragEventArgs) Handles TextBox2.DragDrop'Ottiene i dati di tipo string presenti in memoriaDim S As String = e.Data.GetData(GetType(String))'Imposta il testo della seconda textbox uguale a quello'della primaTextBox2.Text = S

End Sub

Page 444: Guida visual basic

F5. La classe Graphics

La gr afica è una delle par ti meno usate, o meno compr ese, del Fr amewor k .NET. Essenzialmente ser ve a disegnar e

tutto quello che il suppor to .NET di per sé non è pr ogettato per far e. Ad esempio, si possono cr ear e gr afici, modificar e

immagini e r ipr odur r e effetti par ticolar i. Tutta l'infr astr uttur a di contr ollo della gr afica si basa su una classe

por tante, chiamata Gr aphics, che non possiede alcun costr uttor e: per questo motivo non è istanaziabile. Dopo aver

chiar ito un concetto del gener e, dovr ebbe sor ger e spontaneamente il dubbio su come si possa far e, allor a, per usar la,

dato che non espone metodi statici e che non può esser e inizializzata. La r isposta è semplice: ogni contr ollo possiede

un pr opr io oggetto Gr aphics associato, per mezzo del quale viene disegnato sullo scher mo e gr azie a cui il

pr ogr ammator e inter viene nella sua visualizzazione. Questo fa pensar e che in r ealtà il costr uttor e esista, ma sia

specificato come Pr ivate (o al massimo Fr iend) e per ciò accessibile solo all'inter no degli oscur i meccanismi .NET, i quali

si occupano di for nir ne uno a ogni contr ollo dur ante la costr uzione dell'inter faccia. Bisogna comunque r icor dar e che ci

sono metodi statici factor y per la cr ezione di Gr aphics a par tir e da altr e immagini o da altr e finestr e, ma nessuna

for nisce un nuovo oggetto vuoto.

N.B.: Diver samente dall'appr occio adottato nelle ver sioni pr ecedenti della guida, non user emo l'evento Paint per

disegnar e su un contr ollo. Tale evento viene gener ato ogniqualvolta un contr ollo deve esser e r idisegnato sullo scher mo

e per ciò, se ne facessimo uso, far emmo eseguir e lo stesso codice più volte senza bisogno. Piuttosto, user emo

un'alter nativa più elengate e decisamente più per for mante. Cr eer emo una nuova immagine vuota, associandovi un

oggetto Gr aphics, e disegner emo su questa immagine, che ver r à poi depositata su un contr ollo (o sul suo sfondo). È

possibile eseguir e questa oper azione fino a un centinaio di volte al secondo senza che l'utente si accor ga di quei

fastidiosi sfar falii che si avver tono quando si inser isce il codice di disegno nell'evento Paint.

Ecco or a un elenco dei membr i più impor tanti di Gr aphics:

Clear (C) : cancella tutto il contenuto di Gr aphics, nei suoi mar gini, e lo r impie con un color e unifor me definito da

C

CompositingMode : deter mina il modo in cui due o più immagini sovr apposte vengano disegnate. È deter minato

da un enumer ator e che assume solamente due valor i: Sour ceCopy (la par te più r ecente r impiazza quella

esistente, "sovr ascr ivendo" i pr opr i color i sui vecchi) e Sour ceOver (le due par ti vengono sovr apposte in modo

da for mar e una sfumatur a, in cui entr a pr epotentemente in gioco il fattor e Alpha, ossia la tr aspar enza, che

deter mina quale dei due color i pr evalga sull'altr o e come debbano esser e miscelati)

CompositingQuality : deter mina la qualità dell'oper azione di composizione sopr a illustr ata. I valor i

dell'enumer ator e sono pochi, e per mettono di sceglier e se ottener e una maggior qualità o una maggior velocità

oppur e se consider ar e il fattor e di cor r ezione gamma

CopyFr omScr een(P1, P2, Size) : questa pr ocedur a è davver o molto utile. Per mette di cattur ar e una par te dello

scher mo e r ipr odur la sul suppor to dell'oggetto Gr aphics (che, in definitiva, è la super ificie del contr ollo a cui

esso appar tiene). Accetta tr e ar gomenti: il pr imo, P1 As Point, deter mina il mar gine super ior e sinistr o della

r egione dello scher mo da cui pr elevar e l'immagine; il secondo, P2 As Point, deter mina il mar gine super ior e

sinistr o della r egione di Gr aphics su cui copiar e l'immagine; l'ultimo è di tipo Size e specifica lar ghezza e altezza

della r egione da pr elevar e. Ad esempio, si supponga che questo codice sia eseguito nell'evento Paint del for m:

Ebbene, il quadr ato di lato 200 pix el che inizia nel punto (0,0) dello scher mo (ossia in alto a sinistr a), ver r à

copiato nella super ficie del for m a par tir e dal punto (50,50)

DpiX, DpiY : r estituiscono r ispettivamente la r isoluzione su X e su Y, in punti per pix el

1.2.

e.Graphics.CopyFromScreen(New Point(0, 0), New Point(50, 50), _New Size(200, 200))

Page 445: Guida visual basic

Dr aw... : tutte le pr ocedur e che iniziano per "Dr aw " per mettono di disegnar e l'elemento cor r ispondente sul

suppor to di Gr aphics. A seconda dell'entità geometr ica, cambiano i par ametr i, che sono sempr e visibili gr azie al

fumetto che li sugger isce

Fill... : tutte le pr ocedur a che iniziano per "Fill" disegnano una figur a e la r iempiono con lo stesso color e

Fr omHdc(Ptr ) : inizializza e r estituisce un oggetto Gr aphics par tendo da un'immagine: di tale immagine si

dispone solo di un puntator e inter o (System.IntPtr ). Può esser e utilizzata in r ar i casi, ad esempio nel

Mar shalling di oggetti

Fr omHwnd(Ptr ) : inizializza e r estituisce un oggetto Gr aphics par tendo da una finestr a: di tale finestr a si

dispone solo dell'handle

Fr omImage(Img) : inizializza e r estituisce un oggetto Gr aphics par tendo da un'immagine Img As Image

Render ingOr igin : specifica quale sia l'or igine del r ender ing, ossia il punto consider ato (0,0)

ResetTr ansor m : annulla ogni tr asfor mazione

RotateTr ansfor m(A) : effettua una r otazione di A gr adi su tutti gli elementi di Gr aphics

ScaleTr ansfor m(sX, sY) : effettua una tr asfor mazione delle dimensioni, mltiplicandole per sX su X e per sY su Y

SmoothingMode : deter mina quale modalità usar e per smussar e linee e per cor si cur vi. Per un buon r isultato si

può usar e l'anti-alias, che r iduce di molto le sgr anatur e evidenti pr odotte dai pix el

Tr ansfor mation : r estituisce o imposta una matr ice che r appr esenta tutti i cambiamenti dello spazio 2D

Tr anslateTr ansfor m(dX, dY) : tr asla tutto il contenuto di Gr aphics di un vettor e (dX, dY)

Nell'esempio che segue, scr iver ò un pr ogr amma per disegnar e gr afici a tor ta bidimensionali.

Il tutto si divide in due diver si sor genti: una libr er ia di classi Gr aphItems e l'applicazione pr incipale.

GraphItemsLa libr er ia espone tr e classi. La pr ima è Gr aphItem, una classe astr atta che r appr esenta la base per gli altr i elementi.

Si usa questo tipo di tecnica poichè ser vir à immagazzinar e diver si tipi di elementi in una sola lista: per evitar e liste a

tipizzazione debole come Ar r ayList, si usa una lista a tipizzazione for te in cui il tipo gener ics collegato è costituito da

una classe base comune a tutte. Accade molto spesso di usar e questa tecnica, per ciò fate attenzione.

La seconda classe esposta r appr esenta uno spicchio del gr afico a tor ta e contiene le infor mazioni e la pr ocedur a per

poter lo disegnar e. La ter za, invece, r appr esenta l'etichetta cor r ispondente al color e nella legenda: il r isultato che

visualizzer à sullo scher mo è un quadr atino color ato al cui fianco pr esenzia la didascalia associata al color e e il suo

valor e. Ecco il codice:

001.002.003.004.005.006.007.008.009.010.011.012.013.014.015.016.017.018.019.020.021.022.023.024.

'Questa classe astratta costituisce la base per ogni'elemento che andrà ad essere disegnato sul controlloPublic MustInherit Class GraphItem

'Per disegnare delle forme geometriche con i metodi Draw'si usano oggetti di tipo Pen (penna): una penna definisce'il colore usato per tracciare le linee e il loro'spessore. Sono presenti delle penne predefinite nella'classe statica Pens: una per ogni colore (per tutte,'l'ampiezza del tratto è costante e pari a 1).'Noi useremo sempre delle penne nere per il contorno'dei prossimi oggetti, ma ho voluto aggiungere'questo membro per completezza.Private _ColorPen As Pen 'Allo stesso modo, per riempire delle forme deometriche'coi metodi Fill, si usano i pennelli (Brush). Brush è'una classe astratta che costituisce la base di tutti'i pennelli derivati. Noi useremo dei SolidBrush, oggetti'che colorano un'area con colore uniforme. Tuttavia, come'spiegherò in seguito, esistono molti altri tipi'di pennelli, ad esempio per eseguire sfumature o per'riempire un'area con delle immaginiPrivate _ColorBrush As Brush

Page 446: Guida visual basic

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.084.085.086.087.088.089.090.091.092.093.094.095.096.

Public Property ColorPen() As Pen

GetReturn _ColorPen

End GetSet(ByVal Value As Pen)

_ColorPen = ValueEnd Set

End Property

Public Property ColorBrush() As BrushGet

Return _ColorBrushEnd GetSet(ByVal Value As Brush)

_ColorBrush = ValueEnd Set

End Property

'Ogni elemento deve esporre la procedura Draw,'per mezzo della quale esso disegnerà la propria'rappresentazione sul supporto grafico specificato'da G. Come già accennato, disegneremo tutto'su un'immagine vuota creata da noiPublic MustOverride Sub Draw(ByVal G As Graphics)

End Class 'Un pezzo di torta XDPublic Class PiePiece

Inherits GraphItem

'I parametri necessari a disegnarla sono: il centro'della torta, il raggio, l'ampiezza (in gradi) e'l'angolo inizialePrivate _Center As PointPrivate _Radius As Int32Private _StartAngle, _EndAngle As Single

'CentroPublic Property Center() As Point

GetReturn _Center

End GetSet(ByVal Value As Point)

_Center = ValueEnd Set

End Property

'RaggioPublic Property Radius() As Int32

GetReturn _Radius

End GetSet(ByVal Value As Int32)

_Radius = ValueEnd Set

End Property

'Angolo di partenzaPublic Property StartAngle() As Single

GetReturn _StartAngle

End GetSet(ByVal Value As Single)

_StartAngle = ValueEnd Set

End Property

'AmpiezzaPublic Property SweepAngle() As Single

GetReturn _EndAngle

Page 447: Guida visual basic

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.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.

End GetSet(ByVal Value As Single)

_EndAngle = ValueEnd Set

End Property

Sub New(ByVal Center As Point, ByVal Radius As Int32, _ByVal StartAngle As Single, ByVal SweepAngle As Single)Me.Center = CenterMe.Radius = RadiusMe.StartAngle = StartAngleMe.SweepAngle = SweepAngle

End Sub

Public Overrides Sub Draw(ByVal G As Graphics)'Calcola il quadrato in cui è inscritta la circonferenza'della quale lo spicchio fa parteDim UpperLeft As New Point(Me.Center.X - Me.Radius, _

Me.Center.Y - Me.Radius)'Calcola la dimensione di tale quadratoDim Size As New Size(Me.Radius * 2, Me.Radius * 2) 'Riempie il pezzo di torta con il colore. FillPie'riempie col pennello specificato un settore circolare'dell'ellisse inscritto nel rettangolo passato come'parametro, a partire dall'angolo StartAngle,'spazzando un angolo SweepAngleG.FillPie(Me.ColorBrush, New Rectangle(UpperLeft, Size), _

Me.StartAngle, Me.SweepAngle)'Quindi disegna il contorno del pezzo in nero. Gli'argomenti sono gli stessi, ad eccezione della penna'al posto del pennello. Pens.Black è una'penna nera di tratto 1G.DrawPie(Pens.Black, New Rectangle(UpperLeft, Size), _

Me.StartAngle, Me.SweepAngle)End Sub

End Class 'Un'etichetta che visualizza il colore e il testo'corrispondentePublic Class ColorLabel

Inherits GraphItem

'I parametri necessari a disegnarla sono: il testo,'le coordinate e il colore, che viene definito'nella classe basePrivate _Text As StringPrivate _Location As Point

'TestoPublic Property Text() As String

GetReturn _Text

End GetSet(ByVal Value As String)

_Text = ValueEnd Set

End Property

'CoordinatePublic Property Location() As Point

GetReturn _Location

End GetSet(ByVal Value As Point)

_Location = ValueEnd Set

End Property

Sub New(ByVal Text As String)Me.Text = Text

End Sub

Page 448: Guida visual basic

L'applicazione princ ipaleL'applicazione pr incipale contiene due componenti: un DataGr idView e una Pictur eBox . Per veder e come li ho

impostati, guar date lo scr eenshot in fondo alla pagina.

169.170.171.172.173.174.175.176.177.178.179.180.

181.182.

Public Overrides Sub Draw(ByVal G As System.Drawing.Graphics)

'Disegna un quadratino coloratoG.FillRectangle(Me.ColorBrush, New Rectangle(Me.Location, _

New Size(20, 10)))'Disegna il contorno nero al quadratinoG.DrawRectangle(Pens.Black, New Rectangle(Me.Location, _

New Size(20, 10)))

'Disegna il testo'New Font... inizializza un nuovo font, ossia Microsoft'Sans Serif di dimensione 12, senza stili aggiuntiviG.DrawString(Me.Text, New Font("Microsoft Sans Serif", 12, FontStyle.Regular),

Brushes.Black, Me.Location.X + 30, Me.Location.Y - 5)End Sub

End Class

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.

Class Form1Private Items As New List(Of GraphItem) Private Sub cmdDraw_Click(ByVal sender As Object, ByVal e As EventArgs) _

Handles cmdDraw.ClickDim Total As Single

Items.Clear() 'Calcola il totaleFor Each Row As DataGridViewRow In dgvValues.Rows

'Controlla che il valore sia diverso da NULLIf Row.Cells Is Nothing Then

Continue ForEnd If'Quindi somma il valore della cella al totaleTotal += Row.Cells(0).Value

Next

'Costruisce gli spicchi'Valore di una rigaDim Value As Single'Variabile ausiliare del ciclo: tiene traccia dell'angolo a cui'si è arrivatiDim PrevAngle As Single = 0'Anche questa, come sopra: tiene traccia a quale altezza si'è arrivati con la legendaDim Y As Int32 = 20'Testo della rigaDim Text As String'Pennello > coloreDim Br As Brush'Una etichetta della legendaDim Lab As ColorLabel'Un pezzo della tortaDim Piece As PiePiece

For Each Row As DataGridViewRow In dgvValues.Rows

'Controlla che i valori esistano e che la cella non'sia l'ultima (che è sempre vuota)If Row.Cells Is Nothing OrElse _

Row.Index = dgvValues.RowCount - 1 ThenContinue For

End If

Value = Row.Cells(0).Value'Costruisce il testo della legenda, formato da quello della

Page 449: Guida visual basic

Ed ecco un esempio di come si pr esenter à alla fine, tutta l'applicazione:

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.

'riga, con la specificazione, tra parentesi, del valore'corrispondente e della percentualeText = String.Format("{0} ({1:N2} - {2:N2}%)", _

Row.Cells(1).Value, Value, Value * 100 / Total)'Questo sempre per l'intelligenza di DataGridView,Select Case Row.Cells(2).Value

Case "Rosso"Br = Brushes.Red

Case "Arancio"Br = Brushes.Orange

Case "Giallo"Br = Brushes.Yellow

Case "Verde"Br = Brushes.Green

Case "Azzurro"Br = Brushes.LightBlue

Case "Indaco"Br = Brushes.Blue

Case "Viola"Br = Brushes.Violet

Case "Nero"Br = Brushes.Black

End Select

'Inizializza la nuova etichettaLab = New ColorLabel(Text)Lab.ColorBrush = BrLab.Location = New Point(280, Y)

'E il nuovo pezzo di torta. Value * 360 / Totale è'l'ampiezza dell'angolo, ottenuta con la proporzione:'Value : Total = x : 360Piece = New PiePiece(New Point(150, 125), 100, _

PrevAngle, Value * 360 / Total)Piece.ColorBrush = Br

'Tiene traccia dell'angoloPrevAngle += Value * 360 / Total'Si sposta più in giù per la prossima etichettaY += 20'Aggiunge gli elementiItems.Add(Lab)Items.Add(Piece)

Next

'Crea una nuova immagine vuotaDim Img As New Bitmap(imgPreview.Width, imgPreview.Height)'Prende l'oggetto Graphics associato a quell'immagineDim G As Graphics = Graphics.FromImage(Img)'Attiva l'anti-aliasG.SmoothingMode = Drawing2D.SmoothingMode.AntiAlias'E disegna ogni elementoFor Each Item As GraphItem In Me.Items

Item.Draw(G)Next'Ogni cosa disegnata mediante G verrà trasferita'sull'immagine Img associataG.Flush()

imgPreview.Image = Img

End SubEnd Class

Page 450: Guida visual basic
Page 451: Guida visual basic

F6. Utilizzo avanzato della classe Graphics

Penne alternativeNel capitolo pr ecedente abbiamo impiegato penne pr edefinite. Or a vogliamo cer car e qualcosa di più accattivante. Dopo

aver inizializzato un nuovo oggetto Pen, possiamo modificar ne le pr opr ietà:

Br ush : associando un pennello a questa pr opr ietà è possibile r iempir e il tr atto della penna mediante tale

pennello con sfumatur e, disegni, immagini, ecceter a...

CompoundAr r ay : ammettiamo che l'oggetto Pen abbia una lar ghezza abbondante, ad esempio 10. Tutto il tr atto

viene r iempito unfir memente con un color e (a meno che non abbiate modificato la pr opr ietà Br ush). Mediante

questa pr opr ietà possiamo decider e di r impiazzar e il blocco cr omatico con più str isce color ate di lar gezza

definita. Ecco un esempio:

La penna lascia un tr atto di lar ghezza 14, ma esso non è unifor ma. La pr opr ietà

CompoundAr r ay è di tipo ar r ay di Single. In questo ar r ay vanno specificate delle posizioni

per centuali, che indicano l'inter vallar si di str isce e spazi. Nel codice sopr a, ho specificato che da

0% a 20% della lar ghezza ci deve esser e una linea, dal 20% al 40% uno spazio, dal 40% all'80% una

linea, dall'80% al 90% uno spazio e dal 90% al 100% una linea. Il tutto ha pr odotto un r isultato come

quello in figur a.

DashStyle : per mette di sceglier e un differ ente tipo di tr atteggio tr a alcuni pr edefiniti. Eccone un esempio:

L'ultimo è stato cr eato modificando la pr opr ietà DashPatter n: è anch'essa un'ar r ay di Single, ma specifica la

lunghezza in pix el di un tr atto e di uno spazio (in figur a er a 5, 5, 10, 5, ossia 5px di linea, 5 di spazio, 10 di

linea e 10 di spazio). Si tr atta di pix el poiché il tr atto si estende in lunghezza (quindi non si possono specificar e

valor i r elativi)

DashCap : deter mina la for ma degli estr emi dei punti o delle linee che costituiscono una linea tr atteggiata. Ecco

un esempio, combinato con la pr opr ietà CompoundAr r ay:

1.2.3.4.5.6.7.8.

Dim b As New Bitmap(300, 300)Dim g As Graphics = Graphics.FromImage(b)Dim p As New Pen(Color.Black, 14) p.CompoundArray = New Single() {0, 0.2, 0.4, 0.8, 0.9, 1}g.SmoothingMode = Drawing2D.SmoothingMode.AntiAliasg.Clear(Color.White)g.DrawLine(p, 10, 10, 80, 100)

Page 452: Guida visual basic

Star tCap / EndCap : specifica la for ma geometr ica posta all'inizio o alla fine della linea (di tutta la linea). Simile a

DashCap, ma con molte più var ianti.

Pennelli alternativiOltr e a SolidBr ush, esistono alcuni altr i pennelli con car atter istiche peculiar i. Ad esempio:

HatchBr ush : r iempie una super ficie con una tr ama specificata. Qui ci sono tutte le var ianti;

Linear Gr adientBr ush : esegue una sfumatur e sull'ar ea da r iempir e. Potete consultar e alcuni esempi nella sezione

Appunti;

Tex tur eBr ush : r iempie un'ar ea specificata con un'immagine, eventualmente r ipetendola e/o allungandola.

Esempio

Esempio 1: Creare una userbarPer mostr ar vi qualche utilizzo pr atico e non pr opr io basilar e alla gr afica in .NET voglio mostr ar e come sia possibile

disegnar e una user bar simile a quelle cr eate con Photoshop.

Per cr ear e una user bar si seguono più o meno sempr e questi passaggi:

si pr ende uno sfondo di dimensioni 350x 19 (for mato standar d), oppur e si r iempie quest'ar ea con un gr adiente

color ato e vi si applica sopr a un'altr a por zione di immagine;

si applica sullo sfondo una tr ama omogenea for mata da r ighe diagonali molto sottili e r avvicinate di color e

scur o;

si cr ea un effetto lucido sovr apponendo all'immagine così cr eata una semiellisse di color e bianco, con una

tr aspar enza del 20-30%;

per ultimar e il lavor o, si pone una scr itta sulla par te destr a della bar r a, di solito usando il font Visitor TT2

BRK.

Noi automatizzer emo tutto questo cr eando una classe apposita:

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.

Namespace Userbars

Class UserbarPrivate _BackgroundImage As ImagePrivate _BackgroundImagePosition As Int32Private _Text As StringPrivate _Size As Size = New Size(350, 25)

Public Property Size() As Size

GetReturn _Size

End GetSet(ByVal value As Size)

If value.Width > 0 And value.Height > 0 Then_Size = value

Else_Size = New Size(350, 25)

End IfEnd Set

End Property

Public Property BackgroundImage() As ImageGet

Return _BackgroundImageEnd Get

Page 453: Guida visual basic

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.

Set(ByVal value As Image)If value.Width > Me.Size.Width Or value.Height > Me.Size.Height Then

Throw New ArgumentOutOfRangeException()Else

_BackgroundImage = valueEnd If

End SetEnd Property

Public Property BackgroundImagePosition() As Int32

GetReturn _BackgroundImagePosition

End GetSet(ByVal value As Int32)

If value > Me.Size.Width ThenThrow New ArgumentOutOfRangeException()

Else_BackgroundImagePosition = value

End IfEnd Set

End Property

Public Property Text() As StringGet

Return _TextEnd GetSet(ByVal value As String)

_Text = valueEnd Set

End Property

Public Function Create() As ImageDim Result As New Bitmap(Me.Size.Width + 1, Me.Size.Height + 1)Dim G As Graphics = Graphics.FromImage(Result)

'Questi valori sono statici poiché costanti. Una'volta inizializzati saranno sempre uguali e non'verranno creati ulteriori oggetti ad ogni invocazione'di Create() 'HatchBrush permette di riempire un'area con una trama'prefissata. Noi vogliamo disegnare delle sottili righe'scure, un motivo a cui corrisponde l'enumeratore'indicato.'Il colore usato è un Nero con opacità pari'a 48: dato che il valore massimo è 255, si tratta di'un nero al 19% di opacità. Il colore di sfondo è'invece trasparente (come se non ci fosse).Static LinesBrush As New HatchBrush(HatchStyle.DarkUpwardDiagonal,

Color.FromArgb(48, Color.Black), Color.Transparent)'Il font usato è Visitor TT2 BRK, grandezza 11pt.'11 va molto bene per le userbar di dimensione'standard. Se volete qualcosa di più generale,'il font deve dipendere dall'altezzaStatic FontUsed As New Font("Visitor TT2 BRK", 11, FontStyle.Regular)'Questo pennello servirà per l'effetto lucido. Dato'che si tratta di un SolidBrush, riempirà l'area con'un colore omogeneo, in questo caso un bianco'trasparenteStatic TranspBrush As New SolidBrush(Color.FromArgb(70, Color.White))

'Imposta la modalità di smussamento delle linee. Dato'che questa funzione viene usata solo quando l'utente 'la richiede (quindi non molte volte al secondo), e che'il risultato deve essere il migliore possibile,'utilizziamo un algoritmo ad alto rendimento e'bassa velocità di rendering.G.SmoothingMode = SmoothingMode.HighQuality'Imposta la modalità di sovrapposizione. Poiché'dobbiamo disegnare molte cose le una sopra alle altre, ci'serve che i nuovi disegni non sovrascrivano quelli

Page 454: Guida visual basic

Ed ecco un esempio:

Come noter ete, la pr opr ietà Backgr oundImagePosition ha senso solo se l'immagine è di lar ghezza infer ior e alla

user bar , ossia nel caso in cui lo sfondo debba esser e r iempito con un gr adiente unifor me (Linear Gr adientBr ush). Non

ho implementato questa funzionalità nel codice, ma la lascio come eser cizio. Potete usar e come r ifer imento il mio

ar ticolo al r iguar do nella sezione Appunti.

Esempio 2: Orologio analogicoEcco uno stupidissimo codice di esempio per disegnar e un or ogoloio:

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.

'precedenti, ma vi si applichino sopra rispettandone'le trasparenzeG.CompositingMode = CompositingMode.SourceOver'Disegna l'immagine di sfondoG.DrawImage(Me.BackgroundImage, Me.BackgroundImagePosition, 0)'Disegna le righe su tutta l'immagineG.FillRectangle(LinesBrush, 0, 0, Me.Size.Width, Me.Size.Height)'Applica il velo di bianco trasparente. Notate che'utilizzo delle coordinate negative per disegnare'l'ellisse fuori dall'immagine. In questo modo,'noi vedremo solo la parte di ellisse che rientra'nell'area effettivamente esistente, ossia solo metà.'Inoltre ho messo qualche coefficiente per aggiustare'la larghezza e rendere migliore l'aspettoG.FillEllipse(TranspBrush, -5, -Me.Size.Height + 3, Me.Size.Width + 10,

CInt(Me.Size.Height * 1.5))'Disegna il contorno della barraG.DrawRectangle(Pens.Black, 0, 0, Me.Size.Width, Me.Size.Height)

'Calcola la dimensione del testo (in pixel)Dim TextSize As SizeF = G.MeasureString(Me.Text, FontUsed)'Quindi disegna la stringa Text in bianco, spostata'rispetto al margine destro in modo che il testo non'vada fuori dall'immagine.G.DrawString(Me.Text, FontUsed, Brushes.White, Me.Size.Width -

CInt(TextSize.Width) - 30, Me.Size.Height \ 3)

'Restituisce il risultatoReturn Result

End Function

End Class

End Namespace

01.02.03.04.05.

06.07.08.09.10.11.12.13.14.15.16.17.18.

Class Form1Private ClockImage As New Bitmap(230, 230)Private G As Graphics = Graphics.FromImage(ClockImage)

Private Sub tmrClock_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs)

Handles tmrClock.TickG.Clear(Me.BackColor)

Dim Time As Date = Date.Now

Static BorderPen As New Pen(Color.Black, 5)Static HourPen As New Pen(Color.Red, 4) With {.EndCap = LineCap.Triangle}Static MinutePen As New Pen(Color.Blue, 2) With {.EndCap = LineCap.Triangle}Static SecondPen As New Pen(Color.Green, 1)

Dim C As Point = New Point(ClockImage.Width / 2, ClockImage.Height / 2)Dim h, m, s As Single

Page 455: Guida visual basic

Ulteriori esempiNella sezione Download ci sono numer osi pr ogr ammi che utilizzando Gr aphics in modo intensivo. Tr a questi: Cur ve Ar t,

Totem Char ting, TWave Editor , Wave, File Compar er , Data View er , MGr aphing, ecceter a... E vi r icor do che sono tutti

open sour ce, quindi potete studiar ne il codice liber amente.

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.

h = Time.Hour + (Time.Minute / 60)m = Time.Minute + (Time.Second / 60)s = Time.Second

G.SmoothingMode = SmoothingMode.AntiAlias

G.FillEllipse(Brushes.White, 5, 5, ClockImage.Width - 10, ClockImage.Height - 10)G.DrawEllipse(BorderPen, 5, 5, ClockImage.Width - 10, ClockImage.Height - 10)

For I As Int32 = 0 To 11

G.FillEllipse(Brushes.Black, _C.X + CInt(ClockImage.Width / 2.3 * Math.Cos(Math.PI / 2 - I / 6 * Math.PI)) -

1, _C.Y - CInt(ClockImage.Width / 2.3 * Math.Sin(Math.PI / 2 - I / 6 * Math.PI)) -

1, _2, 2)

Next

G.DrawLine(HourPen, C.X, C.Y, _C.X + CInt(ClockImage.Width / 4 * Math.Cos(Math.PI / 2 - h / 6 * Math.PI)), _C.Y - CInt(ClockImage.Width / 4 * Math.Sin(Math.PI / 2 - h / 6 * Math.PI)))

G.DrawLine(MinutePen, C.X, C.Y, _

C.X + CInt(ClockImage.Width / 3 * Math.Cos(Math.PI / 2 - m / 30 * Math.PI)), _C.Y - CInt(ClockImage.Width / 3 * Math.Sin(Math.PI / 2 - m / 30 * Math.PI)))

G.DrawLine(SecondPen, C.X, C.Y, _

C.X + CInt(ClockImage.Width / 2.2 * Math.Cos(Math.PI / 2 - s / 30 * Math.PI)), _C.Y - CInt(ClockImage.Width / 2.2 * Math.Sin(Math.PI / 2 - s / 30 * Math.PI)))

imgClock.Image = ClockImage

End SubEnd Class

Page 456: Guida visual basic

F7. Usare la stampante

C'è un motivo per cui ho posizionato pr opr io qui questo capitolo, che in appar enza avr ebbe dovuto tr ovar si nella

sezione B: per stampar e bisogna usar e... la gr afica! E già, bisogna disegnar si tutto da sé.

Il contr ollo Pr intDialog ser ve soltanto a sceglier e le impostazioni adatte e la stampante giusta, ma il r esto viene fatto

per mezzo di un altr o oggetto Pr intDocument, che espone il metodo Pr int. Non deve ingannar e questo nome, poiché dà

solamente inizio al pr ocesso, ma ogni oper azione deve esser e svolta dal pr ogr ammator e. Infatti, una volta avviato,

viene lanciato l'evento Pr intPage, gener ato ogniqualvolta ci sono una o più pagine da stampar e. All'inter o del

sottoscr ittor e di questo evento va scr itto il codice. Ad esempio, si pr enda l'esempio del capitolo F5, aggiungendo poi un

pulsante cmdPr int (Tex t = "Stampa"). Ecco il codice:

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.32.33.34.35.36.37.38.39.40.41.42.43.44.45.46.47.48.49.50.51.52.

'Ricordarsi di importare questo namespaceImports System.Drawing.PrintingClass Form1

'...

Private Sub cmdPrint_Click(ByVal sender As Object, _

ByVal e As EventArgs) Handles cmdPrint.Click'Una nuova finestra di dialogo per la stampanteDim PrintDialog As New PrintDialog'Il nuovo oggetto che fa da mediatore tra l'utente e'la stampanteDim PrintDoc As New PrintDocument

With PrintDialog

'Determina se sia possibile stampare su un file.AllowPrintToFile = False'Determina se sia possibile stampare solo la'selezione. In questo caso non c'è nessuna selezione'quindi non ci sono problemi a disativare'l'impostazione.AllowSelection = False'Determina se sia possibile stampare delle pagine in'particolare. Vale lo stesso discorso fatto sopra.AllowSomePages = False'Assegna l'oggetto PrintDoc a Document, così da'collegare le impostazioni selezionate al documento.Document = PrintDoc

End With

If PrintDialog.ShowDialog = Windows.Forms.DialogResult.OK Then'Copia le impostazioni di stampa nel documentoPrintDoc.PrinterSettings = PrintDialog.PrinterSettings'Quindi aggiunge l'handler d'evento per PrintpageAddHandler PrintDoc.PrintPage, AddressOf PrintDocument_PrintPage'Il nome del documento visualizzato sulla finestra'di stampaPrintDoc.DocumentName = "Grafico a torta"'Fa partire il processo di stampaPrintDoc.Print()

End IfEnd Sub

Private Sub PrintDocument_PrintPage(ByVal sender As Object, _

ByVal e As PrintPageEventArgs)'Bisogna considerare anche i margini della pagina, perciò'sposta l'origine più in basso, come specificato'dai margini superiore e sinistro selezionati in PrintDialoge.Graphics.RenderingOrigin = New Point(e.MarginBounds.Left, _

e.MarginBounds.Top)

Page 457: Guida visual basic

Or a spostiamoci su qualcosa di meno semplice. Bisogna stampar e un file di testo. Ecco un esempio del codice di stampa:

53.54.55.56.57.58.59.60.61.62.63.64.65.

'In questo caso le operazioni sono molto semplici: basta'"disegnare" sulla stampante (ossia sullo stream che'permette l'interazione con essa) gli stessi elementi'presenti nella lista ItemsFor Each Item As GraphItem In Me.Items

Item.Draw(e.Graphics)Next

'Sicuramente ci sta tutto in una pagina, quindi specifica'che non ci sono più pagine da stampare.e.HasMorePages = False

End Sub End Class

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.32.33.34.35.36.37.38.39.40.41.42.43.44.45.46.47.48.49.50.51.52.53.54.

Class Form1Private Reader As IO.StreamReader

Private Sub cmdPrintFile_Click(ByVal sender As Object, _

ByVal e As EventArgs) Handles cmdPrintFile.ClickDim PrintDialog As New PrintDialogDim Open As New OpenFileDialogDim PrintDoc As New PrintDocument

With PrintDialog

.AllowPrintToFile = False

.AllowSelection = False

.AllowSomePages = False

.Document = PrintDocEnd With

Open.Filter = "File di testo|*.txt"

If Open.ShowDialog = Windows.Forms.DialogResult.OK Then

Reader = New IO.StreamReader(Open.FileName)Else

Exit Sub

End If

If PrintDialog.ShowDialog = Windows.Forms.DialogResult.OK ThenPrintDoc.PrinterSettings = PrintDialog.PrinterSettingsAddHandler PrintDoc.PrintPage, AddressOf PrintFile_PrintPagePrintDoc.DocumentName = IO.Path.GetFileName(Open.FileName)PrintDoc.Print()

End If

End Sub

Private Sub PrintFile_PrintPage(ByVal sender As Object, _ByVal e As PrintPageEventArgs)'Imposta l'unità di misura per le misurazioni'successivee.Graphics.PageUnit = GraphicsUnit.Pixel'Il font per il testo da stampare: Times New Romand 12ptStatic Font As New Font("Times New Roman", 12)'Per sapere quante righe ci possono stare nella pagine,'bisogna misurare l'altezza dei caratteriStatic CharHeight As Single = Font.GetHeight(e.Graphics)'Calcola le linee di testo che possono stare in una paginaStatic TotalLines As Int16 = e.MarginBounds.Height / CharHeight

'La linea a cui si è arrivati a leggereDim LineIndex As Int16 = 1'Il testo della rigaDim Line As String

'Tiene conto della posizione attuale

Page 458: Guida visual basic

In conclusione, si tr atta di far e pr atica, poiché la teor ia è molto semplice.

Eccezioni alla regolaÈ pur ver o che per stampar e dati che abbiamo cr eato noi, nel nostr o pr ogr amma, è necessar io usar e un codice simile

a quelli sopr a r ipor tati, tuttavia esiste una scappatoia a questa fatica. Se il nostr o obiettivo consiste solamente nello

stampar e un file per cui esista un pr ogr amma che ne gestisca la stampa, allor a basta eseguir e queste poche r ighe di

codice:

Il pr ocesso avviato gestir à la stampa del file mediante un pr ogr amma ester no. Come già detto, per ò, è necessar io che

esista un pr ogr amma del gener e installato sulla macchina dell'utente. Per contr ollar e la possibilità di eseguir e questo

codice, bisogna ver ificar e l'esistenza della chiave HKEY_CLASSES_ROOT\[ex tkey]\shell\pr int\command, dove [ex tkey] è il

valor e pr edefinito della chiave il cui nome cor r isponde all'estensione del file da stampar e. Ad esempio, vogliamo

stampar e il file "r eadme.tx t". Essendo un file di testo, cer chiamo nel r egistr o di sistema la chiave

HKEY_CLASSES_ROOT\.tx t. Il suo valor e (Pr edefinito) è "tx tfile". Rechiamoci allor a alla chiave HKEY_CLASSES_ROOT\tx tfile:

ver ifichiamo che esista la sottochiave shell\pr int\command. Esiste! Quindi siamo a posto.

Si può r ipeter e lo stesso pr ocedimento per tutti i tipi di file. Per saper e come ispezionar e il r egistr o di sistema da

codice, veder e capitolo r elativo.

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.

Dim Y As Int16 = e.MarginBounds.Top

e.Graphics.RenderingOrigin = New Point(e.MarginBounds.Left, _e.MarginBounds.Top)

Do

'Legge la riga di testo dal fileLine = Reader.ReadLine

'Si suppone che la larghezza della stringa sia minore'di quella della paginae.Graphics.DrawString(Line, Font, Brushes.Black, _

e.MarginBounds.Left, Y)Y += CharHeightLineIndex += 1

Loop While (LineIndex < TotalLines) And Not (Reader.EndOfStream)

'Se il file è alla fine, non ci sono più pagine'da stampare, altrimenti continuae.HasMorePages = Not Reader.EndOfStreamIf Reader.EndOfStream Then

Reader.Close()End If

End Sub

End Class

1.2.3.4.

Dim P As New ProcessP.StartInfo.FileName = "file da stampare"P.StartInfo.Verb = "Print"P.Start()

Page 459: Guida visual basic

F8. Manipolazione di file XML

XML è un acr onimo per eXtensibale Mar kup Language (Linguaggio di Contr assegno Estensibile). Si tr atta di un

linguaggio per mezzo del quale è possibile immagazzinar e dati in una str uttur a for temente ger ar chica e or ganizzata,

un modello ideale che r ispecchia appieno il meccanismo della pr ogr ammazione or ientata agli oggetti. L'XML è oggi

usatissimo in un gr an numer o di casi, e la sua gr ande diffusione si deve attr ibuir e alla sua flessibilità. Non so se "i miei

venticinque lettor i" conoscano l'HTML, ma è pr obabile di sì. Nell'HTML ci sono tag e pr opr ietà definiti: il web master può

usar e solamente quelli. Al contr ar io, in XML è il pr ogr ammator e che definisce i tag e gli attr ibuti, ossia la g rammatica

con cui il documento viene scr itto.

Pr ima di iniziar e, segue una br eve intr oduzione all'XML.

Introduzione all'XMLUn documento XML è un file di testo contenente dati incasellati in una str uttur a ger ar chico-logica for temente definita.

Ogni par te di questa str uttur a viene detta elemento (o nodo in analogia con la for mazione "ad alber o" già descr itta

con il contr ollo Tr eeView). Ogni elemento può posseder e infor mazioni ulter ior i che ne specificano le pr opr ietà

peculiar i: tali infor mazioni sono specificate sotto for ma di attr ibuti. L'elemento pr incipale, di or dine super ior e a tutti

gli altr i, si chiama elemento root o semplicemente root. L'unica entità in gr ado di star e allo stesso livello del r oot è

la dichiar azione della ver sione x ml o altr e dir ettive di inter pr etazione. Ecco un esempio di semplice file:

Questo codice sintetizza i pr imi due libr i che mi sono capitati in mano: ne specifica alcuni dettagli e r ipor ta un pezzo

della pr ima fr ase del pr imo capitolo. Par tendo da questo sor gente, biblioteca, libr o e capitolo sono elementi, mentr e

titolo, autor e, numer o e pagina sono attr ibuti di questi elementi. Biblioteca è il r oot. Dopo aver letto e individuato le

var ie par ti del documento, bisogna far e alcune osser vazioni:

Tutti i valor i, qualsiasi sia il lor o tipo, vanno r acchiusi tr a apici singoli o doppi

Ogni elemento aper to deve esser e chiuso. Se un elemento non ha contenuto si può usar e la sintassi abbr eviata

Tutto il testo è analizzato dai par ser in modalità case-sensitive

Ogni identificator e di elemento o attr ibuto deve esser e un nome valido. Per questo ver so segue le stesse r egole

per la cr eazione di un nome di var iabile VB, ad eccezione della possibilità di usar e il tr attino

Il fatto che ci sia piena liber tà nella cr eazione dei nomi e della pr opr ia gr ammatica non deve far consider ar e l'XML

come un linguaggio poco r igor oso. Ci sono, infatti, degli speciali tipi di file, detti Schema, che sono in gr ado di definir e

con cor r ettezza e pr ecisione tutta la gr ammatica di un dato tipo di documenti: possono indicar e, ad esempio, quali tag

01.02.03.04.05.06.07.08.09.10.11.12.13.

<?xml version="1.0" ?><biblioteca><libro titolo="I sei numeri dell'universo" autore="Martin Rees">

<capitolo titolo="Il cosmo e il microcosmo" numero="1" pagina="11">Alla base della struttura del nostro universo - non solo ...

</capitolo></libro><libro titolo="Le ostinazioni di un matematico" autore="Didier Nordon">

<capitolo titolo="Dalle stalle alle stelle" numero="1" pagina="11">Ecco a voi un romanzo il cui protagonista conosce una morte ...

</capitolo></libro></biblioteca>

1. <elemento attributo="valore" ... />

Page 460: Guida visual basic

possono esser e nidificati in quali altr i; quali valor i possa assumer e un dato attr ibuto; quanti elementi sia possibile

definir e per un cer to tag padr e; ecceter a... Queste r egole sono talmente potenti che esistono dei parser , ossia

inter pr etator i di codice, che possono for nir e gli stessi sugger imenti che for nisce l'Intellisense del .Net per un dato

Schema, oltr e al fatto di poter convalidar e un documento contr ollando che vengano r ispettati i pr incipi definiti. Dato

che questo non è un cor so di XML, con l'intr oduzione mi fer mo qui, ma siete liber i di appr ofondir e l'ar gomento in altr a

sede, ad esempio qui.

Uso di XML in ambiente .NETDescr iver e dettagliatamente tutte le classi atte alla manipolazione dei documenti XML sar ebbe un enor me spr eco di

tempo, e sicur amente non aiuter ebbe poi molto: inoltr e una documentazione esaur iente e dettagliata esiste già

all'inter no della libr er ia MSDN di Micr osoft. In questo par agr afo elencher ò br evemente tali classi con una piccola

descr izione:

System.Xml.XmlAttr ibute : r appr esenta un attr ibuto

System.Xml.XmlCDataSection : r appr esenta una sezione CData. Questo tipo di elemento è un contenitor e di testo

esteso, al cui inter no si possono dichiar ar e anche pezzi di codice XML: in questo modo essi non si confondono con

il r esto del documento

System.Xml.XmlComment : r appr esenta un commento

System.Xml.XmlDocument : r appr esenta un inter o documento XML ed espone metodi per il salvataggio e il

car icamento veloce

System.Xml.XmlElement : r appr esenta un singolo elemento

System.Xml.XmlNode : r appr senta un nodo. Da questa classe der ivano molte altr e

System.Xml.XmlReader : classe astr atta di base per XmlTex tReader e XmlValidatigReader , che consentono

r ispettivamente la lettur a di testo x ml o di uno schema x ml

System.Xml.XmlWr iter : classe astr atta di base per XmlTex tWr iter , che consente la scr ittur a di testo x ml sul

documento

Di quelli elencati, che già sono una r istr etta minor anza r ispetto a quelli effettivamente pr esenti, noi user emo solo

XmlTex tReader e XmlTex tWr iter .

Iniziamo con XmlTex tRaeder . Questa classe espone una quantità gigantesca di membr i, che sar ebbe tr oppo dispendioso

elencar e completamente. Il suo funzionamento non è semplicissimo da capir e a un pr imo impatto, ma un poco di

r agionamento lo r ender à più chiar o. La funzione pr incipale è Read, che legge un nodo e r estituisce False se non c'è

niente da legger e. Una volta letto, le sue infor mazioni diventano disponibili attr aver so le pr opr ietà di XmlTex tReader ,

che funge da totum continens: infatti gli attr ibuti e i contenuti vengono letti tutti nello stesso modo, consider ati,

quindi, tutti nodi. Ecco un esempio che pr ende un file XML, lo analizza e lo r estituisce sotto for ma di file INI (anche se

questo non suppor ta la ger ar chia):

01.02.03.04.05.06.07.08.09.10.11.12.13.14.15.16.

Imports System.XmlModule Module1

Sub Main()'Crea un nuovo lettore xml.Dim Reader As New Xml.XmlTextReader("C:\libri.xml")Dim Indent, Content As String

'La funzione Reader.Read legge il nodo successivo e'restituisce False se non c'è niente altro da'leggere. Il ciclo legge tutti gli elementiDo While Reader.Read

'Indenta il codice a seconda della profondità'del nodoIndent = New String(" ", Reader.Depth * 2)'Se il nodo è un tag di chiusura, come ad

Page 461: Guida visual basic

Il r isultato sar à questo:

Passiamo or a a XmlTex tWr iter : i suoi membr i sono molto meno numer osi ed usar lo è assai semplice. Quasi tutti i

metodi iniziano per "Wr ite" e ser vono a scr iver e diver si tipi di dati, elementi, attr ibuti e specifiche del documento. La

cosa impor tante da r icor dar si quando si lavor a con XmlTex tWr iter è di r ichiamar e sempr e, pr ima di ogni metodo,

Wr iteStar tDocument, che si occupa di inizializzar e il documento cor r ettamente con le dir ettive adatte; e dopo aver

ter minato le var ie oper azioni Wr iteEndDocument, che chiude tutto. Ecco un esempio:

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.

'esempio </libro>, lo saltaIf Not Reader.IsStartElement Then

'Viene considerato un nodo anche il testo all'interno'di un elemento, perciò bisogna controllare'se questo non sia effettivamente un testoIf Reader.HasValue Then

Console.WriteLine(Reader.Value)End IfContinue Do

End If

'Scrive il nome del tag a schermo, tra parentesi quadre,'come nei file INIConsole.WriteLine("{0}[{1}]", Indent, Reader.Name)'Se l'elemento ha un contenuto, lo memorizza per scriverlo'successivamente a schermoIf Reader.HasValue AndAlso Reader.Value <> vbCrLf Then

Content = Reader.ValueElse

Content = NothingEnd If'Se l'elemento possiede attributi, li scriveIf Reader.HasAttributes Then

For I As Int16 = 0 To Reader.AttributeCount - 1Reader.MoveToAttribute(I)Console.WriteLine("{0}{1} = {2}", Indent, _

Reader.Name, Reader.Value)Next

End If

If Content IsNot Nothing ThenConsole.WriteLine("{0}Contenuto = {1}", Indent, Content)

End IfLoopReader.Close()

Console.ReadKey()

End SubEnd Module

01.02.03.04.05.06.07.08.09.10.11.12.13.14.15.16.17.18.19.20.

[biblioteca][libro]titolo = I sei numeri dell'universoautore = Martin Rees[capitolo]titolo = Il cosmo e il microcosmonumero = 1pagina = 11

Alla base della struttura del nostro universo - non solo ...

[libro]titolo = Le ostinazioni di un matematicoautore = Didier Nordon[capitolo]titolo = Dalle stalle alle stellenumero = 1pagina = 11

Ecco a voi un romanzo il cui protagonista conosce una morte ...

Page 462: Guida visual basic

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.32.33.34.35.36.37.

Imports System.TextImports System.XmlModule Module2

Sub Main()'Il secondo parametro del costruttore è obbligatorio'e specifica quale codifica di caratteri si debba usare.'In questo caso ho messo UTF-8, in modo da poter usare anche'i caratteri accentatiDim Writer As New XmlTextWriter("C:\guida.xml", UTF8Encoding.UTF8)

With Writer

.Indentation = 2

.IndentChar = " "

'Scrive l'intestazione.WriteStartDocument()

'Scrive l'elemento root.WriteStartElement("guida")'E l'attributo "capitoli".WriteAttributeString("capitoli", "100")

'Scrive un elemento capitolo.WriteStartElement("capitolo").WriteAttributeString("numero", "1").WriteAttributeString("titolo", "Introduzione").WriteString("A differenza del Visual Basic classico, ...").WriteEndElement()

'Chiude il root e il documento.WriteEndElement().WriteEndDocument().Close()

End WithConsole.ReadKey()

End SubEnd Module

Page 463: Guida visual basic

F9. Serializzazione di oggetti

La ser ializzazione consiste nel salvar e un oggetto su un qualsiasi suppor to compatibile (file, flussi di memor ia,

var iabili, str eam, ecceter a...) per poi poter lo r icar icar e in ogni momento: questo pr ocesso cr ea di fatto una copia

per fetta dell'oggetto di par tenza. Il fr amewor k .NET è in gr ado di ser ializzar e tutti i tipi base, compr esi anche gli

ar r ay di tali tipi: poiché tutte le str uttur e e le classi utilizzano i tipi base, pr aticamente ogni oggetto può esser e

sottoposto senza pr oblemi a un pr ocesso di ser ializzazione di default, anche se in cer ti casi sor gono pr oblemi che,

giustamente, spetta al pr ogr ammator e r isolver e. Esistono tr e possibili tipi di ser ializzazione, ognuno associato a un

deter minato formatter , ossia un oggetto capace di tr asfer ir e i dati sul suppor to:

Binary : i dati vengono salvati in for mato binar io, conser vando solamente i bit effettivi di infor mazione. A

causa della sua natur a, i valor i pr ocessati con la ser ializzazione binar ia sono più compatti e l'oper azione è assai

veloce, tuttavia essi non sono leggibili né dall'utente né dal pr ogr ammator e. Questo non è un gr ave difetto,

poiché pr aticamente sempr e non si deve inter venir e sui suppor ti di memor izzazione: basta che funzionino

cor r ettamente

SOAP : i dati vengono salvati in un for mato intellegibile, ossia inter pr etabili dall'uomo. In questo caso, tale

for mato si identifica con l'XML, per mezzo del quale le infor mazioni vengono per sistite seguendo le dir ettive del

Simple Object Access Pr otocol. Questo tipo di ser ializzazione r ichiede più memor ia e un tempo di elbor azione

maggior e, ma può esser e compr esa dall'uomo. Ad esempio può r isultar e utile nell'inviar e dati ad applicazioni

par ser che li visualizzano in schemi or dinati. Ad ogni modo, la ser ializzazione SOAP è mar chiata come obsoleta

anche nel Fr amewor k 2.0, a favor e della più r apida e meno dispendiosa Binar y

XML : simile a quella SOAP, tr anne per il fatto che viene utilizzato l'oggetto XmlSer ializer come for matter e che

gli attr ibuti che influenzano la ser ializzazione nor male non vengono inter pr etati con l'uso di questa tecnica. Ci

sono molti altr i piccoli par ticolar i che li differ enziano, ma li si vedr à nei pr ossimi esempi

Serializzare oggettiLe classi necessar ie alla ser ializzazione si tr ovano nei namespace System.Runtime.Ser ialization e

System.Xml.Ser ialization. Le classi da usar e sono Binar yFor matter e SoapFor matter , definite nei r ispettivi namespace

Binar y e Soap. Ecco un esempio della pr ima:

001.002.003.004.005.006.007.008.009.010.011.012.013.014.015.016.017.018.019.020.021.

Imports System.Runtime.Serialization.FormattersModule Module1

<Serializable()> _Class Person

Implements IComparableProtected _FirstName, _LastName As StringPrivate ReadOnly _BirthDay As Date

Public Property FirstName() As String

GetReturn _FirstName

End GetSet(ByVal Value As String)

If Value <> "" Then_FirstName = Value

End IfEnd Set

End Property

Public Overridable Property LastName() As String

Page 464: Guida visual basic

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.084.085.086.087.088.089.090.091.092.093.

GetReturn _LastName

End GetSet(ByVal Value As String)

If Value <> "" Then_LastName = Value

End IfEnd Set

End Property

Public ReadOnly Property BirthDay() As DateGet

Return _BirthDayEnd Get

End Property

Public Overridable ReadOnly Property CompleteName() As StringGet

Return _FirstName & " " & _LastNameEnd Get

End Property

Public Overloads Overrides Function ToString() As StringReturn MyBase.ToString

End Function

Public Overloads Function ToString(ByVal FormatString As String) _As StringDim Temp As String = FormatStringTemp = Temp.Replace("{F}", _FirstName)Temp = Temp.Replace("{L}", _LastName)

Return Temp

End Function

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 1End IfDim P As Person = DirectCast(obj, Person)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 = FirstNameMe.LastName = LastNameMe._BirthDay = BirthDay

End SubEnd Class Sub Main()

'Crea un nuovo oggetto Person da serializzareDim P As New Person("Pinco", "Pallino", New Date(1990, 6, 1))'Crea un nuovo Formatter binarioDim Formatter As New Binary.BinaryFormatter()'Crea un nuovo file su cui salvare l'oggettoDim File As New IO.FileStream("C:\person.dat", IO.FileMode.Create)

'Serializza l'oggetto'Attenzione! I Formatter definiti in'System.Runtime.Serilization,'a differenza di quello in System.Xml.Serialization,'richiedono esplicitamente che un oggetto sia'dichiarato serializzabile, anche se questo lo è'logicamente. Per questo, bisogna recuperare la vecchia'classe Person e applicarvi l'attributo Serializable.'Per rinfrescarvi la memoria, ve ne ho scritto

Page 465: Guida visual basic

Se si pr ova ad apr ir e il file per son.dat, si tr ovano molti car atter i incompr ensibili inter vallati da altr i nomi leggibili,

tr a i quali si possono legger e il nome completo dell'assembly e i nomi dei campi ser ializzati. Infatti, quando si ser ializza

in binar io, vengono salvati anche tutti i r ifer imenti agli assembly a cui il tipo dell'oggetto salvato appar tiene, insieme

coi nomi dei campi e i lor o valor i binar i.

Non posso mostr ar e un esempio della ser ializzazione Soap, pur tr oppo, per chè nel momento in cui scr ivo ho ancor a il

fr amewor k 2.0, nel quale il namespace r elativo non esiste. Posso invece mostr ar e tale esempio con l'XmlFor matter su

una lista di oggetti:

094.095.096.097.098.099.100.101.102.103.104.105.106.107.108.109.110.111.112.113.114.115.

'una copia sopraFormatter.Serialize(File, P)'E chiude il fileFile.Close()Console.WriteLine("Salvataggio completato!")

'Crea una nuova variabile di tipo Person per contenere'i dati caricati dal fileDim F As Person'Apre lo stesso file di prima, ma in letturaDim Data As New IO.FileStream("C:\person.dat", IO.FileMode.Open)

'Carica le informazioi salvate nel fileF = Formatter.Deserialize(Data)

'Verifica che F e P siano perfettamente ugualiConsole.Write("F = P -> ")Console.Write(F.CompareTo(P))'> Ricordate che CompareTo restituisce 0 nel caso di uguaglianza

Console.ReadKey()

End SubEnd Module

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.32.33.34.35.36.37.38.

Public Module Module1'...Sub Main()

'Crea nuovi oggetti Person da serializzareDim P1 As New Person("Pinco", "Pallino", New Date(1990, 6, 1))Dim P2 As New Person("Tizio", "Caio", New Date(1967, 4, 13))Dim P3 As New Person("Mario", "Rossi", New Date(1954, 8, 12))'Ho creato un array perchè è più veloce, ma nulla'vieta di usare liste generics o qualsiasi altro tipo'di collezioneDim Persons() As Person = {P1, P2, P3}'Crea un nuovo Formatter Xml. Il serializzatore in questo'caso ha bisogno anche dell'oggetto Type relativo'all'oggetto da serializzare (un array di person).Dim Formatter As New Serialization.XmlSerializer(GetType(Person()))'Crea un nuovo file su cui salvare l'oggettoDim File As New IO.FileStream("C:\persons.dat", IO.FileMode.Create)

'Serializza l'oggetto'Attenzione! Se gli XmlSerializer non hanno bisogno che'l'oggetto in questione possegga l'attributo Serializable,'hanno invece bisogno che questo esponga almeno un'costruttore senza parametri. Quindi bisogna aggiungere'un nuovo New() a Person. Inoltre, altra limitazione'importante, con questo formatter è possibile'serializzare solo tipi pubblici.Formatter.Serialize(File, Persons)'E chiude il fileFile.Close()Console.WriteLine("Salvataggio completato!")'Potrete constatare che il salvataggio impiega un'tempo notevolmente maggiore

'Crea una nuova variabile di tipo Person per contenere'i dati caricati dal fileDim F As Person()'Apre lo stesso file di prima, ma in lettura

Page 466: Guida visual basic

Se si pr ova ad apr ir e il file per sons.dat, si tr ova un nor malissimo file x ml:

Problemi legati alla serializzazionePotr ebbe capitar e di aver e dei r ifer imenti cir colar i all'inter no dei campi di un oggetto, ad esempio un pr incipale e un

dipendente che si puntano vicendevolmente. In questi casi la ser ializzazione Xml fallisce miser amente e questo

costituisce un'altr a delle gr avi pecche che essa por ta con sé: se si tenta l'oper azione, viene lanciata un'eccezione con il

messaggio "Individuato r ifer imento cir colar e", e tutto il meccanismo cade r ovinosamente. Al contr ar io, il binar y

for matter r iesce a individuar e casi del gener e e si limita a ser ializzar e l'oggetto che causa il r ifer imento cir colar e una

sola volta, ar ginando tutti i possibili pr oblemi. Quando ci si tr ova in situazioni di questo tipo, o anche quando si hanno

dei r ifer imenti r icor sivi, la str uttur a che si for ma a par tir e da un oggetto si dice g rafo: si può dir e che tutti i

r ifer imenti "ger moglino" dall'unico oggetto, detto appunto "r adice". Pr opr io per la capacità di individuar e e r isolver e

pr oblematiche di questo tipo, il binar y for matter costituisce la soluzione miglior e alla clonazione Deep di oggetti. Si

er a infatti par lato, nel capitolo sull'inter faccia ICloneable, di come il metodo Member w iseClone si limiti solo a una copia

super ficiale, clonando esclusivamente i campi non r efer ence dell'istanza (clonazione Shallow). La clonazione deep,

invece, r icostr uisce tutto il gr afo dell'istanza.

Un altr o inconveniente legato alla ser ializzazione è costituito dagli eventi. Come spiegato tempo fa, essi non sono altr o

che delegate, i quali a lor o volta sono pur sempr e tipi der ivati da System.Delegate e, in quanto tipi, sono ser ializzabili.

Nulla di male fin qui, ma quella che ho pr ima definito come "astuzia" del CLR nel r ipr odur r e gr afi cor r etti, si tr asfor ma

or a in un incr edibile spaur acchio. Mi spiego meglio. Dur ante il pr ocesso di salvataggio, il for matter cattur a tutti gli

oggetti r aggiungibili dir ettamente o indir ettamente attr aver so le pr opr ietà e i campi di una classe. Allo stesso modo,

in un delegate sono r aggiungibili anche tutti gli oggetti sottoscr ittor i, ossia quelli che hanno r egistr ato alcuni dei

pr opr i metodi come gestor i d'evento dell'oggetto r adice. Questo pr oduce due gr avi effetti collater ali: vegono

ser ializzati anche tutti gli altr i oggetti coinvolti e, con lor o, pr aticamente tutta l'applicazione, il che, oltr e a esser e un

enor me dispendio di spazio, non è il r isultato desider ato; se anche uno solo di tutti gli oggetti nel gr afo non è

39.40.41.42.43.44.45.46.47.48.49.50.51.52.

Dim Data As New IO.FileStream("C:\persons.dat", IO.FileMode.Open)

'Carica le informazioi salvate nel fileF = Formatter.Deserialize(Data)

'Verifica che F e P siano perfettamente ugualiFor I As Byte = 0 To 2

Console.WriteLine("P{0} = F{0} -> {1}", I, _Persons(I).CompareTo(F(I)))

Next'> Ricordate che CompareTo restituisce 0 nel caso di uguaglianza

Console.ReadKey()

End SubEnd Module

01.02.03.04.05.06.07.08.09.10.11.12.13.14.15.16.

<?xml version="1.0"?><ArrayOfPerson xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xmlns:xsd="http://www.w3.org/2001/XMLSchema"><Person><FirstName>Pinco</FirstName><LastName>Pallino</LastName>

</Person><Person><FirstName>Tizio</FirstName><LastName>Caio</LastName>

</Person><Person><FirstName>Mario</FirstName><LastName>Rossi</LastName>

</Person></ArrayOfPerson>

Page 467: Guida visual basic

ser ializzabile, il pr ocesso fallisce lanciando un'eccezione. Par tendo dal pr esupposto che i For m non sono ser ializzabili,

nel 99% dei casi, si otter r ebbe lo stesso er r or e. L'unico modo per r idur r e i danni è comunicar e al for matter di non

ser ializzar e gli eventi, attr aver so l'attr ibuto NonSer ialized (che vedr emo fr a br eve): questo implica definir e l'evento

come custom. Poiché sono utilizzati r ar amente, non ho tr attato gli eventi custom, ma ecco un esempio:

Questo codice evidenzia come sia difficile gestir e gli eventi nella ser ializzazione.

Per modificar e il compor tamente del for matter , è possibile usar e alcuni attr ibuti. Eccone una br eve lista:

NonSer ialized : il campo viene saltato dur ante il pr ocesso di salvataggio. Oltr e a poter evitar e fastidiose

r iper cussioni sul codice come quella analizzata poco fa, contr ibuisce a r ispar miar e un pochetto di memor ia in

più. Solitamente questo attr ibuto viene applicato a quei valor i che possono esser e dedotti da altr i campi (ad

esempio, l'età, che può esser e calcolata par tendo dalla data di nascita) o che al pr ossimo car icamente

sicur amente non conter r anno più valor i validi (come handle di finestr e o di altr e r isor se non gestite). Solo i

for matter Binar y e Soap tengono conto di NonSer ialized, al contr ar io di Xml

OptionalField : il campo viene ser ializzato nor malmente, ma nel pr ocesso di car icamento dei dati la sua

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.32.33.34.35.36.37.38.39.40.41.42.43.44.45.46.47.48.49.

Module Module1Public Class EventLauncher

Private _ID As Int32'Negli eventi custom, bisogna usare una variabile privata'dello stesso tipo dell'evento da lanciare. Si può'vedere un elemento custom un pò come una proprietà,'che media l'interazione con il vero delegate gestorePrivate _IDChangedEventHandler As EventHandler

'Dichiara il nuovo eventoPublic Custom Event IDChanged As EventHandler

'Proprio come nelle proprietà ci sono i'blocchi Get e Set per ottenere e assegnare un valore,'qua di sono i blocchi AddHandler e RomveHandler'per aggiungere o rimuovere un gestore d'evento e'il blocco RaiseEvent per lanciarloAddHandler(ByVal Value As EventHandler)

'Basta richiamare System.Delegate.Combine per'aggiungere il nuovo gestore Value_IDChangedEventHandler = _

[Delegate].Combine(_IDChangedEventHandler, Value)End AddHandler

RemoveHandler(ByVal Value As EventHandler)

_IDChangedEventHandler = _[Delegate].Remove(_IDChangedEventHandler, Value)

End RemoveHandler

RaiseEvent(ByVal sender As Object, ByVal e As EventArgs)'Controlla che ci sia almeno un gestore, quindi li'richiama tuttiIf _IDChangedEventHandler IsNot Nothing Then

_IDChangedEventHandler(sender, e)End If

End RaiseEventEnd Event

Public Property ID() As Int32

GetReturn _ID

End GetSet(ByVal Value As Int32)

_ID = ValueRaiseEvent IDChanged(Me, EventArgs.Empty)

End SetEnd Property

End Class'...

End Module

Page 468: Guida visual basic

mancanza non pr oduce alcun er r or e. Nel caso il campo non sia pr esente, assume il valor e di default della sua

categor ia: 0 per i numer i, Nothing per i tipi r efer ence

Serializzazione customI tipi for niti dal Fr amewor k .Net espongono metodi capaci di r isolver e pr aticamente ogni casistica di pr oblemi e

per ciò solo in r ar i casi si r icor r e alla ser ializzazione custom. Questo tipo di ser ializzazione non inter viene nei

meccanismi che modificano fisicamente il suppor to di memor izzazione e neanche in quelli che r ecuper ano i dati da

questo, ma agisce pr ima e dopo che tali azioni vengano compiute. Per cr ear e un oggetto con queste car atter istiche, si

deve implementar e l'inter faccia ISer ializable, la quale espone solo un metodo: GetDataObject. Esso ha il compito di

selezionar e, tr a tutti i campi disponibili, quali per sister e e quali no, anche sulla base di cer te condizioni, e viene

invocato pr ima che abbia inizio il pr ocesso di ser ializzazione. Inoltr e, l'oggetto deve anche espor r e un costr uttor e

Pr ivate o Pr otected (a seconda che si debba er editar e oppur e no) con una par ticolar e signatur e. Ecco un esempio:

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.

Module Module2<Serializable()> _Public Class Client

Implements ISerializablePrivate _Name As String<OptionalField()> _Private _IP As StringPrivate _IsIPStatic As Boolean

Public Property Name() As String

GetReturn _Name

End GetSet(ByVal Value As String)

_Name = ValueEnd Set

End Property

'ReadOnly perchè l'IP viene deciso alla connessione'e poi non subisce cambiamentiPublic ReadOnly Property IP() As String

GetReturn _IP

End GetEnd Property

'L'IP rilevato dal server, normalmente, cambia ad ogni'connessione e quindi sarebbe inutile serializzarlo.'Tuttavia, se viene reso statico, ad esempio con'l'uso di un DNS, allora lo si dovrebbe serializzare'e ricaricare al successivo avvio. Questa proprietà'ne definisce lo stato e influenza i processi di'serializzazione e deserializzazionePublic Property IsIPStatic() As Boolean

GetReturn _IsIPStatic

End GetSet(ByVal Value As Boolean)

_IsIPStatic = ValueEnd Set

End Property

'GetDataObject seleziona i campi da serializzarePrivate Sub GetDataObject(ByVal info As SerializationInfo, _

ByVal context As StreamingContext) _Implements ISerializable.GetObjectData'Info si comporta come un dictionary(of String, Object).'Basta aggiungere i valori da salvareinfo.AddValue("Name", Me.Name)info.AddValue("IsIPStatic", Me.IsIPStatic)'Questo passaggio è attuabile solo con la

Page 469: Guida visual basic

Impostando IsIPStatic a Tr ue, l'output cambier à in "86.45.8.23".

Altr i attr ibuti che si possono usar e sono OnSer ialization, OnSer ialized, OnDeser ialization e OnDeser ialized, che, se

applicati a un metodo, lo eseguono nelle var ie fasi della ser ializzazione: pr ima o dopo il salavatggio; pr ima o dopo il

car icamento. Per mezzo di questi è anche possibile impostar e campi opzionali che devono assumer e un deter minato

valor e per esser e validi.

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.

'serializzazione customIf Me.IsIPStatic Then

info.AddValue("IP", Me.IP)End If

End Sub

'Private New viene richiamato dal formatter dopo la'deserializzaziore per impostare i valoriPrivate Sub New(ByVal info As SerializationInfo, _

ByVal context As StreamingContext)Me.Name = info.GetString("Name")Me.IsIPStatic = info.GetBoolean("IsIPStatic")If Me.IsIPStatic Then

_IP = info.GetString("IP")Else

_IP = "127.0.0.1"End If

End Sub

'Un costruttore pubblico deve comunque esserciSub New(ByVal Name As String, ByVal IP As String)

Me.Name = Name_IP = IP

End SubEnd Class Sub Main()

'Crea un nuovo clientDim C As New Client("Totem", "86.45.8.23")Dim Formatter As New Binary.BinaryFormatter()Dim File As New IO.FileStream("C:\client.dat", IO.FileMode.Create)

'Lo serializza, con IP dinamicoC.IsIPStatic = FalseFormatter.Serialize(File, C)File.Close()

'Lo ricarica, e osserva che l'IP è stato impostato'su quello della macchina localeDim Data As New IO.FileStream("C:\client.dat", IO.FileMode.Open)

C = Formatter.Deserialize(Data)Console.WriteLine(C.IP)' > 127.0.0.1Data.Close()

Console.ReadKey()

End SubEnd Module

Page 470: Guida visual basic

F10. Compressione di dati

Il .NET Fr amewor k consente la compr essione di dati in modo piuttosto semplice. All'inter no del namespace

System.IO.Compr ession, infatti, sono esposte due classi, di nome DeflateStr eam e GZipStr eam. Entr ambe hanno lo

stesso funzionamento e combinano l'algor itmo di compr essione LZ77 con la codifica di Huffman. Tuttavia, non sono

"indipendenti", poiché devono r egger si sul suppor to di un altr o str eam, che r appr esenta la ver a connessione con il file

da compr imer e/decompr imer e. Il codice da usar e è questo:

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.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.

Imports System.IOImports System.IO.CompressionModule Module1

Sub Main()'I percorsi del file da comprimere e di quello compressoDim File, CompressedFile As String'Lo stream che legge i dati da FileDim Input As FileStream'Lo stream di scrittura associato al file compressoDim Output As FileStream'Lo stream compresso che scrive i dati codificati per mezzo'dell'output streamDim Zipped As DeflateStream

Console.WriteLine("Inserire il percorso del file da compriemere:")File = Console.ReadLineConsole.WriteLine("Inserire il percorso in cui salvare i dati compressi:")CompressedFile = Console.ReadLine

'Controlla che il file esistaIf Not IO.File.Exists(File) Then

Console.WriteLine("File inesistente!")Exit Sub

End If

'Inizializza lo stream di inputInput = New FileStream(File, IO.FileMode.Open)'Inizializza lo stream di outputOutput = New FileStream(CompressedFile, IO.FileMode.Create)'Inizializza lo zipperZipped = New DeflateStream(Output, CompressionMode.Compress)

'Buffer temporaneo che contiene pacchetti di datiDim Buffer(4095) As Byte

'Legge i bytes a blocchi di 4KiBFor I As Int64 = 0 To Input.Length - 1 Step 4096

If Input.Length - I >= 4096 ThenInput.Read(Buffer, 0, 4096)

ElseInput.Read(Buffer, 0, Input.Length - I)

End If'Li scrive sullo stream compressoZipped.Write(Buffer, 0, Buffer.Length)

Next

'Trasferisce dati compressi sullo streamZipped.Flush()'Quindi chiude tutti gli streamZipped.Close()Output.Close()Input.Close()

'Alla fine calcola la compressione totale'Ottiene la dimensione iniziale del file

Page 471: Guida visual basic

Potete pr ovar e il pr ogr amma con il file assembly.tx t for nito nell'ultima lezione sulla Reflection, la cui compr essione

sar à cir ca dell'85%. Bisogna notar e che, alla fine del pr ocesso, i dati sono sì compr essi, ma nessun pr ogr amma è capace

di apr ir li se non quello che si è scr itto: sebbe l'algor itmo usato sia quello dei file *.zip, non vengono salvate le

infor mazioni necessar ie a str uttur ar e lo str eam in manier a standar d così da poter esser e letto nor malmente. Nella

maggior par te dei casi, questo è un vantaggio: il mio pr ogr amma Tot Compr essor (nella sezione dow nload), usa

pr opr io queste tecniche, ma cr ea anche una nuova composizione inter na per i file in modo da r iuscir e ad estr apolar ne

i dati e a stipar e più files in uno solo.

Per decompr imer e lo stesso file, si agisce in manier a inver sa: l'output sar à lo str eam del file decompr esso, l'input quello

compr esso e il DeflateStr eam avr à modalità di compr essione Decompr ess.

57.58.59.60.61.62.63.64.

Dim StartSize As Int64 = FileLen(File)'E quella finale del file compressoDim EndSize As Int64 = FileLen(CompressedFile)

Console.WriteLine("Compressione totale: {0:N2}%", _

100 - (EndSize * 100 / StartSize))Console.ReadKey()

End SubEnd Module

Page 472: Guida visual basic

F11. Sicurezza e criptazione

Anche in questo ambito, il Fr amewor k offr e ottime funzionalità, pr ocur ando al pr ogr ammator e molti modi per cifr ar e

e decifr ar e messaggi che non dovebber o esser e inter cettabili da alcun altr a per sona che non sia l'utente. Le classi che

ser vono per questo scopo sono esposte nei namespace System.Secur ity e System.Secur ity.Cr yptogr aphy. Dopo una

br eve intr oduzione, mostr er ò come sia possibile cr iptar e e decr iptar e dati usando queste potenzialità.

Introduzione alla criptazioneLa cr iptazione è una disciplina infor matica che si occupa di oscur ar e messaggi o dati in modo da r ender li accessibili

solo alle per sone alle quali sono r ealemnte destinati. Così facendo, si evita che qualche cr acker indesider ato possa

impossessar sene ed utilizzar e le infor mazioni ivi contenute per chissà quali scopi. Esistono tr e tipi di algor itmi di

cr iptazione:

Simmetr ici

Sono i più semplici e dir etti metodi di cifr atur a. Per funzionar e necessitano di una chiave (o passwor d), per

mezzo della quale i dati vengono oscur ati. I meccanismi inter ni lavor ano su blocchi di bytes di dimensione

pr efissata, solitamente una potenza di 2: ogni algor itmo ha una chiave di dimensioni pr edefinite, spesso

espr essa in bit. All'inter no di questo tipo, ci sono due modi oper andi diver si: cifrar i a blocchi e cifrar i basati

su stream .

I pr imi pr endono le infor mazioni da cr iptar e e le dividono in blocchi di bytes di lunghezza par i a quella della

chiave, quindi eseguono delle oper azioni di Xor fr a ar r ay successivi e r estituiscono in output il r isultato. Il

pr imo blocco di bytes viene cifr ato sulla base di un vettore di inizializzazione, anche detto IV, ossia

un'accozaglia di dati casuali di dimensioni par i alla chiave.

I secondi gener ano una chiave pseudo-casuale estr atta manipolando la chiave di base e la inser iscono in uno

str eam: pr endono poi pezzi di dati di lunghezza ar bitr ar ia e li Xor ano con il contenuto dello str eam

Asimmetr ici

Costituiscono i più sicur i algor itmi di cr iptazione, a cui devono supplir e, per ò, tempi di elabor azione maggior i.

Un algor itmo del gener e ha bisogno di una chiave pubblica, che può esser e comunicata a tutti, e una chiave

pr ivata, che la per sona tiene segr eta. Il messaggio viene inviato cr iptandolo con la chiave pubblica del

destinatar io, il quale poi lo decr ipta usando la sua per sonale chiave pr ivata. Data la complessità del lor o

funzionamento e la potenza di calcolo r ichiesta, è bene usar li solo in caso di messaggi estr emamente br evi, a

favor e dei più abbor dabili algor itmi simmetr ici

Hash

Questi algor itmi cr eano una str inga di dimensiona fissa che non può esser e decr iptata in nessun modo: testi

uguali gener ano output uguali, ma non c'è manier a di r isalir e al messagio or iginale. L'unico modo per saper e se

un messaggio è equivalente a quello sottoposto all'hash consiste nel compar ar e i due hash

Il .Net Fr amewor k mette a disposizione w r apper per i seguenti algor itmi:

RC2 (Cifr ar io Rivest): algor itmo simmetr ico a blocchi da 64-bit

DES (Data Encr yption Standar d): algor itmo simmetr ico a blocchi da 64-bit

3-DES (Tr iple Data Encr yption Standar d):: algor itmo simmetr ico a blocchi da 192-bit (la chiave ha dimensione

maggior e r ispetto al DES nor male)

AES (Advanced Encr yption Standar d, alias Rijndael): algor itmo simmetr ico a blocchi da 256-bit

Page 473: Guida visual basic

MD5 (Message Digest Algor ithm 5): hash da 128-bit

SHA-1 (Secur e Hash Standar d 1): hash da 160-bit

SHA-256, SHA-384, SHA-512 (var ianti di Secur e Hash Standar d 2): hash da 256, 384 o 512 bit

RSA (Rivest Shamir Adleman, dai nomi dei suoi inventor i): algor itmo asimmetr ico, var iabile da 1024 fino a 4096

bit (di chiave)

Una prova praticaNei seguenti esempi, for nir ò una dimostr azione del funzionamento degli algor itmi simmetr ici e di hashing. Poiché i

pr imi der ivando tutti dalla classe base Symmetr icAlgor ithm e i secondi da HashAlgor ithm, il funzionamento illustr ato

per uno solo di questi può esser e r ipetuto in manier a identica (eccetto che per dimensione della chiave) per ogni altr o

algor itmo della stessa famiglia.

Come esempio per gli algotir mi simmetr ici pr ender ò il Rijndael (per chè mi piace il nome XD). Pr ima di iniziar e con il

codice, bisogna saper e che ogni algor itmo è r appr esentato da una classe detta prov ider cr ittog rafico, che di solito

por ta il nome cor r ispondente. Questo ha il compito di immagazzinar e le infor mazioni sulla chiave e sul blocco di dati e

cr ar e ex novo un oggetto deputato alla cr iptazione o decr iptazione dei messaggi. Tale oggetto implementa l'inter faccia

ICr yptoTr ansfor m, r appr esenta una tr asfor mazione concr eta sulle infor mazioni for nite e ha la funzione di conver tir le

effettivamente tr amite il metodo Tr ansfor mFinalBlock. Ecco un esempio:

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.

Imports System.SecurityImports System.Security.CryptographyImports System.Text.UTF8EncodingModule Module1

'Vettore di bytes casuali usati per oscurare la chiave:'verrà usato nella funzione di derivazione della passwordPrivate SaltBytes As Byte() = New Byte() _{162, 21, 92, 34, 27, 239, 64, 30, 136, 102, 223}

'Questo è un vettore di inizializzazione per algoritmi'simmetrici a 256-bit. Si nota, infatti, che è lungo 32 bytesPrivate IV32 As Byte() = New Byte() _{133, 206, 56, 64, 110, 158, 132, 22, _99, 190, 35, 129, 101, 49, 204, 248, _251, 243, 13, 194, 160, 195, 89, 152, _149, 227, 245, 5, 218, 86, 161, 124}

'La derivazione di password è un'altra delle tecniche'usate in criptazione: si cifra la chiave iniziale con un'algoritmo di derivazione, fornendo come base un vettore'di bytes casuali, chiamato <b>salt crittografico</b>.'L'algoritmo applica una trasformazione sulla chiave un'numero dato di volte (iterazioni) e restituisce alla fine una'password di lunghezza specificata. In questo caso, poiché'si sta utilizzando l'algoritmo Rijndael a 256 bit, sarà'di 32 bytesPrivate Function DerivePassword(ByVal Key As String) As Byte()

'Il provider crittograficoDim Derive As Rfc2898DeriveBytes'Il risultato dell'operazioneDim DerivedBytes() As Byte

'Crea un nuovo provider crittografico per l'algoritmo'di derivazione RFC2898, che ha come input Key, come'salt crittografico l'array SaltBytes sopra definito'e come numero di iterazioni 5. Il secondo e il terzo'parametro sono del tutto casuali: li si può'modificare arbitrariamenteDerive = New Rfc2898DeriveBytes(Key, SaltBytes, 5)'Applica la trasformazione e deriva una nuova password'ottenuta come array di 32 bytes

Page 474: Guida visual basic

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.113.114.

DerivedBytes = Derive.GetBytes(32)

Return DerivedBytesEnd Function

'Data una chiave Key e un messaggio Text, usa l'algoritmo simmetrico'a blocchi Rijndael (AES) per ottenere un insieme di dati criptatoPublic Function RijndaelEncrypt(ByVal Key As String, _

ByVal Text As String) As Byte()'Crea il nuovo provider crittografico per questo algoritmoDim Provider As New RijndaelManaged'La password derivataDim BytePassword As Byte()'L'oggetto che ha il compito di processare le informazioniDim Encryptor As ICryptoTransform'L'output della funzioneDim Output As Byte()'L'input della funzione, ossia il testo convertito'in forma binaria. Il formato UTF8 permette di'mantenere anche i caratteri speciali come quelli accentatiDim Input As Byte() = UTF8.GetBytes(Text)

'Imposta la dimensione della chiaveProvider.KeySize = 256'Imposta la dimensione del bloccoProvider.BlockSize = 256'Ottiene la password tramite derivazione dalla chiaveBytePassword = DerivePassword(Key)'Crea un nuovo oggetto codificatoreEncryptor = Provider.CreateEncryptor(BytePassword, IV32)'Cripta il testoOutput = Encryptor.TransformFinalBlock(Input, 0, Input.Length)

'Elimina le informazioni fornite al providerProvider.Clear()'Distrugge l'oggetto codificatoreEncryptor.Dispose()

Return Output

End Function

'Data una chiave Key e un messaggio cifrato Data, usa l'algoritmo'simmetrico a blocchi Rijndael (AES) per ottenere l'insieme di'dati di partenzaPublic Function RijndaelDecrypt(ByVal Key As String, _

ByVal Data() As Byte) As String'Crea un nuovo provider crittograficoDim Provider As New RijndaelManaged'La password derivataDim BytePassword As Byte()'L'oggetto che ha il compito di processare le informazioniDim Decryptor As ICryptoTransform'L'output della funzione in bytesDim Output As Byte()

Provider.KeySize = 256Provider.BlockSize = 256BytePassword = DerivePassword(Key)'Ottiene l'oggetto decodificatoreDecryptor = Provider.CreateDecryptor(BytePassword, IV32)

'Tenta di decriptare il messaggio: se la chiave è'sbagliata, lancia un'eccezioneTry

Output = Decryptor.TransformFinalBlock(Data, 0, Data.Length)Catch Ex As Exception

Throw New CryptographicException("Criptazione fallita!")Finally

Provider.Clear()Decryptor.Dispose()

End Try

Page 475: Guida visual basic

E questo per l'Hash:

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.

Return UTF8.GetString(Output)End Function

'I dati prodotti in output sono allocati in vettori di bytes,'ma le stringhe non sono il supporto più adatto per'visualizzarli, poiché vengono compresi anche'caratteri di controllo o null terminator. In ogni caso,'la stringa sarebbe o compromessa o illeggibile (non che'non lo debba essere). Questa funzione restituisce tutto'il vettore come rappresentazione esadecimale in stringa'rendendo più gradevole la vista del nostro'magnifico messaggio cifratoPublic Function ToHex(ByVal Bytes() As Byte) As String

Dim Result As New StringBuilder

For I As Int32 = 0 To Bytes.Length - 1'Accoda alla stringa il codice in formato esadecimale,'facendo in modo che occupi sempre due posti, eventualmente'pareggiando con uno zero sulla sinistraResult.AppendFormat("{0:X2}", Bytes(I))

Next

Return Result.ToStringEnd Function

Sub Main()

Dim Input, Output As StringDim Key As String

Console.WriteLine("Inserire un testo qualsiasi:")Input = Console.ReadLineConsole.WriteLine("Inserire una chiave di criptazione:")Key = Console.ReadLine

Try

Output = ToHex(RijndaelEncrypt(Key, Input))Console.WriteLine()Console.WriteLine("Il testo criptato è:")Console.WriteLine(Output)

Catch CE As CryptographicExceptionConsole.WriteLine("Password errata!")

End Try

Console.ReadKey()End Sub

End Module

01.02.03.04.05.06.07.08.09.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.

Imports System.SecurityImports System.Security.CryptographyImports System.Text.UTF8EncodingModule Module2

'Questa semplice funzione genera un hash MD5Public Function GetMd5(ByVal Text As String) As Byte()

Dim Input As Byte() = UTF8.GetBytes(Text)Dim Output As Byte()

'MD5.Create() crea un nuovo provider crittografico per l'hash Md5Output = MD5.Create().ComputeHash(Input, 0, Input.Length)Return Output

End Function

Sub Main()Dim Input As String

Console.WriteLine("Inserire un testo qualsiasi:")Input = Console.ReadLine

Console.WriteLine()Console.WriteLine("Il suo hash è:")Console.WriteLine(ToHex(GetMd5(Input)))

Page 476: Guida visual basic

25.26.

Console.ReadKey()End Sub

End Module

Page 477: Guida visual basic

F12. Giocare con i file multimediali

Per poter r ipr odur r e Audio e Video è possibile usar e una libr er ia del Fr amewor k, r aggr uppata nel namespace delle

Dir ectX. Per questo motivo bisogna pr ima installar e le libr er ie micr osoft Dir ectX: in molti compilator i sono già incluse

nel pacchetto, ma per chi non le possedesse e avesse bisogno di un link di r ifer imento, si possono scar icar e anche qui

nella lor o r elease del Giugno 2007. Dopo aver impor tato nel pr ogetto gli oppor tuni r ifer imenti a Micr osoft.Dir ectX e

Micr osoft.Dir ectX.AudioVideoPlayback tr amite il menù Add Refer ence del solution ex plor er , e scr itto le oppor tune

dir ettive di impor tazione in cima al codice, diventano disponibili le due classi Audio e Video. Entr ambi i costr uttor i

accettano il per cor so del file multimediale da apr ir e: suppor tano anche per cor si ur l web, ed è quindi possibile

r ipr odur r e file in str eaming. Una volta inizializzati gli oggetti, si possono ottener e moltissime pr opr ietà inter essanti,

ed altr ettanti metodi. Eccone una lista:

Cur r entPosition : la posizione cor r ente nel contesto di r ipr oduzione, espr essa in secondi

Dur ation : la dur ata complessiva del file, sempr e in secondi

Fr omFile / Fr omUr l : funzioni statiche che inizializzano un nuovo oggetto Audio o Video a par tir e dal per cor so

HD o Web specificato

Open(F) : apr e il file F nell'oggetto cor r ente (F può esser e una str inga o un Ur i, Unique Resour ce Identifier ).

Questa è una pr ocedur a che può esser e usata nel caso non si voglia cr ear e un ulter ior e oggetto per ogni nuovo

file

Pause : mette in pausa la r ipr oduzione

Paused : deter mina se la r ipr oduzione è stata messa in pausa

Play : r ipr oduce il file multimediale

Playing : deter mina se il file è in r ipr oduzione

SeekCur r entPosition(T, F) : cambia la posizione cor r ente all'inter no del file. T è la posizione desider ata, in

secondi, mentr e F è un enumer ator e che specifica con quale modalità ci si debba spostar e: tr a i possibili valor i,

i più usati sono AbsolutePosition (indica che T è la posizione effettiva) e RelativePosition (indica di spostar si di T

secondi in avanti)

State : pr opr ietà enumer ata che definisce lo stato cor r ente di r ipr oduzione. Può assumer e tr e valor i: Running

(in r ipr oduzione), Paused (in pausa), Stopped (inter r otta)

Stop : fer ma la r ipr oduzione. Quando viene r ichiamata, sposta il cur sor se all'inizio del file, mentr e Pause

mantiene la posizione cor r ente. Così, se si r ichiama Stop e successivamente Play, la r ipr oduzione r ipar tità da

capo

Stopped : deter mina se la r ipr oduzione è stata inter r otta

StopPosition : indica dove la r ipr oduzione è stata inter r otta

Volume : modifica il volume della r ipr oduzione. I valor i che può assumer e vanno da -10000 (muto) a 0 (volume

nor male). Questa pr opr ietà non influenza il volume di sistema, ma solo quello dell'oggetto su cui viene

r ichiamato

I membr i elencati sono esposti sia da Audio che da Video. I seguenti, invece, sono esposti solo da Video:

Audio : r estituisce l'oggetto Audio che cor r isponde al video in r ipr oduzione. In questo modo si può r egolar e il

volume o il bilanciamento nor malmente

Aver ageTimePer Fr ame : il tempo impiegato dall'oggetto per r ipr odur r e un singolo fr ame. Si può ottener e il

classico valor e FPS (fr ame per second) calcolando il r ecipr oco di questo numer o:

Page 478: Guida visual basic

Dim FramePerSecond As SingleFramePerSecond = 1 / Video.AverageTimePerFrame

Caption : se il video viene avviato senza un'adeguata pr opr ietà Owner (illustr ata poco più avanti nell'elenco),

ver r à automaticamente aper ta una nuova finestr a dentr o la quale esso viene r ipr odotto. Caption imposta il

titolo di tale finestr a

DefaultSize : indica la dimensione ottimale del video (ossia quella in cui è stato cr eato)

Fullscr een : indica se il video debba esser e r ipr odotto a tutto scher mo. Questa pr opr ietà deve esser e impostata

prima di r ichiamar e il metodo Play. Dur ante la r ipr oduzione è possibile pr emer e Home per r itor nar e in

modalità finestr a, ma lo si può far e anche agendo da codice dir ettamente sulla pr opr ietà

HideCur sor : nasconde il mouse sopr a al video

IsCur sor Hidden : deter mina se il cur sor e è stato nascosto

Max imumIdealSize : la massima dimensione possibile pr ima di defor mar e il video

MinimumIdealSize : la minima dimensione possibile pr ima di defor mar e il video

Owner : pr opr ietà che definisce il "pr opr ietar io" del video. Con questo s'intende un qualsiasi contr ollo dentr o al

quale esso ver r à r ipr odotto. È consigliabile usar e una Pictur eBox o un Panel per questi compiti

ShowCur sor : r ende il mouse di nuovo visibile

Size : la dimensione del video

Pr ima di concluder e, vor r ei specificar e che le classi sopr a esposte sono valide per la r ipr oduzione di questi tipi di file:

Windows Media Video (*.wmv)

Moving Pictur es Ex per t Gr oup 1 for mat (*.mpg)

Audio Video Inter leave (*.avi)

Micr osoft Audio Wave (*.wav)

Moving Pictur es Ex per t Gr oup 1 Layer 3 (*.mp3)

Windows Media Audio (*.wma)

Page 479: Guida visual basic

F13. Sintesi vocale

Questo capitolo è scr itto per VB2008!

Installazione del softwareNonostante esistano già libr er ie appar tenenti al .Net Fr amewor k 3.0 scr itte apposta per questo ar gomento, esse si

r eggono a lor o volta su altr e libr er ie - le SAPI - che necessitano di installazione. Per questo capitolo, avr emo bisogno

delle SAPI 5.1 per Windows Xp. Recatevi a questo indir izzo e tr over ete un elenco di files da scar icar e:

msttss22L.exe : si tr atta del Micr osoft Tex t-To-Speech Engine. È l'insieme di libr er ie che per mette di far

legger e un testo al computer con una voce scelta. Se avete già installato Micr osoft Agent non è necessar io

scar icar e questo componente

1.

sapi.chm : documentazione completa delle SAPI 5.1. Se volete appr ofondir e l'ar gomento scar icatela pur e2.

Sp5TTIntXP.exe : pacchetto autoestr aente che contiene un Micr osoft Mer ge Module da aggiunger e

all'installazione nor male. Con questo componente aggiuntivo potr ete due usar e due nuove voci, oltr e al mitico

Sam: Micr osoft Mike e Mar y

3.

SpeechSDK51.exe : contiene tutti i files per l'installazione - download obbligator io4.

SpeechSDK51Lang Pack.exe : contiene il nor male installer più alcuni moduli per aggiunger e il r iconocimento

delle lingue Cinese e Giapponese. A meno che non siate appassionati dell'Or iente, non vi conviene scar icar lo, dato

che sono più di 80MB

5.

speechsdk51msm.exe : questo è un pacchetto che contiene tutti gli altr i files messi insieme6.

Per il codice che user emo è necessar io scar icar e il pr imo e il ter zo componente, ossia il pacchetto di installazione

minimo. Una volta scar icati, estr aete il contenuto in una qualsiasi car telle e avviate l'eseguibile "Setup.ex e", che

installer à tutti i componenti necessar i sul computer .

Sintesi vocalePer la sintesi vocale il codice è molto semplice e basta un solo oggetto. Sto par lando della classe

System.Speech.Synthesis.SpeechSynthesizer . Ecco una r apida panor amica dei suoi membr i:

GetCur r entlySpokenPr ompt : r estituisce un oggetto Pr ompt (appar tenente alla classe System.Speech.Synthesis)

che r appr esenta la fr ase che il sintetizzator e sta leggendo. Pr ompt ha solo un membr o, la pr opr ietà booleana

IsCompleted, che comunica quando la lettur a è ter minata

GetInstalledVoice : r estituisce una collezione di VoiceInfo che r appr esentano le voci installate. Ogni oggetto della

collezione espone anche delle pr opr ietà che comunicano infor mazioni sulla voce

Pause : fa una pausa nella lettur a

Resume : r ipr ende a legger e. Utilizzato per r ipr ender e dopo una chiamata a Pause()

SelectVoice(N As Str ing) : seleziona la voce N come voce del sintetizzator e. La più diffusa è senza subbio

"Micr osoft Sam", poiché è installata di default su tutti i sistemi oper ativi Windows. Come dicevo pr ima, per ò, se

ne possono scar icar e altr e dal sito Micr osoft

SelectVoiceByHints(G As VoiceGener , A As VoiceAge) : ogni voce installata ha delle pr opr ie car atter istiche,

pr opr io come una voce nor male. Può esser e di uomo, di donna o di bambino, e in queste categor ie, può aver e

una differ ente età. Questo metodo ser ve a selezionar e una voce in base a questi cr iter i: la pr ima voce installata

Page 480: Guida visual basic

con i r equisiti r ichiesti ver r à pr esa e attivata. Sia G che A sono semplici enumer ator i

SetOutputToAudioStr eam(S As Str eam, F As AudioFor mat.SpeechAudioFor matInfo) : imposta l'output del

sintetizzator e su un flusso di dati, in un dato for mato. Questo è un modo complesso di dir e "r egistr a la voce su

un file audio": infatti se l'output non sono le casse audio ma un file (str eam), la voce ver r à "r egistr ata" in quel

file. Lo str eam deve esser e aper to, altr imenti il sintetizzator e non ci può scr iver e dentr o, mentr e il for mato

deve esser e cr eato come oggetto a se stante in pr ecedenza, ad esempio:

Resta ancor a da capir e in che for mato vengano salvati i dati, dato che con *.wav non funziona... Tuttavia la

documentazione Micr osoft non pr esenta nessun tipo di spiegazione, né esempi al r iguar do

SetOutputToDefaultAudioDevice : imposta l'output del sintetizzator e sul dispositivo standar d di output, ossia le

casse del computer

SetOutputToNull : annulla l'output

SetOutputToWaveFile(F As Str ing) : r egistr a l'output del sintetizzator e sul file wave il cui per cor so è specificato

in F. Sicur amente, questo metodo è molto più utile di SetOutputToAudioStr eam. Un piccolo esempio, sempr e

ammettendo che Synt sia il nostr o SpeechSynthesizer :

Questo metodo r egistr a efficacemente la fr ase "Hello!" sul file Voce.wav

SetOutputToWaveStr eam(S As Str eam) : esattamente come il metodo pr ecedente, solo che il par ametr o passato

è di tipo Str eam

Speak(S As Str ing) : legge tutto il testo contenuto in S. Il codice non pr oseguir à finché non sia stato letto tutto il

testo dato

SpeakAsync(S As Str ing) : come sopr a, solo che questo metodo continua su un thr ead differ ente e per ciò non

blocca l'esecuzione del codice

SpeakAsyncCancel : annulla la lettur a di un testo

SpeakSsml(S As Str ing) / SpeakSsmlAsync(S As Str ing) : come i metodi pr ecedenti, solo che il testo è for mattato in

un modo par ticolar e. Ssml significa "Speech Synthesis Mar kup Language": è un linguaggio di contr assegno

der ivato dall'Xml che indica non solo le fr asi da legger e, ma specifica anche le pr onuncie, per mette di metter e

enfasi in una par ola o di simular e un qualche stato d'animo attr aver so la voce

State : deter mina lo stato del sintetizzator e (fer mo, in pausa, in lettur a)

Voice : oggetto VoiceInfo che designa la voce in uso

Volume : volume della voce. Penso che i valor i validi siano da -10000 a 0, come per Audio e Video

Or a, far par lar e il computer non è da esor cisti, infatti bastano poche linee di codice:

1.2.3.4.5.6.7.8.

Dim Format As New AudioFormat.SpeechAudioFormatInfo( _44100, AudioFormat.AudioBitsPerSample.Sixteen, _AudioFormat.AudioChannel.Stereo)

Dim Stream As New IO.FileStream("Voce.wav", IO.FileMode.Create)Synt.SetOutputToAudioStream(Stream, Format)Synt.Speak("Hello!")Synt.Dispose()Stream.Close()

1.2.3.

Synt.SetOutputToWaveFile("Voce.wav")Synt.Speak("Hello!")Synt.Dispose()

01.02.03.04.05.06.07.08.09.10.11.

Imports System.SpeechImports System.Speech.RecognitionImports System.Speech.Synthesis 'Ricordatevi anche di importare la libreria System.Speech nel progetto,'con Add ReferenceModule Module1

Sub Main()'Inizializza il nuovo sintetizzatoreDim Synt As New SpeechSynthesizer

Page 481: Guida visual basic

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.

'Sceglie la classica voce di Microsoft SamSynt.SelectVoice("Microsoft Sam") 'Imposta l'output sulle casse del computer'In questo passaggio è obbligatorio usare un thread.'La ragione non è ben chiara, ma se non si fa in questo'modo, risulta sempre un errore di tipo ArgumentExceptionDim T As Threading.Thread'Imposta il nuovo thread: il suo compito principale sarà'di eseguire il metodo Synt.SetOutputToDefaultAudioDeviceT = New Threading.Thread(AddressOf _

Synt.SetOutputToDefaultAudioDevice)'Inizia il nuovo threadT.Start()'Aspetta che abbia finito per continuareT.Join() Dim Text As StringDo

Console.WriteLine("Inserisci una frase:")Text = Console.ReadLine 'Fa leggere la frase a MS SamSynt.Speak(Text)

Loop Until Text = "" 'Rilascia le risorseSynt.Dispose()

End SubEnd Module

Page 482: Guida visual basic

F14. Riconoscimento vocale

Questo capitolo è scr itto per VB2008!

Costruire la grammaticaPer il r iconoscimento vocale, la faccenda si fa un po' più complicata. L'oggetto pr incipale su cui si r egge tutto il capitolo

è System.Speech.Recognition.SpeechRecognitionEngine. Non analizzer ò in dettaglio i suoi membr i, poiché la gr an par te

di essi ver r à spiegata nel codice che scr iver ò dopo: baster à dir e che per l'inizializzazione, anch'esso dispone di metodi

SetInpuTo... identici a quelli di SpeechSynthesizer .

Or a, il computer non può pr eveder e tutte le possibili combinazioni di par ole esistenti, quindi dobbiamo esser e noi a

for nir gli un "dizionar io" su cui basar si per il r iconoscimento. La costr uzione di una str uttur a di questo tipo r ichiede

l'uso di un oggetto par ticolar e: Gr ammar Builder . Questa semplice classe aiuta a for mar e delle fr asi che potr anno

venir e cattur ate e r iconosciute dall'Engine attr aver so il micr ofono. Nelle pr ove che ho fatto, l'engine è r iuscito a

cattur ar e una sola par ola alla volta, ma for se mi sono dimenticato di impostar e i tempi giusti di inter vallo tr a una

par ola e l'altr a. Sta di fatto che il modo più semplice per far r iconoscer e una qualsiasi par ola all'engine consiste

nell'aggiunger e a Gr ammar Builder la lista di tutte le par ole contemplate (ossia, solo quelle che vogliamo noi). Ecco un

semplice esempio:

In questo modo si è aggiunta al Gr ammar Builder una gamma di par ole possibili: one, two, thr ee e four . Usando un

oggetto Choices comunichiamo all'engine che ogni par ola può esser e pr esa singolar mente. Ho usato dei numer i per chè

l'esempio di questo capitolo sar à un pr ogr amma in gr ado di r iconoscer e un numer o pr onunciato in inglese. A questo

pr oposito, bisogna dir e che l'oggetto Gr ammar Builder può esser e cr eato per differ enti lingue (anche se non ci sono

ancor a pacchetti per molte lingue diver se), ma esso da solo non è in gr ado di r iconoscer e a quale lingua appar tengano

le par ole che il pr ogr ammator e inser isce: per questo pr ende come nazionalità di default quella del computer su cui sta

cor r endo. Per il 99% di color o che leggono questa guida, quindi, Gr ammar Builder consider er à la cultur a cor r ente come

Italiana, poiché il computer ha installata quella. Tuttavia l'engine che user emo è impostato solo per la lingua inglese e

questo gener er à sicur amente un er r or e. Per ciò, pr ima di inser ir e la gr ammatica di Gr ammar Builder nell'engine di

r iconoscimento vocale, dobbiamo specificar e esplicitamente che si tr atta di inglese:

Una volta fatto questo, per for mar e una nuova gr ammatica usabile (un insieme di par ole in questo caso) bisogna

cr ear e un nuovo oggetto Gr ammar e passar e al costr uttor e Gr ammar Builder come par ametr o:

Or a manca la cosa più impor tante: l'engine di r iconoscimento vocale. User emo la classe SpeechRecognitionEngine, che

descr iver ò dir ettamente nei commenti del pr ossimo esempio.

1.2.3.4.5.6.7.

Imports System.SpeechImports System.Speech.RecognitionImports System.Speech.Synthesis '...Dim GrammarBuilder As New GrammarBuilderGrammarBuilder.Append(New Choices("one", "two", "three", "four"))

1. GrammarBuilder.Culture = Globalization.CultureInfo.GetCultureInfo("en-US")

1.2.3.

Dim Grammar As Grammar'...Grammar = New Grammar(GrammarBuilder)

Page 483: Guida visual basic

Esempio: Tell me how muchPer questo esempio basta un semplicissimo for m, con una label (lblNumber ) e due pulsanti (btnStar t e btnStop):

Questo è il codice:

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.

Imports System.SpeechImports System.Speech.RecognitionImports System.Speech.Synthesis Public Class Form2

'Nuovo Engine di riconoscimento vocalePrivate Engine As New SpeechRecognitionEngine'GrammarBuilder per costruire la grammaticaPrivate GrammarBuilder As New GrammarBuilder'Oggetto Grammar che rappresenta la grammaticaPrivate Grammar As Grammar

'Questo dizionario associa ad ogni parola il'corrispondente valore numerico (one=1)Private TextNumber As Dictionary(Of String, Int32)'Questo array già inizializzato contiene l'elenco di'tutte le parole che l'engine può rilevarePrivate Numbers As String() = _

New String() {"one", "two", "three", "four", _"five", "six", "seven", "eight", "nine", "ten", _"eleven", "twelve", "thirteen", "fourteen", _"fiftheen", "sixteen", "seventeen", "eighteen", _"nineteen", "twenty", "thirty", "fourty", "fifty", _"sixty", "seventy", "eighty", "ninty", "hundred", _"thousand", "reset"}

'L'ultima parola, reset, serve per porre a 0 il'conteggio, nel caso si volesse ripetere

'Prev ricorda l'ultimo numero immessoPrivate Prev As Int32'Result contiene il numero finalePrivate Result As Int32

Private Sub Form2_Load(ByVal sender As Object, ByVal e As EventArgs) _

Handles MyBase.Load'All'avvio del form, si imposta l'input dell'engine sul'normale microfono (che deve essere collegato al computer).'Anche in questo caso si usa un thread, per lo stesso'motivo citato nel capitolo precedenteDim T As New Threading.Thread( _

AddressOf Engine.SetInputToDefaultAudioDevice)T.Start()T.Join()

'Poi si genera il dizionario che associa le parole ai'valori numerici veri e propri. Dato che l'array'Numbers contiene i numeri in ordine, sfruttermo'qualche for per riempire il dizionario in poche'righe di codiceTextNumber = New Dictionary(Of String, Int32)With TextNumber

'I primi 20 numeri sono in ordine crescente,'da 1 a 20. Perciò basta aggiungere 1'all'indice I per ottenere il numero che la'parola indicaFor I As Int16 = 0 To 19

.Add(Numbers(I), I + 1)Next'I successivi sette numeri sono tutti i multipli'di 10, da 30 a 90. Con la formula:'(I-19)*10 + 20

Page 484: Guida visual basic

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.131.132.133.134.

'è come se I andasse da 1 a 7 e quindi'otteniamo tutte le decine da 20+10 a 20+70For I As Int16 = 20 To 26

.Add(Numbers(I), (I - 19) * 10 + 20)Next'Infine si aggiungono centinaia e migliaia a parte.Add("hundred", 100).Add("thousand", 1000)

End With

'Aggiunge tutte le parole-numero al GrammarBuilderGrammarBuilder.Append(New Choices(Numbers))'Imposta la lingua a ingleseGrammarBuilder.Culture = _

Globalization.CultureInfo.GetCultureInfo("en-US")'Costruisce la nuova "grammatica" con il GrammarBuilderGrammar = New Grammar(GrammarBuilder)

'Questo metodo serve per eliminare tutte le grammatiche'già presenti. Anche se quasi sicuramente non ci'sarà nessun grammatica precaricata, è sempre'meglio farlo prima di aggiungerne di nuoveEngine.UnloadAllGrammars()'Quindi carica la grammatica Grammar. Ora Engine è in'grado di riconoscere le parole dell'array NumbersEngine.LoadGrammar(Grammar)'Parte importantissima: aggiunge l'handler di evento per'l'evento SpeechRecognized, che viene lanciato quando'l'engine ha ascoltato la voce, l'ha analizzata e ha'trovato una corrispondenza valida nella sua grammaticaAddHandler Engine.SpeechRecognized, AddressOf Speech_Recognized

End Sub

Private Sub btnStart_Click(ByVal sender As Object, _ByVal e As EventArgs) Handles btnStart.Click'Fa partire il riconoscimento vocale. Il metodo è asincrono,'quindi viene eseguito su un altro thread e non blocca il form'chiamante. L'argomento Multiple indica che si effetteranno più'riconoscimenti e non uno soloEngine.RecognizeAsync(RecognizeMode.Multiple)

'Disabilita Start e abilita StopbtnStart.Enabled = FalsebtnStop.Enabled = True

End Sub

Private Sub btnStop_Click(ByVal sender As Object, _ByVal e As EventArgs) Handles btnStop.Click'Termina il riconoscimento asincronoEngine.RecognizeAsyncCancel()

'Abilita Start e disabilita StopbtnStart.Enabled = TruebtnStop.Enabled = False

End Sub

Private Sub Speech_Recognized(ByVal sender As Object, _ByVal e As SpeechRecognizedEventArgs)Dim N As Int32'Ottiene il testo, ossia la parola pronunciataDim Text As String = e.Result.Text

'Se il testo è "reset", annulla tuttoIf Text = "reset" Then

Result = 0End If

'Se il testo è contenuto nel dizionario, allora'è un numero validoIf TextNumber.ContainsKey(Text) Then

'Ottiene il numeroN = TextNumber(Text)

Page 485: Guida visual basic

Eseguendo questo codice e par lando bene nel micr ofono, si dovr ebbe ottener e un r isultato discr eto (alcune par ole,

comunque, si confondono). Nonostante il codice sia esatto, tuttavia, System.Speech r imane un namespace str ano, poiché

le sue classi gener ano spesso er r or i incompr ensibili. Se siete stati così for tunati da aver r iecevuto, come me, una

Tar getInvocationEx ception nell'evento SpeechRecognized, mi sar ete gr ati per il codice che pr opongo qui, un'alter nativa

più di quella sopr a, ma almeno più sicur a:

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.

'Se è 100, significa che si è pronunciato'"hundred". Hundred indica le centinaia e perciò'sicuramente non si può dire "twenty hundred", né'"one thousand hundred": l'unico caso in cui si può'usare hundred è dopo una singola cifra, ad esempio'"one hundred" o "nine hundred". Quindi controlla che il'numero precedente sia compreso tra 1 e 9If (N = 100) And (Prev > 0 And Prev < 10) Then

'Toglie l'unitàResult -= Prev'E la trasforma in centinaiaResult += Prev * 100

End If'Parimenti, si può usare "thousand" solo dopo un'numero minore di mille. Anche se lecito, nessuno direbbe'"a thousand thousand", ma piuttosto "a million"If (N = 1000) And (Result < 1000) Then

Result *= 1000End If'Se il numero è minore di 100, semplicemente lo'aggiunge. Se quindi si pronunciano "twenty" e "thirty"'di seguito, si otterà 50. Non chiedetemi perchè'l'ho fatto così...If (N < 100) Then

Result += NEnd If

ElseN = 0

End If

Prev = N

'Imposta il testo della labellblNumber.Text = String.Format("{0:N0}", Result)

End SubEnd Class

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.

Imports System.SpeechImports System.Speech.RecognitionImports System.Speech.Synthesis Public Class Form2

'Nuovo Engine di riconoscimento vocalePrivate Engine As New SpeechRecognitionEngine'GrammarBuilder per costruire la grammaticaPrivate GrammarBuilder As New GrammarBuilder'Oggetto Grammar che rappresenta la grammaticaPrivate Grammar As Grammar

'Questo dizionario associa ad ogni parola il'corrispondente valore numerico (one=1)Private TextNumber As Dictionary(Of String, Int32)'Questo array già inizializzato contiene l'elenco di'tutte le parole che l'engine può rilevarePrivate Numbers As String() = _

New String() {"one", "two", "three", "four", _"five", "six", "seven", "eight", "nine", "ten", _"eleven", "twelve", "thirteen", "fourteen", _"fiftheen", "sixteen", "seventeen", "eighteen", _"nineteen", "twenty", "thirty", "fourty", "fifty", _"sixty", "seventy", "eighty", "ninty", "hundred", _"thousand", "reset"}

'L'ultima parola, reset, serve per porre a 0 il'conteggio, nel caso si volesse ripetere

Page 486: Guida visual basic

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.

'Delegato che servirà dopoPrivate Delegate Sub SetLabel(ByVal Res As RecognitionResult)'Prev ricorda l'ultimo numero immessoPrivate Prev As Int32'Result contiene il numero finalePrivate Result As Int32

Private Sub Form2_Load(ByVal sender As Object, _

ByVal e As System.EventArgs) Handles MyBase.Load'All'avvio del form, si imposta l'input dell'engine sul'normale microfono (che deve essere collegato al computer).'Anche in questo caso si usa un thread, per lo stesso'motivo citato nel capitolo precedenteDim T As New Threading.Thread( _

AddressOf Engine.SetInputToDefaultAudioDevice)T.Start()T.Join()

'Poi si genera il dizionario che associa le parole ai'valori numerici veri e propri. Dato che l'array'Numbers contiene i numeri in ordine, sfruttermo'qualche for per riempire il dizionario in poche'righe di codiceTextNumber = New Dictionary(Of String, Int32)With TextNumber

'I primi 20 numeri sono in ordine crescente,'da 1 a 20. Perciò basta aggiungere 1'all'indice I per ottenere il numero che la'parola indicaFor I As Int16 = 0 To 19

.Add(Numbers(I), I + 1)Next'I successivi sette numeri sono tutti i multipli'di 10, da 30 a 90. Con la formula:'(I-19)*10 + 20'è come se I andasse da 1 a 7 e quindi'otteniamo tutte le decine da 20+10 a 20+70For I As Int16 = 20 To 26

.Add(Numbers(I), (I - 19) * 10 + 20)Next'Infine si aggiungono centinaia e migliaia a parte.Add("hundred", 100).Add("thousand", 1000)

End With

'Aggiunge tutte le parole-numero al GrammarBuilderGrammarBuilder.Append(New Choices(Numbers))'Imposta la lingua a ingleseGrammarBuilder.Culture = Globalization.CultureInfo.GetCultureInfo("en-US")'Costruisce la nuova "grammatica" con il GrammarBuilderGrammar = New Grammar(GrammarBuilder)

'Questo metodo serve per eliminare tutte le grammatiche'già presenti. Anche se quasi sicuramente non ci'sarà nessun grammatica precaricata, è sempre'meglio farlo prima di aggiungerne di nuoveEngine.UnloadAllGrammars()'Quindi carica la grammatica Grammar. Ora Engine è in'grado di riconoscere le parole dell'array NumbersEngine.LoadGrammar(Grammar)'Parte importantissima: aggiunge l'handler di evento per'l'evento SpeechRecognized, che viene lanciato quando'l'engine ha ascoltato la voce, l'ha analizzata e ha'trovato una corrispondenza valida nella sua grammaticaAddHandler Engine.SpeechRecognized, AddressOf Speech_Recognized

End Sub

Private Sub btnStart_Click(ByVal sender As Object, _ByVal e As EventArgs) Handles btnStart.Click'Fa partire il riconoscimento vocale. Il metodo è asincrono,'quindi viene eseguito su un altro thread e non blocca il form

Page 487: Guida visual basic

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.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.

'chiamante. L'argomento Multiple indica che si effetteranno più'riconoscimenti e non uno soloEngine.RecognizeAsync(RecognizeMode.Multiple)

'Disabilita Start e abilita StopbtnStart.Enabled = FalsebtnStop.Enabled = True

End Sub

Private Sub btnStop_Click(ByVal sender As Object, _ByVal e As EventArgs) Handles btnStop.Click'Termina il riconoscimento asincronoEngine.RecognizeAsyncCancel()

'Abilita Start e disabilita StopbtnStart.Enabled = TruebtnStop.Enabled = False

End Sub

Private Sub Speech_Recognized(ByVal sender As Object, _ByVal e As SpeechRecognizedEventArgs)'Può capitare che dopo l'esecuzione di questo evento,'sia generata un'eccezione TargetInvocationException, causata'dall'engine, il quale lancia un evento uguale prima che'questo sia terminato. Usando un thread risolviamo tuttoDim T As New Threading.Thread(AddressOf InvokeSetLabel)T.Start(e.Result)

End Sub

Private Sub InvokeSetLabel(ByVal Res As RecognitionResult)'Ovviamente questi stupido tipo di errori ci fa usare'una via alternativa sprecando molto codice in più.'Dato che, come sapete, non si può accedere ai'controlli di un form da un thread differente da quello'in cui sono stati creati, dobbiamo usare Invoke'per far eseguire lo stesso compito al thread principale'partendo da questo thread secondario.'Per chi non si ricorda i delegate, Invoke permette di'far correre un metodo nel thread dell'oggetto da cui è'richiamato (Me, ossia il form). In questo caso usiamo'il delegato di tipo SetLabel che punta ad AnalyuzeText'e gli passiamo dierettamente Res come parametroMe.Invoke(New SetLabel(AddressOf AnalyzeText), _

New Object() {Res})End Sub

Private Sub AnalyzeText(ByVal Res As RecognitionResult)

Dim N As Int32'Ottiene il testo, ossia la parola pronunciataDim Text As String = Res.Text

'Se il testo è "reset", annulla tuttoIf Text = "reset" Then

Result = 0End If

'Se il testo è contenuto nel dizionario, allora'è un numero validoIf TextNumber.ContainsKey(Text) Then

'Ottiene il numeroN = TextNumber(Text)'Se è 100, significa che si è pronunciato'"hundred". Hundred indica le centinaia e perciò'sicuramente non si può dire "twenty hundred", né'"one thousand hundred": l'unico caso in cui si può'usare hundred è dopo una singola cifra, ad esempio'"one hundred" o "nine hundred". Quindi controlla che il'numero precedente sia compreso tra 1 e 9If (N = 100) And (Prev > 0 And Prev < 10) Then

'Toglie l'unitàResult -= Prev'E la trasforma in centinaia

Page 488: Guida visual basic

Potr ebbe anche ver ificar si un altr o er r or e, qui:

di tipo For matEx ception, che non c'entr a assolutamente niente con quello che si sta facendo. Se anche voi siete così

for tunati, chiudete visual studio e r ipr ovateci domani (con me ha funzionato XD).

173.174.175.176.177.178.179.180.181.182.183.184.185.186.187.188.189.190.191.192.193.194.195.196.

Result += Prev * 100End If'Parimenti, si può usare "thousand" solo dopo un'numero minore di mille. Anche se lecito, nessuno direbbe'"a thousand thousand", ma piuttosto "a million"If (N = 1000) And (Result < 1000) Then

Result *= 1000End If'Se il numero è minore di 100, semplicemente lo'aggiunge. Se quindi si pronunciano "twenty" e "thirty"'di seguito, si otterà 50. Non chiedetemi perchè'l'ho fatto così...If (N < 100) Then

Result += NEnd If

ElseN = 0

End If

Prev = N

'Imposta il testo della labellblNumber.Text = String.Format("{0:N0}", Result)

End SubEnd Class

1. Me.Invoke(New SetLabel(AddressOf AnalyzeText), New Object() {Res})

Page 489: Guida visual basic

G1. Il Namespace My

Il namespace My è una novità di Visual Basic 2005: è stato intr odotto per for nir e un accesso facilitato a classi del

fr amewor k utili usate molto spesso, diminuendo così il codice necessar io e di conseguenza in tempo impiegato a

scr iver lo. My contiene oggetti singleton aggior nati dur ante la cr eazione dell'applicazione nell'ambiente di sviluppo: ad

esempio My.Settings viene popolato con le r isor se e le impostazioni aggiunte dall'utente, oppur e i membr i di My.For ms

sono aggiunti ogniqualvolta viene aggiunto un for m all'applicazione e costituiscono una scor ciatoia per r ichiamar li

senza definir ne altr e istanze. Ecco una panor amica dei membr i di My:

My.Application : espone infor mazioni sull'applicazione cor r ente, da dove sia stata lanciata, quale utente la stia

utilizzando e con quali per messi e le infor mazioni assembly quali ver sione e cultur a

My.Computer : per mette un'inter azione r apida e veloce con le per ifer iche del computer quali mouse e tastier a,

con il filesystem, con la memor ia, con i for mati audio e video e per mette di gestir e le por te ser iali e la

stampante

My.For ms : espone una pr opr ietà per ogni for m definito, cor r ispondente alla sua istanza di default

My.Resour ces : contiene oggetti che fanno da w r apper a ciascuna r isor se utilizzata nel pr ogetto e r efer enziata

esplicitamente

My.Settings : ogni pr opr ietà cor r isponde ad un'impostazione definita nei Settings del pr ogetto. Un oggetto può

esser e di qualsiasi tipo suppor tato e può aver e scopo differ ente a seconda che venga usato dall'utente o dal

pr ogr amma

My.User : r estituisce infor mazioni sull'utente che sya usando il computer

My.WebSer vices : per mette di usar e ser vizi web e r ichiamar e metodi web senza dover e scr iver e lo stesso

codice più volte

My.ApplicationEcco una lista dei membr i più significativi di questo oggetto:

ApplicationContex t : r estituisce il contesto applicativo in cui viene eseguito il pr ogr amma, tr amite cui si può

ottener e il Main For m

CommandLineAr gs : r estituisce una lista in sola lettur a a tipizzazione for te di str inghe contenente tutti gli

ar gomenti passati da console al pr ogr amma. Ond'evitar e confusione, per chi pr ovenisse da altr i linguaggi, il

pr imo ar gomento non è il nome del pr ogr amma stesso ma pr opr io il pr imo ar gomento che viene dopo la

dichiar azione dell'applicazione

Cultur e : r estituisce un oggetto Cultur eInfo che per mette di aver e infor mazioni sulla cultur a cor r ente, in

par ticolar e il modo in cui vengono for mattati valor i e date

Deployment : r estituisce un oggetto ApplicationDeployment che per mette di scar icar e e aggior nar e il

pr ogr amma scar icando i nuovi files da inter net. L'uso di questo oggetto ver r à tr attato in un altr o momento,

benchè nel momento in cui scr ivo, un capitolo al r iguar do non sia ancor a stato inser ito nell'indice della guida

Info : r estituisce un oggetto AssemblyInfo che consente di visualizzar e le infor mazioni sul pr ogr amma, quali

titolo, ver sione, autor e, società, descr izione ecc... Queste impostazioni possono esser e for nite al compilator e

tr amite Designer o tr amite codice, ma anche questo punto ver r à tr attato in seguito

Log : r estituisce un oggetto Log del namespace VisualBasic.Logging, i cui metodi per mettono di inter agir e con i

file di log, scr ivendo messaggi o r epor t di er r or i r iscontr ati a r un-time

Page 490: Guida visual basic

OpenFor ms : se il pr ogetto cor r ente è una Windows Application, ottiene una collezione di tutti i for m aper ti

SaveMySettingsOnEx it : se il pr ogetto cor r ente è una Windows Application, deter mina qualor a tutti i campi della

classe My.Settings debbano esser e automaticamente salvati pr ima dell'uscita dal pr ogr amma

DoEvents : se il pr ogetto cor r ente è una Windows Application, pr ocessa tutti i messaggi w indows in coda.

Questo per mette che gli eventi siano gener ati e oppor tunamente gestiti anche dur ante lo svolgimento di una

pr ocedur a par ticolar mente lunga (come quella di r icer ca dei file), oppur e che la gr afica del for m venga

cor r ettamente aggior nata, impendendo all'applicazione di assumer e un aspetto che ver r ebbe altr imenti

inter pr etato dall'utente come "bloccato". Sebbene sia molto comodo usar e DoEvents in casi del gener e, i pr incipi

Micr osoft sugger iscono invece di utilizzar e un contr ollo Backgr oundWor ker oppur e un thr ead separ ato cr eato

manualmente

GetEnvir onmentVar iable(S) : r estituisce il valor e della var iabile d'ambiente di nome S. Le var iabili d'ambiente

sono cr eate, usate e gestite dal sistema oper ativo e contengono infor mazioni sulle dir ector y, sui file e sui

dettagli tecnici dell'har dwar e e del softwar e. È possibile ottener e in questo modo, ma anche attr aver so la classe

System.Envir onment. Non è sicur o modificar e alcune di esse, come ad esempio, la car tella di Windows

Run(C()) : avvia una nuova istanza di questa applicazione passandole gli ar gomenti definiti nell'ar r ay di str inghe

C()

Fr a le altr e cose, questa classe espone anche degli eventi inter essanti: Star tup e Shutdown vengono lanciati

r ispettivamente quando l'applicazione viene aper ta o chiusa, ma possono facilmente esser e sostituiti dai più semplici

For mLoad e For mClosing; inter essanti sono invece gli eventi Star tupNex tInstance, lanciato quando viene avviata

un'altr a istanza dell'applicazione, UnhandledEx ception, gener ato ogniqualvolta si r iscontr a un er r or e non gestito (e

quindi utilissimo per non far appar ir e la famosa messagebox di er r or e cr itico) e Networ kAvaiabilityChanged, che

r ipor ta un cambiamento nello stato di usabilità della r ete (ossia quando ci si connette o disconnette). Gestir e

cor r ettamente tali eventi non può che por tar e benefici alla solidità e all'efficienza del pr ogr amma.

My.ComputerAd eccezione di Name, che r estituisce il nome del computer , tutte le altr e pr opr ietà esposte sono oggetti figli i quali a

lor o volta mettono a disposizione altr e funzionalità: in questo namespace vengono r iassunti i metodi più utili sul piano

dell'audio, del filesystem, del r egistr o di sistema e del r ecuper o infor mazioni. Ecco una lista di quasi tutte le pr opr ietà

(Por ts è stata tr alasciata poichè r ar amente usata, mentr e Registr y non ver r à analizzato, per ovvi motivi):

Audio

Play(S, M) : esegue un suono memor izzato in un file Audio Wave (*.wav), definito dal per cor so S. È

possibile specificar e anche, nell'over load, le modalità con cui avviene la r ipr oduzione. Esse sono espr esse

da un enumer ator e a tr e valor i: Backgr ound (musica di sottofondo), Backgr oundLoop (la musica viene

r ipetuta all'infinito, fino a quando non la si fer ma manualmente), WaitUntilComplete (aspetta che tutto il

file sia r ipr odotto pr ima di passar e all'istr uzione successiva). Ad ogni modo, per la costr uzione di un

Media Player è assai meglio r icor r er e all'aiuto delle libr er ie Dir ectX.AudioVideoPlayback

PlaySystemSound(S) : esegue un suono di default di Windows, definito dall'enumer ator e S di tipo

System.Media.SystemSound. I valor i r ipor tati sono quasi gli stessi dell'enumer ator e che definisce le icone

della MessageBox : infatti i suoni in questione non sono altr o che quelli r ipr odotti all'appar ir e di una

MessageBox

Stop : inter r ompe l'esecuzione di un suono in backgr ound

Clipboar d (gli appunti)

Clear : pulisce la clipboar d, annullando qualsiasi suo contenuto

Contains... : le funzioni che iniziano per "Contains" deter minano quale tipo di dato contengano gli appunti,

se immagini, suoni, files, testo o altr o

Page 491: Guida visual basic

Get... : le funzioni che iniziano con "Get" r estituiscono il contenuto della clipboar d secondo i var i for mati

Set... : allo stesso modo, le funzioni Set impostano il contenuto della clipboar d secondo i var i for mati

Clock

LocalTime : r estituisce la data e l'or a cor r ente memor izzate sul computer

TickCound : r estituisce il numer o di tick passati dall'accesione del computer . Un tick cor r isponde a un

milionesimo di secondo, ma questa pr opr ietà, str anamente, r estituisce il r isultato in millisecondi

FileSystem

CopyDir ector y(S, D, O) : nel suo pr imo over load, questa pr ocedur a copia la dir ector y indicata da S nella

dir ector y D; se D non esiste viene cr eata; se esiste e O (Over wr ite) = Tr ue, viene sovr ascr itta

CopyDir ector y(S, D, Show, Cancel) : nel suo secondo over load, copia la dir ector y da S a D; Show è un

valor e che indica se visualizzar e la finestr a di copia pr edefinita di w indows, mentr e Cancel specifica se è

possibile annullar e l'oper azione tr amite tale finestr a

CopyFile(S, D, ...) : copia un file da S a D. Pr esenta over load uguali a quelli sopr a descr itti

Cr eateDir ector y(D) : cr ea una nuova dir ector y secondo il per cor so D for nito

Cur r entDir ector y : la dir ector y cor r ente

DeleteDir ector y(D, F) : elimina una car tella D, specificando tr amite F, se si debba gener ar e un'eccezione

qualor a la car tella non sia vuota

DeleteDir ector y(D, Show, Recycle, Cancel) : elimina la car tella D, eventualmente visualizzando la finestr a

di dialogo di w indow s; è possibile specificar e se eliminar e completamente la car tella o se mandar la nel

cestino con Recycle, mentr e Cancel indica se sia o meno concessa all'utente la possibilità di annullar e

l'oper azione

DeleteFile(F, ...) : elimina il file F. Pr esenta over load uguali a quelli sopr a descr itti

Dir ector yEx ists(P) / FileEx ists(P) : deter minano se il file o la car tella P esistano o meno

Dr ives : r estituisce una collezione in sola lettur a a tipizzazione for te di Dr iveInfo contenente

infor mazioni sui var i dr ives disponibili

FindInFiles(P, S, Case, Recur se) : r estituisce una collezione in sola lettur a a tipizzazione for te di Str ing

contenente i nomi di tutti i files tr ovati. P è la car tella in cui cer car e, S è la par ola da cer car e nel file,

Case indica se la r icer ca è case sensitive oppur e no, mentr e Recur se indica se analizzar e anche le

sottodir ector y

GetDr iveInfo(P) / GetDir ector yInfo(P) / GetFileInfo(P) : r estituiscono infor mazioni sul per cor so specificato

GetDir ector ies(P, R) / GetFiles(P, R) : r estituiscono r ispettivamente le car telle e i files pr esenti nella

dir ector y P, eventualmente agendo r icor sivamente se R = Tr ue

GetName(P) : r estituisce il nome del file o della car tella (la sua ultima par te)

GetPar entPath(P) : r estituisce la car tella che si tr ova a livello super ior e r ispetto al file o alla dir ector y P

GetTempFileName : cr ea un nuovo file nella car tella tempor anea del computer , vuoto, e ne r estituisce il

per cor so

MoveDir ector y : esattamente come CopyDir ector y, solo che la car tella di par tenza viene r imossa

MoveFile : come sopr a

OpenTex tFieldPar ser (P, W()) : apr e un par ser di file di testo sul file P. Questo oggetto legge i valor i in

esso contenuti seguendo le dir ettive specificate nel Par amAr r ay W. Se si tr atta di dati a lar ghezza fissa,

W specifica le lar ghezza dei dati; se si tr atta di dati separ ati da car atter i speciali, W specifica quei

car atter i

ReadAllBytes / ReadAllTex t : leggono tutti i bytes o tutto il testo del file specificato e li/lo r estituisce/ono

RenameDir ector y(P, N) / RenameFile(P, N) : r inominano il file o la dir ector y P con un nuovo nome N (solo

il nome, non tutto il per cor so)

SpecialDir ector ies : r estituisce un oggetto le cui pr opr ietà indicano il per cor so delle car telle speciali,

come i documenti, le immagini, la car tella tempor anea, i video, ecceter a...

Wr iteAllTex t / Wr iteAllBytes : scr ivono tutto il testo o tutti i bytes dati all'inter no del file specificato

Page 492: Guida visual basic

Info

AvaiablePhysicalMemor y : l'ammontar e di memor ia fisica liber a sul computer , in bytes

OSFullName : il nome completo del sistema oper ativo

OSPlatfor m : identifica la piattafor ma del sistema oper ativo

TotalPhysicalMemor y : l'ammontar e totale di memor ia fisica del computer , in bytes

Keyboar d

AltKeyDown, Ctr lKeyDown, ShiftKeyDown : r estituiscono Tr ue se Alt, Ctr l o Shift r isultano pr emuto

nell'istante in cui la funzione viene r ichiamata

CapsLock, NumLock, Scr ollLock : r estituiscono Tr ue se sono attivi CpasLock, NumLock o Scr ollLock

SendKeys(K, W) : simula la pr essione del tasto K sulla finestr a attiva, opzionalmente aspettando finchè tale

messaggio non venga pr ocessato (W = Tr ue)

Mouse

ButtonsSwapped : deter mina se i pulsanti destr o e sinistr o r isultano scambiati

WheelEx ists : deter mina se il mouse sia dotato di r otellina

WheelScr ollLines : deter mina di quante linee si debba scor r er e quando la r ottelina venga r uotata di una

tacca

Networ k

DownloadFile(S, D) : scar ica il file S sul computer , salvandolo come D. È possibile specificar e come ter zo e

quar to par ametr o un nome utente e una passwor d nel caso il ser ver in questione ne r ichieda uno. Il

quinto par ametr o, se pr esente, specifica se visualizzar e la finestr a di dialogo di default di w indows; il

sesto indica l'opzionale timeout di connessione. Il settimo specifica se sovr ascr iver e un file esistente e il

settimo se sia possibile annullar e l'oper azione. Tutti i par ametr i dopo il secondo sono opzionali e for niti

dagli over loads della funzione

IsAvaiable : deter mina se il computer sia connesso o meno a una r ete

Ping(IP, Timeout) : esegue un'oper azione di Ping sul ser ver IP (Ip può esser e un Ip valido o un Dns). Il ping

ser ve per contr ollar e se il ser ver sia attivo: viene inviato un pacchetto di bytes di contr ollo; se r isponde,

significa che è online e funzionante, altr imenti ci sono dei pr oblemi. Si può specificar e opzionalmente un

Timeout di millisecondi tr ascor so il quale non si pr osegua oltr e nell'oper azione di Ping

UploadFile : car ica il file su un ser ver . I par ametr i sono gli stessi di DownloadFile

Scr een

AltScr een : r estituisce un ar r ay di tutti i dislapy del sistema

BitsPer Pix el : il numer o di bit associati ad un unico pix el in memor ia

Bounds : r estituisce un oggetto Rectangle contenente le dimensioni dello scher mo

DeviceName : il nome del device associato allo scher mo

Pr imar yScr een : lo scher mo pr imar io

Wor kingAr ea : l'ar ea di lavor o

My.UserEspone poche pr opr ietà, la più impor tante delle quali è Name, che r estituisce il nome dell'utente attualmente loggato.

Ci sono altr e funzioni come IsInRole che per mettono anche di definir e il r uolo dell'utente (ad esempio Amministr ator e o

Pr opr ietar io, oppur e un diver so stato se appar tiene a un dato gr uppo di computer ).

My.ResourcesÈ un contenitor e in gr ado di immagazzinar e qualsiasi file o r isor sa: immagini, video, suoni, file di dati, di testo,

str inghe e altr o ancor a. Tutto quello che viene immesso nell'applicazione attr aver so questo w r apper è inglobato

Page 493: Guida visual basic

nell'assembly finale, mentr e dur ante lo sviluppo del softwar e, tali file vengono tempor aneamente salvati nella car tella

Resour ces del pr ogetto. È possibile aggiunger e una nuova r isor sa dalle pr opr ietà del pr ogetto (Nome pr ogetto->Click

col destr o->Pr oper ties), come mostr ato in questa immagine:

Tr amite il menù in alto a sinistr a nella finestr a delle r isor se si può sceglier e quale tipo di dati aggiunger e: la lista al

centr o visualizza r isor se per tipo, quindi in una volta sar anno visibili solo str inghe, solo immagini, solo suoni,

ecceter a... Il pulsante a fianco, "Add Resour ce" per mette di aggiunger e una r isor sa di quel tipo; le altr e sottovoci

pr esenti sono delle scor ciatoie per r isor se usate spesso come str inghe, immagini o file di testo. Una volta aggiunta una

r isor sa tr amite il designer , il compilator e r iscr ive automaticamente tutto il codice nascosto di My.Resour ces,

r endendo disponibili come pr opr ietà tutti i dati immessi. A seconda del tipo specificato, tale pr opr ietà sar à r estituita

in manier a differ ente:

Str inghe e file di testo vengono r estituiti come Str ing

Le immagini vengono r estituite come System.Dr aw ing.Bitmap

I suoi vengono r estituiti come System.IO.UnmanagedMemor yStr eam

Le icone vengono r estituite come System.Dr aw ing.Icon

I file di altr o tipo vengono r estituiti come ar r ay di bytes

My.SettingsPer mezzo di questo oggetto è possibile salvar e le impostazioni dell'applicazione che devono per maner e tr a due

sessioni distinte. I settaggi vengono salvati can il suppor to dell'XML e della ser ializzazione in una car tella dal nome

chilomentr ico all'inter no della dir ector y dell'utente cor r ente. Il nome è stabilito usando le infor mazioni dell'assembly e il

suo str ong name. All'avvio, tutti i campi vengono automaticamente impostati dal pr ogr amma, che si pr eoccupa pr ima

del car icamento del for m, di r ecuper ar e il file XML e legger ne il contenuto. In questo r isiede la comodità di My.Settings,

poichè concede al pr ogr ammator e la liber tà di dedicar si alla scr ittur a del codice significativo, delegando poi alla

macchina l'esecuzione di compiti noiosi quali il car icamento delle impostazioni.

Anche in questo caso, si oper a su My.Settings attr aver so una finestr a nella sezione Pr oper ties del pr ogetto. La

scher mata è semplice e intuitiva, e per mette di cr ear e non solo valor i di tipi semplici come Str ing, Boolean o Double,

ma anche tipi complessi, sia value che r efer ence, anche definiti dallo sviluppator e: il r equisito minimo è che siano

Page 494: Guida visual basic

ser ializzabili (come si vedr à in seguito, di default, tutti gli oggetti sono ser ializzabili). Un'applicazione diffusa e molto

r ichiesta per la sua semplicità consiste nel poter salvar e valor i associati a contr olli. Ad esempio, si vuole che il font, il

testo e il color e di un pulsante vengano conser vati tr a una sessione e l'altr a. In questo caso, ma anche negli altr i, il

lavor o da far e non r isulta affatto lungo o complesso. Per pr ima cosa bisogna cr ear e tr e nuovi valor i attr aver so

l'inter faccia My.Settings dal pannello di contr ollo delle pr opr ietà di pr ogetto: uno di tipo Font, uno Str ing e uno Color .

Ecco:

(Si faccia caso alla finestr a delle pr opr ietà nell'angolo a destr a: anche da lì, come se fosse un nor male contr ollo, si

possono modificar e i campi di My.Settings, eventualmente specificando un commento in Descr iption) Lo scope User o

Application r iguar da la finalità per cui il campo viene cr eato: in gener e le pr opr ietà User sono modificabili, mentr e

quelle Application sono ReadOnly. Or a che sono stati cr eati gli oppor tuni valor i, bisogna collegar li alle r ispettive

pr opr ietà del pulsante (ovviamente dopo aver cr eato anche il pulsante). Selezionato il pulsante, bisogna espander e,

nella finestr a delle pr opr ietà, la voce "(Application Settings)". Al suo inter o sar à pr esente soltanto la voce "Tex t", alla

quale si assegner à il valor e ButtonTex t, tr amite la piccola finestr a di dialogo che appar ir à cliccandoci sopr a. Per le

altr e due pr opr ietà, For eColor e Font, bisogna cliccar e sul pulsantino coi tr e puntini di sospensione sull'elemento appena

sopr a, "(Pr oper tyBinding)": ver r à visualizzata una gr iglia completa di tutte le pr opr ietà, dando quindi la possibilità di

collegar e ciascuna al cor r ispettivo valor e. Risultato:

Page 495: Guida visual basic

Or a, cambiando un valor e di My.Settings, cambier à anche l'aspetto del contr ollo. Le impostazioni vengono car icate

automaticamente all'inizio, ma per salvar e bisogna o r ichiamar e il metodo My.Settings.Save oppur e impostar e a Tr ue

la pr opr ietà My.Application.SaveMySettingsOnEx t.

Per quanto r iguar da gli altr i metodi di questa classe, è necessar io annover ar e solo Save, Reload e Reset: dei pr imi due

è facilmente intuibile la funzione; il ter zo, invece, r eimposta al lor o valor e di default tutti i valor i che vengono

dichiar ati con Per sist = Tr ue. Inoltr e, sono pr esenti anchq quattr o utili eventi: SettingsChanging (gener ato pr ima che

venga modificata un'impostazione), Pr oper tyChanged (dopo che è stato cambiato il valor e di un'impostazione),

SettingsLoaded (all'avvio o dopo aver chiamato i metodi Reload o Reset) e SettingsSaving (pr ima che vengano salvate le

impostazioni). Bisogna notar e che gli eventi gener ati pr ima che avvenga qualcosa possono anche modificar e il

compor tamento dell'azione oppur e anche annullar la, sempr e ispezionando le possibilità offer te dai membr i di e.

My.Forms e My.WebServicesEspongono le istanze di default di tutti i For m o di tutti i Web Ser vice definiti nel pr ogetto: il secondo tipo di oggetto

non è tr attato in questa guida.

Page 496: Guida visual basic

G2. Estendere il Namespace My

Come se non bastasse la sua già str aor dinar ia potenza, il namespace My è ulter ior mente estendibile dal

pr ogr ammator e, in modo da adattar lo alle specifiche esigenze dell'applicazione. Pr ima di utilizzar e questa funzionalità,

tuttavia, sar ebbe oppor tuno valutar e anche le alter native e concluder e se non sia meglio una nor male libr er ia oppur e

un file ser ializzato.

Ad ogni modo, è possibile ampliar e questo namespace gr azie all'ausilio delle classi Par tial: infatti My è a tutti gli effetti

un nor male namespace, definito in un comune file *.vb, nascosto per ò agli occhi dello sviluppator e, dato che solo il

compilator e lo utilizza. In tale file vengono definiti solamente i membr i inser iti dall'utente e non è assolutamente

consigliabile modificar lo manualmente. La soluzione più cor r etta consiste, invece, nel dichiar ar e nel pr ogetto un nuovo

namespace, sempr e di nome My, che, gr azie alla keywor d Par tial usata dal compilator e nella cr eazione automatica,

sar à inter pr etato come estensione di quello or iginale.

Dur ante questo pr ocesso è possibile aggiunger e due tipi di oggetti: di pr imo e di secondo livello. Quelli di pr imo livello

sono classi definite all'inter no di My con un nome nuovo e ne diventano membr i effettivi, in modo da esser e r esi

accessibili con la semplice dicitur a My.[Classe]. In questo ambito esistono anche due differ enti modi di dichiar ar e

oggetti di questo tipo. Il pr imo metodo consiste nel cr ear e una nuova classe all'inter no di My:

Il secondo metodo pr evede la cr eazione di un modulo nascosto entr o il quale figur a una pr opr ietà che espone un'istanza

della classe, definita altr ove. Infatti, l'infr astr uttur a di My non espone dir ettamente i membr i Application, Settings,

ecceter a... ma moduli pubblici nascosti con nomi del tipo MyApplication e MySettings (anteponendo quindi "My" al nome

del membr o) nei quali vengono definiti tali oggetti come pr opr ietà. Per facilitar e la compr esione, ecco un esempio

simile a quello di pr ima:

01.02.03.04.05.06.07.08.09.10.

Namespace MyPublic Class Utilities

'Con questa versione, si utilizza la classe come contenitore'di metodi o membri statici, rendendola simile a un moduloPublic Shared Function Percent(ByVal Num As Int32, _

ByVal Max As Int32) As SingleReturn (Num * 100) / Max

End FunctionEnd Class

End Namespace

01.02.03.04.05.06.07.08.09.10.11.12.13.14.15.16.17.18.19.20.21.22.23.

Namespace My'L'attributo HideModuleName permette di nascondere il'nome del modulo nella scala gerarchica dell'IntelliSense'e accedere direttamente a Utilities<DebuggerNonUserCode(), _Microsoft.VisualBasic.HideModuleName()> _Public Module MyUtilities

Private _Utilities As New Utilities

Public ReadOnly Property Utilities() As UtilitiesGet

Return _UtilitiesEnd Get

End PropertyEnd Module

End Namespace Public Class Utilities

'Con questa versione, si utilizza la classe come vero oggetto,'ma i metodi di istanza che non utilizzano altri campi d'istanza'possono comunque essere utilizzati allo stesso modo dei metodi'statici, poichè l'oggetto è già inizializzato all'interno del

Page 497: Guida visual basic

Per aggiunger e, invece, oggetti di secondo livello, ossia ger ar chicamente subor dinati a uno qualsiasi dei membr i già

appar tenenti a My, basta inser ir e nel nuovo namespace una classe Par tial Fr iend il cui nome sia for mato dal pr efisso

"My" e dal nome del membr o da cui der ivar e la classe in questione. Ad esempio, questo codice espone il dns di un ser ver

usato per le connessioni inter net in My.Application:

24.25.26.27.28.

'modulo sopra definitoPublic Function Percent(ByVal Num As Int32, _

ByVal Max As Int32) As SingleReturn (Num * 100) / Max

End FunctionEnd Class

01.02.03.04.05.06.07.08.09.10.11.12.13.14.15.

Namespace My'Aggiunge membri a My.ApplicationPartial Friend Class MyApplication

Private _ServerName As String = "http://www.example.com"

Public Property ServerName() As StringGet

Return _ServerNameEnd GetSet(ByVal Value As String)

_ServerName = ValueEnd Set

End PropertyEnd Class

End Namespace

Page 498: Guida visual basic

G3. IDE: Alcune semplici funzioni da usare sempre

I capitoli nella sezione G non r iguar dano str ettamente il codice o le tecniche da usar e, ma si occupano di descr iver e

l'ambiente di sviluppo, in par ticolar e il compilator e Visual Basic Ex pr ess 2005/2008/2010. Ecco uno scr eenshot del mio

scher mo con aper to il pr ogetto che uso per scr iver e gli esempi della guida:

Panoramia dell'IDE

Nel capitolo pr ender ò in esame quegli str umenti che possono esser e utili nello sveltir e le oper azioni e aiutar e il

pr ogr ammator e nella stesur a del codice.

Solution ExplorerNella finestr a del Solution Ex plor er vengono visualizzati tutti i file che compongono il pr ogetto cor r ente, o, in caso si

tr atti di una soluzione, anche tutti i suoi pr ogetti. Da qui è possibile acceder e in modo r apido ad ogni r isor sa possibile.

Tutti i sor genti (quindi tutti i file con estensione *.vb) r ipor tati sono r aggiungibili mediante un doppio click e, per i

for m, con il menù contestuale. Sul lato super ior e della finestr a sono visibili cinque pulsanti, nell'or dine:

Pr opr ietà del pr ogetto: una scor ciatoia che por ta dir ettamente alle pr opr ietà di pr ogetto

Show All Files : visualizza tutti i file r ealmente esistenti nella car tella, fr a cui le r isor se, i r ifer imenti e i codici

di design dei for m. Ad esempio:

Show A ll Files

Come si può facilmente notar e dall'immagine, ci sono molte più cose di quante ce ne se aspettasse:

Page 499: Guida visual basic

My Pr oject : sotto questa voce vengono r aggr uppati tutti i sor genti che for mano il namespace My nelle

sue classi modificabili. Infatti nella figur a si osser vano Application.myapp (che for ma My.Application),

Resour ces.r esx (che for ma My.Resour ces) e Settings.settings (che for ma My.Settings). Il file manifest usa

la sintassi XML per comunicar e al sistema oper ativo altr e infor mazioni sottaciute all'utente, quali i

per messi concessi, la ver sione minima r ichiesta e altr i dati sulla sicur ezza

Refer ences: contiene tutti i r ifer imenti aggiunti

bin : questo elemento è solo una r ipr oduzione della ver a car tella bin pr esente nella car tella del pr ogetto.

Per mezzo di questa si possono veder e i file contenuti in Debug e Relase, ma non è niente di più che un

semplice br owser

obj : contiene file con l'elenco delle r isor se associate a ogni for m o contr ollo. Inoltr e contiene anche un

indice di tutti i file inclusi nel pr ogetto e necessar i al suo funzionamento

Resour ces : car tella delle r isor se. Bisogna evidenziar e che è color ata in giallo. Car telle di questo tipo sono

cr eabili anche dal pr ogr ammator e per mezzo del menù contestuale [Nome pr ogetto]->Add->New folder e

ser vono per or ganizzar e meglio l'applicazione

View Code : se il file selezionato è un sor gente, visualizza il codice r elativo

View Designer : se il file selezionate è un for m o un contr ollo, visualizza l'antepr ima di design

Proprietà di progettoQuesta sezione per mette di impostar e tutte le specifiche possibili e immaginabili r iguar danti il pr ogetto cor r ente,

dallo splash scr een iniziale, agli aggior namenti, alle infor mazioni dell'assembly. Ecco una panor amica di tutte le schede

non ancor a esaminate:

Propr ietà - Application

La scheda "Application" per mette di selezionar e i compor tamenti dell'applicazione. Ecco una lista dei campi con annessa

spiegazione:

Assembly name : il nome dell'assembly gener ato. Se il pr ogetto è una libr er ia di classi, si r ifer isce al nome che la

Page 500: Guida visual basic

DLL di output dovr à aver e, altr imenti si r ifer isce al nome dell'eseguibile (sia nella car tella Release che in quella

Debug)

Root namespace : il namespace del pr ogetto. Di default, è composto pr endendo il nome iniziale dato al pr ogetto

in fase di salvataggio e "nor malizzandolo", ossia eliminando tutti quei car atter i che non possono compor r e un

identificator e Visual Basic (ossia una var iabile). Può esser e utile cambiar lo anche solo per un fatto estetico: una

volta modificata la tex tbox , il compilator e esegue una r icer ca nei sor genti e sostituisce tutte le occor r enze,

senza quindi dar e alcun fastidio dur ante la sostituzione del nome

Application type : il tipo di assembly pr odotto in output. Si possono sceglier e tr e valor i: Windows Application,

Class Libr ar y e Console Application

Icon : l'icona dell'eseguibile pr odotto (se il pr ogetto è di tipo Class Libr ar y, non si potr à cambiar e l'icona

associata alla DLL). È valido qualsiasi file *.ico compatibile, pr efer ibilmente di dimensioni r idotte, come 32x 32 o

48x 48

Enable XP Visual Style : abilita la visualizzazione in stile Window s XP su sistemi oper ativi compatibili

Save My.Settings on shutdown : deter mina se salvar e le impostazioni definite in My.Settings quando

l'applicazione è in chiusur a. Equivale a impostar e My.Application.SaveMySettingsOnEx it

Shutdown mode : indica quando ter minar e l'applicazione, se alla chiusur a del pr imo for m (ossia del for m

pr incipale), oppur e alla chiusur a dell'ultimo for m visibile, indipendentemenete dal suo r uolo

Splash scr een : imposta lo splash scr een dell'applicazione. È valido un qualsiasi for m definito nel pr ogetto: esso

viene visualizzato pr ima di ogni altr a cosa all'avvio dell'applicazione per alcuni secondi

Propr ietà - Compile

La finestr a compile per mette di impostar e alcuni settaggi r iguar danti la compilazione. La pr ima casella di testo in alto

specifica dove salvar e l'assembly compilato. Le tr e combobox appena sotto, invece, impostano le opzioni di

compilazione, già esposte nel capitolo r elativo. Il DataGr idView centr ale, invece, fa stabiliar e all'utente come

compor tar si in alcuni casi par ticolar i: definisce se una cer ta situazione debba esser e segnalata oppur e no, e se sì in

for ma di er r or e o di semplice war ning. Queste azioni sono, nell'or dine, dall'alto in basso:

Page 501: Guida visual basic

Conver sioni implicite fr a tipi. Ad esempio:

Late binding, ossia r ichiamar e membr i da una var iabile Object. Ad esempio:

Dichiar azioni di var iabili senza la specificazione del tipo (nelle ver sioni 2005 e anter ior i, si assume che siano

Object). Ad esempio:

La var iabile viene usate pr ima che gli sia stato assegnato un valor e. Vale sia nel caso di valor i Value che

Refer ence:

Il compilator e r iesce ad individuar e anche i casi in cui una str uttur a di contr ollo impedisce a un valor e di esser e

sempr e assegnato con cer tezza. Il seguente codice pr oduce un w ar ning in compilazione e potr ebbe pr odur r e un

er r or e a r untime:

Dichiar azione di funzioni od oper ator e che non r estituiscono alcun tipo. Ad esempio:

Var iabile locale non utilizzata

Si accede a un campo statico attr aver so un'istanza di classe anziché attr aver so la classe stessa. Ad esempio:

1. Dim I As Int32 = 34.78

1.2.3.4.5.

Private Click(ByVal sender As Object, ByVal e As EventArgs)'sender è un generico Object: non si è sicuri che'a runtime possa risultare proprio di tipo Controlsender.Enabled = False

End Sub

1. Dim S

01.02.03.04.05.06.07.08.09.10.11.

Dim I As Int16'I non è inizializzata: sarà 0 e produrrà un erroreDim K As Int16 = 10 / I '... Dim Str As StringBuilder'L'oggetto Str non è inizializzato, poiché manca il suo'costruttore nella dichiarazione. Questo codice produrrà un'errore NullReferenceException a runtimeStr.Append("Ciao")

1.2.3.4.5.6.7.8.

Dim I As Int16 If K >= 26 Then

I = 2 * K + 1End If 'Se K < 26, I varrà 0K /= I

1.2.3.4.5.6.7.8.

Public Function GetName(ByVal Text As String)'...

End Function 'OppurePublic Shared Operator +(ByVal P1 As Person, ByVal P2 As Person)

'...End Operator

01.02.03.04.05.06.07.

Dim S, K As String '... 'IsNullOrEmpty è una funzione statica del tipo String.'Poiché è pur sempre un membro pubblico, diventa

Page 502: Guida visual basic

Il compilator e ha r ilevato una chiamata r icor siva, ossia che fa r ifer imento a se stessa. Er r or i del gener e

potr ebber o por tar e a loop e cr ash del pr ogr amma dur ante l'esecuzione. Ecco un esempio:

Blocchi Catch uguali

Le due checkbox in fondo, invece, per mettono o di soppr imer e ogni war ning, oppur e di tr attar e ogni war ning come se

fosse un er r or e.

La scheda Debug ha pochissime funzionalità. La pr ima tex tbox per mette di inser ir e dei par ametr i da r iga di comando

dir ettamente all'avvio per testar e l'applicazione, mentr e la seconda stabilisce la dir ector y in cui lavor a il pr ogr amma.

Propr ietà - References

La scher mata dei r ifer imenti consente al pr ogr ammator e di impor tar e velocementi componenti COM o assembly .NET.

La listview centr ale visualizza tutti i r ifer imenti attualmente pr esenti nel pr ogetto, mentr e la CheckedListBox in

basso è una scor ciatoia per aggiunger e velocemente assembly .NET pr ovenienti dalla Global Assembly Cache. Il pulsante

08.09.10.11.12.13.14.

'accessibile anche dai singoli oggetti. Ma, come già'ripetuto molte volte, non costituisce un membro appartenente'all'istanza, ma alla classe in sé. Qualsiasi valore di S,'pertanto, verrà trascurato. La versione corretta è'If String.IsNullOrEmpty(K) Then ...If S.IsNullOrEmpty(K) Then

'...End If

01.02.03.04.05.06.07.08.09.10.11.

Private _Name As StringPublic ReadOnly Property Name() As String

Get'Questo statement deve ottenere il valore della'proprietà Name, della quale si sta definendo ora'il corpo. In questo modo si richiederà il blocco'Get, che a sua volta richiamerà se stesso un'numero infinito di volteReturn Name

End GetEnd Property

Page 503: Guida visual basic

Refr ence Paths aggiunge un'inter a car tella, dalla quale si inser ir anno tutti i componenti ivi contenuti. Il pulsante di

fianco, invece, "Unused r efr ences", tr ova nei sor genti i r ifer imenti inutilizzati e per mette di r imuover li.

La scheda Secur ity stabilisce se il pr ogr amma debba esser e consider ato una Full Tr ust Application ("Applicazione

completamente affidabile") o una Par tial Tr ust Application ("Application non completamente affidabile"): la pr ima

gar antisce una sicur ezza massima, non inter fer isce in meccanismi delicati e per ciò ottiene dal sistema oper ativo tutti

i per messi necessar i a oper ar e sulla macchina; la seconda agisce su par ti sensibili del sistema e potr ebbe causar e

er r or i, per ciò Windows non gli concede tutti i per messi. La DataGr idView appena sotto definisce quali per messi siano

r ichiesti e quali no, mentr e il pulsante Pr oper ties apr e una finestr a in cui si possono definir e i per messi R-W delle

var iabili d'ambiente.

Propr ietà - Publish

La scheda Publish consente di pubblicar e il pr ogr amma come setup eseguibile: questo viene costr uito dal compilator e

sulla base delle infor mazioni immesse. Una volta avviato, installa il pr ogr amma in una locazione sconosciuta e non

modifica il menù Installazione Applicazioni. In questo modo l'utente non può disinstallar lo e se si ver ificano pr oblemi, non

può r icor r er e a nessun suppor to poiché i file necessar i sono nascosti chissà dove. Per tutti i motivi appena esposti,

sconsiglio vivamente la cr eazione del setup con l'editor pr edefinito: maggior i infor mazioni sar anno for nite nel capitolo

sui pacchetti d'installazione. Ecco una lista dei campi:

Publishing location : per cor so della car tella dove depositar e il setup

Installation URL : per cor so della car tella dove installar e il softwar e

Application files : apr e una finestr a di dialogo che per mette di selezionar e quali file includer e nel setup

Pr er equisites : seleziona i pr er equisiti ed eventualmente la locazione o il sito da dove scar icar li se non pr esenti

sul computer dell'utente

Updates : imposta le modalità di contr ollo degli aggior namenti, il per iodo di tempo ogni quanto eseguir e un

contr ollo per ver ificar ne l'esistenza, la ver sione minima r ichiesta all'aggior namento e, ovviamente, l'indir izzo

dove contr ollar e

Options : opzioni dell'assembly

Publish ver sion : ver sione di pubblicazione del pr ogr amma

Page 504: Guida visual basic

Publish w izar d : uno w izar d guida il pr ogr ammator e attar ver so la cr eazione del setup. Vengono pr oposte le

stesse opzioni che è possibile impostar e nella scheda

Publish Now : cr ea immediatamente il setup

Object BrowserL'Object br owser è una ver sione molto (ma molto!) più sofisticata e dettagliata del nostr o Br owser per Assembly,

scr itto nell'ultimo capitolo sulla Reflection. Per mette di navigar e tr a gli assembly del pr ogetto, sia quelli cr eati

dall'utente (contr olli utente, for m, moduli, classi, namespace), sia quelli gener ati dal compilator e (namespace My e

r elative sottoclassi) sia quelli depositati nella GAC (ad esempio System.Data o System.Xml). Visualizza tutte le

pr opr ietà, i metodi, i campi, gli enumer ator i, le str uttur e, le inter facce, le classi, i costr uttor i, i distr uttor i e gli

oper ator i cr eati, ossia ogni possibile infor mazione su un dato tipo. Il r iquadr o in basso mostr a anche la dichiar azione

del membr o e, se si tr atta di una pr ocedur a o di una funzione, anche la signatur e, la descr izione del funzionamento e

di ogni singolo par ametr o.

Object Browser

Dopo aver navigato un pò per i tipi, si sar à notato che queste infor mazioni non sono disponibili per gli oggetti cr eati

dal pr ogr ammator e, ma la spiegazione è semplice: non si è cr eata una documentazione adatta per quei membr i.

Consultar e il capitolo r elativo per maggior i dettagli.Si può osser var e che:

I membr i statici non vengono r ipor tati, tr anne i moduli

Tutta la documantazione viene tr asfer ita cor r ettamente nel r iquadr o della descr izione

I tipi vengono esposti fuor i dalla classe con l'oper ator e punto (ad esempio Documentazione.Str uttur a)

Str uttur e, delegate, inter facce ed enumer ator i, poiché der ivati dalle classi basi System.Str uctur e,

System.Delegate e System.Enumer ator , espongono molti metodi addizionali

I membr i pr ivati hanno una piccola icona a for ma di lucchetto in basso a destr a

I membr i fr iend hanno una piccola icona a for ma di r ombo azzur r o in basso a sinstr a

Page 505: Guida visual basic

IntelliSenseC'è un altr o modo per r aggiunger e velocemente la documentazione di un membr o, ed è usar e l'IntelliSense. Questa

tecnologia legge quello che il pr ogr ammator e ha digitato nel codice e, in cor r ispondenza di cer ti simboli (come lo

spazio o il punto), for nisce dei sugger imenti tr amite un menù a cascata. Ecco un esempio:

IntelliSense in azione!

Oltr e a visualizzar e infor mazioni sulla classe selezionata, per mette di scor r er e velocemente tutti i membr i con le

fr ecce dir ezionali: per attivar e l'autocompletamento, basta digitar e il car atter e punto (se ci si deve addentr ar e più

all'inter no nella ger ar chia), spazio (se si deve continuar e l'espr essione) o invio (se una volta completato, si ter mina

l'espr essione). Così facendo, dichiar o un nuovo Str ingBuilder digitando questi tasti:

E ottengo lo stesso r isultato in un tempo notevolmente infer ior e. Usando questa tecnica si possono indagar e anche i

par ametr i dei metodi apr endo le par entesi:

1. dim Str as new sys.te.s[Invio]

Page 506: Guida visual basic

IntelliSense in azione!

Oltr e ad ottener e la descr izione del par ametr o e il tipo r ichiesto, è possibile veder e anche tutte le ver sioni modificate

con over loading scor r endole con le fr eccette dir ezioni indicate (anche da tastier a).

Un altr o pr egio dell'IntelliSense consiste nel poter r ilevar e la dichiar azione di una var iabile quando il mouse ci passa

sopr a.

Altri strumenti utiliIn questo par agr afetto cito due str umenti di minor impor tanza, ma molto utili. Il pr imo è il Code Snippet, che

per mette di inser ir e all'inter no del sor gente fr ammenti di codice già scr itti. Per inser ir e un fr ammento esistente,

basta selezionar e "Inser t Snippet" dal menù contestuale dell'editor di codice. Dopodiché appar ir à una lista contenente

delle car telle con nomi in inglese: sono le categor ie di codice disponibili. Ad esempio, per cr ear e in modo veloce una

pr opr ietà, si seleziona questo:

Code Snippet

Il compilator e gener er à un codice in cui sono evidenziate delle par ti in ver de: se se ne modifica una, quelle

cor r ispondenti vengono modificate a lor o volta con lo stesso valor e.

L'altr o str umento è un tool di r inominazione. Se si clicca col destr o su un indentificator e e si sceglie "Rename", lo si può

r inominar e e il compilator e r inomina anche tutti i suoi r ifer imenti altr ove.

Page 507: Guida visual basic

G4. Guida all'uso di IntelliSense

Data la mole di gente che non usa o non sa usar e IntelliSense, ho deciso di scr iver e questo capitolo in più per spiegar e

l'utilizzo di questa funzione vitale del compilator e.

Attivare IntelliSensePer attivar e IntelliSense in Visual Studio o Visual Basic Ex pr ess, sotto la voce Tools (Str umenti), sceglier e Options

(opzioni), quindi selezionar e ed espander e la voce Tex t Editor e apr ir e il pannello Basic, come mostr ato in figur a:

Il pannello Basic, nelle opzioni

Assicur atevi che le voci "Auto list member s" e "Par ameter infor mation" siano spuntate, quindi pr emete OK.

Or a, ogniqualvolta dovr ete sceglier e un tipo, acceder e ai membr i di una classe, passar e par ametr i ad un metodo e

molto altr o, IntelliSense vi dar à sugger imenti mostr ando una lista di tutte le possibili scelte che potete far e (for nendo

anche una descr izione di ogni possibilità). Alcuni esempi:

Scelta del tipo di una var iabile

Accesso ai membr i di un og g etto

Passag g io di parametr i a un metodo

Asseg nazione di valor i a un enumeratore

Or a che avete visto le potenzialità di IntelliSense sper o che inizier ete ad utilizzar lo, o almeno, pr oseguiate nella

lettur a.

IntelliSense distingue i membri di c lasseTutte le icone che IntelliSense usa per contr assegnar e gli oggetti della lista dei sugger imenti non sono assolutamente

messe a caso. Hanno un significato ben pr eciso:

Campo (var iabile)

Page 508: Guida visual basic

Costante o Valor e di enumer ator e

Metodo

Pr opr ietà

Str uttur a

Enumer ator e

Inter faccia

Classe

Namespace

Modulo

Delegato

Evento

Oper ator e

Tipo base (Str ing, Byte, Shor t, Integer , Long, Single, Double, Boolean, Date, Object, SByte e Char ). In alcuni casi, ad

esempio nell'autocompletamente degli statement "Ex it" (come Ex it For , Ex it Do, Ex it Sub, ecceter a...), indica lo

statement stesso. Nell'autocompletamento di VB2008, indica anche ogni par ola chiave

Inoltr e, sempr e tr amite le icone, è possibile infer ir e il livello di accesso di cui ogni membr o è dotato. Infatti, l'icona di

ognuna delle categor ie sopr a citate può esser e modificata con l'aggiunta di un piccolo simbolo in basso a sinistr a:

Il piccolo r ombo azzur r o indica che il membr o è Fr iend

La piccola chiave gialla indica che il membr o è Pr otected

Il piccolo lucchetto gr igio indica che il membr o è Pr ivate

Se il membr o è Public, non avr à nessun simbolo aggiuntivo. Invece, per i membr i Pr otected Fr iend si assume come

simbolo lo stesso di quelli Pr otected.

IntelliSense suggerisce i parametri di ogni metodoQuando vi è stato sugger ito di usar e un metodo, non occor r e che vi facciate dir e anche quali devono esser e i suoi

par ametr i, poiché IntelliSense ve li sugger isce automaticamente, cor r edandoli, qualor a sia possibile, di una descr izione.

Ad esempio, questo codice:

Dim B As New Bitmap()

r estituir à un er r or e: "Over load r esolution failed because no accessible New accepts this number of ar guments", ossia

stiamo tentando di usar e un costr uttor e senza par ametr i quando la classe in questione non espone nessun New senza

par ametr i. Per ciò dobbiamo obbligator iamente passar e un qualche ar gomento al costr uttor e, ma non sapiamo quale.

Basta apr ir e la par entesi dopo Bitmap per veder e la lista di sugger imenti for nita da IntelliSense:

come si vede ci sono ben 12 over load, ossia 12 ver sioni dello stesso metodo e ognuna di questa ha una sua descr izione.

Alla fine, sicur amente tr over emmo quello che stiamo cer cando, ad esempio l'over load 12, che è quello più semplice e che

ci consente di inizializzar e una nuova bitmap con date dimensioni, oppur e il 5, con un solo par ametr o di tipo str inga

che contiene il nome del file. Insomma, tutto quello che è documentato su msdn lo potete tr ovar e anche usando

l'IntelliSense.

IntelliSense permette di trovare c lassi e funzioni nuoveUna funzione meno canonica, ma sicur amente altr ettanto utile, di IntelliSense sta nel fatto che, gr azie alla lista di

Page 509: Guida visual basic

sugger imenti for niti nella deter minazione del tipo di una var iabile, potete esplor ar e TUTTE le classi ESISTENTI sul

vostr o computer (ovviamente quelle impor tante nell'applicazione cor r ente). Solo guar dandone il nome potete cer car e di

capir e la lor o funzione e, con qualche r icer ca, tr ovar e quello che vi inter essa. Ad esempio, gir ovaghiamo per

System.Net... tr oviamo un inter essante namespace Networ kInfor mation e vi scor giamo numer ose classi che iniziano

con "Ping": abbiamo tr ovato il luogo giusto per eseguir e un ping da codice. Pr ovando a inizializzar e un nuovo oggetto

Net.Networ kInfor mation.Ping non si ottengono er r or i, quindi se ne possono esplor ar e le funzioni. Salta subito all'occhio

la funzione Send, gr azie alla quale possiamo ottener e molte infor mazioni utili sul viaggio dei nostr i pacchetti in r ete.

Non c'er a neanche bisogno di cer car e su Inter net!

IntelliSense è vostro amicoQuindi non fate a meno di usar lo e impar ate a sfr uttar ne tutte le potenzialità, sempr e e comunque!

Page 510: Guida visual basic

G5. Debugging

Nei capitoli pr ecedenti ho esposto alcuni str umenti da usar e dur ante la scr ittur a del sor gente, ma una volta scr itto,

bisogna testar lo ed elimiar e i bug, gli er r or i del pr ogr amma. Questa oper azione si dice debug g ing (dall'inglese

de-bug).

Finestra degli erroriIl componente più impor tante che si ha a disposizione è la finestr a degli er r or i, nella quale vengono visualizzati tutti

gli er r or i, gli war ning e i messaggi. Pr ima di pr oseguir e bisogna far e una distinzione tr a le tr e tipologie di notifiche

esistenti:

Er r or i : sono er r or i tutte le espr essioni incomplete, l'incongr uenza dei tipi dati con quelli r ichiesti, la mancanza

di identificator i, la scr ittur a er r ata di un'istr uzione, ecceter a... Gli er r or i non per mettono all'applicazione di

cor r er e in modo sicur o e por tano nel 100% dei casi a un cr ash del pr ogr amma. Il compilator e r iesce ad

individuar e in modo semplice tutti quelli basati sulla sintassi, poiché si tr atta semplicemente di confr ontar e

schemi pr edefiniti con str uttur e date. Tutti gli er r or i vengono sottolineati in viola

War ning : sono delle "avver tenze", per cor si di esecuzione che potr ebber o condur r e a un er r or e o a un loop, o

semplicemente segnalazioni di metodi obsoleti o di var iabili inutilizzate. Scovar e questo tipo di aber r azioni nel

codice è meno semplice e pr esuppone il cer car e di consider e un'evenienza e capir e come il codice fluir à dur ante

l'esecuzione. Il compilator e può individuar e ad esempio che in una funzione, non tutti i per cor si di codice

conducono a un r isultato, oppur e che un metodo r ichiama se stesso in un loop r icor sivo. Tutti gli war ning

vengono sottolineati in ver de

Messages : semplici messaggi del compilator e. Pr aticamente sempr e assenti

Si può abilitar e/disabilitar e la visualizzazione di er r or i, war ning o messaggi cliccando sul nome. Nella lista è possibile

r aggiunger e con un click su un elemento il punto del codice che ha gener ato l'er r or e.

BreakpointI br eakpoint sono punti in cui il pr ogr amma si fer ma, si mette in "pausa", mantenendo per ò i valor i di tutte le

var iabili, per consentir e al pr ogr ammator e di studiar e cosa sta avvenendo all'inter no dell'applicazione. Questo

per mette di scovar e molti tipi di er r or i, sia di valor e, sia di logica. Per attivar e un br eakpoint, basta cliccar e col

mouse sul mar gine sinistr o dell'editor di codice, nella par te gr igia. La r iga selezionata ver r à evidenziata e sar à posto

un pallino r osso di fianco. Quando il codice ar r iva a quel punto, si fer ma pr ima di eseguir e la r iga selezionata e va in

pausa, por tando in pr imo piano il sor gente in questione ed evidenziandolo in giallo. Una volta giunti a questo punto, si

può contr ollar e il valor e di ogni var iabile semplicemente posizionandovi sopr a il mouse per alcuni decimi di secondo.

Ecco come potr ebbe appar ir e una scher mata in pausa:

In questo modo è possibile analizzar e l'or igine di ogni pr oblema o compor tamento str ano. Se i valor i delle var iabili sono

di tipo str inga e sono anche piuttosto lunghi è possibile, con un doppio click sulla lente d'ingr andimento, visualizzar li in

una finestr a separ ata. Il compilator e pr edispone anche due modalità di visualizzazione alter native: XML e HTML.

Entr ambe sono attivabili dal menù a discesa accessibile cliccando sulla fr eccetta in giù vicino alla lente (al fianco di ogni

str inga).

Page 511: Guida visual basic

Inoltr e, c'è un modo per aggiunger e br eakpoint da codice. Consiste nell'utilizzar e la keywor d Stop. Ad esempio:

Finestra WatchQuando l'applicazione è in pausa, in basso a destr a (usualmente), si apr e una finestr a che contiene i valor i delle var iabili

in gioco. È divisa in tr e schede: la pr ima, Auto, contiene le var iabili pr esenti sul br eakpoint e quelle locali; la seconda,

Local, contiene solo le var iabili locali; la ter za, Watch, contiene le var iabili il cui valor e è stato for zatamente fatto

analizzar e dal pr ogr ammator e. Per aggiunger e una var iabile alla lista Watch bisogna tr ovar si pr ima di tutto in

modalità pausa, quindi si clicca con il pulsante destr o del mouse sulla var iabile in questione e si sceglie "Add watch". Da

questo momento in poi, ogni cambiamento della var iabile ver r à r egistr ato e monitor ato, anche in pezzi di codice al di

fuor i del blocco in cui essa si tr ova. C'è anche un altr o modo per ottener e dei valor i pr ima di un br eakpoint, ossia la

finestr a Debug.

Finestra DebugLa finestr a Debug è anche nota come Immediate Window ed è visibile solo in pausa, selezionando l'oppor tuna scheda

nell'angolo in basso a destr a. Al suo inter no ci si può scr iver e di tutto, qualsiasi infor mazione utile al debugging. Infatti

si usa l'oggetto singleton Debug come se fosse un oggetto Console, e l'output viene scr itto, appunto, sulla finestr a. Ad

esempio:

1.2.3.4.

'...If I = 50 Then

StopEnd If

01.02.03.04.05.06.07.08.09.10.11.12.13.14.15.16.17.

Module Module1Sub Main()

Dim P As New Person("Pinco", "Pallino", New Date(2008, 1, 1))'...

Debug.WriteLine("Nome completo: " & P.CompleteName)'Si possono scrivere messaggi solo se vige una certa condizioneDebug.WriteLineIf(P.BirthDay > New Date(2007, 9, 27), _

"Data di nascita posteriore al 27/9/2007")

If P IsNot Nothing ThenStop

End If

Console.ReadKey()End Sub

End Module

Page 512: Guida visual basic
Page 513: Guida visual basic

G6. Documentare il sorgente

Bene, or a che avete pensato, scr itto e testato la vostr a applicazione siete pr onti per lanciar la sul mer cato... o quasi.

Nel caso il pr ogetto che avete completato sia pensato per poter esser e utilizzato da altr i pr ogr ammator i, è bene

(anzi, è necessar io) documentar e il codice che avete scr itto. In questa sezione ho intr odotto i pr imi tools per il

debugging, ivi compr eso l'IntelliSense. Esso ci per mette di veder e la descr izione di ogni entità, e sar ebbe ver amente

comodo se anche le nostr e classi e i nostr i metodi avesser o una lor o descr izione. Per far e ciò, bisogna documentar e il

codice: in .NET, la documentazione si attua mediante un ver o e pr opr io str umento sintattico basato su XML. Per

documentar e una qualsiasi entità la si fa pr eceder e da uno speciale attr ibuto posto dopo tr e apici.

Tag di documentazioneEcco una lista dei pr incipali attr ibuti/tag di documentazione:

summar y : una descr izione del membr o e della sua funzione. Ad esempio:

ex ample : un esempio di come utilizzar e il membr o in questione. Può contener e anche dei tag <code>, che

visualizzano il testo compr eso come un codice sor gente

ex ception : contiene infor mazioni r iguar do al ver ificar si di un'eccezione e magar i anche qualche consiglio su

come r isolver e l'er r or e. Poiché le eccezioni var iano, accetta un par ametr o di nome cr ef che espone il nome

dell'eccezione. Ad esempio:

Ovviamente, per il par ametr o cr ef, il compilator e offr e l'aiuto dell'IntelliSense nel completamento automatico

par am : descr ive cosa debba esser e passato come par ametr o. Dato che un metodo può aver e più par ametr i,

bisogna indicar e un par ametr o name per discer ner e gli ar gomenti. Ripr endendo l'esempio di pr ima:

typepar am : come par am, ma usato per descr iver e i par ametr i gener ics aper ti:

1.2.3.4.5.6.

''' <summary>''' Descrizione del metodo''' </summary>Sub DoSomething()

'...End Sub

1.2.3.4.5.6.7.

''' <exception cref="IndexOutOfRangeException">''' Si è specificato un valore di Iterazions''' troppo elevato.''' </exception>Public Sub TransformName(ByVal Name As String, ByVal Iterations As Byte)

'...End Sub

1.2.3.4.5.6.

''' <param name="Name">Un nome qualsiasi, non nullo.</param>''' <param name="Iterations">Il numero di volte che''' la funzione di modifica viene applicata al nome.</param>Public Sub TransformName(ByVal Name As String, ByVal Iterations As Byte) End Sub

1.2.3.4.5.6.

''' <summary>''' Fa qualcosa...''' </summary>''' <typeparam name="T">Un qualsiasi tipo reference.</typeparam>Sub DoSomething(Of T As Class)()

Page 514: Guida visual basic

r emar ks : altr i dettagli utili sul membr o

r etur ns : descr izione dell'oggetto r estituito (funzioni/pr opr ietà)

value : descr izione dell'oggetto r estituito (solo pr opr ietà)

see, seealso : indicano un r ifer imento ad un altr a entità collegata logicamente a questa (un classico "vedi

anche...")

Questo sor gente mostr a l'uso dei tag di documentazione applicato ad ogni tipo di membr o possibile:

7. End Sub

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.

''' <summary>''' Rappresenta un esempio di tutti i tag di documentazione,''' applicati ad ogni tipo di membro.''' </summary>''' <remarks>Servirà anche per mostrare le varie''' icone nell'Object Browser</remarks>Public Class Documentazione

''' <summary>''' Espone lo scheletro di una classe.''' </summary>Friend Interface Interfaccia

'Lascio vuoto, poiché i membri di un'interfaccia'sono pur sempre uguali a quelli di una classe, di'cui sto scrivendo esempi.

End Interface

''' <summary>''' Espone alcune costanti numeriche sotto la forma di''' identificatori che si ricordano più facilmente.''' </summary>''' <remarks>Anche i singoli valori possono avere''' dei tag proprio come ogni altro membro.</remarks>Private Enum Enumeratore

''' <summary>''' Il primo valore dell'enumeratore. Vale 1.''' </summary>Primo = 1''' <summary>''' Il secondo valore dell'enumeratore. Vale 2.''' </summary>Secondo''' <summary>''' Il terzo valore dell'enumeratore. Vale 3.''' </summary>Terzo

End Enum

''' <summary>''' Raggruppa al suo interno più valori di tipo base.''' </summary>Friend Structure Struttura

Dim A, B, C As Int16End Structure

''' <summary>''' Rappresenta un puntatore a metodo in modo sicuro.''' </summary>''' <param name="A">Un valore interno positivo, compreso tra 0''' e 255. Costituisce la trasparenza del messaggio.</param>''' <param name="B">Un messaggio in forma di stringa.</param>Public Delegate Sub Delegato(ByVal A As Int16, ByVal B As String)

''' <summary>''' Rappresenta un cambiamento di stato dell'oggetto.''' </summary>Friend Event Evento As EventHandler

''' <summary>

Page 515: Guida visual basic

E viene visualizzato così:

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.

''' Una qualsiasi data.''' </summary>Friend Shared VariabileStatica As Date''' <summary>''' Rappresenta un valore booleano, vero o falso.''' </summary>Public VariabileIstanza As Boolean

''' <summary>''' Media l'interazione tra un campo privato e il programmatore.''' </summary>''' <value>Un valore che rappresenta VariabileIstanza.</value>''' <returns>Restituisce un valore Booleano.</returns>Public Property Proprietà() As Boolean

GetReturn Me.VariabileIstanza

End GetSet(ByVal Value As Boolean)

Me.VariabileIstanza = ValueEnd Set

End Property

''' <summary>''' Esegue un certo insieme di istruzioni.''' </summary>''' <param name="C">Il delegate da richiamare alla fine''' del processo.</param>Public Sub Procedura(ByVal C As Delegato)

End Sub

''' <summary>''' Esegue un certo insieme di istruzioni, o manipola''' un valore e quindi restituisce un risultato.''' </summary>''' <param name="I">Un numero intero qualsiasi.</param>''' <returns>Restituisce l'antireciproco del numero dato.</returns>Public Function Funzione(ByVal I As Int16) As Single

Return -(1 / I)End Function

End Class ''' <summary>''' Un semplice modulo, ossia una classe statica.''' </summary>Module Modulo End Module

Page 516: Guida visual basic
Page 517: Guida visual basic

G7. Costruire un pacchetto di installazione

Questo capitolo non spiega come compilar e un setup, ma come cr ear ne uno tr amite un pr ogr amma molto buono scr itto

apposta per questo. Si chiama Inno setup ed è scar icabile da questo sito.

Creazione di un setup tramite wizardInno Setup non è un pr ogr amma comunemente inteso, ma è un compilator e, pr opr io come Visual Basic Ex pr ess. Nella

fattispecie, compila e pr oduce eseguibili di scr ipt che l'utente scr ive: quindi ogni dato va immesso con l'editor di testo.

Dato che può r isultar e molto lungo, questo pr ocedimento viene in par te velocizzato dal Wizar d, un'applicativo con il

compito di guidar e l'utente attr aver so un per cor so pr edefinito che r ichiede ad ogni finestr a di aggiunger e altr e

infor mazioni. Per attivar e il w izar d, cliccar e su File->New dopo aver aper to il pr ogr amma (o pr emer e CTRL+N):

Fig ura 1

Cliccar e Nex t per pr oseguir e.

Fig ura 2

Nella seconda finestr a bisogna specificar e:

Il nome dell'applicazione

Il nome dell'applicazione, includendo anche l'indicator e di ver sione

La società o la community che pubblica il softwar e

Il sito di r ifer imento per quel softwar e (ver r à inser ito nel menù di Installazione Applicazioni di Windows)

Fig ura 3

Nella ter za finestr a ci sono specifiche r iguar danti la locazione d'installazione:

La combobox iniziale deter mina se l'applicazione sar à installata nella nor male car tella Pr ogr ammi oppur e in un

altr o per cor so. Se si sceglie la seconda opzione ("Custom"), ver r à sbloccata la tex tbox sottostante nella quale

inser ir e il per cor so adatto

La tex tbox centr ale indica il nome della car tella nella quale il pr ogr amma viene installato. Di solito coincide con

il nome dello stesso o al massimo con il nome della società

Le altr e due checkbox indicano se lasciar e all'utente la possibilità di cambiar e la car tella oppur e se il softw ar e

non necessita di car tella. In quest'ultimo caso, si tr atta di applicazioni con setup XCOPY, ossia delle quali basta

una semplice copia sull'har d disk e niente di più per l'installazione

Page 518: Guida visual basic

Fig ura 4

Nella quar ta finestr a vengono r ichiesti i file da installar e. La pr ima tex tbox r ichiede il per cor so sull'har d disk

dell'utente che sta scr ivendo il setup dell'eseguibile pr incipale, mentr e la listbox sottostante per mette di aggiunger e

altr i file o altr e car telle. Se si tr atta di una car tella, non ver r à copiato solo il suo contenuto, ma ver r à anche cr eata la

car tella stessa, compr ensiva di ogni sottodir ector y. Le checkbox in mezzo deter minano se si possa lanciar e il softwar e

alla fine del setup e se non ci sia un eseguibile pr incipale.

Fig ura 5

Nella quinta finestr a ci sono un pò di opzioni r iguar do al menù Star t. Esse sono, nell'or dine:

Il nome della car tella da cr ear e nel menù "Tutti i pr ogr ammi"

La possibilità di consentir e all'utente la modifica di tale nome

La possibilità di consentir e all'utente l'annullamento della cr eazione di una car tella nel menù

La possibilità di cr ear e un collegamento al sito inter net del softw ar e

La possibilità di cr ear e un collegamento al pr ogr amma di disinstallazione

La possibilità di posizionar e un link sul desktop

La possibilità di aggiunger e un link nel menù Quick Launch

La sesta finestr a r ichiede il file della licenza (un file tx t in cui vengono specificate le condizioni sotto le quali il softwar e

viene r ilasciato) e altr i due file visualizzati pr ima e dopo l'installazione: tutti questi sono opzionali. La settima, invece,

per mette di selezionar e le lingue suppor tate.

Fig ura 6

L'ottava finestr a r ichiede:

La car tella ove cr ear e il setup eseguibile completo

Il nome dell'eseguibile (di solito Setup)

Un'icona per l'eseguibile (sono validi tutti i file *.ico)

Una passwor d per acceder e al setup

Una volta cliccato Finish, appar e nell'editor di testo una ser ie di istr uzioni scr itte con una sintassi simile a quella dei

file INI. Inno Setup compiler à tali istr uzioni per poi cr ear e il pacchetto di installazione: subito dopo la fine del w izar d

ver r à chiesto se eseguir e la compilazione subito. Cliccate sì se vi basta, altr imenti continuate a legger e.

Aggiunta di dettagli tramite editorNon consiglio di scr iver e tutto a mano, ma invece di usar e il w izar d per impostar e le opzioni più comuni e in seguito

modificar e lo scr ipt con l'editor per aggiunger e dei dettagli in più. Per apr ir e la documentazione, cliccar e Help->Inno

Setip Documentation. Qui si tr ovano tutte le istr uzioni possibili e i comandi suppor tati. Basta dar e uno sguar do ai

nomi per tr ovar e quello adatto alle esigenze dell'utente. Ecco alcune delle esigenze più comuni non pr oposte dal w izar d:

Page 519: Guida visual basic

Ag g iung ere un'immag ine

A me piacciono molto le finestr e con una buona gr afica, e le possibilità di cr ear e il setup con una mia immagine

per sonalizzata mi attir a altr ettanto. Di solito questa immagine è il logo della società oppur e del pr ogr amma

stesso. Se ne possono aggiunger e due tipi: medie (visualizzate sul lato sinistr o del setup) e piccole (visualizzate

nell'angolo in alto a sinistr a del setup). Ecco un esempio:

; Queste sono le istruzioni generate dal wizard se si; lasciano tutti i campi esattamente come sono. Per aggiungere ; un'immagine bisogna impostare la proprietà WizardImageFile[Setup]AppName=My ProgramAppVerName=My Program 1.5AppPublisher=My Company, Inc.AppPublisherURL=http://www.example.com/AppSupportURL=http://www.example.com/AppUpdatesURL=http://www.example.com/DefaultDirName={pf}\My ProgramDefaultGroupName=My ProgramOutputBaseFilename=setupCompression=lzmaSolidCompression=yes; Carica l'immagine. Sono supportate solo bitmap, massimo 164x314WizardImageFile=C:\immagine.bmp; Se l'immagine è più piccola, ma si vuole che occupi; lo stesso spazio, si può attivare l'opzione StretchWizardImageStretch=yes; Se invece si vuole mantenere l'immagine delle stess dimensioni,; ma rimpire i buchi che rimangono con un dato colore, si usa; WizardImageBackColor, che deve essere ipostato a una tripletta di; valori esadecimali rappresentanti le varie componenti RGBWizardImageBackColor=$FFFFFF

Ag g iung ere chiav i di reg istro

Per aggiunger e delle chiavi, si deve apr ir e una nuova sezione [Registr y], al cui inter no vanno specificate le

chiavi e i valor i da aggiunger e. Ad esempio, questo codice aggiunge l'applicazione all'esecuzione automatica:

[Registry]; Aggiunge alla chiave dell'esecuzione automatica una valore stringa; My Program i cui dati corrispondono al percorso dell'eseguibile.; {app} è una costante che definisce la directory di installazioneRoot: HKLM; Subkey="SOFTWARE\Microsoft\Windows\CurrentVersione\Run";ValueType: string; ValueName: "My Program"; ValueData: "{app}\myprg.exe"; Non andate a capo, io l'ho fatto per questioni di spazio


Recommended