• Save
 Vb.net
Upcoming SlideShare
Loading in...5
×
 

Vb.net

on

  • 2,257 views

guida introduttiva sviluppo VBNET

guida introduttiva sviluppo VBNET

Statistics

Views

Total Views
2,257
Views on SlideShare
2,257
Embed Views
0

Actions

Likes
3
Downloads
0
Comments
0

0 Embeds 0

No embeds

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

 Vb.net Vb.net Document Transcript

  • A1. IntroduzioneBenvenuti, aspir anti pr ogr ammator i! In questa guida dalla lunghezza chiolemtr ica impar er ete cosa significa e cosacompor ta pr ogr ammar e, e tutti i tr ucchi e gli espedienti per costr uir e solide e sicur e applicazioni.Una veloc e panoramic a sulla programmazioneLa pr ogr ammazione è quella disciplina dellinfor matica che si occupa di idear e, costr uir e e mantener e il softw ar e.Queste sono le tr e pr incipali divisioni che si possono oper ar e allinter no di questa speciale e affascinante br ancadellingegner ia. Infatti, un buon pr ogr ammator e deve pr ima di tutto analizzar e il pr oblema, quindi pensar e a unapossibile soluzione, se esiste, e costr uir e mentalmente unipotetica str uttur a del softw ar e che dovr à impegnar si ascr iver e: questa par te della pr ogettazione si chiama analisi. Successivamente, si viene alla fase più tecnica, e cheimplica una conoscenza dir etta del linguaggio di pr ogr ammazione usato: in questa guida, mi occuper ò di descr iver e ilVisual 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 unamanutenzione per iodica dello stesso, od or ganizzar e un efficiente sistema di aggior namento. Inutile dir e che lultimafase è necessar ia solo nel caso di gr andi applicazioni commer ciali e non cer tamente nel contesto di piccoli pr ogr ammiamator 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 dausar e.Alc uni termini da c onosc ereCo dice so r g ente o so r g ente: linsieme di tutte le istr uzioni che il pr ogr ammator e scr ive e fa eseguir e alpr ogr amma. Il file testuale che contiene tali istr uzioni viene esso stesso chiamato sor genteCo m pilato r e: il softw ar e utilizzato per cr ear e il pr ogr amma finito (un eseguibile *.ex e) a par tir e dal solo codicesor genteDebug g er : il softw ar e usato per lanalisi e la r isoluzione degli er r or i (bugs) allinter no di un pr ogr amma;Par o le r iser v ate o k eyw o r ds: di solito vengono evidenziate dai compilator i in un color e diver so e sono par olepr edefinite intr inseche del linguaggio, che ser vono per scopi ben pr ecisi.Ambiente di sviluppoLambiente di sviluppo che pr ender ò come r ifer imento per questa guida è Visual Basic Ex pr ess 2008 (scar icabile dalSito Ufficiale della M icr o so ft; se si ha un pr ofilo Passpor t.NET è possibile r egistr ar e il pr odotto e ottener e unaver sione completa). Potete comunque scar icar e Shar pDevelop da qui (vedi sezione dow nloads), un pr ogr amma gr atis emolto buono (tr over ete una r ecensione nella sezione Sofw tar e di Pier oTofy.it r edatta da me e HeDo qui). Dato che lever sioni pr ecedenti della guida, dalle quali è r ipr esa la maggior anza dei sor genti pr oposti, sono state r edattepr endendo come esempio Visual Basic Ex pr ess 2005, potete scar icar e anche quello da qui.
  • A2. Classi, Moduli e NamespaceObjec t Oriented ProgrammingI linguaggi .NET sono orien tati agli oggetti e così lo è anche VB.NET. Questo appr occio alla pr ogr ammazione ha avutomolto successo negli ultimi anni e si basa fondamentalmente sui concetti di astr azione, oggetto e inter azione fr aoggetti. A lor o volta, questi ultimi costituiscono un potente str umento per la modellizzazione e un nuovo modo diavvicinar si alla r isoluzione dei pr oblemi. La par ticolar e mentalità che questa linea di sviluppo adotta è favor evole allar appr esentazione dei dati in modo ger ar chico, e per questo motivo il suo par adig m a di pr ogr ammazione - ossialinsieme degli str umenti concettuali messi a disposizione dal linguaggio e il modo in cui il pr ogr ammator e concepiscelapplicativo - è definito da tr e concetti car dine: ler editar ietà, il po lim o r fism o e lincapsulam ento . Molto pr estoar 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 conlintr odur r e lentità 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 unicoimpor tantissimo concetto fondamentale: gli o g g etti, i quali vengono r appr esentati da classi. Una classe non è altr o chela r appr esentazio ne - o v v iam ente astr atta - di qualco sa di co ncr eto , mentr e loggetto sar à una concr etizzazionedi questa r appr esentazione (per una discussione più appr ofondita sulla differ enza tr a classe e oggetto, veder e capitoloA7). Ad esempio, in un pr ogr amma che deve gestir e una videoteca, ogni videocassetta o DVD è r appr esentato da unaclasse; in un pr ogr amma per la fattur azione dei clienti, ogni cliente e ogni fattur a vengono r appr esentati da unaclasse. Insomma, ogni cosa, ogni entità, ogni r elazione - per fino ogni er r or e - tr ova la sua r appr esentazione in unaclasse.Detto questo, viene spontaneo pensar e che, se ogni cosa è astr atta da una classe, questa classe dovr à anche contener edei dati su quella cosa. Ad esempio, la classe Utente dovr à contener e infor mazioni sul nome dellutente, sulla suapassw or d, sulla sua data di nascita e su molto altr o su cui si può sor volar e. Si dice che tutte queste infor mazioni sonoespo ste dalla classe: ognuna di esse, inoltr e, è r appr esentata da quello che viene chiamato m em br o. I membr i di unaclasse 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. Latto di dichiar ar e unaqualsiasi entità le per mette di iniziar e ad "esister e": il pr ogr ammator e deve infatti ser vir si di qualcosa che è già statodefinito 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 cosasi possa usar e in pr ogr ammazione: dato che le vostr e conoscenze sono limitate, non posso che usar e dei ter minigener ici e piuttosto vaghi, ma in br eve il mio lessico si far à più pr eciso. Nella pr atica, una classe si dichiar a così: 1. Class [NomeClasse] 2. ... 3. End Classdove [NomeClasse] è un qualsiasi nome che potete decider e ar bitr ar iamente, a seconda di cosa debba esser er appr esentato. Tutto il codice compr eso tr a le par ole sopr a citate è inter no alla classe e si chiama co r po ; tutte leentità esistenti nel cor po sono dei membr i. Ad esempio, se si volesse idealizzar e a livello di codice un tr iangolo, siscr iver ebbe questo: 1. Class Triangolo 2. ... 3. End ClassNel cor po di Tr iangolo si potr anno poi definir e tutte le infor mazioni che gli si possono attr ibuir e, come la lunghezza
  • dei lati, la tipologia, lampiezza 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 dueter mini ver r à chiar ita molto più avanti nella guida, poiché le vostr e attuali competenze non sono sufficienti a uncompleto appr endimento. Tuttavia, i moduli sar anno la tipologia di classe più usata in tutta la sezione A.I Namespac ePossiamo definir e classi e moduli come un ità fun zion ali: essi r appr esentano qualcosa, possono esser e usate,manipolate, istanziate, dichiar ate, ecceter a... Sono quindi str umenti attivi di pr ogr ammazione, che ser vono ar ealizzar e concr etamente azioni e a pr odur r e r isultati. I namespace, invece, appar tengono a tuttaltr o gener e dicategor ia: essi sono solo dei r aggr uppamenti "passivi" di classi o di moduli. Possiamo pensar e a un namespace come aduna 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 etipo di infor mazione. Ad esempio, volendo scr iver e un pr ogr amma che aiuti nel calcolo geometr ico di alcune figur e, sipotr ebbe usar e un codice str uttur ate come segue: 01. Namespace Triangoli 02. Class Scaleno 03. ... 04. End Class 05. 06. Class Isoscele 07. ... 08. End Class 09. 10. Class Equilatero 11. ... 12. End Class 13. End Namespace 14. 15. Namespace Quadrilateri 16. Namespace Parallelogrammi 17. Class Parallelogramma 18. ... 19. End Class 20. 21. Namespace Rombi 22. Class Rombo 23. ... 24. End Class 25. 26. Class Quadrato 27. ... 28. End Class 29. End Namespace 30. End Namespace 31. End NamespaceCome si vede, tutte le classi che r appr esentano tipologie di tr iangoli (Scaleno, Isoscele, Equilater o) sono allinter no delnamespace Tr iangoli; allo stesso modo esiste anche il namespace Quadr ilater i, che contiene al suo inter no un altr onamespace Par allelogr ammi, poiché tutti i par allelogr ammi sono quadr ilater i, per definizione. In questultimo esiste laclasse Par allelogr amma che r appr esenta una gener ica figur a di questo tipo, ma esiste ancor a un altr o namespaceRombi: come noto, infatti, tutti i r ombi sono anche par allelogr ammi.Dallesempio si osser va che i namespace categor izzano le unità funzionali, dividendole in insiemi di per tinenza. Quandoun namespace si tr ova allinter no di un altr o namespace, lo si definisce nidificato: in questo caso, Par alleloogr ammi eRombi sono namespace nidificati. Altr a cosa: al contr ar io della classi, gli spazi di nomi (italianizzazione dellinglesename-space) non possiedono un "cor po", poiché questo ter mine si può usar e solo quando si par la di qualcosa di attivo;
  • per lo stesso motivo, non si può neanche par lar e di membr i di un namespace.
  • A3. Panoramica sul Framework .NETCome 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 eche 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. Linsieme di tutti questi str umenti di pr ogr ammazione è ilFr am ew o r k .NET, lossatur 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 ebbetuttavia r iduttivo descr iver e tale piattafor ma come un semplice agglomer ato di libr er ie (vedi oltr e), quando essacontempla meccanismi assai più complessi, che sovr intendono alla gener ale esecuzione di tutte le applicazioni .NET.Linter a str uttur a del Fr amew or k si pr esente come str atificata in diver si livelli:1. Sistema operativoIl Fr amew or k .NET pr esenta una str uttur a str atificata, alla base della quale r isiede il sistema oper ativo, Window s. Piùpr ecisamente, si consider a il sistema oper ativo e lAPI (Application Pr ogr amming Inter face) di Window s, che esponetutti 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 lagestione della memor ia e la sua liber azione tr amite il meccanismo di Gar bage Collection (vedi capitolo r elativo), lagestione str uttur ata delle eccezioni (er r or i) e il multithr eading. Nessuna applicazione inter agisce mai dir ettamentecon 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 sidefinisce il codice .NET M an aged o Safe ("Gestito" o "Sicur o"), poichè questo str ato del Fr amew or k gar antisce che nonvengano mai eseguite istr uzioni dannose che possano mandar e in cr ash il pr ogr amma o il sistema oper ativo stesso. Alcontr ar io, il codice Unmanaged o Unsafe può eseguir e oper azioni r ischiose per il computer : sor genti pr odotti in Vb6 oC++ 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 nelFr amew or k (il che cor r isponde in numer o a diver se migliaia di elementi), r aggr uppati in una tr entina di file pr incipali(assembly). In questi ultimi è compr esa la definizione della classe System.Object, dalla quale der iva pr essochè ogni altr aclasse. I dati contenuti nella BCL per mettono di svolger e ogni oper azione possibile sulla macchina.4. X MLSuccessivamente tr oviamo i dati, le r isor se. Per salvar e i dati viene usato quasi sempr e il for mato XM L (eXtensibleMar kup Language), che utilizza dei tag spesso nidificati per contener e i campi necessar i. La str uttur a di questo tipo difile, inoltr e, è adatta alla r appr esentazione ger ar chica, un metodo che nellambiente .net è impor tantissimo. I file diconfigur azione e quelli delle opzioni impostate dellutente, ad esempio, vengono salvati in for mato XML. Anche la nuovatecnologia denominata Window s Pr esentation Foundation (WPF), intr odotta nella ver sione 3.5 del Fr amew or k, cheper mette di cr ear e contr olli dalla gr afica accattivante e str avagante, si basa su un linguaggio di contr assegno (dimar kup) sur r ogato dellXML.
  • 5. W indow s Forms e ASP.NETAl livello super ior e tr oviamo ASP.NET e Window s For ms, ossia le inter facce gr afiche che r icopr ono il codicedellapplicazione ver a e pr opr ia. La pr ima è una tecnologia pensata per lo sviluppo sul Web, mentr e la seconda for niscesostanzialmente la possibilità di cr ear e una inter faccia gr afica (Gr aphical User Inter face, GUI) in tutto e per tuttouguale a quella classica, a finestr e, dei sistemi oper ativi Window s. La costr uzione di una Window s For m (ossia unasingola finestr a) è semplice e avviene come nel Vb classico, e chi sta leggendo questa guida per passar e dal VB6 alVB.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 iveil codice associato ad ognuno dei lor o eventi.6. Common Language Spec ific ationsIl penultimo stadio della str atificazione del Fr amew or k coincide con le Common Language Specifications (CLS), ossia uninsieme di specifiche che definiscono i r equisiti minimi r ichiesti a un linguaggio di pr ogr ammazione per esser equalificato come .NET. Un esempio di tali dir ettive: il linguaggio deve saper e gestir e tipi base come str inghe e numer iinter i, vettor i e collezioni a base zer o e deve saper pr ocessar e uneccezione scatenata dal Fr amew or k.7. Linguaggi .NETIn cima alla str uttur a ci sono tutti i linguaggi .net: Vb, C#, J#, ecceter a.V ersioni del Framew orkCon il passar e degli anni, a par tir e dal 2002, Micr osoft ha r ilasciato ver sioni successive del Fr amew or k .NET e ognunadi queste r elease ha intr odotto nuovi concetti di pr ogr ammazione e nuove possibilità per lo sviluppator e.Par allelamente alluscita di queste nuove ver sioni, sono state cr eate anche edizioni successive del linguaggio VB.NET,ognuna delle quali è stata natur almente accostata alla ver sione del Fr amew or k su cui si r eggeva. Ecco una r apidapanor amica dellevoluzione del linguaggio: VB2002: si basa sulla ver sione 1.0 del Fr amew or k VB2003: si basa sulla ver sione 1.1 del Fr amew or k VB2005: si basa sulla ver sione 2.0 del Fr amew or k. Questa è la ver sione maggior mente utilizzata in questa guida, sebbene cer ti capitoli si concentr er anno sullintr oduzione di alcuni nuovi aspetti por tati da VB2008 VB2008: si basa sulla ver sione 3.5 del Fr amew or k. La ver sione 3.0 si fondava ancor a sulla 2.0 del CLR e per ciò le modifiche consistevano sostanzialmente nellaggiunta di alcuni componenti e nellappor to di diver se miglior ie e cor r ezioni VB2010: si basa sulla ver sione 4.0 del Fr amew or k
  • A4. Utilizzo base dellIDEIDE? Me lo sono dimentic ato a c asa...Non vi pr eoccupate: se avete seguito tutti i capitoli fino a questo punto, siete già un possesso di un IDE: Visual Basic2005 (o 2008) Ex pr ess. Lacr onimo IDE significa Integr ated Development Envir onment ("ambiente di sviluppo integr ato")ed indica un softw ar e che aiuta il pr ogr ammator e nella stesur a del codice. Il softw ar e che vi ho consigliato for nisce,sebbene sia la ver sione fr ee, un numer o molto alto di str umenti e tools. In pr imis, contiene, ovviamente, un editor dicodice sor gente, pr ogettato in modo da evidenziar e in modo differ ente le keyw or ds e da suppor tar e molte funzioni dir icer ca e r aggr uppamento che vedr emo in seguito. Accanto a questo, i pr incipali componenti che non possono mancar ein un IDE sono il compilator e ed il debugger , di cui ho dato una veloce definizione nel capitolo intr oduttivo. Il pr imo halo 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 vengonopor tati a ter mine dur ante un pr ocesso di compilazione sono in r ealtà più di uno (di solito compilazion e e lin kin g), mamolto spesso si semplifica il tutto par lando semplicemente di compilazione. Il secondo, invece, è il pr ogr amma che vidar à 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): ildebugger ha la funzione di analizzar e e segnalar e i bugs (bachi, er r or i) che si ver ificano dur ante lesecuzione; assiemead un r appor to dettagliato del tipo di er r or e ver ificatosi, segnala par allelamente anche il punto del codice che ha datopr oblemi, in modo da r ender e molto più semplice individuar e e cor r egger e la falla.Funzionamento del c ompilatore .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 minatolinguaggio in un pr ogr amma eseguibile. Nor malmente, un compilator e pr odur r ebbe un applicativo tr aducendo leistr uzioni testuali intr odotte dal pr ogr ammator e in linguaggio macchina, ossia una ser ie di bit univocamenteinter pr etabile dal pr ocessor e. I compilator i .NET, invece, hanno un compor tamento differ ente, in quanto il lor o outputnon è un "nor male pr ogr amma" scr itto in linguaggio macchina, ma si tr atta di una ser ie di istr uzioni codificate in unaltr o linguaggio speciale, chiamato IL (Inter mediate Language). Come sugger isce il nome, esso si tr ova ad un livellointer medio tr a la macchina e lastr azione: è super ior e r ispetto al pur o codice binar io, ma allo stesso tempo è ungr adino più sotto r ispetto ai linguaggi .NET. Venendo a conoscenza di queste infor mazioni, dovr ebbe sor ger espontaneamente una domanda: come fa allor a un pr ogr amma .NET ad esser e eseguito? La r isposta è semplice: è lostesso Fr amew or k che si occupa di inter pr etar ne le istr uzioni e di eseguir le, sempr e sotto la super visione del CLR. Perquesto motivo, si hanno tr e impor tanti conseguenze: Non è possibile far cor r er e unapplicazione .NET su una macchina spr ovvista del Fr amew or k; Il codice .NET è sempr e sicur o; Un pr ogr amma .NET è sempr e disassemblabile: su questo punto mi soffer mer ò in seguito.Creare una Console A pplic ationNei 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 uncodice. Per tutti gli esempi della sezione A, far ò uso di applicazioni conso le (avete pr esente la finestr ella con lo sfondoner o?), che lavor ano in DOS. Per cr ear e una Applicazione Console bisogna selezionar e dal menù File del compilator e, lavoce New Pr oject, e quindi sceglier e il tipo di applicazione desider ata. Dopodichè, il compilator e scr iver àaumaticamente alcune r ighe di codice pr eimpostate, che possono esser e simili a queste: Module Module1
  • Sub Main() End Sub End ModuleNello scr eenshot pr oposto qui sopr a si possono veder e le tr e ar ee in cui è solitamente divisa linter faccia delcompilator e: non vi pr eoccupate se la vostr a appar e differ ente, poiché, essendo modificabile a piacimento, la miapotr ebbe esser e diver sa dal layout pr eimpostato del compilator e. Per or a, le finestr e impor tanti sono due: quella delcodice, dove andr emo a scr iver e le istr uzioni, e quella degli er r or i, dove potr ete tener e costantemente sottocchio seavete commesso degli er r or i di sintassi. Nello scr eenshot la seconda di queste non è visibile, ma la si può por tar e inpr 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, efin 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 Subed End Sub: anche in questo caso, la tr attazione par ticolar e di queste keyw or ds sar à r imandata più in là. Per or apossiamo 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 inalter nativa F5.Compilazione del programma finitoUna volta finito di scr iver e il codice e di testar lo usando le funzioni dellIDE (ivi compr esa lesecuzione in modalità debugpr emendo F5), sar à necessar io cr ear e il pr ogr amma finito. Quello che avete eseguito finor a non er a altr o che unaver sione più lenta e meno ottimizzata del softw ar e scr itto, poiché cer 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 lapplicazioner 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 (lultima voce del sottomenù); selezionate la scheda Compile e cambiateil campo Configur ation su Release, quindi pr emete Build > Build Pr oject (Build è sempr e una voce del menù pr incipale).
  • Tr over ete leseguibile compilato nella car tella DocumentiVisual Studio 2008Pr ojects[Nome pr ogetto]binRelease.
  • A5. Variabili e costantiLe 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. Questonome si dice anche iden tificatore (o, più r ar amente, mn emon ico), e può esser e costituito da un qualunque insieme dicar atter i alfanumer ici e under scor e: lunica 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 inmemor 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 unagr 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 sipoi 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, bisognaanalizzar e in che modo si dichiar a una var iabile. La dichiar azione, tanto di una costante quanto di una classe, è lattodefinitivo con cui si stabilisce lesistenza di unentità e la si r ende disponibile o accessibile alle altr i par ti delpr ogr amma. Ogni cosa, per esser e usata, deve pr ima esser e dichiar ata da qualche par te: questa oper azione equivale,ad esempio, a definir e un concetto in matematica: la definizione è impor tantissima.Ecco un semplice esempio: 01. Module Module1 02. Sub Main() 03. Dim Ciao As Int16 04. Ciao = 78 05. Ciao = Ciao + 2 06. Console.WriteLine(Ciao) 07. Console.Readkey() 08. End Sub 09. End ModuleFacendo 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 avediamo.Come avr ete notato, le var iabili si dichiar ano in un modo specifico, usando le keyw or ds Dim e A s: 1. Dim [nome] As [tipo]Dove [nome] è lidentificator 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 atikeyw or ds): Byte: inter o a 8 bit che può assumer e valor i da 0 a 255; Char : valor e a 8 bit che può assumer e i valor i di ogni car atter e della tastier a (compr esi quelli speciali); Int16 o Sho r t: inter o a 16 bit che può assumer e valor i da -32768 a +32767; Int32 o Integ er : inter o a 32 bit da -2147483648 a +2147483647; Int64 o Lo ng : inter o a 64 bit da cir ca -922000000000000000 a +9220000000000000000; Sing le: decimale da cir ca -3,4e+38 a +3,4e+38, con un inter vallo minimo di cir ca 1,4e-45; Do uble: decimale da cir ca -1,79e+308 a +1,79e+308, con un inter vallo minimo di cir ca 4,9e-324; Bo o lean: dato a 4 bytes che può assumer e due valor i, Tr ue (ver o) e False (falso). Nonostante la limitatezza del suo campo di azione, che concettualmente potr ebbe r estr inger si ad un solo bit, il tipo Boolean occupa 32bit di memor ia: sono quindi da evitar e gr andi quantità di questo tipo; Str ing : valor e di minimo 10 bytes, composto da una sequenza di car atter i. Se vogliamo, possiamo assimilar lo ad
  • un testo; Object: r appr esenta un qualsiasi tipo (ma non è un tipo base).I tipi base vengono detti anche ato m ici o pr im itiv i, poiché non possono esser e ulter ior mente scomposti. Esistono,quindi, anche tipi der iv ati, appar tenenti a svar iate tipologie che analizzer emo in seguito, fr a cui si annover ano anchele classi: ogni tipo der ivato è scomponibile in un insieme di tipi base.Or a, quindi, possiamo estr apolar e delle infor mazioni in più dal codice pr oposto: dato che segue la keyw or d Dim, "Ciao"è lidentificator 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 didato piuttosto che un altr o var ia in funzione del compito che si intende svolger e: maggior e è la pr ecisione e lor dine digr andezza dei valor i coinvolti e maggior e sar à anche luso di memor ia che si dovr à sostener e. Continuando a legger e,si incontr a, nella r iga successiva, unassegnazione, ossia una oper azione che pone nella var iabile un cer to valor e, inquesto caso 78; lassegnazione avviene mediante luso delloper ator e uguale "=". Listr uzione successiva è simile a questa,ma con una sostanziale differ enza: il valor e assegnato alla var iabile è influenzato dalla var iabile stessa. Nellesempiopr oposto, il codice: 1. Ciao = Ciao + 2ha la funzione di incr ementar e di due unità il contenuto di Ciao. Questa istr uzione potr ebbe sembr ar e algebr icamentescor r etta, ma bisogna r icor dar e che si tr atta di un comando (e non di unequazione): pr ima di scr iver e nella cella dimemor ia associata alla var iabile il numer o che il pr ogr ammator e ha designato, il pr ogr amma r isolve lespr essione adestr a delluguale 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 unpulsante.Come si è visto dallesempio pr ecedente, con le var iabili di tipo numer ico si possono eseguir e oper azioni ar itmetiche.Gli oper ator i messi a disposizione dal Fr amew or k sono: + : addizione; - : sottr azione; * : pr odotto; / : divisione; : divisione tr a inter i (r estituisce come r isultato un numer o inter o a pr escinder e dal tipo degli oper andi, che possono anche esser e decimali); Mod : r estituisce il r esto di una divisione inter a; = : assegna alla var iabile posta a sinistr a delluguale il valor e posto dopo luguale; & : concatena una str inga con un numer o o una str inga con unaltr a str inga. 01. Module Module1 02. Sub Main() 03. Interi 04. Dim Intero, Ese As Int16 05. Decimale 06. Dim Decimale As Single 07. Booleano 08. Dim Vero As Boolean 09. Stringa 10. Dim Frase As String 11. 12. Intero = 90 13. Ese = Intero * 2 / 68 14. Intero = Ese - Intero * Intero 15. Decimale = 90.76 16. Decimale = Ese / Intero 17. Vero = True 18. Frase = "Ciao." 19. Loperatore "+" tra stringhe concatena due stringhe. Dopo la 20.
  • prossima istruzione, la variabile Frase conterrà: 21. "Buon giornoCiao" 22. Frase = "Buon giorno" + "Ciao" 23. Loperatore "&" può concatenare qualsiasi dato e 24. restituisce una stringa. Dopo la prossima istruzione, la 25. variabile Frase conterrà: 26. "Il valore decimale è: -0,0003705076" 27. Frase = "Il valore decimale è: " & Decimale 28. End Sub 29. End ModuleEsistono poi degli speciali oper ator i di assegnamento, che velocizzano lassegnazione di valor i, alcuni sono: 01. Module Module1 02. Sub Main() 03. Dim V, B As Int32 04. 05. V += B Equivale a V = V + B 06. B -= V Equivale a B = B - V 07. V *= B Equivale a V = V * B 08. B /= V Equivale a B = B / V 09. End Sub 10. End ModuleLe fr asi poste dopo un apice () sono dette co m m enti e ser vono per spiegar e cosa viene scr itto nel codice. Il contenutodi un commento NON influisce in nessun modo su ciò che è scr itto nel sor gente, ma ha una funzione ESCLUSIVAMENTEesplicativa.Le c ostantiAbbiamo visto che il valor e delle var iabili può esser e modificato a piacimento. Ebbene quello delle costanti, come ilnome sugger isce, no. Esistono per semplificar e le oper azioni. Per esempio, invece di digitar e 3,1415926535897932 peril Pi g r eco , è possibile dichiar ar e una costante di nome Pi che abbia quel valor e ed utilizzar la nelle espr essioni. Lasintassi per dichiar ar e una costante è la seguente: 1. Const [nome] As [tipo] = [valore]Ci sono due lampanti differ enze r ispetto al codice usato per dichiar ar e una var iabile. La pr ima è, ovviamente, lusodella keyw or d Con s t al posto di Dim; la seconda consiste nellassegnazione posta subito dopo la dichiar azione. Infatti,una costante, per esser e tale, dev e contener e qualcosa: per questo motivo è o bblig ato r io specificar e sempr e, dopola dichiar azione, il valor e che la costante assumer à. Questo valor e non potr à mai esser e modificato.Esempio: 01. Module Module1 02. Sub Main() 03. Const Pi As Single = 3.1415926535897932 04. Dim Raggio, Area As Double 05. 06. Questa istruzione scrive sul monitor il messaggio posto tra 07. virgolette nelle parentesi 08. Console.WriteLine("Inserire il raggio di un cerchio:") 09. 10. Questa istruzione leggè un valore immesso dalla tastiera e 11. lo deposita nella variabile Raggio 12. Raggio = Console.ReadLine 13. Area = Raggio * Raggio * Pi 14. 15. Console.WriteLine("LArea è: " & Area) 16. 17. Questa istruzione ferma il programma in attesa della pressione 18. di un pulsante 19. Console.ReadKey() 20. End Sub 21. End Module
  • N.B.: a causa della lor o stessa natur a, le costanti NON possono esser e inizializzate con un valor e che dipenda da unafunzione. Scr ivo questo appunto per pur a utilità di consultazione: anche se or a potr à non r isultar e chiar o, vi capiter àpiù avanti di imbatter vi in er r or i del gener e: 1. Const Sqrt2 As Single = Math.Sqrt(2)Sqr t2 dovr ebbe esser e una costante numer ica decimale che contiene la r adice quadr ata di due. Sebbene il codicesembr i cor r etto, il compilator e segnaler à come er r or e lespr essione Math.Sqr t(2), poiché essa è una funzione, mentr edopo luguale è r ichiesto un valor e sempr e costante. Il codice cor r etto è 1. Const Sqrt2 As Single = 1.4142135Le 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 pr o cedur e o funzio ni, in sostanza sottopr ogr ammi giàscr itti. Pr ocedur e e funzioni possono esser e globalmente indicate con la par ola m eto do . I metodi accettano deipar am etr i passatigli tr a par entesi: se i par ametr i sono di più di uno vengono separ ati da vir gole. I par ametr iser 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 unafunzione r isiede nel fatto che la pr ima fa semplicemente eseguir e istr uzioni al computer , mentr e la seconda r estituiseun valor e. Ad esempio: 01. Module Module1 02. Sub Main() 03. Dim F As Double 04. 05. Questa è una funzione che restituisce la radice quadrata di 56 06. F = Math.Sqrt(56) 07. 08. Questa è una procedura che scrive a video un messaggio 09. Console.WriteLine("La radice di 56 è " & F) 10. Console.ReadKey() 11. End Sub 12. End ModuleAnche i metodi ver r anno tr attai successivamente in dettaglio.
  • A6. Tipi Reference e tipi ValueTutti 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 imisi compor tano come oggetti, mentr e i secondi r appr esentano tipi scalar i o numer ici, ma vediamo di metter e un poor 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 dellapr ogr ammazione (in qualsiasi altr o linguaggio) di legger e questo ar tico lo sullutilizzo della memor ia da par te di unpr ogr amma.Differenza tra Classi e OggettiAllinizio della guida mi sono soffer mato ad elogiar e le classi e la lor o enor me impor tanza nellambiente .NET.Successivamente ho fatto menzione al tipo System.Object e al fatto che ogni cosa sia un oggetto. La differ enza tr ao g g etto e classe è di vitale impor tanza per capir e come vanno le cose nellambito della pr ogr ammazione OO. Unaclasse r appr esenta lastr azione di qualcosa di concr eto; un oggetto, invece, è qualcosa di concr eto e viener appr esentato da una classe. Per far e un esempio banale, sappiamo benissimo che esiste il concetto di "uomo", ma ogniindividuo sul pianeta, pur mantenendo alcune car atter istiche simili e costanti, è differ ente r ispetto agli altr i. Facendoun par allelismo con la pr ogr ammazione, quindi, il singolo individuo, ad esempio io stesso, è un oggetto, mentr e ilgener 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 istanzadella classe che lo r appr esenta (voi siete istanze della classe Uomo XD) e is tan ziare un oggetto significa cr ear lo. 1. New serve per creare fisicamente degli oggetti in memoria 2. Dim O1 As New Object 3. Dim O2 As New ObjectO1 e O2 sono entr ambe istanze della classe Object, ma sono diver si fr a di lor o: in comune hanno solo lappar tenenza allostesso tipo.N.B.: come si è notato, "tipo" e "classe" sono ter mini spesso equivalenti, ma non gener alizzate questa associazione.Tipi Referenc eOgni cosa nel Fr amew or k è un oggetto e la maggior par te di essi sono tipi r efer ence. Si dicono tipi r efer ence tuttiquei 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 allinter no di una libr er ia della Base Class Libr ar y, ossia lar chivio di classi delFr amew or k. Nel capitolo pr ecedente si è visto come sia possibile assegnar e un valor e ad una var iabile utilizzandoloper ator e uguale "=". Con questo meccanismo, un deter minato valor e viene depositato nella casella di memor ia che lavar iabile occupa. Ebbene, facendo uso dei tipi r efer ence, questo non avviene. Quando si utilizza luguale per assegnar eun 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 a32bit (su sistemi oper ativi a 32bit). Per chi non lo sapesse, un puntator e è una speciale var iabile che, invece dicontener e un pr opr io valor e, contiene lindir izzo di unar ea di memor ia contenente altr i dati. Il puntator e vienememor izzato come al solito sullo stack , mentr e il ver o oggetto viene cr eato e deposto in unar ea di memor iadiffer ente, detta heap m anag ed, dove esiste sotto la super visione del CLR. Quando una var iabile di questo tipo vieneimpostata a Nothing (una costante che vedr emo tr a poco), la par te dellheap managed che loggetto occupa viener ilasciata dur ante il pr ocesso di g ar bag e co llectio n ("r accolta dei r ifiuti"). Tuttavia, ciò non avviene subito, poichè ilmeccanismo del Fr amew or k fa in modo di avviar e la gar bage collection solo quando è necessar io, quindi quando la
  • memor ia comincia a scar seggiar e: supponendo che un pr ogr amma abbia r elativamente pochi oggetti, questipotr ebber o "viver e" indistur bati fino alla fine del pr ogr amma anche dopo esser e stati lo g icam ente distr utti, il chesignifica che è stato eliminato manualmente qualsiasi r ifer imento ad essi (vedi par agr afo successivo). Datalimpossibilità di deter minar e a pr ior i quando un oggetto ver r à distr utto, si ha un fenomeno che va sotto il nome difinalizzazio ne no n deter m inistica (il ter mine "finalizzazione" non è casule: veder e il capitolo sui distr uttor i permaggior i infor mazioni).NothingNothing è una costante di tipo r efer ence che r appr esenta lassenza di un oggetto piuttosto che un oggetto nullo. Infatti,por r e una var iabile oggetto uguale a Nothing equivale a distr ugger la logicamente. 1. Dim O As New Object Loggetto viene creato 2. O = Nothing Loggetto viene logicamente distruttoLa distr uzione logica non coincide con la distr uzione fisica delloggetto (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 è possibileassegnar e Nothing a un tipo value, ma è possibile usar e speciali tipi value che suppor tano tale valor e: per ulter ior idettagli, veder e "Tipi Nullable".Tipi V alueOgni tipo v alue der iva dalla classe System.ValueType, che der iva a sua volta da System.Object, ma ne r idefinisce imetodi. 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 sullostack 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 esignifica r ecar si allindir izzo di memor ia puntato e legger ne il contenuto); non cè necessità di occupar e spazio nelloheap managed: se la var iabile viene distr utta, cessa di esister e allistante e non si deve attuar e nessuna oper azione dir ilascio delle r isor se. Notar e che non è possibile distr ugger e logicamente una var iabile value, fatta eccezione per cer titipi 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 diassegnamento. Come già detto, i r efer ence contengono un puntator e, per ciò se si scr ive questo codice: 1. Dim O1, O2 As Object 2. ... 3. O1 = O2quello 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. Questopr ovoca un fatto str ano, poichè sia O1 che O2 puntano alla stessa ar ea di memor ia: quindi O1 e O2 so no lo stessoo g g etto , soltanto r ifer ito con nomi difer si. In casi simili, si può utilizzar e loper ator e Is per ver ificar e che duevar iabili puntino allo stesso oggetto: 1. Scrive a schermo se è vero oppure no che 2. O1 e O2 sono lo stesso oggetto 3. Console.WriteLine(O1 Is O2)La scr itta che appar ir à sullo scher mo sar à "Tr ue", ossia "Ver o". Utilizzar e Is per compar ar e un oggetto a Nothingequivale 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 loper ator e =, si passa
  • effettivamente una co pia del valor e. Non è possibile utilizzar e Is con i tipi value poichè Is è definito solo per ir efer ence.Boxing e UnboxingConsider iamo il seguente codice: 1. Dim I As Int32 = 50 2. Dim O As Object 3. O = II è un tipo value, mentr e O è un tipo r efer ence. Quello che succede dietr o le quinte è semplice: il .NET cr ea un nuovooggetto, 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 vienedefinita come bo xing . Loper azione inver sa è lunboxing e consiste nellassegnar e un tipo r efer ence a un tipo value. Leoper azioni che si svolgono sono le stesse, ma al contr ar io: entr ambe spr ecano tempo e cpu, quindi sono da evitar e senon 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 è unoggetto 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 ilfor malismo tecnico, poiché, se una var iabile è dichiar ata di tipo r efer ence, essa è pr opr iamente un riferimen toalloggetto e non un oggetto. Gli oggetti "vivono" indistur bati nellheap 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! lhai visto? ma sì,pr opr io là! non lo vedi?").
  • A7. Il costrutto IfCapita spessissimo di dover eseguir e un contr ollo per ver ificar e se vigono cer te condizioni. È possibile attuar e taleoper azione tr amite un co str utto di co ntr o llo , la cui for ma più comune e diffusa è il costr utto If. Questo per mette dicontr ollar e se una condizione è ver a. Ad esempio: in un pr ogr amma che calcoli lar ea di un quadr ato si deve impor r e divisualizzar e un messaggio di er r or e nel caso lutente immetta una misur a negativa, poichè, come è noto, non esistonolati la cui misur a è un numer o negativo: 01. Module Module1 02. Sub Main() 03. Dim Lato As Single 04. 05. Console.WriteLine("Inserire il lato di un quadrato:") 06. Lato = Console.ReadLine 07. 08. If Lato < 0 Then Se Lato è minore di 0... 09. Console.WriteLine("Il lato non può avere una misura negativa!") 10. Else Altrimenti, se non lo è... 11. Console.WriteLine("Larea del quadrato è: " & Lato * Lato) 12. End If Fine controllo 13. 14. Console.ReadKey() 15. End Sub 16. End ModuleCome sicur amente avr ete intuito, questo contr ollo si può associar e al costr utto italiano "Se avviene qualcosa Allor a faiquesto Altr imenti fai quellaltr o". Si può eseguir e qualsiasi tipo di compar azione tr a If e Then utilizzando i seguentioper 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 limpor tante è r icor dar si di attener si a questa sintassi: 1. If [Condizione] Then 2. [istruzioni] 3. Else 4. [istruzioni alternative] 5. End IfIf nidific atiQuando si tr ova un costr utto If allinter no di un altr o costr utto If, si dice che si tr atta di un Co str utto If Nidificato .Questo avviene abbastanza spesso, specie se si ha bisogno di far e contr olli multipli: 01. Module Module1 02. Sub Main() 03. Dim Numero As Int16 04. 05.
  • Console.WriteLine("Inserisci un numero:") 06. Numero = Console.ReadLine 07. 08. If Numero > 0 Then 09. If Numero < 5 Then 10. Console.WriteLine("Hai indovnato il numero!") 11. End If 12. Else 13. Console.WriteLine("Numero errato!") 14. End If 15. 16. Console.ReadKey() 17. End Sub 18. End ModuleSe il numer o inser ito da tastier a è compr eso fr a 0 e 5, estr emi esclusi, allor a lutente ha indovinato il numer o,altr imenti no. Si può tr ovar e un numer o illimitato di If nidificati, ma è meglio limitar ne luso e, piuttosto, far e utilizzodi co nnettiv i lo g ici.I c onnettivi logic iI connettivi logici sono 4: And, Or , Xor e Not. Ser vono per costr uir e contr olli complessi. Di seguito unillustr azione dellor o funzionamento: If A And B : la condizione r isulta ver ificata se sia A che B sono ver e co ntem po r aneam e nte If A Or B : la condizione r isulta ver ificata se è ver a alm eno una delle due condizioni If A Xor B: la condizione r isulta ver a se una so la delle due condizioni è ver a If Not A: la condizione r isulta ver ificata se è falsaUn esempio pr atico: 01. Module Module1 02. Sub Main() 03. Dim a, b As Double 04. 05. Console.WriteLine("Inserire i lati di un rettangolo:") 06. a = Console.ReadLine 07. b = Console.ReadLine 08. 09. Se tutti e due i lati sono maggiori di 0 10. If a > 0 And b > 0 Then 11. Console.WriteLine("Larea è: " & a * b) 12. Else 13. Console.WriteLine("Non esistono lati con misure negative!") 14. End If 15. Console.Readkey() 16. End Sub 17. End ModuleContinuare il c ontrollo: 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 sen on si ver ifica la condizione pr oposta dalla pr ima par te. Il suo valor e è, quindi, di pur a alter nativa. Esiste, tuttavia,una var iante di Else che consente di continuar e con un altr o contr ollo senza dover r icor r er e ad If nidificati (a cui èsempr e meglio supplir e con qualcosa di più or dinato). Ammettiamo, ad esempio, di aver e un codice autocr itico simile: 01. Module Module1 02. Sub Main() 03. Dim Voto As Single 04. 05. Console.WriteLine("Inserisci il tuo voto:") 06.
  • Voto = Console.ReadLine 07. 08. If Voto < 3 Then 09. Console.WriteLine("Sei senza speranze!") 10. Else 11. If Voto < 5 Then 12. Console.WriteLine("Ancora un piccolo sforzo...") 13. Else 14. If Voto < 7 Then 15. Console.WriteLine("Stai andando discretamente") 16. Else 17. If Voto < 9 Then 18. Console.WriteLine("Molto bene, continua così") 19. Else 20. Console.WriteLine("Sei praticamente perfetto!") 21. End If 22. End If 23. End If 24. End If 25. 26. Console.ReadKey() 27. End Sub 28. End ModuleE abbastanza disor dinato... La var iante ElseIf è molto utile per miglior e la leggibilità del codice: 01. Module Module1 02. Sub Main() 03. Dim Voto As Single 04. 05. Console.WriteLine("Inserisci il tuo voto:") 06. Voto = Console.ReadLine 07. 08. If Voto < 3 Then 09. Console.WriteLine("Sei senza speranze!") 10. ElseIf Voto < 5 Then 11. Console.WriteLine("Ancora un piccolo sforzo...") 12. ElseIf Voto < 7 Then 13. Console.WriteLine("Stai andando discretamente") 14. ElseIf Voto < 9 Then 15. Console.WriteLine("Molto bene, continua così") 16. Else 17. Console.WriteLine("Sei praticamente perfetto!") 18. End If 19. 20. Console.ReadKey() 21. End Sub 22. End ModuleNotate che tutti gli ElseIf fanno par te dello s tes s o costr utto: mentr e nellesempio 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 dellunico Ifiniziale, pr otr atto solamente un poco più a lungo.Bloc c hi di istruzioniFino a questo punto, gli esempi pr oposti non hanno mai dichiar ato una var iabile dentr o un costr utto If, ma soloallinizio del pr ogr amma, dopo Sub Main(). È possibile dichiar ar e var iabili in altr i punti del codice che non siano alliniziodella Sub? Cer tamente sì. A differ enza di altr i, i linguaggi .NET per mettono di dichiar ar e var iabili in qualunque puntodel sor gente, dove occor r e, evitando un gigantesco agglomer ato di dichiar azioni iniziali, for temente disper sive per chilegge. Questo è un gr ande vantaggio, ma bisogna far e attenzione ai blocchi di codice. Con questo ter mine ci si r ifer iscea par ti del sor gente compr ese tr a due par ole r iser vate, che in VB di solito sono accoppiate in questo modo: 1. [Keyword] 2. Blocco di codice 3. End [Keyword]
  • Ad esempio, tutto il codice compr eso tr a Sub ed End Sub costituisce un blocco, così come lo costituisce quello compr esotr 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 distinzionesar à facile intuir e che una var iabile dichiar ata in un blocco no n è v isibile al di fuor i di esso. Con questo voglio dir eche la sua dichiar azione vale solo allinter no di quel blocco. Ecco una dimostr azione: 01. Module Module1 02. Sub Main() 03. a, b e c fanno parte del blocco delimitato da Sub ... 04. End Sub 05. Dim a, b, c As Single 06. 07. Semplice esempio di risoluzione di equazione di 08. secondo grado 09. Console.WriteLine("Equazione: ax2 + bx + c = 0") 10. Console.WriteLine("Inserisci, in ordine, a, b e c:") 11. a = Console.ReadLine 12. b = Console.ReadLine 13. c = Console.ReadLine 14. 15. If a = 0 Then 16. Console.WriteLine("Lequazione si abbassa di grado") 17. Console.ReadKey() 18. Con Exit Sub si esce dalla Sub, che in questo caso 19. coincide con il programma. Equivale a terminare 20. il programma stesso 21. Exit Sub 22. End If 23. 24. Anche delta fa parte del blocco delimitato da Sub ... 25. End Sub 26. Dim delta As Single = b ^ 2 - 4 * a * c 27. 28. Esistono due soluzioni distinte 29. If delta > 0 Then 30. Queste variabili fanno parte del blocco di If ... 31. ElseIf 32. Dim x1, x2 As Single 33. È possibile accedere senza problemi alla variabile 34. delta, poiché questo blocco è a sua volta 35. allinterno del blocco in cui è dichiarato delta 36. x1 = (-b + Math.Sqrt(delta)) / (2 * a) 37. x2 = (-b - Math.Sqrt(delta)) / (2 * a) 38. Console.WriteLine("Soluzioni: ") 39. Console.WriteLine("x1 = " & x1) 40. Console.WriteLine("x2 = " & x2) 41. 42. Esiste una soluzione doppia 43. ElseIf delta = 0 Then 44. Questa variabile fa parte del blocco ElseIf ... Else 45. Dim x As Single 46. x = -b / (2 * a) 47. Console.WriteLine("Soluzione doppia: ") 48. Console.WriteLine("x = " & x) 49. 50. Non esistono soluzioni in R 51. Else 52. Console.WriteLine("Non esistono soluzioni in R") 53. End If 54. 55. Console.ReadKey() 56. End Sub 57. End ModuleSe 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 emmoun er r or e:
  • Questo succede per chè nessuna var iabile dichiar ata allinter no di un blocco è accessibile al di fuor i di esso. Con questoschemino 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 dallinter no di un blocco ver solester no, mentr e sono pr oibite tutte quelle che vanno dallester no ver so linter no. Questa r egola vale sempr e, inqualsiasi cir costanza e per qualsiasi tipo di blocco: non ci sono eccezioni.
  • A8. Il costrutto Select CaseAbbiamo visto nel capitolo pr ecedente come si possa far pr ocessar e al computer un contr ollo per ver ificar e cer tecondizioni. Supponiamo, or a, di aver e 20 contr olli di uguaglianza del tipo: 01. ... 02. If A = 1 Then 03. istruzioni 04. End If 05. If A = 2 Then 06. istruzioni 07. End If 08. If A = 3 Then 09. istruzioni 10. End If 11. ecceteraIn questo caso il costr utto If diventa non solo noioso, ma anche ingombr ante e disor dinato. Per eseguir e questo tipo dicontr olli multipli esiste un costr utto apposito, Select Case, che ha questa sintassi: 01. ... 02. Select Case [Nome variabile da analizzare] 03. Case [valore1] 04. istruzioni 05. Case [valore2] 06. istruzioni 07. Case [valore3] 08. istruzioni 09. End SelectQuesto tipo di contr ollo r ende molto più linear e, semplice e veloce il codice sor gente. Un esempio: 01. Module Module 1 02. Sub Main() 03. Dim a, b As Double 04. Dim C As Byte 05. 06. Console.WriteLine("Inserire due numeri: ") 07. a = Console.ReadLine 08. b = Console.ReadLine 09. Console.WriteLine("Inserire 1 per calcolare la somma, 2 per la differenza, 3 per il prodotto, 4 per il quoziente:") 10. C = Console.ReadLine 11. 12. Select Case C 13. Case 1 14. Console.WriteLine(a + b) 15. Case 2 16. Console.WriteLine(a - b) 17. Case 3 18. Console.WriteLine(a * b) 19. Case 4 20. Console.WriteLine(a / b) 21. End Select 22. 23. Console.ReadKey() 24. End Sub 25. End ModuleMolto semplice, ma anche molto efficace, specialmente utile nei pr ogr ammi in cui bisogna consider ar e par ecchi valor i.Anche se nellesempio ho utilizzato solamente numer i, è possibile consider ar e var iabili di qualsiasi tipo, sia base(str inghe, date), sia der ivato (str uttur e, classi). Ad esempio: 1.
  • Dim S As String 2. ... 3. Select Case S 4. Case "ciao" 5. ... 6. Case "buongiorno" 7. ... 8. End SelectV arianti del c ostruttoAnche in questo caso, esistono numer ose var ianti, che per mettono non solo di ver ificar e uguaglianze come nei casipr ecedenti, ma anche di contr ollar e disuguaglianze e analizzar e insiemi di valor i. Ecco una lista delle possibilità: Uso della v ir g o la La vir gola per mette di definir e non solo uno, ma molti valor i possibili in un solo Case. Ad esempio: 01. Dim A As Int32 02. ... 03. Select Case A 04. Case 1, 2, 3 05. Questo codice viene eseguito solo se A 06. contiene un valore pari a 1, 2 o 3 07. Case 4, 6, 9 08. Questo codice viene eseguito solo se A 09. contiene un valore pari a 4, 6 o 9 10. End Select Il codice sopr a pr oposto con Select equivale ad un If scr itto come segue: 1. If A = 1 Or A = 2 Or A = 3 Then 2. ... 3. ElseIf A = 4 Or A = 6 Or A = 9 Then 4. ... 5. End If Uso di To Al contr ar io, la keyw or d To per mette di definir e un ran ge di valor i, ossia un inter vallo di valor i, per il quale la condizione r isulta ver ificata se la var iabile in analisi r icade in tale inter vallo. 1. Select Case A 2. Case 67 To 90 3. Questo codice viene eseguito solo se A 4. contiene un valore compreso tra 67 e 90 (estremi inclusi) 5. Case 91 To 191 6. Questo codice viene eseguito solo se A 7. contiene un valore compreso tra 91 e 191 8. End Select Questo cor r isponde ad un If scr itto come segue: 1. If A >= 67 And A <= 90 Then 2. ... 3. ElseIf A >= 91 And A <= 191 Then 4. ... 5. End If Uso di Is Is è usato in questo contesto per ver ificar e delle condizioni facendo uso di nor mali oper ator i di confr onto (meggior e, minor e, diver so, ecceter a...). LIs usato nel costr utto Select Case non ha assolutamente niente a che veder e con quello usato per ver ificar e lidenticità di due oggetti: ha lo stesso nome, ma la funzione è completamente differ ente. 01.
  • Select Case A 02. Case Is >= 6 03. Questo codice viene eseguito solo se A 04. contiene un valore maggiore o uguale di 6 05. Case Is > 1 06. Questo codice viene eseguito solo se A 07. contiene un valore maggiore di 1 (e minore di 6, 08. dato che, se si è arrivati a questo Case, 09. significa che la condizione del Case precedente non 10. è stata soddisfatta) 11. End SelectIl suo equivalente If: 1. If A >= 6 Then 2. ... 3. ElseIf A > 1 Then 4. ... 5. End IfUso di ElseAnche nel Select è lecito usar e Else: il Case che include questa istr uzione è solitamente lultimo di tutte lealter native possibili e pr escr ive di eseguir e il codice che segue solo se tutte le altr e condizioni non sono statesoddisfatte: 01. Select Case A 02. Case 1, 4 03. Questo codice viene eseguito solo se A 04. contiene 1 o 4 05. Case 9 To 12 06. Questo codice viene eseguito solo se A 07. contiene un valore compreso tra 9 e 12 08. Case Else 09. Questo codice viene eseguito solo se A 10. contiene un valore minore di 9 o maggiore di 12, 11. ma diverso da 1 e 4 12. End SelectUso delle pr ecedenti alter nativ e in co m binazioneTutti i modi illustr ati fino ad or a possono esser e uniti in un solo Case per ottener e potenti condizioni dicontr ollo: 1. Select Case A 2. Case 7, 9, 10 To 15, Is >= 90 3. Questo codice viene eseguito solo se A 4. contiene 7 o 9 o un valore compreso tra 10 e 15 5. oppure un valore maggiore o uguale di 90 6. Case Else 7. ... 8. End Select
  • A9. I costrutti iterativi: Do LoopAbbiamo visto che esistono costr utti per ver ificar e condizioni, o anche per ver ificar e in modo semplice e veloce molteugualiglianze. Or a vedr emo i cicli o costr utti iter ativi (dal latino iter , itiner is = "viaggio", ma anche "per la secondavolta"). Essi hanno il compito di r ipeter e un blocco di istr uzioni un numer o deter minato o indeter minato di volte. Ilpr imo che analizzer emo è, appunto, il costr utto Do Loop, di cui esistono molte var ianti. La più semplice è ha questasintassi: 1. Do 2. istruzioni 3. LoopIl suo compito consiste nel r ipete delle istr uzioni compr ese tr a Do e Loop un numer o infinito di volte: lunico modo peruscir e dal ciclo è usar e una speciale istr uzione: "Ex it Do", la quale ha la capacità di inter r omper e il ciclo allistante eduscir e da esso. Questa semplice var iante viene usata in un numer o r idotto di casi, che si possono r icondur r esostanzialmente a due: quando si lavor a con la gr afica e le libr er ie Dir ectX, per disegnar e a scher mo i costanticambiamenti del mondo 2D o 3D; quando è necessar io ver ificar e le condizioni di uscita dal ciclo allinter no del suo bloccodi codice. Ecco un esempio di questo secondo caso: 01. Module Module1 02. 03. Sub Main() 04. Dim a, b As Single 05. 06. Do 07. Pulisce lo schermo 08. Console.Clear() 09. Lunderscore serve per andare a capo nel codice 10. Console.WriteLine("Inserire le misure di base e altezza " & _ 11. "di un rettangolo:") 12. a = Console.ReadLine 13. b = Console.ReadLine 14. 15. Controlla che a e b non siano nulli. In quel caso, esce 16. dal ciclo. Se non ci fosse questo If in mezzo al codice, 17. verrebbe scritto a schermo il messaggio: 18. "Larea del rettangolo è: 0" 19. cosa che noi vogliamo evitare. Se si usasse unaltra 20. variante di Do Loop, questo succederebbe sempre. Ecco 21. perchè, in questa situazione, è meglio 22. servirsi del semplice Do Loop 23. If a = 0 Or b = 0 Then 24. Exit Do 25. End If 26. 27. Console.WriteLine("Larea del rettangolo è: " & (a * b)) 28. Console.ReadKey() 29. Loop 30. End Sub 31. 32. End ModuleLe altr e ver sioni del costr utto, invece, sono le seguenti: 1. Do 2. istruzioni 3. Loop While [condizione] Esegue le istr uzioni specificate fintanto che una condizione r imane valida, ma tutte le istr uzioni vengono eseguite almeno una volta, poichè While si tr ova dopo Do. Esempio:
  • 01. Module Module1 02. Sub Main() 03. Dim a As Int32 = 0 04. 05. Do 06. a += 1 07. Loop While (a < 2) And (a > 0) 08. Console.WriteLine(a) 09. 10. Console.ReadKey() 11. End Sub 12. End ModuleIl codice scr iver à a scher mo "2". 1. Do While [condizione] 2. istruzioni 3. LoopEsegue le istr uzioni specificate fintanto che una condizione r imane valida, ma se la condizione non è validaallinizio, non viene eseguita nessuna istr uzione nel blocco. Esempio: 01. Module Module1 02. Sub Main() 03. Dim a As Int32 = 0 04. 05. Do While (a < 2) And (a > 0) 06. a += 1 07. Loop 08. Console.WriteLine(a) 09. 10. Console.ReadKey() 11. End Sub 12. End ModuleIl codice scr iver à a scher mo "0". Bisogna notar e come le stesse condizioni del caso pr ecedente, spostate da dopoLoop a dopo Do, cambino il r isultato di tutto lalgor itmo. In questo caso, il codice nel ciclo non viene neppur eeseguito per chè la condizione nel While diventa subito falsa (in quanto a = 0, e la pr oposizione "a < 0" r isultafalsa). Nel caso pr ecedente, invece, il blocco veniva eseguito almeno una volta poiché la condizione di contr ollo sitr ovava dopo di esso: in quel caso, a er a or mai stato incr ementato di 1 e per ciò soddisfaceva la condizioneaffinché il ciclo continuasse (fino ad ar r ivar e ad a = 2, che er a il r isultato visualizzato). 1. Do 2. istruzioni 3. Loop Until [condizione]Esegue le istr uzioni specificate fino a che non viene ver ificata la condizione, ma tutte le istr uzioni vengonoeseguite almeno una volta, poichè Until si tr ova dopo Do. Esempio: 01. Module Module1 02. Sub Main() 03. Dim a As Int32 = 0 04. 05. Do 06. a += 1 07. Loop Until (a <> 1) 08. Console.WriteLine(a) 09. 10. Console.ReadKey() 11. End Sub 12. End ModuleA scher mo appar ir à "2". 1. Do Until [condizione] 2. istruzioni 3.
  • Loop Esegue le istr uzioni specificate fino a che non viene soddisfatta la condizione, ma se la condizione è valida allinizio, non viene eseguita nessuna istr uzione del blocco. Esempio: 01. Module Module1 02. Sub Main() 03. Dim a As Int32 = 0 04. 05. Do Until (a <> 1) 06. a += 1 07. Loop 08. Console.WriteLine(a) 09. 10. Console.ReadKey() 11. End Sub 12. End Module A scher mo appar ir à "0".Un piccolo esempio finale: 01. Module Module1 02. Sub Main() 03. Dim a, b, c As Int32 04. Dim n As Int32 05. 06. Console.WriteLine("-- Successione di Fibonacci --") 07. Console.WriteLine("Inserire un numero oltre il quale terminare:") 08. n = Console.ReadLine 09. 10. If n = 0 Then 11. Console.WriteLine("Nessun numero della successione") 12. Console.ReadKey() 13. Exit Sub 14. End If 15. 16. a = 1 17. b = 1 18. Console.WriteLine(a) 19. Console.WriteLine(b) 20. Do While c < n 21. c = a + b 22. b = a 23. a = c 24. Console.WriteLine(c) 25. Loop 26. 27. Console.ReadKey() 28. End Sub 29. End ModuleSuggerimen toPer impostar e il valor e di Default (ossia il valor e pr edefinito) di una var iabile si può usar e questa sintassi: 1. Dim [nome] As [tipo] = [valore]Funziona solo per una var iabile alla volta. Questo tipo di istr uzione si chiama in izializzazion e in -lin e.
  • A10. I costrutti iterativi: ForDopo aver visto costr utti iter ativi che eseguono un ciclo un numer o indeter minato di volte, è ar r ivato il momento dianalizzar ne uno che, al contr ar io, esegue un deter minato numer o di iter azioni. La sintassi è la seguente: 1. Dim I As Int32 2. 3. For I = 0 To [numero] 4. istruzioni 5. NextLa var iabile I, usata in questo esempio, viene definita co ntator e e, ad ogni step, ossia ogni volta che il blocco diistr uzioni si r ipete, viene automaticamente incr ementata di 1, sicchè la si può usar e allinter no delle istr uzioni comeun ver o e pr opr io indice, per r ender e conto del punto al quale liter azione del For è ar r ivata. Bisogna far notar e che iltipo 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 dinumer i inter i, con segno e senza segno, fino anche ai numer i decimali. Un esempio: 01. Module Module1 02. Sub Main() 03. Dim a As Int32 04. 05. Scrive 46 volte (da 0 a 45, 0 compreso, sono 46 numeri) 06. a schermo ciao 07. For a = 0 To 45 08. Console.WriteLine("ciao") 09. Next 10. 11. Console.ReadKey() 12. End Sub 13. End ModuleOvviamente il valor e di par tenza r imane del tutto ar bitr ar io e può esser e deciso ed inizializzato ad un qualsiasivalor e: 01. Module Module1 02. Sub Main() 03. Dim a, b As Int32 04. 05. Console.WriteLine("Inserisci un numero pari") 06. b = Console.ReadLine 07. 08. Se b non è pari, ossia se il resto della divisione 09. b/2 è diverso da 0 10. If b Mod 2 <> 0 Then 11. Lo fa diventare un numero pari, aggiungendo 1 12. b += 1 13. End If 14. 15. Scrive tutti i numeri da b a b+20 16. For a = b To b + 20 17. Console.WriteLine(a) 18. Next 19. 20. Console.ReadKey() 21. End Sub 22. End ModuleIntr 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 dab a b+20. Esistono due modi per r ealizzar e quanto detto. Il pr imo è abbastanza intuitivo, ma meno r affinato, econsiste nel contr ollar e ad ogni iter azione la par ità del contator e: 1.
  • For a = b To b + 20 2. If a Mod 2 = 0 Then 3. Console.WriteLine(a) 4. End If 5. NextIl secondo, invece, è più elegante e usa una ver sione "ar r icchita" della str uttur a iter ativa For , nella quale vienespecificato che lincr emento del contator e non deve più esser e 1, ma bensì 2: 1. For a = b To b + 20 Step 2 2. Console.WriteLine(a) 3. NextInfatti, 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 eaumentata la var iabile contator e del ciclo (in questo caso a) ad ogni step. Lincr 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 dazione del tipo delcontator e: ed esempio, non si può dichiar ar e una var iabile contator e di tipo Byte e un incr emento di -1, poichè Bytecompr ende solo numer i positivi (invece è possibile far lo con SByte, che va da -127 a 128). Allo stesso modo non sidovr ebber o specificar e incr ementi decimali con contator i inter i.Suggerimen toSe 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 unavar iabile al suo inter no in questo modo: 1. For [variabile] As [tipo] = [valore] To [numero] 2. istruzioni 3. Next 4. Che, se volessimo descrivere con un esempio, diverrebbe così: 5. For H As Int16 = 78 To 108 6. istruzioni 7. Next
  • A11. Gli Array - Parte IArray a una dimensioneFino a questo momento abbiamo avuto a che far e con var iabili "singole". Con questo voglio dir e che ogni identificator edichiar ato puntava ad una cella di memor ia dove er a contenuto un solo valor e, leggibile e modificabile usando il nomespecificato nella dichiar azione della var iabile. Lesempio classico che si fa in questo contesto è quello della scatola, doveuna var iabile viene, appunto, assimilata ad una scatola, il cui contenuto può esser e pr eso, modificato e r eimmessosenza pr oblemi.Allo stesso modo, un ar r ay è un insieme di scatole, tutte una vicina allaltr a (tanto nellesempio quando nella posizionefisica allinter no della memor ia), a for mar e ununica fila che per comodità si indica con un solo nome. Per distinguer eogni "scompar to" si fa uso di un numer o inter o (che per convenzione è un inter o a 32 bit, ossia Integer ), detto indice.Tutti i linguaggi .NET utilizzano sempr e un indice a base 0: ciò significa che si inizia a contar e da 0 anziché da 1:La sintassi usata per dichiar ar e un ar r ay è simile a quella usata per dichiar ar e una singola var iabile: 1. Dim [nome]([numero elementi - 1]) As [tipo]La differ enza tr a le due r isiede nelle par entesi tonde che vengono poste dopo il nome della var iabile. Tr a queste
  • par entesi può anche esser e specificato un numer o (sempr e inter o, ovviamente) che indica lindice 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 nellacollezione sar à di ununità super ior e r ispetto allindice massimo. Ad esempio, con questo codice: 1. Dim A(5) As Stringil pr ogr ammator e indica al pr ogr amma che la var iabile A è un ar r ay contenente questi elementi: 1. A(0), A(1), A(2), A(3), A(4), A(5)che sono per la pr ecisione 6 elementi. Ecco un listato che esemplifica i concetti finor a chiar iti: 01. Module Module1 02. Sub Main() 03. Array che contiene 10 valori decimali, rappresentanti voti 04. Dim Marks(9) As Single 05. Questa variabile terrà traccia di quanti voti 06. lutente avrà immesso da tastiera e permetterà di 07. calcolarne una media 08. Dim Index As Int32 = 0 09. 10. Mark conterrà il valore temporaneo immesso 11. da tastiera dallutente 12. Dim Mark As Single 13. Console.WriteLine("Inserisci un altro voto (0 per terminare):") 14. Mark = Console.ReadLine 15. 16. Il ciclo finisce quando lutente immette 0 oppure quando 17. si è raggiunto lindice massimo che è 18. possibile usare per identificare una cella dellarray 19. Do While (Mark > 0) And (Index < 10) 20. Se il voto immesso è maggiore di 0, lo memorizza 21. nellarray e incrementa lindice di 1, così da 22. poter immagazzinare correttamente il prossimo voto nellarray 23. Marks(Index) = Mark 24. Index += 1 25. 26. Console.WriteLine("Inserisci un altro voto (0 per terminare):") 27. Mark = Console.ReadLine 28. Loop 29. Decrementa lindice di 1, poiché anche se lutente 30. ha immesso 0, nel ciclo precedente, lindice era stato 31. incrementato prevedendo unulteriore immissione, che, 32. invece, non cè stata 33. Index -= 1 34. 35. Totale dei voti 36. Dim Total As Single = 0 37. Usa un ciclo For per scorrere tutte le celle dellarray 38. e sommarne i valori 39. For I As Int32 = 0 To Index 40. Total += Marks(I) 41. Next 42. 43. Mostra la media 44. Console.WriteLine("La tua media è: " & (Total / (Index + 1))) 45. Console.ReadKey() 46. End Sub 47. End ModuleIl 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 lutente immetta due voti: Richiede un voto da tastier a: lutente immette 5 (Mar k = 5) Mar k è maggior e di 0 Inser isce il voto nellar r ay: Mar ks(Index ) = Mar ks(0) = 5 Incr ementa Index di 1: Index = 1
  • Entr ambe le condizioni non sono ver ificate: Mar k <> 0 e Index < 9. Il ciclo continua Richiede un voto da tastier a: lutente immette 10 (Mar k = 10) Mar k è maggior e di 0 Inser isce il voto nellar 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: lutente 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 è laseguente: 1. Dim [nome]() As [tipo] = {elementi dellarray separati da virgole}Ad esempio: 1. Dim Parole() As String = {"ciao", "mouse", "penna"}Questa sintassi br eve equivale a questo codice: 1. Dim Parole(2) As String 2. Parole(0) = "ciao" 3. Parole(1) = "mouse" 4. Parole(2) = "penna"Unulter ior e sintassi usata per dichiar ar e un ar r ay è la seguente: 1. Dim [nome] As [tipo]()Questultima, 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 dellar r ay.Gli ar r ay a più dimensioni, invece, sono contr addistinti da più di un indice: il numer o di indici che identificaunivocamente un elemento dellar r ay di dice r ang o . Un ar r ay di r ango 2 (a 2 dimensioni) potr à, quindi, esser epar agonato a un piano, o ad una gr iglia di scatole estesa in lunghezza e in lar ghezza. La sintassi usata è: 1. Dim [nome]( , ) As [tipo] array di rango 2 2. Dim [nome]( , , ) As [tipo] array di rango 3Ecco un esempio che consider a un ar r ay di r ango 2 come una matr ice quadr ata: 01. Module Module1 02. Sub Main() 03. Dichiara e inizializza un array di rango 2. Dato che 04. in questo caso abbiamo due dimensioni, e non una sola, 05. non si può specificare una semplice lista di 06. valori, ma una specie di "tabella" a due entrate. 07. Nellesempio che segue, ho creato una semplice 08. tabella a due righe e due colonne, in cui ogni cella 09. è 0. 10.
  • Dim M(,) As Single = _ 11. {{0, 0}, _ 12. {0, 0}} 13. Bisogna notare il particolare uso delle graffe: si 14. considera larray di rango 2 come un array i cui 15. elementi sono altri array 16. 17. Console.WriteLine("Inserire gli elementi della matrice:") 18. For I As Int32 = 0 To 1 19. For J As Int32 = 0 To 1 20. Console.Write("Inserire lelemento (" & I & ", " & J & "): ") 21. M(I, J) = Console.ReadLine 22. Next 23. Next 24. 25. Dim Det As Single 26. Det = M(0, 0) * M(1, 1) - M(0, 1) * M(1, 0) 27. Console.WriteLine("Il determinante della matrice è: " & Det) 28. 29. Console.ReadKey() 30. End Sub 31. End ModuleRappr esentando gr aficamente lar r ay M, potr emmo disegnar lo così:Ma il computer lo può anche veder e in questo modo, come un ar r ay di ar r ay:
  • Come si vede dal codice di inizializzazione, seppur concettualmente diver si, i due modi di veder e un ar r ay sonocompatibili. Tuttavia, bisogna chiar ir e che so lo e so ltanto in questo caso, le due visioni sono conciliabili, poiché unar 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 ear r ay di ar r ay, come segue: 1. Dim [nome]()() As [tipo] array di arrayE se si pr ova a far e una cosa del gener e: 1. Dim A(,) As Int32 2. Dim B()() As Int32 3. ... 4. A = BSi 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 lapar ola r iser vata ReDim, da non confonder e con la keyw or d Dim: hanno due funzioni totalmente differ enti. Quando sir idimensiona un ar r ay, tutto il suo contenuto viene cancellato: per evitar e questo inconveniente, si deve usar elistr uzione ReDim Pres erve, che tuttavia ha pr estazioni molto scar se a causa delleccessiva lentezza. Entr ambe leistr uzioni der ivano dal Visual Basic classico e non fanno par te, per tanto, della sintassi .NET, sebbene continuino adesser e molto usate, sia per comodità, sia per abitudine. Il metodo più cor r etto da adottar e consiste nellusar e lapr ocedur a Ar r ay.Resize. Eccone un esempio: 01. Module Module1 02. Sub Main() 03. Dim A() As Int32 04. Dim n As Int32 05. 06. Console.WriteLine("Inserisci un numero") 07. n = Console.ReadLine 08. 09. Reimposta la lunghezza di a ad n elementi 10. Array.Resize(A, n) 11. 12. Calcola e memorizza i primi n numeri pari (zero compreso) 13. For I As Int16 = 0 To n - 1 14. A(I) = I * 2 15. Next 16. 17. Console.ReadKey() 18. End Sub 19. End ModuleLa r iga Ar r ay.Resize(A, n) equivale, usando ReDim a: 1. ReDim A(n - 1)Per r idimensionar e un ar r ay a più dimensioni, la faccenda si fa abbastanza complessa. Per pr ima cosa, non si puòutilizzar e Ar r ay.Resize a meno che non si utilizzi un ar r ay di ar r ay, ma anche in quel caso le cose non sono semplici.Infatti, è possibile stabilir e la lunghezza di una sola dimensione alla volta. Ad esempio, avendo un ar r ay M di r ango 2con nove elementi, r aggr uppati in 3 r ighe e 3 colonne, non si può semplicemente scr iver e: 1. ReDim M(2, 2)
  • per chè, così facendo, solo la r iga 2 ver r à r idimensionata a 3 elementi, mentr e la 0 e la 1 sar anno vuote. Il codice dausar e, quindi, è: 1. ReDim M(0, 2) 2. ReDim M(1, 2) 3. ReDim M(2, 2)In questo modo, ogni "r iga" viene aggiustata alla lunghezza giusta.
  • A12. Gli Array - Parte IIIl c ostrutto iterativo For Eac hQuesto costr utto iter ativo è simile al nor male For , ma, invece di aver e una var iabile contator e numer ica, ha unavar 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, lelemento che si tr ova alla posizione cor r ente nellar 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 epar lando dellinter faccia IEnumer able. La sintassi è la seguente: 1. Dim A As [tipo] 2. For Each A In [array/collezione] 3. istruzioni 4. NextOvviamente anche in questo caso, come nel nor male For , è possibile inizializzar e una var iabile contator e allinter no delcostr utto: 1. For Each A As [tipo] in [array/collezione] ...Esempio: 01. Module Module1 02. Sub Main() 03. Dim Words() As String = {"Questo", "è", "un", "array", "di", "stringhe"} 04. 05. For Each Str As String In Words 06. Console.Write(Str & " ") 07. Next 08. 09. A schermo apparirà la frase: 10. "Questo è un array di stringhe " 11. 12. Console.ReadKey() 13. End Sub 14. End ModulePer aver e un ter mine di par agone, il semplicissimo codice pr oposto equivale, usando un for nor male, a questo: 1. Words.Length restituisce il numero di elementi 2. presenti nellarray Words 3. For I As Int32 = 0 To Words.Length - 1 4. Console.Write(Words(I) & " ") 5. NextGli array sono un tipo referenc eDiver samente da come accade in altr i linguaggi, gli ar r ay sono un tipo r efer ence, indipendentemente dal tipo di datida essi contenuto. Ciò significa che si compor tano come ho spiegato nel capitolo "Tipi r efer ence e tipi value": lar ea dimemor ia ad essi associata non contiene il lor o valor e, ma un puntator e alla lor o posizione nellheap managed. Questosignifica che loper ator e = tr a due ar r ay non copia il contenuto di uno nellaltr o, ma li r ende identici, ossia lo stessooggetto. Per lo stesso motivo, è anche lecito distr ugger e logicamente un ar r ay ponendolo uguale a Nothing: questaoper 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 difile binar i, ed è sempr e bene annullar e un ar r ay dopo aver lo usato. 01. Module Module1 02. Sub Main() 03.
  • A e B sono due array di interi 04. Dim A() As Int32 = {1, 2, 3} 05. Dim B() As Int32 = {4, 5, 6} 06. 07. Ora A e B sono due oggetti diversi e contengono 08. numeri diversi. Questa riga stamperà sullo 09. schermo "False", infatti A Is B = False 10. Console.WriteLine(A Is B) 11. Adesso poniamo A uguale a B. Dato che gli array 12. sono un tipo reference, da ora in poi, entrambi 13. saranno lo stesso oggetto 14. A = B 15. Infatti questa istruzione stamperà a schermo 16. "True", poiché A Is B = True 17. Console.WriteLine(A Is B) 18. Dato che A e B sono lo stesso oggetto, se modifichiamo 19. un valore dellarray riferendoci ad esso con il nome 20. B, anche richiamandolo con A, esso mostrerà 21. che lultimo elemento è lo stesso 22. B(2) = 90 23. Su schermo apparirà 90 24. Console.WriteLine(A(2)) 25. 26. Dim C() As Int32 = {7, 8, 9} 27. B = C 28. Ora cosa succede? 29. 30. Console.ReadKey() 31. End Sub 32. End ModuleEcco come appar e la memor ia dopo lassegnazione A = B:Ed ecco come appar e dopo lassegnazione B = C:
  • Come si vede, le var iabili contengono solo lindir 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.Lindir 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 perconsultazione.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 nellar r ay A Clone() : cr ea una coppia esatta dellar r ay Constr ainedCopy(A1, I1, A2, I2, L) : copia L elementi dallar r ay A1 a par tir e dallindice I1 nellar r ay A2, a par tir e dallindice I2; se la copia non ha successo, ogni cambiamento sar à annullato e lar 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 dellar r ay A1 e la deposita in A2 Find / FindLast (A, P(Of T)) As T : cer ca il pr imo elemento dellar 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 dellar 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 lultimo elemento dellar 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 lindice For Each(A(Of T)) : esegue unazione A deter minata da un delegate Sub per ogni elemento dellar r ay GetLength(A) : r estituisce la dimensione dellar r ay Index Of(A, T) / LastIndex Of(A, T) : r estituisce il pr imo o lultimo indice delloggetto T nellar r ay A Rever se(A) : inver te lor dine di tutti gli elementi nellar r ay A Sor t(A) : or dina alfabeticamente lar r ay A. Esistono 16 ver sioni di questa pr ocedur a, tr a le quali una accetta
  • come secondo par ametr o un oggetto che implementa uninter faccia ICompar er che per mette di decider e come or dinar e lar r ayMolti di questi metodi, come si è visto, compr endono ar gomenti molto avanzati: quando sar ete in gr ado dicompr 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 questimetodi.
  • A13. I Metodi - Parte IAnatomia di un metodoIl Fr amew or k .NET mette a disposizione dello sviluppator e un enor me numer o di classi contenenti metodi davver o utili,già scr itti e pr onti alluso, ma solo in pochi casi questi bastano a cr ear e unapplicazione 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 nelpr 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 dir acchiuder e sotto un unico nome (il nome del metodo) una ser ie più o meno gr ande di istr uzioni.Un metodo è costituito essenzialmente da tr e par ti: Nome : un identificator e che si può usar e in altr e par ti del pr ogr amma per in vocare il metodo, ossia per eseguir e le istr uzioni di cui esso consta; Elenco dei par ametr i : un elenco di var iabili attr aver so i quali il metodo può scambiar e dati con il pr ogr amma; Cor po : contiene il codice effettivo associato al metodo, quindi tutte le istr uzioni e le oper azioni che esso deve eseguir eMa or a scendiamo un po più nello specifico...Proc edure senza parametriIl caso più semplice di metodo consiste in una pr ocedur a senza par ametr i: essa costituisce, gr osso modo, unsottopr ogr amma a sè stante, che può esser e r ichiamato semplicemente scr ivendone il nome. La sua sintassi è moltosemplice: 1. Sub [nome]() 2. istruzioni 3. End SubCr 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 sitr atta solo di un caso par ticolar e, come vedr emo fr a poco). Quando il pr ogr amma inizia, Main è il pr imo metodoeseguito: 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 odelle 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 nellambito della pr ogr ammazione si intende latto di dichiar ar e qualcosaallinter no del cor po di una classe, ma fuor i dal cor po di un qualsiasi suo membr o. Ad esempio, la dichiar azione seguenteè cor r etta: 01. Module Module1 02. Sub Esempio() 03. istruzioni 04. End Sub 05. 06. Sub Main() 07. istruzioni 08. End Sub 09. End Modulementr e la pr ossima è SBAGLI ATA: 1. Module Module1 2. Sub Main() 3.
  • Sub Esempio() 4. istruzioni 5. End Sub 6. istruzioni 7. End Sub 8. End ModuleAllo stesso modo, i metodi sono lunica categor ia, oltr e alle pr opr ietà e agli oper ator i, a poter contener e delleistr uzioni: sono str umenti "attivi" di pr ogr ammazione e solo lor o possono eseguir e istr uzioni. Quindi astenetevi dalloscr iver e un abominio del gener e: 1. Module Module1 2. Sub Main() 3. istruzioni 4. End Sub 5. Console.WriteLine() 6. End SubE totalmente e concettualmente sbagliato. Ma or a veniamo al dunque con un esempio: 01. Module Module1 02. Dichiarazione di una procedura: il suo nome è "FindDay", il 03. suo elenco di parametri è vuoto, e il suo corpo è 04. rappresentato da tutto il codice compreso tra "Sub FindDay()" 05. ed "End Sub". 06. Sub FindDay() 07. Dim StrDate As String 08. Console.Write("Inserisci giorno (dd/mm/yyyy): ") 09. StrDate = Console.ReadLine 10. 11. Dim D As Date 12. La funzione Date.TryParse tenta di convertire la stringa 13. StrDate in una variabile di tipo Date (che è un tipo 14. base). Se ci riesce, ossia non ci sono errori nella 15. data digitata, restituisce True e deposita il valore 16. ottenuto in D; se, al contrario, non ci riesce, 17. restituisce False e D resta vuota. 18. Quando una data non viene inizializzata, dato che è un 19. tipo value, contiene un valore predefinito, il primo 20. Gennaio dellanno 1 d.C. a mezzogiorno in punto. 21. If Date.TryParse(StrDate, D) Then 22. D.DayOfWeek contiene il giorno della settimana di D 23. (lunedì, martedì, eccetera...), ma in un 24. formato speciale, lEnumeratore, che vedremo nei 25. prossimi capitoli. 26. Il ".ToString()" converte questo valore in una 27. stringa, ossia in un testo leggibile: i giorni della 28. settimana, però, sono in inglese 29. Console.WriteLine(D.DayOfWeek.ToString()) 30. Else 31. Console.WriteLine(StrDate & " non è una data valida!") 32. End If 33. End Sub 34. 35. Altra procedura, simile alla prima 36. Sub CalculateDaysDifference() 37. Dim StrDate1, StrDate2 As String 38. Console.Write("Inserisci il primo giorno (dd/mm/yyyy): ") 39. StrDate1 = Console.ReadLine 40. Console.Write("Inserisci il secondo giorno (dd/mm/yyyy): ") 41. StrDate2 = Console.ReadLine 42. 43. Dim Date1, Date2 As Date 44. 45. If Date.TryParse(StrDate1, Date1) And _ 46. Date.TryParse(StrDate2, Date2) Then 47. La differenza tra due date restituisce il tempo 48. trascorso tra luna e laltra. In questo caso noi 49. prendiamo solo i giorni 50. Console.WriteLine((Date2 - Date1).Days) 51.
  • Else 52. Console.WriteLine("Inserire due date valide!") 53. End If 54. End Sub 55. 56. Sub Main() 57. Command è una variabile di tipo char (carattere) che 58. conterrà una lettera indicante quale compito eseguire 59. Dim Command As Char 60. 61. Do 62. Console.Clear() 63. Console.WriteLine("Qualche operazione con le date:") 64. Console.WriteLine("- Premere F per sapere in che giorno " & _ 65. "della settimana cade una certa data;") 66. Console.WriteLine("- Premere D per calcolare la differenza tra due date;") 67. Console.WriteLine("- Premere E per uscire.") 68. Console.ReadKey() è la funzione che abbiamo sempre 69. usato finora per fermare il programma in attesa della 70. pressione di un pulsante. Come vedremo fra breve, non 71. è necessario usare il valore restituito da una 72. funzione, ma in questo caso ci serve. Ciò che 73. ReadKey restituisce è qualcosa che non ho ancora 74. trattato. Per ora basti sapere che 75. Console.ReadKey().KeyChar contiene lultimo carattere 76. premuto sulla tastiera 77. Command = Console.ReadKey().KeyChar 78. Analizza il valore di Command 79. Select Case Command 80. Case "f" 81. Invoca la procedura FindDay() 82. FindDay() 83. Case "d" 84. Invoca la procedura CalculateDaysDifference() 85. CalculateDaysDifference() 86. Case "e" 87. Esce dal ciclo 88. Exit Do 89. Case Else 90. Console.WriteLine("Comando non riconosciuto!") 91. End Select 92. Console.ReadKey() 93. Loop 94. End Sub 95. End ModuleIn questo pr imo caso, le due pr ocedur e dichiar ate sono effettivamente sottopr ogr ammi a sé stanti: non hanno nulla incomune con il modulo (eccetto il semplice fatto di esser ne membr i), né con Main, ossia non scambiano alcun tipo diinfor mazione con essi; sono come degli ingr anaggi sigillati allinter no di una scatola chiusa. A questo r iguar do, bisognainser ir e una pr ecisazione sulle var iabili dichiar ate ed usate allinter no di un metodo, qualsiasi esso sia. Esse si diconolo cali o tem po r anee, poiché esistono solo allinter no del metodo e vengono distr utte quando il flusso di elabor azionene r aggiunge la fine. Anche sotto questo aspetto, si può notar e come le pr ocedur e appena stilate siano par ticolar mentechiuse e r estr ittive. Tuttavia, si può benissimo far inter agir e un metodo con oggetti ed entità ester ne, e questoappr occio è decisamente più utile che non il semplice impacchettar e ed etichettar e blocchi di istr uzioni in locazionidistinte. Nel pr ossimo esempio, la pr ocedur a attinge dati dal modulo, poiché in esso è dichiar ata una var iabile a livellodi classe. 01. Module Module1 02. Questa variabile è dichiarata a livello di classe 03. (o di modulo, in questo caso), perciò è accessibile 04. a tutti i membri del modulo, sempre seguendo il discorso 05. dei blocchi di codice fatto in precedenza 06. Dim Total As Single = 0 07. 08. Legge un numero da tastiera e lo somma al totale 09. Sub Sum() 10. Dim Number As Single = Console.ReadLine 11.
  • Total += Number 12. End Sub 13. 14. Legge un numero da tastiera e lo sottrae al totale 15. Sub Subtract() 16. Dim Number As Single = Console.ReadLine 17. Total -= Number 18. End Sub 19. 20. Legge un numero da tastiera e divide il totale per 21. tale numero 22. Sub Divide() 23. Dim Number As Single = Console.ReadLine 24. Total /= Number 25. End Sub 26. 27. Legge un numero da tastiera e moltiplica il totale 28. per tale numero 29. Sub Multiply() 30. Dim Number As Single = Console.ReadLine 31. Total *= Number 32. End Sub 33. 34. Sub Main() 35. Questa variabile conterrà il simbolo matematico 36. delloperazione da eseguire 37. Dim Operation As Char 38. Do 39. Console.Clear() 40. Console.WriteLine("Risultato attuale: " & Total) 41. Operation = Console.ReadKey().KeyChar 42. Select Case Operation 43. Case "+" 44. Sum() 45. Case "-" 46. Subtract() 47. Case "*" 48. Multiply() 49. Case "/" 50. Divide() 51. Case "e" 52. Exit Do 53. Case Else 54. Console.WriteLine("Operatore non riconosciuto") 55. Console.ReadKey() 56. End Select 57. Loop 58. End Sub 59. End ModuleProc edure c on parametriAvviandoci ver so linter azione sempr e maggior e del metodo con lambiente in cui esso esiste, tr oviamo le pr ocedur econ par ametr i. Al contr ar io delle pr ecedenti, esse possono r icever e e scambiar e dati con il chiaman te: con questultimoter mine ci si r ifer isce alla gener ica entità allinter no della quale il metodo in questione è stato invocato. I par ametr isono come delle var iabili locali fittizie: esistono solo allinter no del cor po, ma non sono dichiar ate in esso, bensìnellelenco dei par ametr i. Tale elenco deve esser e specificato dopo il nome del metodo, r acchiuso da una coppia dipar entesi tonde, e ogni suo elemento deve esser e separ ato dagli altr i da vir gole. 1. Sub [nome](ByVal [parametro1] As [tipo], ByVal [parametro2] As [tipo], ...) 2. istruzioni 3. End SubCome si vede, anche la dichiar azione è abbastanza simile a quella di una var iabile, fatta eccezione per la par olar iser vata ByVal, di cui tr a poco vedr emo lutilià. Per intr odur r e semplicemente lar gomento, facciamo subito un
  • esempio, r iscr ivendo lultimo codice pr oposto nel par agr afo pr ecedente con laggiunta dei par ametr i: 01. Module Module1 02. Dim Total As Single = 0 03. 04. Sub Sum(ByVal Number As Single) 05. Total += Number 06. End Sub 07. 08. Sub Subtract(ByVal Number As Single) 09. Total -= Number 10. End Sub 11. 12. Sub Divide(ByVal Number As Single) 13. Total /= Number 14. End Sub 15. 16. Sub Multiply(ByVal Number As Single) 17. Total *= Number 18. End Sub 19. 20. Sub Main() 21. Questa variabile conterrà il simbolo matematico 22. delloperazione da eseguire 23. Dim Operation As Char 24. Do 25. Console.Clear() 26. Console.WriteLine("Risultato attuale: " & Total) 27. Operation = Console.ReadKey().KeyChar 28. Select Case Operation 29. Se si tratta di simboli accettabili 30. Case "+", "-", "*", "/" 31. Legge un numero da tastiera 32. Dim N As Single = Console.ReadLine 33. E a seconda delloperazione, utilizza una 34. procedura piuttosto che unaltra 35. Select Case Operation 36. Case "+" 37. Sum(N) 38. Case "-" 39. Subtract(N) 40. Case "*" 41. Multiply(N) 42. Case "/" 43. Divide(N) 44. End Select 45. Case "e" 46. Exit Do 47. Case Else 48. Console.WriteLine("Operatore non riconosciuto") 49. Console.ReadKey() 50. End Select 51. Loop 52. End Sub 53. End ModuleRichiamando, 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 antelesecuzione un valor e pr eciso, che può anche esser e, come in questo caso, il contenuto di unaltr a var iabile. Nel ger gotecnico, Number - ossia, più in gener ale, lidentificator e dichiar ato nellelenco dei par ametr i - si dice par am etr ofor m ale, mentr e N - ossia ciò che viene concr etamente pas s ato al metodo - si dice par am etr o attuale. Non hovolutamente assegnato al par ametr o attuale lo stesso nome di quello for male, anche se è del tutto lecito far lo: ho agitoin questo modo per far capir e che non è necessar io nessun legame par ticolar e tr a i due; lunico vincolo che devesussister e r isiede nel fatto che par ametr o for male ed attuale abbiano lo stesso tipo. Questultima 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 unnumer o (Total)?
  • Or a pr oviamo a modificar e il codice pr ecedente r iassumendo tutte le oper azioni in una sola pr ocedur a, a cui, per ò,vengono passati due par ametr i: il numer o e loper ator e da usar e. 01. Module Module1 02. Dim Total As Single = 0 03. 04. Sub DoOperation(ByVal Number As Single, ByVal Op As Char) 05. Select Case Op 06. Case "+" 07. Total += Number 08. Case "-" 09. Total -= Number 10. Case "*" 11. Total *= Number 12. Case "/" 13. Total /= Number 14. End Select 15. End Sub 16. 17. Sub Main() 18. Dim Operation As Char 19. Do 20. Console.Clear() 21. Console.WriteLine("Risultato attuale: " & Total) 22. Operation = Console.ReadKey().KeyChar 23. Select Case Operation 24. Case "+", "-", "*", "/" 25. Dim N As Single = Console.ReadLine 26. A questa procedura vengono passati due 27. parametri: il primo è il numero da 28. aggiungere/sottrarre/moltiplicare/dividere 29. a Total; il secondo è il simbolo 30. matematico che rappresenta loperazione 31. da eseguire 32. DoOperation(N, Operation) 33. Case "e" 34. Exit Do 35. Case Else 36. Console.WriteLine("Operatore non riconosciuto") 37. Console.ReadKey() 38. End Select 39. Loop 40. End Sub 41. End ModulePassare parametri al programma da riga di c omandoCome 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 vieneeseguito da r ig a di co m ando . In questultimo caso, Main viene dichiar ata con un solo ar gomento, un ar r ay di str inghe: 01. Module Module1 02. Sub Main(ByVal Args() As String) 03. If Args.Length = 0 Then 04. Se la lunghezza dellarray è 0, significa che è vuoto 05. e quindi non è stato passato nessun parametro a riga 06. di comando. Scrive a schermo come utilizzare 07. il programma 08. Console.WriteLine("Utilizzo: nomeprogramma.exe tuonome") 09. Else 10. Args ha almeno un elemento. Potrebbe anche averne di 11. più, ma a noi interessa solo il primo. 12. Saluta lutente con il nome passato da riga di comando 13. Console.WriteLine("Ciao " & Args(0) & "!") 14. End If 15. Console.ReadKey() 16. End Sub 17.
  • End ModulePer pr ovar lo, potete usar e cmd.ex e, il pr ompt dei comandi. Io ho digitato: 1. CD "C:UsersTotemDocumentsVisual Studio 2008ProjectsConsoleApplication2binDebug" 2. ConsoleApplication2.exe TotemLa pr ima istr uzione per cambiar e la dir ector y di lavor o, la seconda linvocazione ver a e pr opr ia del pr ogr amma, dove"Totem" è lunico 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 Debugdelle 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 ogettonella finestr a a destr a, quindi scegliete Pr oper ties e r ecatevi alla tabella Debug:
  • A14. I Metodi - Parte IIBy V al e By RefNel capitolo pr ecedente, tutti i par ametr i sono stati dichiar anti anteponendo al lor o nome la keyw or d ByVal. Essa ha ilcompito di comunicar e al pr ogr amma che al par ametr o for male deve esser e passata una co pia del par ametr o attuale.Questo significa che qualsiasi codice sia scr itto entr o il cor po del metodo, ogni manipolazione e ogni oper azioneeseguita su quel par ametr o agisce, di fatto, su un altra variabile, tempor anea, e no n sul par ametr o attuale for nito.Ecco un esempio: 01. Module Module1 02. Sub Change(ByVal N As Int32) 03. N = 30 04. End Sub 05. 06. Sub Main() 07. Dim A As Int32 = 56 08. Change(A) 09. Console.WriteLine(A) 10. Console.ReadKey() 11. End Sub 12. End ModuleA 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 aChange. In questultima, 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 unaltr a ar ea di memor ia, totalmentedistinta, e per questo motivo ogni oper azione eseguita su questultima non cambia il valor e di A. Di fatto, ByVal indicadi tr attar e il par ametr o come un tipo value (pas s aggio per valore).Al contr ar io, ByRef indica di tr attar e il par ametr o come un tipo r efer ence (pas s aggio per in dirizzo). Questo significache, dur ante il passaggio dei par ametr i, al par ametr o for male non viene attr ibuito come valor e una coppia di quelloattuale, ma bensì viene for zato a puntar e alla sua stessa cella di memor ia. In questa situazione, quindi, anche i tipivalue come numer i, date e valor i logici, si compor tano come se fosser o oggetti. Ecco lo stesso esempio di pr ima, conuna piccola modifica: 01. Module Module1 02. Sub Change(ByRef N As Int32) 03. N = 30 04. End Sub 05. 06. Sub Main() 07. Dim A As Int32 = 56 08. Change(A) 09. Console.WriteLine(A) 10. Console.ReadKey() 11. End Sub 12. End ModuleNel codice, la sola differ enza consiste nella keyw or d ByRef, la quale, tuttavia, cambia r adicalmente il r isultato. Infatti, ascher mo appar ir à "30" e non "56". Dato che è stata applicata la clausola ByRef, N punta alla stessa ar ea di memor ia di A,quindi ogni alter azione per petr ata nel cor po del metodo sul par ametr o for male si r iper cuote su quello attuale.A questo punto è molto impor tante sottolinear e che i tipi r efer ence si compor tano SEM PRE allo stesso modo, anche sevengono inser iti nellelenco dei par ametr i accompagnati da ByVal. Eccone una dimostr azione: 01. Module Module1 02. Dim A As New Object 03. 04. Sub Test(ByVal N As Object) 05. Console.WriteLine(N Is A) 06.
  • End Sub 07. 08. Sub Main() 09. Test(A) 10. Console.ReadKey() 11. End Sub 12. End ModuleSe ByVal modificasse il compor tamento degli oggetti, allor a N conter r ebbe una copia di A, ossia un altr o oggettosemplicemente uguale, ma non identico. Invece, a scher mo appar e la scr itta "Tr ue", che significa "Ver o", per ciò N e Asono 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: 1. Function [name]([elenco parametri]) As [tipo] 2. ... 3. Return [risultato] 4. End FunctionLa pr ima differ enza che salta allocchio è lAs che segue lelenco dei par ametr i, come a sugger ir e che la funzione sia diun cer to tipo. Ad esser e pr ecisi, quellAs non indica il tipo della funzione, ma piuttosto quello del suo r isultato. Infatti, lefunzioni r estituiscono qualcosa alla fine del lor o ciclo di elabor azione. Per questo motivo, pr ima del ter mine del suocor po, deve esser e posta almeno unistr uzione Retur n, seguita da un qualsiasi dato, la quale for nisce al chiamante ilver o r isultato di tutte le oper azioni eseguite. Non è un er r or e scr iver e funzioni pr ive dellistr uzione Retur n, ma nonavr ebbe comunque senso: si dovr ebbe usar e una pr ocedur a in quel caso. Ecco un esempio di funzione: 01. Module Module1 02. Questa funzione calcola la media di un insieme 03. di numeri decimali passati come array 04. Function Average(ByVal Values() As Single) As Single 05. Total conterrà la somma totale di tutti 06. gli elementi di Values 07. Dim Total As Single = 0 08. Usa un For Each per ottenere direttamente i valori 09. presenti nellarray piuttosto che enumerarli 10. attraverso un indice mediante un For normale 11. For Each Value As Single In Values 12. Total += Value 13. Next 14. Restituisce la media aritmetica, ossia il rapporto 15. tra la somma totale e il numero di elementi 16. Return (Total / Values.Length) 17. End Function 18. 19. Sub Main(ByVal Args() As String) 20. Dim Values() As Single = {1.1, 5.2, 9, 4, 8.34} 21. Notare che in questo caso ho usato lo stesso nome 22. per il parametro formale e attuale 23. Console.WriteLine("Media: " & Average(Values)) 24. Console.ReadKey() 25. End Sub 26. End ModuleE un altr o esempio in cui ci sono più Retur n: 01. Module Module1 02. Function Potenza(ByVal Base As Single, ByVal Esponente As Byte) As Double 03. Dim X As Double = 1 04. 05. If Esponente = 0 Then 06. Return 1 07. Else 08. If Esponente = 1 Then 09.
  • Return Base 10. Else 11. For i As Byte = 1 To Esponente 12. X *= Base 13. Next 14. Return X 15. End If 16. End If 17. End Function 18. 19. Sub Main() 20. Dim F As Double 21. Dim b As Single 22. Dim e As Byte 23. 24. Console.WriteLine("Inserire base ed esponente:") 25. b = Console.ReadLine 26. e = Console.ReadLine 27. F = Potenza(b, e) 28. Console.WriteLine(b & " elevato a " & e & " vale " & F) 29. Console.ReadKey() 30. End Sub 31. End ModuleIn questultimo esempio, il cor po della funzione contiene ben tr e Retur n, ma ognuno appar tiene a un path di co dicediffer ente. Path significa "per cor so" e la locuzione appena usata indica il flusso di elabor azione seguito dal pr ogr ammaper deter minati valor i di Base ed Esponente. Disegnando un diagr amma di flusso della funzione, sar à facile capir e comeci siano tr e per cor si differ enti, ossia quando lesponente 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 laltr o, ma non ha nessun senso logico:Retur n, infatti, non solo r estituisce un r isultato al chiamante, ma ter mina anche lesecuzione della funzione. A questopr oposito, bisogna dir e che esiste anche lo statement (=istr uzione) Exit Fun ction , che for za il pr ogr amma ad uscir eimmediatamente dal cor po della funzione: inutile dir e che è abbastanza per icoloso da usar e, poiché si cor r e il r ischio dinon 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 delvalor e r estituito dalla funzione, anche se è for temente consigliato: in questo caso, il pr ogr amma suppor r à che si tr attidel tipo Object.Usi partic olari 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. Diseguito sono elencati alcuni casi: Quando una funzione si tr ova a destr a delluguale, in qualsiasi punto di unespr 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. Lesempio classico è la funzione Console.Readline. Luso più cor r etto sar ebbe: 1. a = Console.ReadLine() ma è possibile scr iver e, come abbiamo fatto finor a: 1. a = Console.ReadLine
  • Non è obbligator io usar e il valor e r estituito da una funzione: nei casi in cui esso viene tr alasciato, la si tr atta come se fosse una pr ocedur a. Ne è un esempio la funzione Console.ReadKey(). A noi ser ve per fer mar e il pr ogr amma in attesa della pr essione di un pulsante, ma essa non si limita a questo: r estituisce anche infor mazioni dettagliate sulle condizioni di pr essione e sul codice del car atter e inviato dalla tastier a. Tuttavia, a noi non inter essava usar e queste infor mazioni; così, invece di scr iver e un codice come questo: 1. Dim b = Console.ReadKey() ci siamo limitati a: 1. Console.ReadKey() Questa ver satilità può, in cer ti casi, cr ear e pr oblemi, poiché si usa una funzione convinti che sia una pr ocedur a, mentr e il valor e r estituito è impor tante per evitar e linsor ger e di er r or i. Ne è un esempio la funzione IO.File.Cr eate, che vedr emo molto più in là, nella sezione E della guida.V ariabili StaticLe var iabili Static sono una par ticolar e eccezione alle var iabili locali/tempor anee. Avevo chiar amente scr itto pochipar agr afi fa che queste ultime esistono solo nel cor po del metodo, vengono cr eate al momento dellinvocazione edistr utte al ter mine dellesecuzione. Le Static, invece, possiedono soltanto le pr ime due car atter istiche: non vengonodistr 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 unaseconda volta nel metodo. Ecco un esempio: 01. Module Module1 02. Sub Test() 03. Static B As Int32 = 0 04. B += 1 05. Console.WriteLine(B) 06. End Sub 07. 08. Sub Main(ByVal Args() As String) 09. For I As Int16 = 1 To 6 10. Test() 11. Next 12. Console.ReadKey() 13. End Sub 14. End ModuleIl pr ogr amma stamper à a scher mo, in successione, 1, 2, 3, 4, 5 e 6. Come volevasi dimostr ar e, nonostante B siatempor anea, mantiene il suo valor e tr a una chiamata e la successiva.
  • A15. I Metodi - Parte IIIParametri opzionaliCome sugger isce il nome stesso, i par ametr i opzionali sono speciali par ametr i che non è obbligator io specificar equando si invoca un metodo. Li si dichiar a facendo pr eceder e la clausola ByVal o ByRef dalla keyw or d Optional: inoltr e,dato che un par ametr o del gener e può anche esser e omesso, bisogna necessar iamente indicar e un valor e pr edefinitoche esso possa assumer e. Tale valor e pr edefinito deve esser e una costante e, per questo motivo, se r icor date ildiscor so pr ecedentemente fatto sullassegnamento delle costanti, i par ametr i opzionali possono esser e solo di tipo base.Ecco un esempio: 01. Module Module1 02. Disegna una barra "di caricamento" animata con dei trattini 03. e dei pipe (|). Length indica la sua lunghezza, ossia quanti 04. caratterei debbano essere stampati a schermo. AnimationSpeed 05. è la velocità dellanimazione, di default 1 06. Sub DrawBar(ByVal Length As Int32, _ 07. Optional ByVal AnimationSpeed As Single = 1) 08. La variabile static tiene conto del punto a cui si è 09. arrivati al caricamento 10. Static Index As Int32 = 1 11. 12. Disegna la barra 13. For I As Int32 = 1 To Length 14. If I > Index Then 15. Console.Write("-") 16. Else 17. Console.Write("|") 18. End If 19. Next 20. 21. Aumenta lindice di uno. Notare il particolare 22. assegnamento che utilizza loperatore Mod. Finché 23. Index è minore di Length, questa espressione equivale 24. banalmente a Index + 1, poiché a Mod b = a se a < b. 25. Quando Index supera il valore di Length, allora loperatore 26. Mod cambia le cose: infatti, se Index = Length + 1, 27. lespressione restituisce 0, che, sommato a 1, dà 1. 28. Il risultato che otteniamo è che Index reinizia 29. da capo, da 1 fino a Length. 30. Index = (Index Mod (Length + 1)) + 1 31. Il metodo Sleep, che vedremo approfonditamente solo nella 32. sezione B, fa attendere al programma un certo numero di 33. millisecondi. 34. 1000 / AnimationSpeed provoca una diminuzione del tempo 35. di attesa allaumentare della velocità 36. Threading.Thread.CurrentThread.Sleep(1000 / AnimationSpeed) 37. End Sub 38. 39. Sub Main() 40. Disegna la barra con un ciclo infinito. Potete invocare 41. DrawBar(20) tralasciando lultimo argomento e lanimazione 42. sarà lenta poiché la velocità di default è 1 43. Do 44. Console.Clear() 45. DrawBar(20, 5) 46. Loop 47. End Sub 48. End ModuleParametri indefiniti
  • Questo par ticolar e tipo di par ametr i non r appr esenta un solo elemento, ma bensì una collezione di elementi: infatti, sispecifica un par ametr o come indefinito quando non si sa a pr ior i quanti par ametr i il metodo r ichieder à. A sostegno diquesto fatto, i par ametr i indefiniti sono dichiar ati come ar r ay, usando la keyw or d Par amAr r ay inter posta tr a laclausola ByVal o ByRef e il nome del par ametr o. 01. Module Module1 02. Somma tutti i valori passati come parametri. 03. Function Sum(ByVal ParamArray Values() As Single) As Single 04. Dim Result As Single = 0 05. 06. For I As Int32 = 0 To Values.Length - 1 07. Result += Values(I) 08. Next 09. 10. Return Result 11. End Function 12. 13. Sub Main() 14. Dim S As Single 15. 16. Somma due valori 17. S = Sum(1, 2) 18. Somma quattro valori 19. S = Sum(1.1, 5.6, 98.2, 23) 20. Somma un array di valori 21. Dim V() As Single = {1, 8, 3.4} 22. S = Sum(V) 23. End Sub 24. End ModuleCome si vede, mediante Par amAr r ay, la funzione diventa capace si accettar e sia una lista di valor i specificata dalpr 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 eesser e posto alla fine dellelenco dei par ametr i. Esempio: 01. Module Module1 02. Questa funzione calcola un prezzo includendovi anche 03. il pagamento di alcune tasse (non sono un esperto di 04. economia, perciò mi mantengono piuttosto sul vago XD). 05. Il primo parametro rappresenta il prezzo originale, mentre 06. il secondo è un parametro indefinito che 07. raggruppa tutte le varie tasse vigenti sul prodotto 08. da acquistare che devono essere aggiunte allimporto 09. iniziale (espresse come percentuali) 10. Function ApplyTaxes(ByVal OriginalPrice As Single, _ 11. ByVal ParamArray Taxes() As Single) As Single 12. Dim Result As Single = OriginalPrice 13. For Each Tax As Single In Taxes 14. Result += OriginalPrice * Tax / 100 15. Next 16. Return Result 17. End Function 18. 19. Sub Main() 20. Dim Price As Single = 120 21. 22. Aggiunge una tassa del 5% a Price 23. Dim Price2 As Single = _ 24. ApplyTaxes(Price, 5) 25. 26. Aggiunge una tassa del 5%, una del 12.5% e una 27. dell1% a Price 28. Dim Price3 As Single = _ 29. ApplyTaxes(Price, 5, 12.5, 1) 30. 31. Console.WriteLine("Prezzo originale: " & Price) 32. Console.WriteLine("Presso con tassa 1: " & Price2) 33. Console.WriteLine("Prezzo con tassa 1, 2 e 3: " & Price3) 34.
  • 35. Console.ReadKey() 36. End Sub 37. End ModuleRic orsioneSi 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 unconseguente aumento della leggibilità; il difetto più r ilevante è luso 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ò facilmentepr ovocar e il cr ash di unapplicazione a causa di un over flow dello stack. Infatti, se un metodo continuaindiscr iminatamente a invocar e se stesso, senza alcun contr ollo per poter si fer mar e (o con costr utti di contr ollocontenenti 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 iabililocali, oltr e che per linvocazione 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 incui è davver o utile. Come esempio non posso che pr esentar e il classico calcolo del fatto r iale: 01. Module Module1 02. Notare che il parametro è di tipo Byte perchè il 03. fattoriale cresce in modo abnorme e già a 170! Double non 04. basta più a contenere il risultato 05. Function Factorial(ByVal N As Byte) As Double 06. If N <= 1 Then 07. Return 1 08. Else 09. Return N * Factorial(N - 1) 10. End If 11. End Function 12. 13. Sub Main() 14. Dim Number As Byte 15. 16. Console.WriteLine("Inserisci un numero (0 <= x < 256):") 17. Number = Console.ReadLine 18. Console.WriteLine(Number & "! = " & Factorial(Number)) 19. 20. Console.ReadKey() 21. End Sub 22. End Module
  • A16. Gli EnumeratoriGli enumer ator i sono tipi value par ticolar i, che per mettono di r aggr uppar e sotto un unico nome più costanti. Essivengono utilizzati sopr attutto per r appr esentar e opzioni, attr ibuti, car atter istiche o valor i pr edefiniti, o, più ingener ale, qualsiasi dato che si possa "sceglier e" in un insieme finito di possibilità. Alcuni esempi di enumer ator epotr 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 questultimo, il .NET impiega ver amente unenumer ator e. Ma pr ima di andar e oltr e, ecco la sintassi da usar e nella dichiar azione: 1. Enum [Nome] 2. [Nome valore 1] 3. [Nome valore 2] 4. ... 5. End EnumAd esempio: 01. Module Module1 02. A seconda di come sono configurati i suoi caratteri, una 03. stringa può possedere diverse denominazioni, chiamate 04. Case. Se è costituita solo da caratteri minuscoli 05. (es.: "stringa di esempio") si dice che è in Lower 06. Case; al contrario se contiene solo maiuscole (es.: "STRINGA 07. DI ESEMPIO") sarà Upper Case. Se, invece, ogni 08. parola ha liniziale maiuscola e tutte le altre lettere 09. minuscole si indica con Proper Case (es.: "Stringa Di Esempio"). 10. In ultimo, se solo la prima parola ha liniziale 11. maiuscola e il resto della stringa è tutto minuscolo 12. e questa termina con un punto, si ha Sentence Case 13. (es.: "Stringa di esempio."). 14. Questo enumeratore indica questi casi 15. Enum StringCase 16. Lower 17. Upper 18. Sentence 19. Proper 20. End Enum 21. 22. Questa funzione converte una stringa in uno dei Case 23. disponibili, indicati dallenumeratore. Il secondo parametro 24. è specificato fra parentesi quadre solamente perchè 25. Case è una keyword, ma noi la vogliamo usare come 26. identificatore. 27. Function ToCase(ByVal Str As String, ByVal [Case] As StringCase) As String 28. Le funzioni per convertire in Lower e Upper 29. case sono già definite. E sufficiente 30. indicare un punto dopo il nome della variabile 31. stringa, seguito a ToLower e ToUpper 32. Select Case [Case] 33. Case StringCase.Lower 34. Return Str.ToLower() 35. Case StringCase.Upper 36. Return Str.ToUpper() 37. Case StringCase.Proper 38. Consideriamo la stringa come array di 39. caratteri: 40. Dim Chars() As Char = Str.ToLower() 41. Iteriamo lungo tutta la lunghezza della 42. stringa, dove Str.Length restituisce appunto 43. tale lunghezza 44. For I As Int32 = 0 To Str.Length - 1 45. Se questo carattere è uno spazio oppure 46. è il primo di tutta la stringa, il 47. prossimo indicherà linizio di una nuova 48.
  • parola e dovrà essere maiuscolo. 49. If I = 0 Then 50. Chars(I) = Char.ToUpper(Chars(I)) 51. End If 52. If Chars(I) = " " And I < Str.Length - 1 Then 53. Char.ToUpper rende maiuscolo un carattere 54. passato come parametro e lo restituisce 55. Chars(I + 1) = Char.ToUpper(Chars(I + 1)) 56. End If 57. Next 58. Restituisce larray modificato (un array di caratteri 59. e una stringa sono equivalenti) 60. Return Chars 61. Case StringCase.Sentence 62. Riduce tutta la stringa a Lower Case 63. Str = Str.ToLower() 64. Imposta il primo carattere come maiuscolo 65. Dim Chars() As Char = Str 66. Chars(0) = Char.ToUpper(Chars(0)) 67. Str = Chars 68. La chiude con un punto 69. Str = Str & "." 70. Return Str 71. End Select 72. End Function 73. 74. Sub Main() 75. Dim Str As String = "QuEstA è una stRingA DI prova" 76. 77. Per usare i valori di un enumeratore bisogna sempre scrivere 78. il nome dellenumeratore seguito dal punto 79. Console.WriteLine(ToCase(Str, StringCase.Lower)) 80. Console.WriteLine(ToCase(Str, StringCase.Upper)) 81. Console.WriteLine(ToCase(Str, StringCase.Proper)) 82. Console.WriteLine(ToCase(Str, StringCase.Sentence)) 83. 84. Console.ReadKey() 85. End Sub 86. End ModuleLenumer ator e Str ingCase offr e quattr o possibilità: Low er , Upper , Pr oper e Sentence. Chi usa la funzione è invitato asceglier e una fr a queste costanti, ed in questo modo non si r ischia di dimenticar e il significato di un codice. Notar e cheho scr itto "invitato", ma non "obbligato", poichè lEnumer ator e è soltanto un mezzo attr aver so il quale ilpr 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 dellenumer ator e viene associato un numer o (sempr einter 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 ovacome questo: 1. Console.WriteLine(StringCase.Lower) 2. Console.WriteLine(StringCase.Upper) 3. Console.WriteLine(StringCase.Sentence) 4. Console.WriteLine(StringCase.Proper)A scher mo appar ir à 1. 0 2. 1 3. 2 4. 3Come si vede, le costanti assegnate par tono da 0 per il pr imo campo e vengono incr ementate di 1 via via che sipr ocede a indicar e nuovi campi. È anche possibile deter minar e esplicitamente il valor e di ogni identificator e: 1. Enum StringCase 2. Lower = 5 3. Upper = 10 4. Sentence = 20 5.
  • Proper = 40 6. End EnumSe ad un nome non viene assegnato valor e, esso assumer à il valor e del suo pr ecedente, aumentato di 1: 1. Enum StringCase 2. Lower = 5 3. Upper = 6 4. Sentence = 20 5. Proper = 21 6. End EnumGli 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 dibase. È, infatti, per fettamente lecito usar e una costante numer ica al posto di un enumer ator e e vicever sa. Ecco unesempio lampante in cui utilizzo un enumer ator e indicante le note musicali da cui r icavo la fr equenza delle suddette: 01. Module Module1 02. Usa i nomi inglesi delle note. Lenumerazione inizia 03. da -9 poiché il Do centrale si trova 9 semitoni 04. sotto il La centrale 05. Enum Note 06. C = -9 07. CSharp 08. D 09. DSharp 10. E 11. F 12. FSharp 13. G 14. GSharp 15. A 16. ASharp 17. B 18. End Enum 19. 20. Restituisce la frequenza di una nota. N, in concreto, 21. rappresenta la differenza, in semitoni, di quella nota 22. dal La centrale. Ecco lutilittà degli enumeratori, 23. che danno un nome reale a ciò che un dato indica 24. indirettamente 25. Function GetFrequency(ByVal N As Note) As Single 26. Return 440 * 2 ^ (N / 12) 27. End Function 28. 29. Per ora prendete per buona questa funzione che restituisce 30. il nome della costante di un enumeratore a partire dal 31. suo valore. Avremo modo di approfondire nei capitoli 32. sulla Reflection 33. Function GetName(ByVal N As Note) As String 34. Return [Enum].GetName(GetType(Note), N) 35. End Function 36. 37. Sub Main() 38. Possiamo anche iterare usando gli enumeratori, poiché 39. si tratta pur sempre di semplici numeri 40. For I As Int32 = Note.C To Note.B 41. Console.WriteLine("La nota " & GetName(I) & _ 42. " risuona a una frequenza di " & GetFrequency(I) & "Hz") 43. Next 44. 45. Console.ReadKey() 46. End Sub 47. End ModuleÈ anche possibile specificar e il tipo di inter o di un enumer ator e (se Byte, Int16, Int32, Int64 o SByte, UInt16, UInt32,UInt64) apponendo dopo il nome la clausola As seguita dal tipo: 1. Enum StringCase As Byte 2. Lower = 5 3.
  • Upper = 10 4. Sentence = 20 5. Proper = 40 6. End EnumQuesta par ticolar ità si r ivela molto utile quando bisogna scr iver e enumer ator i su file in modalità binar ia. In questicasi, essi r appr esentano solitamente un campo detto Flags, di cui mi occuper ò nel pr ossimo par agr afo.Campi c odific ati a bit (Flags)Chi non conosca il codice binar io può legger e un ar ticolo su di esso nella sezione FFS.I campi codificati a bit sono enumer ator i che per mettono di immagazzinar e numer ose infor mazioni in pochissimospazio, anche in un solo byte! Di solito, tuttavia, si utilizzano tipi Int32 per chè si ha bisogno di un numer o maggior e diinfor mazioni. Il meccanismo è molto semplice. Ogni opzione deve poter assumer e due valor i, Ver o o Falso: questivengono quindi codificati da un solo bit (0 o 1), ad esempio: 1. 00001101Rappr esenta un inter o senza segno a un byte, ossia il tipo Byte: in esso si possono immagazzinar e 8 campi (uno perogni 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 tovalor 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 ioche ogni valor e dellenumer 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. Perunir e insieme più opzioni bisogna usar e loper ator e logico Or . Un esempio: 01. Module Module1 02. È convenzione che gli enumeratori che codificano a bit 03. abbiano un nome al plurale 04. Questo enumeratore definisce alcuni tipi di file 05. Public Enum FileAttributes As Byte 06. 1 = 2 ^ 0 07. In binario: 08. 00000001 09. Normal = 1 10. 11. 2 = 2 ^ 1 12. 00000010 13. Hidden = 2 14. 15. 4 = 2 ^ 2 16. 00000100 17. System = 4 18. 19. 8 = 2 ^ 3 20. 00001000 21. Archive = 8 22. End Enum 23. 24. Sub Main() 25. Dim F As FileAttributes 26. F allinizio è 0, non contiene niente: 27. 00000000 28. 29. F = FileAttributes.Normal 30. Ora F è 1, ossia Normal 31. 00000001 32. 33. F = FileAttributes.Hidden Or FileAttributes.System 34. La situazione diventa complessa: 35. Il primo valore è 2: 000000010 36. Il secondo valore è 4: 000000100 37. Abbiamo già visto loperatore Or: restituisce True se 38. almeno una delle condizioni è vera: qui True è 39. 1 e False è 0: 40.
  • 000000010 Or 41. 000000100 = 42. 000000110 43. Come si vede, ora ci sono due campi attivi: 4 e 2, che 44. corrispondono a Hidden e System. Abbiamo fuso insieme due 45. attributi con Or 46. 47. F = FileAttributes.Archive Or FileAttributes.System Or _ 48. FileAttributes.Hidden 49. La stessa cosa: 50. 00001000 Or 51. 00000100 Or 52. 00000010 = 53. 00001110 54. End Sub 55. End ModuleOr a sappiamo come immagazzinar e i campi, ma come si fa a legger li? Nel pr ocedimento inver so si una invece un And: 01. Module Module1 02. Sub Main() 03. Dim F As FileAttributes 04. 05. F = FileAttributes.Archive Or FileAttributes.System Or _ 06. FileAttributes.Hidden 07. 08. Ora F è 00001110 e bisogna eseguire unoperazione di And 09. sui bit, confrontando questo valore con Archive, che è 8. 10. And restituisce Vero solo quando entrambe le condizioni 11. sono vere: 12. 00001110 And 13. 00001000 = 14. 00001000, ossia Archive! 15. If F And FileAttributes.Archive = FileAttributes.Archive Then 16. Console.WriteLine("Il file è marcato come Archive") 17. End If 18. Console.ReadKey() 19. End Sub 20. End ModuleIn definitiva, per immagazzinar e più dati in poco spazio occor r e un enumer ator e contenente solo valor i che sonopotenze di due; con Or si uniscono più campi; con And si ver ifica che un campo sia attivo.
  • A17. Le StruttureNel 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 chesi possano sceglier e in un insieme finito di elementi. Le str uttur e, invece, appar tengono ad unaltr a categor ia.Anchesse 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 ealla logica dellapplicazione che si sta scr ivendo: in r ealtà, quello che per mettono di far e è una specie di "collage" divar iabili. Ad esempio, ammettiamo di voler scr iver e una r ubr ica, in gr ado di memor izzar e nome, cognome e numer odi telefono dei nostr i pr incipali amici e conoscenti. Ovviamente, dato che si tr atta di tan te per sone, avr emo bisogno diar r ay per contener e tutti i dati, ma in che modo li potr emmo immagazzinar e? Per quello che ho illustr ato fino aquesto 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 euno per i numer i telefonici. 1. Dim Names() As String 2. Dim Surnames() As String 3. Dim PhoneNumbers() As StringInutile dir e che seguendo questo appr occio il codice r isulter ebbe molto confusionar io e poco aggior nabile: se si volesseaggiunger 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 suointer no tutti i campi necessar i: 1. Structure Contact 2. Dim Name, Surname, PhoneNumber As String 3. End Structure 4. 5. ... 6. 7. Un array di conttati, ognuno rappresentato dalla struttura Contact 8. Dim Contacts() As ContactCome si vede dallesempio, la sintassi usata per dichiar ar e una str uttur a è la seguente: 1. Structure [Nome] 2. Dim [Campo1] As [Tipo] 3. Dim [Campo2] As [Tipo] 4. ... 5. End StructureUna 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? Siusa loper ator e punto ".", posto dopo il nome della var iabile: 01. Module Module1 02. Structure Contact 03. Dim Name, Surname, PhoneNumber As String 04. End Structure 05. 06. Sub Main() 07. Dim A As Contact 08. 09. A.Name = "Mario" 10. A.Surname = "Rossi" 11. A.PhoneNumber = "333 33 33 333" 12. End Sub 13. End Module[Ricor date che le dichiar azioni di nuovi tipi di dato (fino ad or a quelli che abbiamo analizzato sono enumer ator i e
  • str uttur e, e le classi solo come intr oduzione) possono esser e fatte solo a livello di classe o di namespace, e m ai dentr oad 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 moltoimpor tante, è un tipo value, quindi si compor ta esattamente come Integer , Shor t, Date, ecceter a... e vienememor izzata dir ettamente sullo stack, senza uso di puntator i.Ac robazie c on 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. Module Module1 02. Structure Contact 03. Dim Name, Surname, PhoneNumber As String 04. End Structure 05. 06. Sub Main() 07. Contacts(-1) inizializza un array vuoto, 08. ossia con 0 elementi 09. Dim Contacts(-1) As Contact 10. Dim Command As Char 11. 12. Do 13. Console.Clear() 14. Console.WriteLine("Rubrica -----") 15. Console.WriteLine("Selezionare lazione desiderata:") 16. Console.WriteLine("N - Nuovo contatto;") 17. Console.WriteLine("T - Trova contatto;") 18. Console.WriteLine("E - Esci.") 19. Command = Char.ToUpper(Console.ReadKey().KeyChar) 20. Console.Clear() 21. 22. Select Case Command 23. Case "N" 24. Usa ReDim Preserve per aumentare le dimensioni 25. dellarray mentenendo i dati già presenti. 26. Luso di array e di redim, in questo caso, è 27. sconsigliato, a favore delle più versatili 28. Liste, che però non ho ancora introdotto. 29. Ricordate che il valore specificato tra 30. parentesi indica lindice massimo e non 31. il numero di elementi. 32. Se, allinizio, Contacts.Length è 0, 33. richiamando ReDim Contacts(0), si aumenta 34. la lunghezza dellarray a uno, poiché 35. in questo caso lindice massimo è 0, 36. ossia quello che indica il primo e 37. lunico elemento 38. ReDim Preserve Contacts(Contacts.Length) 39. 40. Dim N As Contact 41. Console.Write("Nome: ") 42. N.Name = Console.ReadLine 43. Console.Write("Cognome: ") 44. N.Surname = Console.ReadLine 45. Console.Write("Numero di telefono: ") 46. N.PhoneNumber = Console.ReadLine 47. 48. Inserisce nellultima cella dellarray 49. lelemento appena creato 50. Contacts(Contacts.Length - 1) = N 51. 52. Case "T" 53. Dim Part As String 54. 55. Console.WriteLine("Inserire nome o cognome del " & _ 56. "contatto da trovare:") 57. Part = Console.ReadLine 58. 59.
  • For Each C As Contact In Contacts 60. Il confronto avviene in modalità 61. case-insensitive: sia il nome/cognome 62. che la stringa immessa vengono 63. ridotti a Lower Case, così da 64. ignorare la differenza tra 65. minuscole e maiuscole, qualora presente 66. If (C.Name.ToLower() = Part.ToLower()) Or _ 67. (C.Surname.ToLower() = Part.ToLower()) Then 68. Console.WriteLine("Nome: " & C.Name) 69. Console.WriteLine("Cognome: " & C.Surname) 70. Console.WriteLine("Numero di telefono: " & C.PhoneNumber) 71. Console.WriteLine() 72. End If 73. Next 74. 75. Case "E" 76. Exit Do 77. 78. Case Else 79. Console.WriteLine("Comando sconosciuto!") 80. End Select 81. Console.ReadKey() 82. Loop 83. End Sub 84. End ModuleOr a ammettiamo di voler modificar e il codice per per metter e linser imento di più numer i di telefono: 01. Module Module1 02. Structure Contact 03. Dim Name, Surname As String 04. Importante: NON è possibile specificare le dimensioni 05. di un array dentro la dichiarazione di una struttura. 06. Risulta chiaro il motivo se ci si pensa un attimo. 07. Noi stiamo dichiarando quali sono i campi della struttura 08. e quale è il loro tipo. Quindi specifichiamo che 09. PhoneNumbers è un array di stringhe, punto. Se scrivessimo 10. esplicitamente le sue dimensioni lo staremmo creando 11. fisicamente nella memoria, ma questa è una 12. dichiarazione, come detto prima, e non una 13. inizializzazione. Vedremo in seguito che questa 14. differenza è molto importante per i tipi reference 15. (ricordate, infatti, che gli array sono tipi reference). 16. Dim PhoneNumbers() As String 17. End Structure 18. 19. Sub Main() 20. Dim Contacts(-1) As Contact 21. Dim Command As Char 22. 23. Do 24. Console.Clear() 25. Console.WriteLine("Rubrica -----") 26. Console.WriteLine("Selezionare lazione desiderata:") 27. Console.WriteLine("N - Nuovo contatto;") 28. Console.WriteLine("T - Trova contatto;") 29. Console.WriteLine("E - Esci.") 30. Command = Char.ToUpper(Console.ReadKey().KeyChar) 31. Console.Clear() 32. 33. Select Case Command 34. Case "N" 35. ReDim Preserve Contacts(Contacts.Length) 36. 37. Dim N As Contact 38. Console.Write("Nome: ") 39. N.Name = Console.ReadLine 40. Console.Write("Cognome: ") 41. N.Surname = Console.ReadLine 42. 43. Ricordate che le dimensioni dellarray non 44.
  • sono ancora state impostate: 45. ReDim N.PhoneNumbers(-1) 46. 47. Continua a chiedere numeri di telefono finché 48. non si introduce più nulla 49. Do 50. ReDim Preserve N.PhoneNumbers(N.PhoneNumbers.Length) 51. Console.Write("Numero di telefono " & N.PhoneNumbers.Length & ": ") 52. N.PhoneNumbers(N.PhoneNumbers.Length - 1) = Console.ReadLine 53. Loop Until N.PhoneNumbers(N.PhoneNumbers.Length - 1) = "" 54. Ora lultimo elemento dellarray è sicuramente 55. vuoto, lo si dovrebbe togliere. 56. 57. Contacts(Contacts.Length - 1) = N 58. 59. Case "T" 60. Dim Part As String 61. 62. Console.WriteLine("Inserire nome o cognome del " & _ 63. "contatto da trovare:") 64. Part = Console.ReadLine 65. 66. For Each C As Contact In Contacts 67. If (C.Name.ToLower() = Part.ToLower()) Or _ 68. (C.Surname.ToLower() = Part.ToLower()) Then 69. Console.WriteLine("Nome: " & C.Name) 70. Console.WriteLine("Cognome: " & C.Surname) 71. Console.WriteLine("Numeri di telefono: ") 72. For Each N As String In C.PhoneNumbers 73. Console.WriteLine(" - " & N) 74. Next 75. Console.WriteLine() 76. End If 77. Next 78. 79. Case "E" 80. Exit Do 81. 82. Case Else 83. Console.WriteLine("Comando sconosciuto!") 84. End Select 85. Console.ReadKey() 86. Loop 87. End Sub 88. End ModuleIn 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: unastr uttur a for mata da campi di tipo base e una composta dagli stessi campi, con laggiunta di un tipo a sua voltader ivato, lar 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 tipobase, 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 diqualsiasi tipo, compr ese altr e str uttur e. Ad esempio, un contatto avr ebbe potuto anche contener e lindir izzo dir esidenza, il quale avr ebbe potuto esser e stato r appr esentato a sua volta da unulter ior e str uttur a: 01. Structure Address 02. Dim State, Town As String 03. Dim Street, CivicNumber As String 04. Dim Cap As String 05. End Structure 06. 07. Structure Contact 08. Dim Name, Surname As String 09. Dim PhoneNumbers() As String 10. Dim Home As Address 11. End StructurePer acceder e ai campi di Home si sar ebbe utilizzato un ulter ior e punto: 01. Dim A As Contact 02. 03.
  • A.Name = "Mario"04. A.Surname = "Rossi"05. ReDim A.PhoneNumbers(0)06. A.PhoneNumbers(0) = "124 90 87 111"07. A.Home.State = "Italy"08. A.Home.Town = "Pavia"09. A.Home.Street = "Corso Napoleone"10. A.Home.CivicNumber = "96/B"11. A.Home.Cap = "27010"
  • A18. Le ClassiBene bene. Eccoci ar r ivati al sugo della questione. Le classi, entità alla base di tutto ledificio del .NET. Già nei pr imicapitoli 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 (onon avesse voglia di lasciar e questa magnifica pagina per r itor nar e indietr o nei capitoli), una classe si dichiar asemplicemente così: 1. Class [Nome Classe] 2. ... 3. End ClassCon latto della dichiar azione, la classe inizia ad esister e allinter no del codice sor gente, cosicchè il pr ogr ammator e lapuò 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 epiù allusar e le classi nei pr ossimi pr ogr ammi, tuttavia, è dover oso r icor dar e ancor a una volta la sostanziale differ enzatr a dichiar azione e inizializzazione, tr a classe e oggetto, giusto per r infr escar e le memor ie più fr agili e, lungi dalfar vi odiar e questo concetto, per far e in modo che il messaggio penetr i: 01. Module Module1 02. Classe che rappresenta un cubo. 03. Segue la dichiarazione della classe. Da questo momento 04. in poi, potremo usare Cube come tipo per le nostre variabili. 05. Notare che una classe si dichiara e basta, non si 06. "inizializza", perchè non è qualcosa di concreto, 07. è unastrazione, cè, esiste in generale. 08. Class Cube 09. Variabile che contiene la lunghezza del lato 10. Dim SideLength As Single 11. Variabile che contiene la densità del cubo, e quindi 12. ci dice di che materiale è composto 13. Dim Density As Single 14. 15. Questa procedura imposta i valori del lato e 16. della densità 17. Sub SetData(ByVal SideLengthValue As Single, ByVal DensityValue As Single) 18. SideLength = SideLengthValue 19. Density = DensityValue 20. End Sub 21. 22. Questa funzione restituisce larea di una faccia 23. Function GetSurfaceArea() As Single 24. Return (SideLength ^ 2) 25. End Function 26. 27. Questa funzione restituisce il volume del cubo 28. Function GetVolume() As Single 29. Return (SideLength ^ 3) 30. End Function 31. 32. Questa funzione restituisce la massa del cubo 33. Function GetMass() As Single 34. Return (Density * GetVolume()) 35. End Function 36. End Class 37. 38. Sub Main() 39. Variabile di tipo Cube, che rappresenta uno specifico cubo 40. La riga di codice che segue contiene la dichiarazione 41. della variabile A. La dichiarazione di una variabile 42. fa sapere al compilatore, ad esempio, di che tipo 43. sarà, in quale blocco di codice sarà 44. visibile, ma nulla di più. 45. Non esiste ancora un oggetto Cube collegato ad A, ma 46. potrebbe essere creato in un immediato futuro. 47.
  • N.B.: quando si dichiara una variabile di tipo reference, 48. viene comunque allocata memoria sullo stack; viene 49. infatti creato un puntatore, che punta alloggetto 50. Nothing, il cui valore simbolico è stato 51. spiegato precedentemente. 52. Dim A As Cube 53. 54. Ed ecco limmediato futuro: con la prossima linea di 55. codice, creiamo loggetto di tipo Cube che verrà 56. posto nella variabile A. 57. A = New Cube 58. Quando New è seguito dal nome di una classe, si crea un 59. oggetto di quel tipo. Nella fattispecie, in questo momento 60. il programma si preoccuperà di richiedere della 61. memoria sullheap managed per allocare i dati relativi 62. alloggetto e di creare un puntatore sullo stack che 63. punti a tale oggetto. Esso, inoltre, eseguirà 64. il codice contenuto nel costruttore. New, infatti, 65. è uno speciale tipo di procedura, detta 66. Costruttore, di cui parlerò approfonditamente 67. in seguito 68. 69. Come per le strutture, i membri di classe sono accessibili 70. tramite loperatore punto ".". Ora imposto le variabili 71. contenute in A per rappresentare un cubo di alluminio 72. (densità 2700 Kg/m<sup>3</sup>) di 1.5m di lato 73. A.SetData(1.5, 2700) 74. 75. Console.WriteLine("Superficie faccia: " & A.GetSurfaceArea() & " m2") 76. Console.WriteLine("Volume: " & A.GetVolume() & " m3") 77. Console.WriteLine("Massa: " & A.GetMass() & " Kg") 78. Its Over 9000!!!! 79. 80. Console.ReadKey() 81. End Sub 82. End ModuleIn questo esempio ho usato una semplice classe che r appr esenta un cubo di una cer ta dimensione e di un cer tomater iale. Tale classe espone quattr o funzioni, che ser vono per ottener e infor mazioni o impostar e valor i. Cè unpr eciso motivo per cui non ho usato dir ettamente le due var iabili accedendovi con loper ator e punto, e lo spiegher ò abr eve nella pr ossima lezione. Quindi, tali funzioni sono membr i di classe e, sopr attutto, funzioni di istanza. Questolemma 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 edusate solo da un oggetto. Per far vi capir e, non si possono invocar e con questa sintassi: 1. Cube.GetVolume()ma solo passando attr aver so unistanza: 1. Dim B As New Cube 2. ... 3. B.GetVolume()E questo, tr a laltr o, è abbastanza banale: infatti, come sar ebbe possibile calcolar e ar ea, volume e massa se non sidisponesse della misur a della lunghezza del lato e quella della densità? È ovvio che ogni cubo ha le sue pr opr ie misur e, eil concetto gener ale di "cubo" non ci dice niente su queste infor mazioni.Un semplic e c ostruttoreAnche se entr er emo nel dettaglio solo più in là, è necessar io per i pr ossimi esempi che sappiate come funziona uncostr uttor e, anche molto semplice. Esso viene dichiar ato come una nor male pr ocedur a, ma si deve sempr e usar e comenome "New ": 1.
  • Sub New([parametri]) 2. codice 3. End SubQualor a non si specificasse nessun costr uttor e, il compilator e ne cr eer à uno nuovo senza par ametr i, che equivale alseguente: 1. Sub New() 2. End SubIl codice pr esente nel cor po del costr uttor e viene eseguito in una delle pr ime fasi della cr eazione delloggetto, appenadopo che questo è statao fisicamente collocato nella memor ia (ma, badate bene, non è la pr ima istr uzione ad esser eeseguita dopo la cr eazione). Lo scopo di tale codice consiste nellinizializzar e var iabili di tipo r efer ence pr ima solodichiar ate, attr ibuir e valor i alle var iabili value, eseguir e oper azioni di pr epar azione alluso di r isor se ester ne,ecceter a... Insomma, ser ve a spianar e la str ada alluso della classe. In questo caso, luso che ne far emo è molto r idotto e,non vor r ei dir lo, quasi mar ginale, ma è lunico compito possibile e utile in questo contesto: dar emo al costr uttor e ilcompito di inizializzar e SideLength e Density. 01. Module Module1 02. Class Cube 03. Dim SideLength As Single 04. Dim Density As Single 05. 06. Quasi uguale a SetData 07. Sub New(ByVal SideLengthValue As Single, ByVal DensityValue As Single) 08. SideLength = SideLengthValue 09. Density = DensityValue 10. End Sub 11. 12. Function GetSurfaceArea() As Single 13. Return (SideLength ^ 2) 14. End Function 15. 16. Function GetVolume() As Single 17. Return (SideLength ^ 3) 18. End Function 19. 20. Function GetMass() As Single 21. Return (Density * GetVolume()) 22. End Function 23. End Class 24. 25. Sub Main() 26. Questa è una sintassi più concisa che equivale a: 27. Dim A As Cube 28. A = New Cube(2700, 1.5) 29. Tra parentesi vanno passati i parametri richiesti dal 30. costruttore 31. Dim A As New Cube(2700, 1.5) 32. 33. Console.WriteLine("Superficie faccia: " & A.GetSurfaceArea() & " m<sup>2</sup>") 34. Console.WriteLine("Volume: " & A.GetVolume() & " m<sup>3</sup>") 35. Console.WriteLine("Massa: " & A.GetMass() & " Kg") 36. 37. Console.ReadKey() 38. End Sub 39. End ModuleUna 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, possonoespor r e anche costr uttor i... e questo dovr ebbe appar ir vi str ano. Infatti, ho appena illustr ato limpor tanza deicostr uttor i nellistanziar e oggetti, quindi tipi r efer ence, mentr e le str uttur e sono palesemente tipi value. Il conflitto si
  • r isolve con una soluzione molto semplice: i costr uttor i dichiar ati nelle str uttur e possono esser e usati esattamentecome 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 ditipo str uttur ato viene cr eata sullo stack allatto della sua dichiar azione. 01. Module Module1 02. Structure Cube 03. Dim SideLength As Single 04. Dim Density As Single 05. 06. Sub New(ByVal SideLengthValue As Single, ByVal DensityValue As Single) 07. SideLength = SideLengthValue 08. Density = DensityValue 09. End Sub 10. 11. Function GetSurfaceArea() As Single 12. Return (SideLength ^ 2) 13. End Function 14. 15. Function GetVolume() As Single 16. Return (SideLength ^ 3) 17. End Function 18. 19. Function GetMass() As Single 20. Return (Density * GetVolume()) 21. End Function 22. End Structure 23. 24. Sub Main() 25. Questo codice 26. Dim A As New Cube(2700, 1.5) 27. 28. Equivale a questo 29. Dim B As Cube 30. B.SideLength = 1.5 31. B.Density = 2700 32. 33. A e B sono uguali 34. 35. Console.ReadKey() 36. End Sub 37. End Module
  • A19. Le Classi - Specificatori di accessoLe classi possono posseder e molti membr i, di svar iate categor ie, e ognuno di questi è sempr e contr addistinto da unliv ello di accesso . Esso specifica "chi" può acceder e a quali membr i, e da quale par te del codice. Molto spesso, infatti,è necessar io pr ecluder e laccesso a cer te par ti del codice da par te di fr uitor i ester ni: fate bene attenzione, non stopar lando di pr otezione del codice, di sicur ezza, intendiamoci bene; mi sto r ifer endo, invece, a chi user à il nostr ocodice (fossimo anche noi stessi). I motivi sono dispar ati, ma molto spesso si vuole evitar e che vengano modificatevar iabili che ser vono per calcoli, oper azioni su file, r isor se, ecceter a. Al contr ar io, è anche possibile espander elaccesso ad un membr o a chiunque. Con questi due esempi intr oduttivi, apr iamo la str ada agli specificato r i diaccesso , 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 allinter no della classe in cui è stato dichiar ato; Public: un membr o pubblico è accessibile da qualsiasi par te del codice (dalla stessa classe, dalle sottoclassi, da classi ester ne, per fino da pr ogr ammi ester ni); Fr iend Pr otected Pr otected Fr iend (esiste solo in VB.NET)Un esempio pratic oRipr endiamo il codice della classe Cube r ipr oposto nel capitolo pr ecedente. Pr oviamo a scr iver e nella Sub Main questocodice: 1. Sub Main() 2. Dim A As New Cube(2700, 1.5) 3. A.SideLength = 3 4. End SubLa r iga "A.SideLength = 3" ver r à sottolineata e appar ir à il seguente er r or e nel log degli er r or i: 1. ConsoleApplication2.Module1.Cube.SideLength is not accessible in this 2. context because it is Private.Questo è il motivo per cui ho usato una pr ocedur a per impostar e i valor i: laccesso 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 statocosì: 01. Module Module1 02. Class Cube 03. Quando gli specificatori di accesso sono anteposti alla 04. dichiarazione di una variabile, si toglie il "Dim" 05. Private SideLength As Single 06. Private Density As Single 07. 08. Sub New(ByVal SideLengthValue As Single, ByVal DensityValue As Single) 09. SideLength = SideLengthValue 10. Density = DensityValue 11. End Sub 12. 13. Function GetSurfaceArea() As Single 14. Return (SideLength ^ 2) 15. End Function 16.
  • 17. Function GetVolume() As Single 18. Return (SideLength ^ 3) 19. End Function 20. 21. Function GetMass() As Single 22. Return (Density * GetVolume()) 23. End Function 24. End Class 25. ... 26. End ModuleIn questo specifico caso, sar ebbe stato meglio impostar e tali var iabili come Public, poiché nel lor o scope (= livello diaccesso) attuale non ser vono a molto e, anzi, r ichiedono molto più codice di gestione. Ma immaginate una classe checompia oper azioni cr ittogr afiche sui dati che gli sono passati in input, usando var iabili distanza per i suoi calcoli: setali var iabili fosser o accessibili al di fuor i della classe, lo sviluppator e che non sapesse esattamente cosa far ci potr ebbecompr ometter e ser iamente il r isultato di tali oper azioni, e quindi danneggiar e i pr otocolli di sicur ezza usatidallapplicazione. Etichettar e un membr o come pr ivate equivar r ebbe scher zosamente a por vi sopr a un gr ande car tellocon 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 devonoesser 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 stascr ivendo una libr er ia che sar à usata successivamente da altr e per sone) sono dichiar ati come Public, ossia sempr eaccessibili, 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 nonvengono esplicitamente mar cati dal pr ogr ammator e con una delle keyw or d sopr a elencate hanno uno scopepr edefinito: si tr atta di Fr iend. Esso ha un compito par ticolar e che potr ete capir e meglio quando affr onter emo lascr ittur a di una libr er ia di classi: per or a vi baster à saper e che, allinter no di uno stesso pr ogetto, equivale a Public.Unaltr a cosa impor tante: anche le classi (e i moduli) sono contr addistinte da un livello di accesso, che segueesattamente le stesse r egole sopr a esposte. Ecco un esempio: 01. Public Class Classe1 02. Private Class Classe2 03. ... 04. End Class 05. 06. Class Classe3 07. ... 08. End Class 09. End Class 10. 11. Class Classe4 12. Public Class Classe5 13. Private Class Classe6 14. ... 15.
  • End Class 16. End Class 17. End Class 18. 19. Module Module1 20. Sub Main() 21. ... 22. End Sub 23. End ModuleIl 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 Classe4mentr e non può acceder e a: Classe2, per chè è Pr ivate Classe6, per chè è Pr ivatedaltr 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 allinter no di unaltr a classe (altr imenti non sar ebbemai accessibile, e quindi inutile).Spec ific atori di ac c esso 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, adesempio: 1. Structure Contact 2. Dim Name, Surname, PhoneNumber As String 3. End Structureche equivale a: 1. Structure Contact 2. Public Name, Surname, PhoneNumber As String 3. End StructureOvviamente, 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 laccesso ai membr i da par te delcodice di Main (e quindi da chi usa la classe, poiché lutente finale può anche esser e un altr o pr ogr ammator e). Oltr e aquesto tr over ete anche un esempio di un diffuso e semplice algor itmo di or dinamento, 2 in 1! 001. Module Module1 002. Dato che usiamo la classe solo in questo programma, possiamo 003. evitare di dichiararla Public, cosa che sarebbe ideale in 004. una libreria 005. Class BubbleSorter 006. Enumeratore pubblico: sarà accessibile da tutti. In questo 007. caso è impossibile dichiararlo come Private, poiché 008. uno dei prossimi metodi richiede come parametro una 009.
  • variabile di tipo SortOrder e se questo fosse private,010. non si potrebbe usare al di fuori della classe, cosa011. che invece viene richiesta.012. Public Enum SortOrder013. Ascending Crescente014. Descending Decrescente015. None Nessun ordinamento016. End Enum017.018. Mantiene in memoria il senso di ordinamento della lista,019. per evitare di riordinarla nel caso fosse richiesto due020. volte lo stesso021. Private CurrentOrder As SortOrder = SortOrder.None022. Mantiene in memoria una copia dellarray, che è023. accessibile ai soli membri della classe. In024. questo modo, è possibile eseguire tutte025. le operazioni di ordinamento usando un solo metodo026. per linserimento dellarray027. Private Buffer() As Double028.029. Memorizza in Buffer larray passato come parametro030. Public Sub PushArray(ByVal Array() As Double)031. Se Buffer è diverso da Nothing, lo imposta032. esplicitamente a Nothing (equivale a distruggere033. loggetto)034. If Buffer IsNot Nothing Then035. Buffer = Nothing036. End If037. Copia larray: ricordate come si comportano i tipi038. reference e pensate a quali ripercussioni tale039. comportamento potrà avere sul codice040. Buffer = Array041. Annulla CurrentOrder042. CurrentOrder = SortOrder.None043. End Sub044.045. Procedura che ordina larray secondo il senso specificato046. Public Sub Sort(ByVal Order As SortOrder)047. Se il senso è None, oppure è uguale a quello corrente,048. è inutile proseguire, quindi si ferma ed esce049. If (Order = SortOrder.None) Or (Order = CurrentOrder) Then050. Exit Sub051. End If052.053. Questa variabile tiene conto di tutti gli scambi054. effettuati055. Dim Occurrences As Int32 = 0056.057. Il ciclo seguente ordina larray in senso crescente:058. se lelemento i è maggiore dellelemento i+1,059. ne inverte il posto, e aumenta il contatore di 1.060. Quando il contatore rimane 0 anche dopo il For,061. significa che non cè stato nessuno scambio062. e quindi larray è ordinato.063. Do064. Occurrences = 0065. For I As Int32 = 0 To Buffer.Length - 2066. If Buffer(I) > Buffer(I + 1) Then067. Dim Temp As Double = Buffer(I)068. Buffer(I) = Buffer(I + 1)069. Buffer(I + 1) = Temp070. Occurrences += 1071. End If072. Next073. Loop Until Occurrences = 0074.075. Se lordine era discendente, inverte larray076. If Order = SortOrder.Descending Then077. Array.Reverse(Buffer)078. End If079.080. Memorizza lordine081.
  • CurrentOrder = Order 082. End Sub 083. 084. Restituisce larray ordinato 085. Public Function PopArray() As Double() 086. Return Buffer 087. End Function 088. End Class 089. 090. Sub Main() 091. Crea un array temporaneo 092. Dim a As Double() = {1, 6, 2, 9, 3, 4, 8} 093. Crea un nuovo oggetto BubbleSorter 094. Dim b As New BubbleSorter() 095. 096. Vi inserisce larray 097. b.PushArray(a) 098. Invoca la procedura di ordinamento 099. b.Sort(BubbleSorter.SortOrder.Descending) 100. 101. E per ogni elemento presente nellarray finale 102. (quello restituito dalla funzione PopArray), ne stampa 103. il valore a schermo 104. For Each n As Double In (b.PopArray()) 105. Console.Write(n & " ") 106. Next 107. 108. Console.ReadKey() 109. End Sub 110. End ModuleRic apitolando...Ricapitolando, quindi, davanti a ogni membr o si può specificar e una keyw or d tr a Pr ivate, Public e Fr iend (per quelloche abbiamo visto in questo capitolo), che ne limita laccesso. Nel caso non si specifichi nulla, lo specificator e pr edefinitovar ia a seconda dellentità a cui è stato applicato, secondo questa tabella: Pr ivate per var iabili contenute in una classe Public per var iabili contenute in una str uttur a Fr iend per tutte le altr e entità
  • A20. Le Proprietà - Parte ILe 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 uoloconsiste nel mediar e linter azione tr a codice ester no alla classe e campo di una classe. Esse si "avvolgono" intor no a uncampo (per questo motivo vengono anche chiamate w r apper , dallinglese w r ap = impacchettar e) e decidono, tr amitecodice scr itto dal pr ogr ammator e, quali valor i siano leciti per quel campo e quali no - stile buttafuor i, per intender ci.La sintassi con cui si dichiar a una pr opr ietà è la seguente: 01. Property [Nome]() As [Tipo] 02. Get 03. ... 04. Return [Valore restituito] 05. End Get 06. Set(ByVal value As [Tipo]) 07. ... 08. End Set 09. End PropertyOr a, questa sintassi, nel suo insieme, è molto diver sa da tutto ciò che abbiamo visto fino ad or a. Tuttavia, guar dandobene, 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 dovesi pr ende il valor e che essa assume? Come ho già r ipetuto, una pr opr ietà media linter azione tr a codice ester no ecampo di una classe: quindi dobbiamo stabilir e un modo per collegar e la pr opr ietà al campo che ci inter essa. Ecco unesempio: 01. Module Module1 02. Class Example 03. Campo pubblico di tipo Single. 04. Public _Number As Single 05. 06. La proprietà Number media, in questo caso, luso 07. del campo _Number. 08. Public Property Number() As Single 09. Get 10. Quando viene chiesto il valore di Number, viene 11. restituito il valore della variabile _Number. Si 12. vede che la proprietà non fa altro che manipolare 13. una variabile esistente e non contiene alcun 14. dato di per sé 15. Return _Number 16. End Get 17. Set(ByVal value As Single) 18. Quando alla proprietà viene assegnato un valore, 19. essa modifica il contenuto di _Number impostandolo 20. esattamente su quel valore 21. _Number = value 22. End Set 23. End Property 24. End Class 25.
  • 26. Sub Main() 27. Dim A As New Example() 28. 29. Il codice di Main sta impostando il valore di A.Number. 30. Notare che una proprietà si usa esattamente come una 31. comunissima variabile di istanza. 32. La proprietà, quindi, richiama il suo blocco Set come 33. una procedura e assegna il valore 20 al campo A._Number 34. A.Number = 20 35. 36. Nella prossima riga, invece, viene richiesto il valore 37. di Number per poterlo scrivere a schermo. La proprietà 38. esegue il blocco Get come una funzione e restituisce al 39. chiamante (ossia il metodo/oggetto che ha invocato Get, 40. in questo caso Console.WriteLine) il valore di A._Number 41. Console.WriteLine(A.Number) 42. 43. Per gli scettici, facciamo un controllo per vedere se 44. effettivamente il contenuto di A._Number è cambiato. 45. Potrete constatare che è uguale a 20. 46. Console.WriteLine(A._Number) 47. 48. Console.ReadLine() 49. End Sub 50. End ModulePer 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 lappunto 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 lunder scor e iniziale, come in questo esempio. Il tipo definito per la pr opr ietà è identico a quello usato per il campo. Abbastanza ovvio, daltr onde: se essa deve mediar e luso di una var iabile, allor a anche tutti i valor i r icevuti e r estituiti dovr anno esser e compatibili.La potenza nasc osta delle proprietàAr r ivati a questo punto, uno potr ebbe pensar e che, dopotutto, non vale la pena di spr ecar e spazio per scr iver e unapr opr ietà quando può acceder e dir ettamente al campo. Bene, se cè ver amente qualcuno che leggendo quello che hoscr itto ha pensato ver amente a questo, può anche andar e a compianger si in un angolino buio. XD Scher zi a par te,lutilità cè, ma spesso non si vede. Pr ima di tutto, iniziamo col dir e che se un campo è mediato da una pr opr ietà, perconvenzione (ma anche per buon senso), deve esser e Pr ivate, altr imenti lo si potr ebbe usar e indiscr iminatamentesenza limitazioni, il che è pr opr io quello che noi vogliamo impedir e. A questo possiamo anche aggiunger e unaconsider azione: visto che abbiamo la possibilità di far lo, aggiungendo del codice a Get e Set, per chè non far e qualchecontr 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 lanostr a bella classe: 01. Module Module1 02. Questa classe rappresenta un semplice sistema inerziale, 03. formato da un piano orizzontale scabro (con attrito) e 04. una massa libera di muoversi su di esso 05. Class InertialFrame 06. Private _DynamicFrictionCoefficient As Single 07. Private _Mass As Single 08. Private _GravityAcceleration As Single 09. 10. Coefficiente di attrito radente (dinamico), μ 11. Public Property DynamicFrictionCoefficient() As Single 12. Get 13.
  • Return _DynamicFrictionCoefficient 14. End Get 15. Set(ByVal value As Single) 16. _DynamicFrictionCoefficient = value 17. End Set 18. End Property 19. 20. Massa, m 21. Public Property Mass() As Single 22. Get 23. Return _Mass 24. End Get 25. Set(ByVal value As Single) 26. _Mass = value 27. End Set 28. End Property 29. 30. Accelerazione di gravità che vale nel sistema, g 31. Public Property GravityAcceleration() As Single 32. Get 33. Return _GravityAcceleration 34. End Get 35. Set(ByVal value As Single) 36. _GravityAcceleration = value 37. End Set 38. End Property 39. 40. Calcola e restituisce la forza di attrito che agisce 41. quando la massa è in moto 42. Public Function CalculateFrictionForce() As Single 43. Return (Mass * GravityAcceleration) * DynamicFrictionCoefficient 44. End Function 45. 46. End Class 47. 48. Sub Main() 49. Dim F As New InertialFrame() 50. 51. Console.WriteLine("Sistema inerziale formato da:") 52. Console.WriteLine(" - Un piano orizzontale e scabro;") 53. Console.WriteLine(" - Una massa variabile.") 54. Console.WriteLine() 55. 56. Console.WriteLine("Inserire i dati:") 57. Console.Write("Coefficiente di attrito dinamico = ") 58. F.DynamicFrictionCoefficient = Console.ReadLine 59. Console.Write("Massa (Kg) = ") 60. F.Mass = Console.ReadLine 61. Console.Write("Accelerazione di gravità (m/s<sup>2</sup>) = ") 62. F.GravityAcceleration = Console.ReadLine 63. 64. Console.WriteLine() 65. Console.Write("Attrito dinamico = ") 66. Console.WriteLine(F.CalculateFrictionForce() & " N") 67. 68. Console.ReadLine() 69. End Sub 70. End ModuleI 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 ovail modo di metter e μ = 2 e m = -7, valor i assur di poiché 0 < μ <= 1 ed m > 0. Modificando il codice delle pr opr ietàpossiamo impor r e questi vincoli ai valor i inser ibili: 01. Module Module1 02. Class InertialFrame 03. Private _DynamicFrictionCoefficient As Single 04. Private _Mass As Single 05. Private _GravityAcceleration As Single 06. 07. Public Property DynamicFrictionCoefficient() As Single 08. Get 09.
  • Return _DynamicFrictionCoefficient 10. End Get 11. Set(ByVal value As Single) 12. If (value > 0) And (value <= 1) Then 13. _DynamicFrictionCoefficient = value 14. Else 15. Console.WriteLine(value & " non è un valore consentito!") 16. Console.WriteLine("Coefficiente attrito dinamico = 0.1") 17. _DynamicFrictionCoefficient = 0.1 18. End If 19. End Set 20. End Property 21. 22. Public Property Mass() As Single 23. Get 24. Return _Mass 25. End Get 26. Set(ByVal value As Single) 27. If value > 0 Then 28. _Mass = value 29. Else 30. Console.WriteLine(value & " non è un valore consentito!") 31. Console.WriteLine("Massa = 1") 32. _Mass = 1 33. End If 34. End Set 35. End Property 36. 37. Public Property GravityAcceleration() As Single 38. Get 39. Return _GravityAcceleration 40. End Get 41. Set(ByVal value As Single) 42. _GravityAcceleration = Math.Abs(value) 43. End Set 44. End Property 45. 46. Public Function CalculateFrictionForce() As Single 47. Return (Mass * GravityAcceleration) * DynamicFrictionCoefficient 48. End Function 49. 50. End Class 51. 52. ... 53. End ModuleIn 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 uneccezione.La soluzione for malmente più cor r etta sar ebbe la seconda: il codice chiamante dovr ebbe poi cattur ar e e gestir e taleeccezione, lasciando allutente la possibilità di decider e cosa far e. Tuttavia, per far vi fr onte, bisogner ebbe intr odur r eancor 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 allutente ler r or e nel cor po della pr opr ietà e, più ingener ale, nella classe stessa, poiché questo codice potr ebbe esser e r iutilizzato in unaltr a applicazione che magar i nonusa 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 luso delle pr opr ietà offr a molta più gestibilità e flessibilità di un semplice campo. E non èancor a finita...Curiosità: dietro le quinte di una proprietàN.B.: Potete anche pr oceder e a legger e il pr ossimo capitolo, poiché questo par agr afo è pur amente illustr ativo.
  • Come esempio user ò questa pr opr ietà: 01. Property Number() As Single 02. Get 03. Return _Number 04. End Get 05. Set(ByVal value As Single) 06. If (value > 30) And (value < 100) Then 07. _Number = value 08. Else 09. _Number = 31 10. End If 11. End Set 12. End PropertyQuando una pr opr ietà viene dichiar ata, ci sembr a che essa esista come unentità unica nel codice, ed è più o menover o. Tuttavia, una volta che il sor gente passa nelle fauci del compilator e, succede una cosa abbastanza singolar e. Lapr opr ietà cessa di esister e e viene invece spezzata in due elementi distinti: Una funzione senza par ametr i, di nome "get_[Nome Pr opr ietà]", il cui cor po viene cr eato copiando il codice contenuto nel blocco Get. Nel nostr o caso, get_Number : 1. Function get_Number() As Single 2. Return _Number 3. End Function Una pr ocedur a con un par ametr o, di nome "set_[Nome Pr opr ietà]", il cui cor po viene cr eato copiando il codice contenuto nel blocco Set. Nel nostr o caso, set_Number : 1. Sub set_Number(ByVal value As Single) 2. If (value > 30) And (value < 100) Then 3. _Number = value 4. Else 5. _Number = 31 6. End If 7. End SubEntr ambi i metodi hanno come specificator e di accesso lo stesso della pr opr ietà. Inoltr e, ogni r iga di codice del tipo 1. [Proprietà] = [Valore]oppur e 1. [Valore] = [Proprietà]viene sostituita con la cor r ispondente r iga: 1. set_[Nome Proprietà]([Valore])oppur e: 1. [Valore] = get_[Nome Proprietà]Ad esempio, il seguente codice: 1. Dim A As New Example 2. A.Number = 20 3. Console.WriteLine(A.Number)viene tr asfor mato, dur ante la compilazione, in: 1. Dim A As New Example 2. A.set_Number(20) 3. Console.WriteLine(A.get_Number())
  • Questo per dir e che una pr opr ietà è un costr utto di alto livello, uno str umento usato nella pr ogr ammazione astr atta:esso viene scomposto nelle sue par ti fondamentali quando il pr ogr amma passa al livello medio, ossia quando è tr adottoin IL, lo pseudo-linguaggio macchina del Fr amew or k .NET.
  • A21. Le Proprietà - Parte IIProprietà ReadOnly e W riteOnlyFinor a abbiamo visto che le pr opr ietà sono in gr ado di mediar e linter azione tr a codice ester no alla classe e suoicampi, e tale mediazione compr endeva la possibilità di r ifiutar e cer ti valor i e consentir ne altr i. Ma non è finita qui:usando delle apposite keyw or ds è possibile r ender e una pr opr ietà a sola lettur a (ossia è possibile legger ne il valor e manon modificar lo) o a sola scr ittur a (ossia è possibile modificar ne il valor e ma non ottener lo). Per quanto r iguar da lapr ima, viene abbastanza natur ale pensar e che ci possano esser e valor i solo esposti ver so cui è pr oibita lamanipolazione dir etta, magar i per ché par ticolar mente impor tanti o, più spesso, per chè logicamente immutabili (vedioltr e per un esempio); spostando lattenzione per un attimo sulla seconda, per ò, sar à par imenti del tutto lecitodomandar si quale sia la lor o utilità. Le var iabili, i campi, e quindi, per estensione, anche le pr opr ietà, sono per lor onatur a atti a contener e dati, che ver r anno poi utilizzati in altr e par ti del pr ogr amma: tali dati vengonocontinuamente letti e/o modificati e, per quanto sia possibile cr eder e che ve ne siano di immodificabili, come costantie valor i a sola lettur a, appar e invece assur da lesistenza di campi solo modificabili. Per modificar e qualcosa, infatti, sene deve conoscer e almeno qualche infor mazione. La r ealtà è che le pr opr ietà Wr iteOnly sono innatur ali per lastr agr ande maggior andza dei pr ogr ammator i; piuttosto di usar le è meglio definir e pr ocedur e. Mi occuper ò quindi ditr attar e solo la keyw or d ReadOnly.In br eve, la sintassi di una pr opr ietà a sola lettur a è questa: 1. ReadOnly Property [Nome]() As [Tipo] 2. Get 3. ... 4. Return [Valore] 5. End Get 6. End PropertyNotate che il blocco Set è assente: ovviamente, si tr atta di codice inutile dato che la pr opr ietà non può esser emodificata. Per continuar e il discor so iniziato pr ima, ci sono pr incipalmente tr e motivi per dichiar ar e unentità delgener 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 linser 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 uninfor mazione in più nella classe, ma questa viene solo desunta o inter polata da altr i dati noti. Esempio: 01. Class Cube 02. Private _SideLength As Single 03. Private _Density As Single 04. 05. Public Property SideLength() As Single 06.
  • Get 07. Return _SideLength 08. End Get 09. Set(ByVal value As Single) 10. If value > 0 Then 11. _SideLength = value 12. Else 13. _SideLength = 1 14. End If 15. End Set 16. End Property 17. 18. Public Property Density() As Single 19. Get 20. Return _Density 21. End Get 22. Set(ByVal value As Single) 23. If value > 0 Then 24. _Density = value 25. Else 26. _Density = 1 27. End If 28. End Set 29. End Property 30. 31. Public ReadOnly Property SurfaceArea() As Single 32. Get 33. Return (SideLength ^ 2) 34. End Get 35. End Property 36. 37. Public ReadOnly Property Volume() As Single 38. Get 39. Return (SideLength ^ 3) 40. End Get 41. End Property 42. 43. Public ReadOnly Property Mass() As Single 44. Get 45. Return (Volume * Density) 46. End Get 47. End Property 48. End Class Vedendola dallester no, si può pensar e che la classe Cube contenga come dati concr eti (var iabili) SideLength, Density, Sur faceAr ea, Volume e Mass, e che questi siano esposti tr amite una pr opr ietà. In r ealtà essa ne contiene solo i pr imi due e in base a questi calcola gli altr i.In questo esempio teor ico, le due pr opr ietà esposte sono r eadonly per il pr imo e il secondo motivo: 01. Module Esempio3 02. Class LogFile 03. Private _FileName As String 04. Private _CreationTime As Date 05. 06. Niente deve modificare il nome del file, altrimenti 07. potrebbero verificarsi errori nella lettura o scrittura 08. dello stesso, oppure si potrebbe chiudere un file 09. che non esiste ancora 10. Public ReadOnly Property FileName() As String 11. Get 12. Return _FileName 13. End Get 14. End Property 15. 16. Allo stesso modo non si pu� modificare la data di 17. creazione di un file: una volta creato, viene 18. prelevata lora e il giorno e impostata la 19. variabile. Se potesse essere modificata 20. non avrebbe più alcun significato 21.
  • Public ReadOnly Property CreationTime() As Date 22. Get 23. Return _CreationTime 24. End Get 25. End Property 26. 27. Public Sub New(ByVal Path As String) 28. _FileName = Path 29. _CreationTime = Date.Now 30. End Sub 31. End Class 32. End ModuleUna nota sui tipi referenc eCè ancor a unultima, ma impor tante, clausola da far notar e per le pr opr ietà ReadOnly. Si è gi� vista la differ enza tr ai tipi value e i tipi r efer ence: i pr imi contengono un valor e, mentr e i secondi un puntator e allar ea di memor ia in cuir isiede loggetto 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 lindir izzo, il che equivale ad ottener e il valor e delloggetto puntato. Non è quindiassolutamente sbagliato scr iver e: 01. Class ASystem 02. Private _Box As Cube 03. 04. Public ReadOnly Property Box() As Cube 05. Get 06. Return _Box 07. End Get 08. End Property 09. 10. ... 11. End Class 12. 13. ... 14. 15. Dim S As New ASystem() 16. S.Box.SideLength = 4In questo modo, noi staimo effettivamente modificando loggetto 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 stiamoas s egn an do un nuovo oggetto alla var iabile S._Box , ma stiamo solo manipolando i dati di un oggetto esistente, equesto è consentito. Anzi, è molto meglio dichiar ar e pr opr ietà di tipo r efer ence come ReadOnly quando non ènecessar io assegnar e o impostar e nuovi oggetti.
  • A22. Le Proprietà - Parte IIIProprietà c on parametriNei due capitoli pr ecedenti, ho sempr e scr itto pr opr ietà che semplicemente r estituivano il valor e di un campo, ossia ilcodice del blocco Get non er a nulla di più di un semplice Retur n. Intr oduciamo or a, invece, la possibilità di ottener einfor 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 cer a una coppia di par entesi tonde vicino al nome della pr opr ietà, ossia pr opr io la sintassiche si usa per dichiar ar e metodi senza par ametr i. Ecco un esempio: 01. Module Module1 02. Classe che rappresenta un estrattore di numeri 03. casuali 04. Class NumberExtractor 05. Private _ExtractedNumbers() As Byte 06. Generatore di numeri casuali. Random è una classe 07. del namespace System 08. Private Rnd As Random 09. 10. Questa proprietà ha un parametro, Index, che 11. specifica a quale posizione dellarray si intende recarsi 12. per prelevarne il valore. Nonostante larray abbia solo 6 13. elementi di tipo Byte, lindice viene comunemente sempre 14. indicato come intero a 32 bit. È una specie di 15. convenzione, forse derivante dalla maggior facilità di 16. elaborazione su macchine a 32 bit 17. Public ReadOnly Property ExtractedNumbers(ByVal Index As Int32) As Byte 18. Get 19. If (Index >= 0) And (Index < _ExtractedNumbers.Length) Then 20. Return _ExtractedNumbers(Index) 21. Else 22. Return 0 23. End If 24. End Get 25. End Property 26. 27. Public Sub New() 28. Essendo di tipo reference, si deve creare un nuovo 29. oggetto Random e assegnarlo a Rnd. La ragione per cui 30. Rnd è un membro di classe consiste nel fatto 31. che se fosse stata variabile temporanea del corpo 32. della procedura ExtractNumbers, sarebbero usciti 33. gli stessi numeri. Questo perchè la sequenza 34. pseudocasuale creata dalla classe non cambia se 35. non glielo si comunica espressamente usando un altro 36. costruttore. Non tratterò questo argomento ora 37. Rnd = New Random() 38. ReDim _ExtractedNumbers(5) 39. End Sub 40. 41. Public Sub ExtractNumbers() 42. Estrae 6 numeri casuali tra 1 e 90 e li pone nellarray 43. For I As Int32 = 0 To 5 44. _ExtractedNumbers(I) = Rnd.Next(1, 91) 45. Next 46. End Sub 47. End Class 48. 49. Sub Main() 50. Dim E As New NumberExtractor() 51. 52. E.ExtractNumbers() 53. Console.WriteLine("Numeri estratti: ") 54. For I As Int32 = 0 To 5 55. Console.Write(E.ExtractedNumbers(I) & " ") 56.
  • Next 57. 58. Console.ReadKey() 59. End Sub 60. End ModuleNotar e che sar ebbe stato logicamente equivalente cr ear e una pr opr ietà che r estituisse tutto lar r ay, in questo modo: 1. Public ReadOnly Property ExtractedNumbers() As Byte() 2. Get 3. Return _ExtractedNumbers 4. End Get 5. End PropertyMa non si sar ebbe avuto alcun contr ollo sullindice che lutente avr ebbe potuto usar e: nel pr imo modo, invece, è possibilecontr ollar e lindice usato e r estituir e 0 qualor a esso non sia coer ente con i limiti dellar r ay. La r estituzione di elementidi una lista, tuttavia, è solo una delle possibilità che le pr opr ietà par ametr iche offr ono, e non cè limite alluso che se nepuò 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 oelabor azioni non immediati, diciamo oltr e le 20/30 r ighe di codice, ma anche di meno, a seconda della pesantezza delleoper 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 duer equisiti: Deve posseder e almeno un par ametr o; Deve esser e unica.Anche se solitamente si usa in altr e cir costanze, ecco una pr opr ietà di default applicata al pr ecedente esempio: 01. Module Module1 02. Class NumberExtractor 03. Private _ExtractedNumbers() As Byte 04. Private Rnd As Random 05. 06. Una proprietà di default si dichiara come una 07. normalissima proprietà, ma anteponendo allo specificatore 08. di accesso la keyword Default 09. Default Public ReadOnly Property ExtractedNumbers(ByVal Index As Int32) As Byte 10. Get 11. If (Index >= 0) And (Index < _ExtractedNumbers.Length) Then 12. Return _ExtractedNumbers(Index) 13. Else 14. Return 0 15. End If 16. End Get 17. End Property 18. 19. Public Sub New() 20. Rnd = New Random() 21. ReDim _ExtractedNumbers(5) 22. End Sub 23. 24. Public Sub ExtractNumbers() 25. For I As Int32 = 0 To 5 26. _ExtractedNumbers(I) = Rnd.Next(1, 91) 27. Next 28. End Sub 29. End Class 30. 31. Sub Main() 32.
  • Dim E As New NumberExtractor() 33. 34. E.ExtractNumbers() 35. Console.WriteLine("Numeri estratti: ") 36. For I As Int32 = 0 To 5 37. Ecco lutilità delle proprietà di default: si possono 38. richiamare anche omettendone il nome. In questo caso 39. E(I) è equivalente a scrivere E.ExtractedNumbers(I), 40. ma poiché ExtractedNumbers è di default, 41. viene desunta automaticamente 42. Console.Write(E(I) & " ") 43. Next 44. 45. Console.ReadKey() 46. End Sub 47. End ModuleDal codice salta subito allocchio 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 alloggetto 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 nellambito degli oggetti w indow s for m, ma spesso non le si sar iconoscer e. Anche per quello che abbiamo impar ato finor a, per ò, possiamo scovar e un esempio di pr opr ietà didefault. 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 ovaalla posizione I nella str inga, ad esempio: 1. Dim S As String = "Ciao" 2. Dim C As Char = S.Chars(1) 3. > C = "i", poiché "i" è il carattere alla posizione 1 4. nella stringa SEbbene, Char s è una pr opr ietà di default, ossia è possibile scr iver e: 1. Dim S As String = "Ciao" 2. Dim C As Char = S(1) 3. > C = "i"Get e Set c on spec ific atori di ac c essoAnche se a pr ima vista potr ebbe sembr ar e str ano, sì, è possibile assegnar e uno specificator e di accesso anche ai singoliblocchi Get e Set allinter no di una pr opr ietà. Questa peculiar e car atter istica viene sfr uttata ver amente poco, ma offr euna gr ande flessibilità e unaltr ettanto gr ande potenzialità di gestione. Limitando laccesso ai singoli blocchi, è possibiler 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 edir ettamente tali keyw or ds. Ovviamente, per esser e logicamente applicabili, gli specificator i di accesso dei blocchiinter 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 ivateAltr a condizione necessar ia è che uno solo tr a Get e Set può esser e mar cato con uno scope diver so da quello della
  • pr opr ietà. Non avr ebbe senso, infatti, ad esempio, definir e una pr opr ietà pubblica con un Get Fr iend e un Set Pr ivate,poiché non sar ebbe più pubblica (in quanto sia get che set non sono pubblici)! Ecco un esempio: 1. Public Property A() As Byte 2. Get 3. ... 4. End Get 5. Private Set(ByVal value As Byte) 6. ... 7. End Set 8. End PropertyLa pr opr ietà A è sempr e leggibile, ma modificabile solo allinter no della classe che la espone. In pr atica, è come unanor male pr opr ietà per il codice inter no alla classe, ma come una ReadOnly per quello ester no. È pur ver o che in questocaso, si sar ebbe potuto r ender la dir ettamente ReadOnly e modificar e dir ettamente il campo da essa avvolto invece cheespor r e un Set pr ivato, ma sono punti di vista. Ad ogni modo, luso di scope diver sificati per mette di far e di tutto e dipiù ed è solo un caso che non mi sia venuto in mente un esempio più significativo.Mettiamo un po dordine sulle key w ordIn questi ultimi capitoli ho spiegato un bel po di keyw or d diver se, e specialmente nelle pr opr ietà può accader e di doverspecificar e molte keyw or d insieme. Ecco lor dine cor r etto (anche se leditor del nostr o ambiente di sviluppo le r ior dinaper noi nel caso dovessimo sbagliar e): 1. [Default] [ReadOnly/WriteOnly] [Public/Friend/Private/...] Property ...E or a quelle che conoscete sono ancor a poche... pr ovate voi a scr iver e una pr opr ietà del gener e: 1. Default Protected Friend Overridable Overloads ReadOnly Property A(ByVal Index As Int32) As Byte 2. Get 3. ... 4. End Get 5. End PropertyN.B.: ovviamente, tutto quello che si è detto finor a sulle pr opr ietà nelle classi vale anche per le str uttur e!
  • A23. Membri SharedTutte 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 unistanza di classe. Infatti, ci si r ifer isce ad unapr opr ietà o a un metodo di un o s pecifico oggetto, dicendo ad esempio "La pr opr ietà SideLength delloggetto A di tipoCube vale 3, mentr e quella delloggetto B anchesso di tipo Cube vale 4.". La classe, ossia il tipo di una var iabiler 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, talaltr e pr opr ietà e via dicendo.Or a, un membr o shar ed, o co ndiv iso , o statico (ter mine da usar si più in C# che non in VB.NET), non appar tiene piùad unistanza di classe, ma alla classe stessa. Mi r endo conto che il concetto possa esser e allinizio difficile da capir e e dainter ior izzar e cor r ettamente. Per questo motivo far ò un esempio il più semplice, ma più significativo possibile.Ripr endiamo la classe Cube, modificata come segue: 01. Module Module1 02. 03. Class Cube 04. Private _SideLength As Single 05. Private _Density As Single 06. 07. Questo campo è Shared, condiviso. Come vedete, 08. per dichiarare un membro come tale, si pone la 09. keyword Shared dopo lo specificatore di accesso. Questa 10. variabile conterrà il numero di cubi creati 11. dal nostro programma. 12. N.B.: I campi Shared sono di default Private... 13. Private Shared _CubesCount As Int32 = 0 14. 15. Public Property SideLength() As Single 16. Get 17. Return _SideLength 18. End Get 19. Set(ByVal value As Single) 20. If value > 0 Then 21. _SideLength = value 22. Else 23. _SideLength = 1 24. End If 25. End Set 26. End Property 27. 28. Public Property Density() As Single 29. Get 30. Return _Density 31. End Get 32. Set(ByVal value As Single) 33. If value > 0 Then 34. _Density = value 35. Else 36. _Density = 1 37. End If 38. End Set 39. End Property 40. 41. Public ReadOnly Property SurfaceArea() As Single 42. Get 43. Return (SideLength ^ 2) 44. End Get 45. End Property 46. 47. Public ReadOnly Property Volume() As Single 48. Get 49. Return (SideLength ^ 3) 50.
  • End Get 51. End Property 52. 53. Public ReadOnly Property Mass() As Single 54. Get 55. Return (Volume * Density) 56. End Get 57. End Property 58. 59. Allo stesso modo, la proprietà che espone il membro 60. shared deve essere anchessa shared 61. Public Shared ReadOnly Property CubesCount() As Int32 62. Get 63. Return _CubesCount 64. End Get 65. End Property 66. 67. Ogni volta che un nuovo cubo viene creato, _CubesCount 68. viene aumentato di uno, per rispecchiare il nuovo numero 69. di istanze della classe Cube esistenti in memoria 70. Sub New() 71. _CubesCount += 1 72. End Sub 73. End Class 74. 75. Sub Main() 76. Dim Cube1 As New Cube() 77. Cube1.SideLength = 1 78. Cube1.Density = 2700 79. 80. Dim Cube2 As New Cube() 81. Cube2.SideLength = 0.9 82. Cube2.Density = 3500 83. 84. Console.Write("Cubi creati: ") 85. Notate come si accede a un membro condiviso: poiché 86. appartiene alla classe e non alla singola istanza, vi si 87. accede specificando prima il nome della classe, poi 88. il comune operatore punto, e successivamente il nome 89. del membro. Tutti i membri shared funzionano in questo 90. modo 91. Console.WriteLine(Cube.CubesCount) 92. 93. Console.ReadKey() 94. End Sub 95. End ModuleFacendo cor r er e lapplicazione, si vedr à appar ir e a scher mo il numer o 2, poiché abbiamo cr eato due oggetti di tipoCube. Come si vede, il campo CubesCount non r iguar da un solo specifico oggetto, ma la totalità di tutti gli oggetti ditipo Cube, poiché è un dato globale. In gener ale, esso è di dominio della classe Cube, ossia della r appr esentazione piùastr atta dellessenza di ogni oggetto: per saper e quanti cubi sono stati cr eati, non si può inter pellar e una singolaistanza, per chè essa non "ha per cezione" di tutte le altr e istanze esistenti. Per questo motivo CubesCount è unmembr 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 comecondiviso: Quando contiene infor mazioni r iguar danti la totalità delle istanze di una classe, come in questo caso; Quando contiene infor mazioni accessibili e necessar ie a tutte le istanze della classe, come illustr er ò fr a qualche capitolo; Quando si tr atta di un metodo "di libr er ia". I cosiddetti metodi di libr er ia sono metodi sempr e shar ed che svolgono funzioni gener ali e sono utilizzabili da qualsiasi par te del codice. Un esempio potr ebbe esser e la funzione Math.Abs(x ), che r estituisce il valor e assoluto di x . Come si vede, è shar ed poiché vi si accede usando il nome della classe. Inoltr e, essa è sempr e usabile, poiché si tr atta di una semplice funzione matematica, che, quindi, for nisce ser vizi di or dine gener ale;
  • Quando si tr ova in un modulo, come spiegher ò nel pr ossimo par agr afo.Classi SharedCome!?!? Esistono classi shar ed? Ebbene sì. Può sembr ar e assur do, ma ci sono, ed è lecito domandar si quale sia la lor ofunzione. 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 (ostatiche). Tuttavia, il significato della par ola shar ed, se applicato alle classi, cambia r adicalmente. Un modulo, quindiuna classe shar ed, r ende implicitamente shar ed tutti i suoi membr i. Quindi, tutte le pr opr ietà, i campi e i metodiappar tenenti ad un modulo - ivi compr esa la Sub Main - sono membr i shar ed. Che senso ha questo? I moduli sonoconsuetamente usati, al di fuor i delle applicazioni console, per r ender e disponibili a tutto il pr ogetto membr i dipar 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à diper sé: 1. Module Module1 2. Shared Sub Hello() 3. 4. End Sub 5. 6. ... 7. End SubIl codice sopr a r ipor tato pr ovocher à il seguente er r or e: 1. Methods in a Module cannot be declared Shared.Inoltr e, è anche possibile acceder e a membr i di un modulo senza specificar e il nome del modulo, ad esempio: 01. Module Module2 02. Sub Hello() 03. Console.WriteLine("Hello!") 04. End Sub 05. End Module 06. 07. Module Module1 08. Sub Main() 09. Hello() = Module2.Hello() 10. Console.ReadKey() 11. End Sub 12. End ModuleQuesto fenomeno è anche noto col nome di Im po r ts statico .A dir la ver ità esiste una piccola differ enza tr a classi statiche e moduli. Una classe può esser e statica anche solo setutti i suoi membr i lo sono, ma non gode dellImpor ts Statico. Un modulo, al contr ar io, oltr e ad aver e tutti i membr ishar ed, gode sempr e dellImpor ts Statico. Per far la br eve: 01. Module Module2 02. Sub Hello() 03. Console.WriteLine("Hello Module2!") 04. End Sub 05. End Module 06. 07. Class Class2 08. Shared Sub Hello() 09. Console.WriteLine("Hello Class2!") 10. End Sub 11. End Class 12. 13. Module Module1 14. Sub Main() 15.
  • Per richiamare lHello di Class2, è sempre16. necessaria questa sintassi:17. Class2.Hello()18. Per invocare lHello di Module2, invece, basta19. questa, a causa dellImports Statico20. Hello()21. Console.ReadKey()22. End Sub23. End Module
  • A24. ArrayList, HashTable e SortedListAbbiamo già ampiamente visto e illustr ato il funzionamento degli ar r ay. Ho anche già detto più volte come essi nonsiano sempr e la soluzione miglior e ai nostr i pr oblemi di immagazzinamento dati. Infatti, è difficile decider ne ladimensione 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 semodificar ne la lunghezza mentr e il pr ogr amma gir a; e nel caso contr ar io, è molto limitativo conceder e allutente unnumer o pr efissato massimo di valor i. A questo pr oposito, ci vengono in aiuto delle classi già pr esenti nelle libr er iestandar d del Fr amew or k .NET che aiutano pr opr io a gestir e insiemi di elementi di lunghezza var iabile. Di seguito nepr opongo una br eve panor amica.Array ListSi tr atta di una classe per la gestione di liste di elementi. Essendo un tipo r efer ence, quindi, segue che ogni oggettodichiar ato come di tipo Ar r ayList debba esser e inizializzato pr ima delluso con un adeguato costr uttor e. Una voltacr eata unistanza, la si può utilizzar e nor malmente. La differ enza con lAr r ay r isiede nel fatto che lAr r ayList, alliniziodella sua "vita", non contiene nessun elemento, e, di conseguenza occupa r elativamente meno memor ia. Infatti, quandonoi inizializziamo un ar r ay, ad esempio così: 1. Dim A(100) As Int32nel momento in cui questo codice viene eseguito, il pr ogr amma r ichiede 101 celle di memor ia della gr andezza di 4bytes ciascuna da r iser var e per i pr opr i dati: che esse siano impostate o meno (allinizio sono tutti 0), non haimpor tanza, per chè A occuper à sempr e la stessa quantità di memor ia. Al contr ar io lAr r ayList non "sa" nulla su quantidati vor r emmo intr odur r e, quindi, ogni volta che un nuovo elemento viene intr odotto, esso si es pan de allocandodinamicamente nuova memor ia solo se ce nè bisogno. In questo r isiede la potenza delle liste.Per aggiunger e un nuovo elemento allar r aylist bisogna usar e il metodo distanza Add, passandogli come par ametr o ilvalor e da aggiunger e. Ecco un esempio: 01. Module Module1 02. 03. Class Cube 04. ... 05. End Class 06. 07. Sub Main() 08. Crea un nuovo arraylist 09. Dim Cubes As New ArrayList 10. 11. Console.WriteLine("Inserismento cubi:") 12. Console.WriteLine() 13. Dim Cmd As Char 14. Do 15. Console.WriteLine() 16. Dim C As New Cube 17. Scrive il numero del cubo 18. Console.Write((Cubes.Count + 1) & " - ") 19. Console.Write("Lato (m): ") 20. C.SideLength = Console.ReadLine 21. 22. Console.Write(" Densità (kg/m<sup>3</sup>): ") 23. C.Density = Console.ReadLine 24. 25. Aggiunge un nuovo cubo alla collezione 26. Cubes.Add(C) 27. 28. Console.WriteLine("Termina inserimento? y/n") 29.
  • Cmd = Console.ReadKey().KeyChar 30. Loop Until Char.ToLower(Cmd) = "y" 31. 32. Calcola la massa totale di tutti i cubi nella lista 33. Dim TotalMass As Single = 0 34. Notate che lArrayList si può usare come un 35. normale array. Lunica differenza sta nel fatto che 36. esso espone la proprietà Count al posto di Length. 37. In genere, tutte le liste espongono Count, che comunque 38. ha sempre lo stesso significato: restituisce il numero 39. di elementi nella lista 40. For I As Int32 = 0 To Cubes.Count - 1 41. TotalMass += Cubes(I).Mass 42. Next 43. 44. Console.WriteLine("Massa totale: " & TotalMass) 45. Console.ReadKey() 46. End Sub 47. End ModuleAllo stesso modo, è possibile r imuover e o inser ir e elementi con altr i metodi: Remove(x ) : r imuove lelemento x dallar r aylist RemoveAt(x ) : r imuove lelemento che si tr ova nella posizione x dellAr r ayList Index Of(x ) : r estituisce lindice dellelemento x Contains(x ) : r estituisce Tr ue se x è contenuto nellAr r ayList, altr imenti False Clear : pulisce lar r aylist eliminando ogni elemento Clone : r estituisce una copia esatta dellAr r ayList. Questo ar gomento ver r à discusso più in là nella guida.HashtableLHashtable possiede un meccanismo di allocazione della memor ia simile a quello di un Ar r ayList, ma è concettualmentediffer ente in ter mini di utilizzo. LAr r ayList, infatti, non si discosta molto, par lando di pr atica, da un Ar r ay - e infattivediamo 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. Questultima peculiar ità ci per mette di dir e che in un Ar r ayList gli elementi sono logicamenteor dinati. In un Hashtable, al contr ar io, tutto ciò che ho esposto finor a non vale. Questa nuova classe si basasullassociazione di una chiav e (key) con un v alo r e (value). Quando si aggiunge un nuovo elemento allHashtable, se nedeve 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 iinter i come chiavi si può s imulare il compor tamento di un Ar r ayList, ma il meccanismo intr inseco di questo tipo dicollezione r imane pur sempr e molto diver so. Ecco un esempio: 01. Hashtabel contenente alcuni materiali e le 02. relative densità 03. Dim H As New Hashtable 04. Aggiunge un elemento, contraddistinto da una chiave stringa 05. H.Add("Acqua", 1000) 06. H.Add("Alluminio", 2700) 07. H.Add("Argento", 10490) 08. H.Add("Nichel", 8800) 09. 10. ... 11. Possiamo usare lhashtable per associare 12. facilmente densità ai nostri cubi: 13. Dim C As New Cube(1, H("Argento"))Notar e che è anche possibile far e il contr ar io, ossia: 1. Dim H As New Hashtable 2.
  • H.Add(1000, "Acqua") 3. H.Add(2700, "Alluminio") 4. H.Add(10490, "Argento") 5. H.Add(8800, "Nichel")In questultimo esempio, lHashtable contiene quattr o chiavi costituite da valor i numer ici: non è comunque possibileciclar le usando un For . Infatti, negli Ar r ayList e negli Ar r ay, abbiamo la gar anzia che se la collezione contiene 8elementi, ad esempio, ci sar anno sempr e degli indici inter i validi tr a 0 e 7; con gli Hashtable, al contr ar io, nonpossiamo desumer e n ulla sulle chiavi osser vando il semplice numer o di elementi. In gener e, per iter ar e attr aver so glielementi di un Hashtable, si usano dei costr utti For Each: 1. For Each V As String In H.Values 2. Enumera tutti gli elementi di H 3. V = "Acqua", "Alluminio", "Argento", ... 4. Next 1. For Each K As Int32 In H.Keys 2. Enumera tutte le chiavi 3. K = 1000, 2700, 10490, ... 4. NextPer liter azione ci vengono in aiuto le pr opr ietà Values e Keys, che contengono r ispettivamente tutti i valor i e tutte lechiavi dellHashtable: queste collezioni sono a sola lettur a, ossia non è possibile modificar le in alcun modo. Daltr onde, èabbastanza ovvio: se aggiungessimo una chiave lHashtable non sapr ebbe a quale elemento associar la. Lunico modo permodificar le è indir etto e consiste nellusar 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 lachiave.
  • A25. Metodi factorySi definisce Factor y un metodo che ha come unico scopo quello di cr ear e una nuova istanza di una classe e r estituir etale istanza al chiamante (dato che si par la di "r estituir e", i metodi Factor y sar anno sempr e funzioni). Or a, ci sipotr 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 è dasottovalutar e: i costr uttor i ser vono ad istanziar e un oggetto, ma, una volta avviati, non possono "fer mar si". Conquesto voglio dir e che, qualor a venisser o r iscontr ati degli er r or i nei par ametr i di cr eazione dellistanza (nel caso ce nesiano), il costr uttor e cr eer ebbe comunque un nuovo oggetto, ma molto pr obabilmente questultimo conter r ebbe datier r onei. Un metodo Factor y, invece, contr olla che tutto sia a posto pr im a di cr ear e il nuovo oggetto: in questo modo,se cè qualcosa che non va, lo può comunicar e al pr ogr ammator e (o allutente), ad esempio lanciando uneccezione ovisualizzando un messaggio di er r or e. E convenzione - ma è anche logica - che un metodo Factor y sia definito sempr eallinter no della stessa classe che cor r isponde al suo tipo di output e che sia Shar ed (altr imenti non si potr ebber ichiamar e pr ima della cr eazione delloggetto, ovviamente). Un esempio di quanto detto: 01. Module Module1 02. Class Document 03. Campo statico che contiene tutti i documenti 04. aperi finora 05. Private Shared Documents As New Hashtable 06. Identificatore del documento: un paragrafo nel prossimo 07. capitolo spiegherà in dettaglio i significato e 08. lutilità delle variabili ReadOnly 09. Private ReadOnly _ID As Int16 10. Nome del file e testo contenuto in esso 11. Private ReadOnly _FileName, _Text As String 12. 13. Public ReadOnly Property ID() As Int16 14. Get 15. Return _ID 16. End Get 17. End Property 18. 19. Public ReadOnly Property FileName() As String 20. Get 21. Return _FileName 22. End Get 23. End Property 24. 25. Public ReadOnly Property Text() As String 26. Get 27. Return _Text 28. End Get 29. End Property 30. 31. Da notare il costruttore Private: nessun client al di 32. fuori della classe può inizializzare il nuovo 33. oggetto. Solo il metodo factory lo può fare 34. Private Sub New(ByVal ID As Int16, ByVal Path As String) 35. Me._ID = ID 36. Me._FileName = Path 37. Me._Text = IO.File.ReadAllText(Path) 38. Me fa riferimento alla classe stessa 39. Documents.Add(ID, Me) 40. End Sub 41. 42. Il metodo factory crea un documento se non esiste lID 43. e se il percorso su disco è diverso, altrimenti 44. restituisce il documento che esiste già 45. Public Shared Function Create(ByVal ID As Int16, _ 46. ByVal Path As String) As Document 47. If Documents.ContainsKey(ID) Then 48. Ottiene il documento già esistente con questo ID 49.
  • Dim D As Document = Documents(ID) 50. Se coincidono sia lID che il nome del file, 51. allora restituisce loggetto già esistente 52. If D.FileName = Path Then 53. Return D 54. Else 55. Altrimenti restituisce Nothing, dato che non 56. possono esistere due documenti con uguale ID, 57. o si farebbe confusione 58. Return Nothing 59. End If 60. End If 61. Se non esiste un documento con questo ID, lo crea 62. Return New Document(ID, Path) 63. End Function 64. End Class 65. 66. Sub Main() 67. Dim D As Document = Document.Create(0, "C:testo.txt") 68. Dim E As Document = Document.Create(0, "C:testo.txt") 69. Dim F As Document = Document.Create(0, "C:file.txt") 70. Dim G As Document = Document.Create(1, "C:file.txt") 71. 72. Dimostra che se ID e Path coincidono, i due oggetti 73. sono la stessa istanza 74. Console.WriteLine(E Is D) 75. Dimostra che se lID esiste già, ma il Path differisce, 76. loggetto restituito è Nothing 77. Console.WriteLine(F Is Nothing) 78. Console.ReadKey() 79. End Sub 80. End ModuleIl codice sopr a r ipor tato cr ea volutamente tutte le situazioni contemplate allinter no del metodo factor y statico: E hagli stessi par ametr i di D, quindi nel metodo factor y usato per cr ear e E viene r estituita listanza D già esistente; F halo stesso ID, quindi è Nothing. A pr ova di ciò, sullo scher mo appar ir à il seguente output: 1. True 2. TrueClassi fac tory e oggetti immutabiliUna classe contenente solo metodi factor y è detta classe factor y. Il più delle volte, luso di una tattica simile a quellasopr a r ipor tata potr ebbe por tar e alcuni dubbi: dato che esistono due var iabili che puntano alla stessa istanza, ilmodificar ne luna potr ebbe causar e lautomatica modifica dellaltr a. Tuttavia, spesse volte, gli oggetti che possonoesser e cr eati con metodi factor y non espongono alcun altr o metodo per la modifica o leliminazione dello stessooggetto, che quindi non può esser e cambiato in alcun modo. Oggetti di questo tipo sono detti im m utabili: un esempiodi 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: lunica cosa che si può far e è assegnar e alla var iabile str inga un nuovo valor e: 1. Questa stringa è immutabile 2. Dim S As String = "Ciao" 3. Viene creata una nuova stringa temporanea con valore "Buongiorno" 4. e assegnata a S. "Ciao" verrà distrutta dal Garbage Colletcion 5. S = "Buongiorno"
  • A26. CostruttoriCome si è accennato nelle pr ecedenti lezioni, i costr uttor i ser vono a cr ear e un oggetto, unistanza mater iale dellaclasse. Ogni costr uttor e, poichè ce ne può esser e anche più di uno, è sempr e dichiar ato usando la keyw or d New e nonpuò esser e altr imenti. Si possono passar e par ametr i al costr uttor e allo stesso modo di come si passano alle nor malipr ocedur e o funzioni, specificandoli tr a par entesi. Il codice scr itto nel costr uttor e viene eseguito pr ima di ogni altr ometodo nella classe, per ciò può anche modificar e le var iabili r ead-only (in sola lettur a), come vedr emo in seguito. Anchei 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 amente è che, nonostante New sia eseguito pr ima di ogni altr a istr uzione, sia le costanti sia i campi con inizializzator e(ad esempio Dim I As Int32 = 50) sono già stati inizializzati e contengono già il lor o valor e. Esempio: 01. Module Module1 02. Classe 03. Class Esempio 04. Costante pubblica 05. Public Const Costante As Byte = 56 06. Variabile pubblica che non pu� essere modificata 07. Public ReadOnly Nome As String 08. Variabile privata 09. Private Variabile As Char 10. 11. Costruttore della classe: accetta un parametro 12. Sub New(ByVal Nome As String) 13. Console.WriteLine("Sto inizializzando un oggetto Esempio...") 14. Le variabili ReadOnly sono assegnabli solo nel 15. costruttore della classe 16. Me.Nome = Nome 17. Me.Variabile = "c" 18. End Sub 19. End Class 20. 21. Costruttore del Modulo 22. Sub New() 23. Console.WriteLine("Sto inizializzando il Modulo...") 24. End Sub 25. 26. Sub Main() 27. Dim E As New Esempio("Ciao") 28. E.Nome = "Io" Sbagliato: Nome è ReadOnly 29. Console.ReadKey() 30. End Sub 31. End ModuleQuando si fa cor r er e il pr ogr amma si ha questo output: 1. Sto inizializzando il Modulo... 2. Sto inizializzando un oggetto Esempio...Lesempio mostr a lor dine in cui vengono eseguiti i costr uttor i: pr ima viene inizializzato il modulo, in seguito vieneinizializzato loggetto E, che occupa la pr ima linea di codice della pr ocedur a Main. È evidente che Main viene eseguitadopo New .V ariabili ReadOnlyHo par lato pr ima delle var iabili ReadOnly e ho detto che possono solamente esser e lette ma non modificate. Ladomanda che viene spontaneo por si è: non sar ebbe meglio usar e una costante? La differ enza è più mar cata di quantosembr i: le costanti devono esser e inizializzate con un valor e immutabile, ossia che definisce il pr ogr ammator e mentr escr ive il codice (ad esempio, 1, 2, "Ciao" ecceter a); la var iabili ReadOnly possono esser e impostate nel costr uttor e, ma,
  • cosa più impor tante, possono assumer e il valor e der ivante da unespr essione o da una funzione. Ad esempio: 1. Public Const Data_Creazione_C As Date = Date.Now Sbagliato! 2. Public ReadOnly Data_Creazione_V As Date = Date.Now GiustoLa pr ima istr uzione gener a un er r or e "Costant ex pr ession is r equir ed!" ("È r ichiesta unespr 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 co str utto r i statici e vengono eseguiti solamente quando è cr eata la pr im a istanza diuna data classe: per questo sono detti anche co str utto r i di classe o di tipo poichè non appar tengono ad ogni singolooggetto 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 ognier 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 adellapplicazione, 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 ilpr imo oggetto Er r or s. Esempio: 01. Module Esempio 02. Class Errors 03. Variabile statica che rappresenta un oggetto in grado 04. di scrivere su un file 05. Public Shared File As IO.StreamWriter 06. 07. Costruttore statico che inizializza loggetto StreamWriter 08. Da notare è che un costruttore statico NON può avere 09. parametri: il motivo è semplice. Se li potesse avere 10. e ci fossero più costruttori normali il compilatore 11. non saprebbe cosa fare, poichè Shared Sub New 12. potrebbe avere parametri diversi dagli altri 13. Shared Sub New() 14. Console.WriteLine("Costruttore statico: sto creando il log...") 15. File = New IO.StreamWriter("Errors.log") 16. End Sub 17. 18. Questo è il costruttore normale 19. Sub New() 20. Console.WriteLine("Costruttore normale: sto creando un oggetto...") 21. End Sub 22. 23. Public Sub WriteLine(ByVal Text As String) 24. File.WriteLine(Text) 25. End Sub 26. End Class 27. 28. Sub Main() 29. Qui viene eseguito il costruttore statico e quello normale 30. Dim E1 As New Errors 31. Qui solo quello normale 32. Dim E2 As New Errors 33. 34. E1.WriteLine("Nessun errore") 35. 36. Console.ReadKey() 37. End Sub 38. End ModuleLouput è: 1. Costruttore statico: sto creando il log... 2. Costruttore normale: sto creando un oggetto... 3. Costruttore normale: sto creando un oggetto...
  • Questo esempio evidenzia bene come vengano eseguiti i costr uttor i: mentr e si cr ea il pr imo oggetto Er r or s inassoluto 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è nellesempio 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 lusodegli specificator i di accesso sui costr uttor i ha par ticolar i effetti collater ali. Dichiar ar e un costr uttor e Pr ivate, adesempio, equivale e impor r e che niente possa inizializzar e loggetto al di fuor i della classe stessa: questo casopar ticolar e è stato analizzato nella lezione pr ecedente con i metodi factor y statici e ser ve a r ender e obbligator io lusodi 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 unclient ester no utilizzasse la classe impor tandola da una libr er ia (vedi oltr e) non potr ebbe usar ne il costr uttor e.
  • A27. Gli OperatoriGli oper ator i sono speciali metodi che per mettono di eseguir e, appunto, oper azioni tr a due valor i mediante luso di unsimbolo (ad esempio, + per la somma, - per la differ enza, ecceter a...). Quando facciamo i calcoli, comunemente usando itipi base numer ici del Fr amew or k, come Int16 o Double, usiamo pr aticamente sempr e degli oper ator i. Essi non sononulla 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 definitiallinter no delle var ie classi come nor mali membr i (statici). Gli oper ator i, come i tipi base, del r esto, non sisottr aggono alla globale astr azione degli linguaggi or ientati agli oggetti: tutto è sempr e incasellato al posto giusto inuna 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 allaspetto più concr eto di questo capitolo. Anche ilpr 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 eoper 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 epoiché, essendo tipi value, si pr estano meglio - come idea, più che altr o - al fatto di subir e oper azioni tr amite simboli.Venendo alla pr atica, la sintassi gener ale di un oper ator e è la seguente: 1. Shared Operator [Simbolo]([Parametri]) As [Tipo Restituito] 2. ... 3. Return [Risultato] 4. End OperatorCome si vede, la sintassi è molto simile a quella usata per dichiar ar e una funzione, ad eccezione della keyw or d edellidentificator e. Inoltr e, per far sì che loper ator e sia non solo sintatticamente, ma anche semanticamente valido,devono esser e soddisfatte queste condizioni: Loper ator e deve SEM PRE esser e dichiar ato come Shar ed, ossia statico. Infatti, loper ator e r ientr a nel dominio della classe in sé e per sé, appar tiene al tipo, e non ad unistanza in par ticolar e. Infatti, loper 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. Loper ator e può specificar e al m assim o due par ametr i (si dice unar io se ne specifica uno, e binar io se due), e di questi almeno uno DEVE esser e dello stesso tipo in cui loper 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 allinter 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 loper ator e dev e esser e scelto tr a quelli disponibili, di cui qui r ipor to un elenco con annessa descr izione della funzione che usualmente loper ator e r icopr e: + (somma) - (differ enza) * (pr odotto) / (divisione) (divisione inter a) ^ (potenza) & (concatenazione) = (uguaglianza) > (maggior e)
  • < (minor e) >= (maggior e o uguale) <= (minor e o uguale) >> (shift destr o dei bit) << (shift sinistr o dei bit) And (inter sezione logica) Or (unione logica) Not (negazione logica) Xor (aut logico) Mod (r esto della divisione inter a) Like (r icer ca di un patter n: di solito il pr imo ar gomento indica dove cer car e e il secondo cosa cer car e) IsTr ue (è ver o) IsFalse (è falso) CType (conver sione da un tipo ad un altr o) Sintatticamente par lando, nulla vieta di usar e il simbolo And per far e una somma, ma sar ebbe meglio attener si alle nor mali nor me di utilizzo r ipor tate.Ed ecco un esempio: 001. Module Module1 002. 003. Public Structure Fraction 004. Numeratore e denominatore 005. Private _Numerator, _Denumerator As Int32 006. 007. Public Property Numerator() As Int32 008. Get 009. Return _Numerator 010. End Get 011. Set(ByVal value As Int32) 012. _Numerator = value 013. End Set 014. End Property 015. 016. Public Property Denumerator() As Int32 017. Get 018. Return _Denumerator 019. End Get 020. Set(ByVal value As Int32) 021. If value <> 0 Then 022. _Denumerator = value 023. Else 024. Il denominatore non può mai essere 0 025. Dovremmo lanciare uneccezione, ma vedremo più 026. avanti come si fa. Per ora lo impostiamo a uno 027. _Denumerator = 1 028. End If 029. End Set 030. End Property 031. 032. Costruttore con due parametri, che inizializza numeratore 033. e denominatore 034. Sub New(ByVal N As Int32, ByVal D As Int32) 035. Me.Numerator = N 036. Me.Denumerator = D 037. End Sub 038. 039. Restituisce la Fraction sottoforma di stringa 040. Function Show() As String 041. Return Me.Numerator & " / " & Me.Denumerator 042. End Function 043. 044. Semplifica la Fraction 045. Sub Semplify() 046.
  • Dim X As Int32047.048. Prende X come il valore meno alto in modulo049. e lo inserisce in X. X servirà per un050. calcolo spicciolo del massimo comune divisore051. X = Math.Min(Math.Abs(Me.Numerator), Math.Abs(Me.Denumerator))052.053. Prima di iniziare, per evitare errori, controlla054. se numeratore e denominatore sono entrambi negativi:055. in questo caso li divide per -1056. If (Me.Numerator < 0) And (Me.Denumerator < 0) Then057. Me.Numerator /= -1058. Me.Denumerator /= -1059. End If060.061. E con un ciclo scova il valore più alto di X062. per cui sono divisibili sia numeratore che denominatore063. (massimo comune divisore) e li divide per quel numero.064.065. Continua a decrementare X finché non trova un066. valore per cui siano divisibili sia numeratore che067. denominatore: dato che era partito dallalto, questo068. sarà indubbiamente il MCD069. Do Until ((Me.Numerator Mod X = 0) And (Me.Denumerator Mod X = 0))070. X -= 1071. Loop072.073. Divide numeratore e denominatore per lMCD074. Me.Numerator /= X075. Me.Denumerator /= X076. End Sub077.078. Somma due frazioni e restituisce la somma079. Shared Operator +(ByVal F1 As Fraction, ByVal F2 As Fraction) _080. As Fraction081. Dim F3 As Fraction082.083. Se i denumeratori sono uguali, si limita a sommare084. i numeratori085. If F1.Denumerator = F2.Denumerator Then086. F3.Denumerator = F1.Denumerator087. F3.Numerator = F1.Numerator + F2.Numerator088. Else089. Altrimenti esegue tutta loperazione090. x a x*b + a*y091. - + - = ---------092. y b y*b093. F3.Denumerator = F1.Denumerator * F2.Denumerator094. F3.Numerator = F1.Numerator * F2.Denumerator + F2.Numerator * F1.Denumerator095. End If096.097. Semplifica la Fraction098. F3.Semplify()099. Return F3100. End Operator101.102. Sottrae due Fraction e restituisce la differenza103. Shared Operator -(ByVal F1 As Fraction, ByVal F2 As Fraction) _104. As Fraction105. Somma lopposto del secondo membro106. F2.Numerator = -F2.Numerator107. Return F1 + F2108. End Operator109.110. Moltiplica due frazioni e restituisce il prodotto111. Shared Operator *(ByVal F1 As Fraction, ByVal F2 As Fraction) _112. As Fraction113. Inizializza F3 con il numeratore pari al prodotto114. dei numeratori e il denominatore pari al prodotto dei115. denominatori116. Dim F3 As Fraction = New Fraction(F1.Numerator * F2.Numerator, _117. F1.Denumerator * F2.Denumerator)118.
  • F3.Semplify() 119. Return F3 120. End Operator 121. 122. Divide due frazioni e restituisce il quoziente 123. Shared Operator /(ByVal F1 As Fraction, ByVal F2 As Fraction) _ 124. As Fraction 125. Inizializza F3 eseguendo loperazione: 126. a x a y 127. - / - = - * - 128. b y b x 129. Dim F3 As Fraction = New Fraction(F1.Numerator * F2.Denumerator, _ 130. F1.Denumerator * F2.Numerator) 131. F3.Semplify() 132. Return F3 133. End Operator 134. End Structure 135. 136. Sub Main() 137. Dim A As New Fraction(8, 112) 138. Dim B As New Fraction(3, 15) 139. 140. A.Semplify() 141. B.Semplify() 142. Console.WriteLine(A.Show()) 143. Console.WriteLine(B.Show()) 144. 145. Dim C As Fraction = A + B 146. Console.WriteLine("A + B = " & C.Show()) 147. 148. Console.ReadKey() 149. End Sub 150. End ModuleCTy peCType è 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 statointr odotto nei pr ecedenti capitoli, ma ne par ler ò più ampiamente in uno dei successivi. Scr ivo comunque un par agr afoa 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 dallacor r etta dichiar azione. Infatti, nonostante CType accetti due par ametr i, la sua dichiar azione ne implica uno solo, ossiail tipo che si desider a conver tir e, in questo caso Fr action. Il secondo par ametr o è implicitamente indicato dal tipo dir itor no: se scr ivessimo "CType(ByVal F As Fr action) As Double", questa istr uzione gener er ebbe un CType in gr ado diconver tir e dal tipo Fr action al tipo Double nella manier a consueta in cui siamo abituati: 1. Dim F As Fraction 2. ... 3. Dim D As Double = CType(F, Double)La dichiar azione di una conver sione ver so Double gener a automaticamente anche loper ator e CDbl, che si può usar etr anquillamente al posto della ver sione completa di CType. Or a conviene por r e laccento sul come CType vienedichiar ato: la sua sintassi non è speciale solo per chè può esser e confuso da unar io a binar io, ma anche per chè devedichiar ar e sem pr e se una conver sione è W idening (di espansione, ossia senza per dita di dati) o Nar r o w ing (dir iduzione, con possibile per dita di dati). Per questo motivo si deve specificar e una delle suddette keyw or d tr a Shar ede 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 diun possibile numer o per iodico, possiamo consider ar e che nel passaggio ver so i Double non ci sia per dita di dati nè dipr ecisione in modo r ilevante. Possiamo quindi definir e la conver sione Widening: 1. Shared Widening Operator CType(ByVal F As Fraction) As Double 2. Return F.Numerator / F.Denumerator 3. End Operator
  • Invece, la conver sione ver so un numer o inter o implica non solo una per dita di pr ecisione r ilevante ma anche di dati,quindi la definir emo Nar r ow ing: 1. Shared Narrowing Operator CType(ByVal F As Fraction) As Int32 2. Notare loperatore di divisione intera (per maggiori 3. informazioni sulla divisione intera, vedere capitolo A6) 4. Return F.Numerator F.Denumerator 5. End OperatorOperatori di c onfrontoGli oper ator i di confr onto godono anchessi 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 enon 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 soloo ver a o falsa. 01. Shared Operator <(ByVal F1 As Fraction, ByVal F2 As Fraction) As Boolean 02. Converte le frazioni in double e confronta questi valori 03. Return (CType(F1, Double) < CType(F2, Double)) 04. End Operator 05. 06. Shared Operator >(ByVal F1 As Fraction, ByVal F2 As Fraction) As Boolean 07. Return (CDbl(F1) > CDbl(F2)) 08. End Operator 09. 10. Shared Operator =(ByVal F1 As Fraction, ByVal F2 As Fraction) As Boolean 11. Return (CDbl(F1) = CDbl(F2)) 12. End Operator 13. 14. Shared Operator <>(ByVal F1 As Fraction, ByVal F2 As Fraction) As Boolean 15. Loperatore "diverso" restituisce sempre un valore opposto 16. alloperatore "uguale" 17. Return Not (F1 = F2) 18. End OperatorÈ da notar e che le espr essioni come (a=b) o (a-c>b) r estituiscano un valor e booleano. Possono anche esser e usate nelleespr 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 ivalor i booleani: 1. ... 2. a = 10 3. b = 20 4. Console.WriteLine("a è maggiore di b: " & (a > b)) 5. A schermo compare: "a è maggiore di b: False"
  • A28. Differenze tra classi e struttureNel 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 questinuove funzionalità, così da estender e le nor mali capacità del Fr amew or k, ma ho detto la stessa cosa delle str uttur e,magar i enfatizzandone di meno limpor tanza. Le classi possono espor r e campi, e le str uttur e anche; le classi possonoespor 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 ecostr 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 ilor 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 eatisolo 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. Ripetendoconcetti già spiegati pr ecedentemente, le pr ime vengono collocate dir ettamente sullo stack, ossia sulla memor iapr incipale, nello spazio r iser vato alle var iabili del pr ogr amma, mentr e le seconde vengono collocate in unaltr a par tedella memor ia (heap managed) e pongono sullo stack solo un puntator e alla lor o ver a locazione. Questo significapr incipalmente due cose: Laccesso 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 siesegue il mar shalling dei dati (ossia la lor o tr asfor mazione da entità alla pur a r appr esentazione in memor ia, costituitada 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 inmemor ia se si devono attuar e oper azioni di basso livello, ed è anche possibile r ispar miar e spazio usando unoppor tunadisposizione delle var iabili. Le classi, al contr ar io, non sono così or dinate, ed è meno facile manipolar le. Non miaddentr er ò oltr e in questo ambito, ma, per chi volesse, ci sono delle mie dispense che spiegano come funziona lamemor izzazione delle str uttur e.IdentitàUnaltr a conseguenza del fatto che le classi siano tipi r efer ence consiste in questo: due oggetti, a par ità di campi, sonosem pr e diver si, poiché si tr atta di due istanze distinte, seppur contenti gli stessi dati. Due var iabili di tipostr uttur ato che contengono gli stessi dati, invece, sono uguali, per chè non esiste il concetto di istanza per i tipi value. Itipi value sono, per lappunto, v alo r i, ossia semplici dati, infor mazione pur a, ammasso di bits, né più né meno. Perquesto motivo, ad esempio, è impossibile modificar e una pr opr ietà di una str uttur a tr amite loper 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. Unoggetto contiene molteplici campi e pr opr ietà sempr e modificabili, per chè indicano solo un aspetto delloggetto: adesempio, 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 esempiconvinceti, ma sper o che con il tempo impar er ete da soli a inter ior izzar e queste differ enze - differ enze che, pur
  • essendo impor tanti, non sono le più impor tanti.Paradigma di programmazione ad oggettiEd eccoci ar r ivati al punto caldo della discussione. La sostanziale differ enza che separ a nettamente str uttur e da classiè lader 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 equesto. 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 dibase: le str uttur e non per mettono di far e neppur e questo.Queste tr e car ater istiche (ma le pr ime due in par ticolar e) sono potenti str umenti a disposizione del pr ogr ammator e,e nei pr ossimi capitoli le analizzer emo nel dettaglio.
  • A29. LEreditarietàEccoci ar r ivati a par lar e degli aspetti peculiar i di un linguaggio ad oggetti! Iniziamo con lEder editar ietà.Lereditarietà è la possibilità di un linguaggio ad oggetti di far der ivar e una classe da unaltr a: in questo caso, la pr imaassume il nome di classe der iv ata, mentr e la seconda quello di classe base. La classe der ivata acquisisce tutti imembr i della classe base, ma può r idefinir li o aggiunger ne di nuovi. Questa car atter istica di ogni linguaggio ObjectOr iented è par ticolar mente efficace nello schematizzar e una r elazione "is-a" (ossia "è un"). Per esempio, potr emmodefinir e una classe Vegetale, quindi una nuova classe Fior e, che er edita Vegetale. Fior e è un Vegetale, come mostr a lastr uttur a ger ar chica deller editar ietà. Se definissimo unaltr a classe Pr imula, der ivata da Fior e, dir emmo che Pr imulaè un Fior e, che a sua volta è un Vegetale. Questultimo tipo di r elazione, che cr ea classi der ivate che sar anno basi perer editar e altr e classi, si chiama er editar ietà indir e tta.Passiamo or a a veder e come si dichiar a una classe der ivata: 1. Class [Nome] 2. Inherits [Classe base] 3. Membri della classe 4. End ClassLa keyw or d Inher its specifica quale classe base er editar e: si può aver e solo UNA dir ettiva Inher its per classe, ossia nonè possibile er editar e più classi base. In questo fr angente, si può scopr ir e come le pr opr ietà siano utili e flessibili: seuna classe base definisce una var iabile pubblica, questa diver r à par te anche della classe der ivata e su tale var iabilever r anno basate tutte le oper azioni che la coinvolgono. Siccome è possibile che la classe der ivata voglia r idefinir e talioper azioni e molto pr obabilmente anche lutilizzo della var iabile, è sempr e consigliabile dichiar ar e campi Pr ivateavvolti da una pr opr ietà, poichè non cè mai alcun per icolo nel modificar e una pr opr ietà in classi der ivate, ma non èpossibile modificar e i campi nella stessa classe. Un semplice esempio di er editar ietà: 01. Class Person 02. Per velocizzare la scrittura del codice, assumiamo che 03. questi campi pubblici siano proprietà 04. Public FirstName, LastName As String 05. 06. Public ReadOnly Property CompleteName() As String 07. Get 08. Return FirstName & " " & LastName 09. End Get 10. End Property 11. End Class 12. 13. Lo studente, ovviamente, è una persona 14. Class Student 15. Student eredita da Person 16. Inherits Person 17. 18. In più, definisce anche questi campi pubblici 19. La scuola frequentata 20. Public School As String 21. E lanno di corso 22. Public Grade As Byte 23. End ClassIn 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 esplicitamentedefinito nel suo cor po, ma anche tutti quei membr i pr esenti nella classe base che si sono implicitamente acquisitinellatto stesso di scr iver e "Inher its". Se vogliamo, possiamo assimilar e una classe ad un insieme, i cui elementi sono i
  • suoi membr i: una classe base è sottoinsieme della cor r ispondente classe der ivata. Di solito, lambiente di sviluppo aiutamolto in questo, poiché, nei sugger imenti pr oposti dur ante la scr ittur a del codice, vengono automaticamente inser iteanche le voci er editate da altr e classi. Ciò che abbiamo appena visto vale anche per er editar ietà indir etta: se Aer edita da B e B er edita da C, A dispor r à dei membr i di B, alcuni dei quali sono anche membr i di C (semplice pr opr ietàtr ansitiva).Or a, per ò, bisogna por r e un bel Nota Bene alla questione. Infatti, non tutto è semplice come sembr a. For se nessuno si èchiesto che fine fanno gli specificator i di accesso quando un membr o viene er editato da una classe der ivata. Ebbene,esistono delle pr ecise r egole che indicano come gli scope vengono tr attati quando si er edita: Un membr o Public o Fr iend della classe base diventa un membr o Public o Fr iend della classe der ivata (in pr atica, non cambia nulla; viene er editato esattamente comè); Un membr o Pr iv ate della classe base non è accessibile dalla classe der ivata, poichè il suo ambito di visibilità impedisce a ogni chiamante ester no alla classe base di far vi r ifer imento, come già visto nelle lezioni pr ecedenti; Un membr o Pr o tected della classe base diventa un membr o Pr otected della classe der ivata, ma si compor ta come un membr o Pr ivate.Ed ecco che abbiamo intr odotto uno degli specificator i che ci er avamo lasciati indietr o. I membr i Pr otected sonopar ticolar mente utili e costituiscono una sor ta di "scappatoia" al fatto che quelli pr ivati non subiscono ler editar ietà.Infatti, un memebr o Pr otected si compor ta esattamente come uno Pr ivate, con ununica eccezione: è er editabile, ed inquesto caso diventa un membr o Pr otected della classe der ivata. Lo stesso discor so vale anche per Pr otected Fr iend.Ecco uno schema che esemplifica il compor tamento dei pr incipali Scope:Esempio: 001. Module Esempio 002. Class Person 003. Due campi protected 004. Protected _FirstName, _LastName As String 005. Un campo private readonly: non cè ragione di rendere 006. questo campo Protected poichè la data di nascita non 007. cambia ed è sempre accessibile tramite la proprietà 008. pubblica BirthDay 009. Private ReadOnly _BirthDay As Date 010. 011. Public Property FirstName() As String 012. Get 013. Return _FirstName 014. End Get 015. Set(ByVal Value As String) 016. If Value <> "" Then 017. _FirstName = Value 018. End If 019. End Set 020. End Property 021. 022. Public Property LastName() As String 023. Get 024. Return _LastName 025. End Get 026. Set(ByVal Value As String) 027. If Value <> "" Then 028.
  • _LastName = Value029. End If030. End Set031. End Property032.033. Public ReadOnly Property BirthDay() As Date034. Get035. Return _BirthDay036. End Get037. End Property038.039. Public ReadOnly Property CompleteName() As String040. Get041. Return _FirstName & " " & _LastName042. End Get043. End Property044.045. Costruttore che accetta tra parametri obbligatori046. Sub New(ByVal FirstName As String, ByVal LastName As String, _047. ByVal BirthDay As Date)048. Me.FirstName = FirstName049. Me.LastName = LastName050. Me._BirthDay = BirthDay051. End Sub052. End Class053.054. Lo studente, ovviamente, è una persona055. Class Student056. Student eredita da Person057. Inherits Person058.059. La scuola frequentata060. Private _School As String061. E lanno di corso062. Private _Grade As Byte063.064. Public Property School() As String065. Get066. Return _School067. End Get068. Set(ByVal Value As String)069. If Value <> "" Then070. _School = Value071. End If072. End Set073. End Property074.075. Public Property Grade() As Byte076. Get077. Return _Grade078. End Get079. Set(ByVal Value As Byte)080. If Value > 0 Then081. _Grade = Value082. End If083. End Set084. End Property085.086. Questa nuova proprietà si serve anche dei campi FirstName087. e LastName nel modo corretto, poichè sono Protected anche088. nella classe derivata e fornisce un profilo completo089. dello studente090. Public ReadOnly Property Profile() As String091. Get092. Da notare laccesso a BirthDay tramite la proprietà093. Public: non è possibile accedere al campo _BirthDay094. perchè è privato nella classe base095. Return _FirstName & " " & _LastName & ", nato il " & _096. BirthDay.ToShortDateString & " frequenta lanno " & _097. _Grade & " alla scuola " & _School098. End Get099. End Property100.
  • 101. Altra clausola importante: il costruttore della classe 102. derivata deve sempre richiamare il costruttore della 103. classe base 104. Sub New(ByVal FirstName As String, ByVal LastName As String, _ 105. ByVal BirthDay As Date, ByVal School As String, _ 106. ByVal Grade As Byte) 107. MyBase.New(FirstName, LastName, BirthDay) 108. Me.School = School 109. Me.Grade = Grade 110. End Sub 111. End Class 112. 113. Sub Main() 114. Dim P As New Person("Pinco", "Pallino", Date.Parse("06/07/90")) 115. Dim S As New Student("Tizio", "Caio", Date.Parse("23/05/92"), _ 116. "Liceo Classico Ugo Foscolo", 2) 117. 118. Console.WriteLine(P.CompleteName) 119. Come si vede, la classe derivata gode degli stessi membri 120. di quella base, acquisiti secondo le regole 121. dellereditarietà appena spiegate 122. Console.WriteLine(S.CompleteName) 123. E in più ha anche i suoi nuovi membri 124. Console.WriteLine(S.Profile) 125. 126. Altra cosa interessante: dato che Student è derivata da 127. Person ed espone tutti i membri di Person, più altri, 128. non è sbagliato assegnare un oggetto Student a una 129. variabile Person 130. P = S 131. Console.WriteLine(P.CompleteName) 132. 133. Console.ReadKey() 134. End Sub 135. End ModuleLoutput: 1. Pinco Pallino 2. Tizio Caio 3. Tizio Caio, nato il 23/5/1992 frequenta lanno 2 alla scuola Liceo Classico Ugo 4. Foscolo 5. Tizio Caio(Per maggior i infor mazioni sulle oper azioni con le date, veder e il capitolo B13)Anche se il sor gente è ampiamente commentato mi soffer mer ei su alcuni punti caldi. Il costr uttor e della classe der ivatadeve sem pr e r ichiamar e il costr uttor e della classe base, e questo avviene tr amite la keyw or d MyBase che, usata inuna classe der ivata, fa r ifer imento alla classe base cor r ente: attr aver so questa par ola r iser vata è possibile ancher aggiunger e i membr i pr ivati della classe base, ma si fa r ar amente, poichè il suo impiego più fr equente è quello dir ipr ender e le vecchie ver sioni di metodi modificati. Il secondo punto r iguar da la conver sione di classi: passar e daStudent a Per son non è, come potr ebbe sembr ar e, una conver sione di r iduzione, poichè dur ante il pr ocesso, nulla vaper duto nel ver o senso della par ola. Cer to, si per dono le infor mazioni supplementar i, ma alla classe base queste nonser vono: la sicur ezza di eseguir e la conver sione r isiede nel fatto che la classe der ivata gode degli stessi membr i diquella 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 nelcaso 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 ividi valor e e ciò gener ebbe un er r or e. Per eseguir e questo tipo di passaggi è necessar io loper ator e Dir ectCast.
  • A30. PolimorfismoIl polimor fismo è la capacità di un linguaggio ad oggetti di r idefinir e i membr i della classe base in modo tale che sicompor tino in manier a differ ente allinter no delle classi der ivate. Questa possibilità è quindi str ettamente legataaller editar ietà. Le keyw or ds che per mettono di attuar ne il funzionamento sono due: Over r idable e Over r ides. Lapr 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 odella classe der ivata che ne costituisce la nuova ver sione. È da notar e che solo membr i della stessa categor ia con no m eug uale e sig natur e identica (ossia con lo stesso numer o e lo stesso tipo di par ametr i) possono subir e questopr ocesso: ad esempio non si può r idefinir e la pr ocedur a Show Tex t() con la pr opr ietà Tex t, per chè hanno nomediffer ente e sono di diver sa categor ia (una è una pr ocedur a e laltr a una pr opr ietà). La sintassi è semplice: 1. Class [Classe base] 2. Overridable [Membro] 3. End Class 4. 5. Class [Classe derivata] 6. Inherits [Classe base] 7. Overrides [Membro] 8. End ClassQuesto esempio pr ende come base la classe Per son definita nel capitolo pr ecedente e sviluppa da questa la classeTeacher (insegnante), modificandone le pr opr ietà LastName e CompleteName: 001. Module Module1 002. Class Person 003. Protected _FirstName, _LastName As String 004. Private ReadOnly _BirthDay As Date 005. 006. Public Property FirstName() As String 007. Get 008. Return _FirstName 009. End Get 010. Set(ByVal Value As String) 011. If Value <> "" Then 012. _FirstName = Value 013. End If 014. End Set 015. End Property 016. 017. Questa proprietà sarà ridefinita nella classe Teacher 018. Public Overridable Property LastName() As String 019. Get 020. Return _LastName 021. End Get 022. Set(ByVal Value As String) 023. If Value <> "" Then 024. _LastName = Value 025. End If 026. End Set 027. End Property 028. 029. Public ReadOnly Property BirthDay() As Date 030. Get 031. Return _BirthDay 032. End Get 033. End Property 034. 035. Questa proprietà sarà ridefinita nella classe Teacher 036. Public Overridable ReadOnly Property CompleteName() As String 037. Get 038. Return _FirstName & " " & _LastName 039. End Get 040.
  • End Property041.042. Costruttore che accetta tra parametri obbligatori043. Sub New(ByVal FirstName As String, ByVal LastName As String, _044. ByVal BirthDay As Date)045. Me.FirstName = FirstName046. Me.LastName = LastName047. Me._BirthDay = BirthDay048. End Sub049. End Class050.051. Class Teacher052. Inherits Person053. Private _Subject As String054.055. Public Property Subject() As String056. Get057. Return _Subject058. End Get059. Set(ByVal Value As String)060. If Value <> "" Then061. _Subject = Value062. End If063. End Set064. End Property065.066. Ridefinisce la proprietà LastName in modo da aggiungere067. anche il titolo di Professore al cognome068. Public Overrides Property LastName() As String069. Get070. Return "Prof. " & _LastName071. End Get072. Set(ByVal Value As String)073. Da notare luso di MyBase e LastName: in questo074. modo si richiama la vecchia versione della075. proprietà LastName e se ne imposta il076. valore. Viene quindi richiamato il blocco Set077. vecchio: si risparmiano due righe di codice078. poichè non si deve eseguire il controllo079. If su Value080. MyBase.LastName = Value081. End Set082. End Property083.084. Ridefinisce la proprietà CompleteName in modo da085. aggiungere anche la materia insegnata e il titolo di086. Professore087. Public Overrides ReadOnly Property CompleteName() As String088. Get089. Anche qui viene richiamata la vecchia versione di090. CompleteName, che restituisce semplicemente il091. nome completo092. Return "Prof. " & MyBase.CompleteName & _093. ", dottore in " & Subject094. End Get095. End Property096.097. Sub New(ByVal FirstName As String, ByVal LastName As String, _098. ByVal BirthDay As Date, ByVal Subject As String)099. MyBase.New(FirstName, LastName, BirthDay)100. Me.Subject = Subject101. End Sub102. End Class103.104. Sub Main()105. Dim T As New Teacher("Mario", "Rossi", Date.Parse("01/01/1950"), _106. "Letteratura italiana")107.108. Usiamo le nuove proprietà, ridefinite nella classe109. derivata110. Console.WriteLine(T.LastName)111. > "Prof. Rossi"112.
  • Console.WriteLine(T.CompleteName) 113. > "Prof. Mario Rossi, dottore in Letteratura italiana" 114. 115. Console.ReadKey() 116. End Sub 117. End ModuleIn 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 tamentopar ticolar e: 1. Dim P As Person = T 2. Console.WriteLine(P.LastName) 3. Console.WriteLine(P.CompleteName)In questo caso ci si aspetter ebbe che le pr opr ietà r ichiamate da P agiscano come specificato nella classe base (ossiasenza 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 loper ator e =, puntano or a allo stesso oggetto in memor ia, solo che P lo vedecome di tipo Per son e T come di tipo Teacher . Tuttavia, loggetto r eale è di tipo Teacher e per ciò i suoi metodi sono atutti gli effetti quelli r idefiniti nella classe der ivata. Quando P tenta di r ichiamar e le pr opr ietà in questione, ar r ivaallindir izzo di memor ia dove sono conser vate le istr uzioni da eseguir e, solo che queste si tr ovano allinter no di unoggetto Teacher e il lor o codice è, di conseguenza, diver so da quello della classe base. Questo compor tamento, alcontr 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 sonesia studenti che insegnanti, e ci per mette di scr iver e a scher mo i lor o nomi differ entemente senza eseguir e unaconver sione. Ecco un esempio: 01. Dim Ps(2) As Person 02. 03. Ps(0) = New Person("Luigi", "Ciferri", Date.Parse("7/7/1982")) 04. Ps(1) = New Student("Mario", "Bianchi", Date.Parse("19/10/1991"), _ 05. "Liceo Scientifico Tecnologico Cardano", 5) 06. Ps(2) = New Teacher("Ubaldo", "Nicola", Date.Parse("11/2/1980"), "Filosofia") 07. 08. For Each P As Person In Ps 09. Console.WriteLine(P.CompleteName) 10. NextÈ lecito assegnar e oggetti Student e Teacher a una cella di un ar r ay di Per son in quanto classi der ivate da Per son. Imetodi r idefiniti, tuttavia, r imangono e modificano il compor tamento di ogni oggetto anche se r ichiamato da una"mascher a" di classe base. Pr oviamo or a con un piccolo esempio sul polimor fismo dei metodi: 01. Class A 02. Public Overridable Sub ShowText() 03. Console.WriteLine("A: Testo di prova") 04. End Sub 05. End Class 06. 07. Class B 08. Inherits A 09. 10. Come si vede il metodo ha: 11. - lo stesso nome: ShowText 12. - lo stesso tipo: è una procedura 13. - gli stessi parametri: senza parametri 14. Qualunque tentativo di cambiare una di queste caratteristiche 15. produrrà un errore del compilatore, che comunica di non poter 16. ridefinire il metodo perchè non ne esistono di uguali nella 17. classe base 18. Public Overrides Sub ShowText() 19. Console.WriteLine("B: Testo di prova") 20. End Sub 21. End ClassUltime due pr ecisazioni: le var iabili non possono subir e polimor fismo, così come i membr i statici.
  • Shadow ingSe il polimor fismo per mette di r idefinir e accur atamente membr i che pr esentano le stesse car atter istiche, ed è quindipiù pr eciso, lo shadow ing per mette letter almente di oscur ar e qualsiasi membr o che abbia lo stesso nome,indipendentemente dalla categor ia, dalla signatur e e dalla qauntità di ver sioni alter native pr esenti. La keyw or d dausar e è Shadow s, e si applica solo sul membr o della classe der ivata che intendiamo r idefinir e, oscur ando lomonimonella classe base. Ad esempio: 01. Module Esempio 02. Class Base 03. Friend Control As Byte 04. End Class 05. 06. Class Deriv 07. Inherits Base 08. Public Shadows Sub Control(ByVal Msg As String) 09. Console.WriteLine("Control, seconda versione: " & Msg) 10. End Sub 11. End Class 12. 13. Sub Main() 14. Dim B As New Base 15. Dim D As New Deriv 16. 17. Entrambe le classe hanno lo stesso membro di nome 18. "Control", ma nella prima è un campo friend, 19. mentre nella seconda è una procedura pubblica 20. Console.WriteLine(B.Control) 21. D.Control("Ciao") 22. 23. Console.ReadKey() 24. End Sub 25. End ModuleCome si vede, la sintassi è come quella di Over r ides: Shadow s viene specificato tr a lo specificator e di accesso (se ce) ela tipologia del membr o (in questo caso Sub, pr ocedur a). Entr ambe le classi pr esentano Contr ol, ma la seconda ne fa unuso totalmente diver so. Ad ogni modo luso dello shadow ing in casi come questo è for tememente sconsigliabile: più chealtr 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 chepr esentano lo stesso nome di quelli della classe der ivata da noi definita, non ci siano pr oblemi di compatibilità.Se una var iabile è dichiar ata Shadow s, viene omessa la keyw or d Dim.
  • A31. Conversioni di datiIl Fr amew or k .NET è in gr ado di eseguir e conver sioni automatiche a r untime ver so tipi di ampiezza maggior e, peresempio è in gr ado di conver tir e Int16 in Int32, Char in Str ing, Single in Double e via dicendo. Queste oper azioni diconver sione vengono dette w idening (dallinglese 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 idi conver sione ser vono per eseguir e conver sioni che vanno nella dir ezione opposta, e che sono quindi, nar r o w ing(dallinglese 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 seimplicite.CTy peCType è loper ator e di conver sione univer sale e per mette la conver sione di qualsiasi tipo in qualsiasi altr o tipo, almenoquando questa è possibile. La sintassi è molto semplice: [Variabile] = CType([Valore da convertire], [Tipo in cui convertire])Ad esempio: 1. Dim I As Int32 = 50 2. Converte I in un valore Byte 3. Dim B As Byte = CType(I, Byte)Questa lista r ipor ta alcuni casi in cui è bene usar e esplicitamente loper 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 questultimo che conver tono in un solo tipo: CInt conver tesempr 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 ogniclasse 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 tipiRefer ence.Direc tCastDir ectCast lavor a in un modo legger mente di diver so: CType tenta sempr e di conver tir e lar gomento di or gine nel tipospecificato, mentr e Dir ectCast lo fa solo se tale valor e può esser e sottoposto al casting (al "passaggio" da un tipoallaltr o, piuttosto che alla conver sione) ver so il tipo indicato. Per ciò non è, ad esempio, in gr ado di conver tir e unastr inga in inter o, e neanche un valor e shor t in un integer , sebbene questa sia una conver sione di espansione. Questiultimi esempi non sono validi anche per chè questo par ticolar e oper ator e può accettar e come ar gomenti solo oggetti, equindi tipi Refer ence. In gener ale, quindi, dato il legger o r ispar mio di tempo di Dir ectCast in confr onto a CType, èconveniente usar e Dir ectCast:
  • Per eseguir e lunbox 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 uninter faccia.N.B.: notar e che tutti i casi sopr a menzionati hanno come tipo di par tenza un oggetto, pr opr io come dettopr ecedentemente.Try CastTr yCast ha la stessa sintassi di Dir ectCast, e quindi anche di CType, ma nasconde un piccolo pr egio. Spesso, quando siesegue una conver sione si deve pr ima contr ollar e che la var iabile in questione sia di un deter minato tipo base oimplementi una deter minata inter faccia e solo successivamente si esegue la conver sione ver a e pr opr ia. Con ciò sicontr olla due volte la stessa var iabile, pr ima con lIf e poi con Dir ectCast. Tr yCast, invece, per mette di eseguir e il tuttoin un unico passaggio e r estituisce semplicemente Nothing se il cast fallisce. Questo appr occio r ende tale oper ator ecir 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 amew or k.Questa classe, essendo statica (e qui facciamo un po di r ipasso), espone solo metodi statici e non può esser e istanziata(non espone costr uttor i e comunque sar ebbe inutile far lo). Essa contiene molte funzioni per eseguir e la conver sionever 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: adesempio, cè, ToInt32, ToDouble, ToByte, ToStr ing, ecceter a... Un esempio: 01. Dim I As Int32 = 34 02. Dim D As Double = Convert.ToDouble(I) 03. D = 34.0 04. Dim S As String = Convert.ToString(D) 05. S = "34" 06. Dim N As Single = Convert.ToSingle(S) 07. N = 34.0 08. Dim K As String = "31/12/2008" 09. Dim A As Date = Convert.ToDate(K)Allinter no di Conver t sono definiti anche alcuni metodi per conver tir e una str inga da e ver so il for mato Base64, unapar ticolar e codifica che utilizza solo 64 car atter i, al contr ar io dellASCII standar d che ne utilizza 128 o di quello estesoche ne utilizza 256. Tale codifica viene usata ad esempio nellinvio delle e-mail e pr oduce output un ter zo più voluminosidegli 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 lar gomento, cliccate su w ik ipedia.Per r ipr ender e il discor so conver sioni, sar ebbe lecito pensar e che la definizione di una classe del gener e, quandoesistono già altr i oper ator i come CType e Dir ectCast - altr ettanto qualificati e per for manti - sia abbastanzar idondante. Più o meno è così. Utilizzar e la classe Conver t al posto degli altr i oper ator i di casting non gar antisce alcunvantaggio di sor ta, e può anche esser e r icondotta ad una questione di gusti (io per sonalmente pr efer isco CType). Adogni modo, cè da dir e unaltr a cosa al r iguar do: i metodi di Conver t sono piuttosto r igor osi e for niscono dei ser vizimolto mir ati. Per questo motivo, in casi molto vantaggiosi, ossia quando il cast può esser e ottimizzato, essi eseguonopur sempr e le stesse istr uzioni: al contr ar io, CType può "ingegnar si" e for nir e una conver sione più efficiente.Questultimo, quindi, è legger mente più elastico ed adattabile alle situazioni.
  • ParseUnoper 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 unutilizzo di Par se nelluso delle date, poiché il tipo Date espone il metodo Par se, che ci per mette di conver tir e lar appr esentazione testuale di una data in un valor e date appr opr iato. Quasi tutti i tipi base del Fr amew or k espongonoun metodo Par se, che per mette di passar e da una str inga a quel tipo: possiamo dir e che Par se è linver sa di ToStr ing.Ad esempio: 01. Dim I As Int32 02. 03. I = Int32.Parse("27") 04. I = 27 05. 06. I = Int32.Parse("78.000") 07. Errore di conversione! 08. 09. I = Int32.Parse("123,67") 10. Errore di conversione!Come vedete, Par se ha pur sempr e dei limiti: ad esempio non contempla i punti e le vir gole, sebbene la conver sione,vista da noi "umani", sia del tutto lecita (78.000 è settantottomila con il separ ator e delle migliaia e 123,67 è un numer odecimale, quindi conver tibile in inter o con un ar r otondamento). Inoltr e, Par se viene anche automaticamente chiamatodai metodi di Conver t quando il valor e passato è una str inga. Ad esempio, Conver t.ToInt32("27") r ichiama a sua voltaInt32.Par se("27"). Per far vi veder e in che modo CType è più flessibile, r ipetiamo lesper imento di pr ima usando appuntoCType: 01. Dim I As Int32 02. 03. I = CType("27", Int32) 04. I = 27 05. 06. I = CType("78.000", Int32) 07. I = 78000 08. 09. I = CType("123,67", Int32) 10. I = 124Per fetto: niente er r or i di conver sione e tutto come ci si aspettava!Try ParseUna var iante di Par se è Tr yPar se, anchessa 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 nonr estituisce neppur e il r isultato. Infatti, Tr yPar se accetta due ar gomenti, come nella seguente signatur e: 1. TryParse(ByVal s As String, ByRef result As [Tipo]) As BooleanDove [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 lavr à 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 cir iesce, r estituisce False. Ecco un esempio: 01. Dim S As String = "56/0/1000" 02. S contiene una data non valida 03. Dim D As Date 04. 05. If Date.TryParse(S, D) Then 06. Console.WriteLine(D.ToLongDateString()) 07. Else 08. Console.WriteLine("Data non valida!") 09. End If
  • Ty peOfTypeOf 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 tainter faccia, ad esempio: 1. Dim I As Int32 2. If TypeOf I Is Int32 Then 3. Questo blocco viene eseguito poichè I è di tipo Int32 4. End IfOppur e: 1. Dim T As Student 2. If TypeOf T Is Person Then 3. Questo blocco viene eseguito perchè T, essendo Student, è 4. anche di tipo Person, in quanto Student è una sua classe 5. derivata 6. End IfEd infine un esempio sulle inter facce, che potr ete tor nar e a guar dar e da qui a qualche capitolo: 1. Dim K(9) As Int32 2. If TypeOf Is IEnumerable Then 3. Questo blocco viene eseguito poiché gli array implementano 4. sempre linterfaccia IEnumerable 5. End If
  • A31. LOverloadingLOver loading è la capacità di un linguaggio ad oggetti di poter definir e, nella stessa classe, più var ianti dello stessometodo. Per poter eseguir e cor r ettamente lover 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 dallaltr a.Per far e un esempio, il metodo Console.Wr iteLine espone ben 18 ver sioni diver se, che ci consentono di stampar epr 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 diintr 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 secondoPar amAr r ay di oggetti: 1. Console.WriteLine("stringa", arg0, arg1, arg2, arg3, ...)Il pr imo par ametr o pr ende il nome di str ing a di fo r m ato , poiché specifica il for mato in cui i dati costituiti dagliar gomenti addizionali dovr anno esser e visualizzati. Allinter no di questa str inga, si possono specificar e, oltr e ainor 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 imeno uno: "{I}" viene detto segnaposto e ver r à sostituito dal par ametr o I nella str inga. Ad esempio: 1. A = 1 2. B = 3 3. Console.WriteLine("La somma di {0} e {1} è {2}.", A, B, A + B) 4. > "La somma di 1 e 3 è 4."Ulter ior i infor mazioni sulle str inghe di for mato sono disponibili nel capitolo "Magie con le str inghe".Ma or a passiamo alla dichiar azione dei metodi in over load. La par ola chiave da usar e, ovviamente, è Over loads,specificata poco dopo lo scope, e dopo gli eventuali Over r idable od Over r ides. Le entità che possono esser e sottopostead over load, oltr e ai metodi, sono: Metodi statici Oper ator i Pr opr ietà Costr uttor i Distr uttor iAnche se gli ultimi due sono sempr e metodi - per or a tr alasciamo i distr uttor i, che non abbiamo ancor a analizzato - èbene specificar e con pr ecisione, per chè a compiti speciali spesso cor r ispondono compor tamenti altr ettanto speciali.Ecco un semplicissimo esempio di over load: 01. Module Module1 02. 03. Restituisce il numero di secondi passati dalla data D a oggi 04. Private Function GetElapsed(ByVal D As Date) As Single 05. Return (Date.Now - D).TotalSeconds 06. End Function 07. 08. Come sopra, ma il parametro è di tipo intero e indica 09. un anno qualsiasi 10. Private Function GetElapsed(ByVal Year As Int32) As Single 11. Utilizza Year per costruire un nuovo valore Date 12.
  • e usa la precedente variante del metodo per 13. ottenere il risultato 14. Return GetElapsed(New Date(Year, 1, 1)) 15. End Function 16. 17. Come le due sopra, ma il parametro è di tipo stringa 18. e indica la data 19. Private Function GetElapsed(ByVal D As String) As Single 20. Return GetElapsed(Date.Parse(D)) 21. End Function 22. 23. Sub Main() 24. GetElapsed viene chiamata con tre tipi di parametri 25. diversi, ma sono tutti leciti 26. Dim El1 As Single = GetElapsed(New Date(1987, 12, 4)) 27. Dim El2 As Single = GetElapsed(1879) 28. Dim El3 As Single = GetElapsed("12/12/1991") 29. 30. Console.ReadKey() 31. End Sub 32. 33. End ModuleCome avr ete notato, nellesempio pr ecedente non ho usato la keyw or d Over loads: anche se le r egole dicono che imembr i in over load vanno segnati, non è sempr e necessar io far lo. Anzi, molte volte si evita di dichiar ar eesplicitamente i membr i di cui esistono var ianti come Over loads. Ci sono var ie r agioni per questa pr atica: lover 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 inVB tendono ad esser e un poco pr olissi), si omette Over loads. Tuttavia, esistono casi in cui è assolutamente necessar iousar e la keyw or d; eccone un esempio: 01. Module Module1 02. Class Person 03. Protected _FirstName, _LastName As String 04. Private ReadOnly _BirthDay As Date 05. 06. Public Property FirstName() As String 07. Get 08. Return _FirstName 09. End Get 10. Set(ByVal Value As String) 11. If Value <> "" Then 12. _FirstName = Value 13. End If 14. End Set 15. End Property 16. 17. Public Overridable Property LastName() As String 18. Get 19. Return _LastName 20. End Get 21. Set(ByVal Value As String) 22. If Value <> "" Then 23. _LastName = Value 24. End If 25. End Set 26. End Property 27. 28. Public ReadOnly Property BirthDay() As Date 29. Get 30. Return _BirthDay 31. End Get 32. End Property 33. 34. Public Overridable ReadOnly Property CompleteName() As String 35. Get 36. Return _FirstName & " " & _LastName 37. End Get 38. End Property 39.
  • 40. ToString è una funzione definita nella classe 41. System.Object e poiché ogni cosa in .NET 42. deriva da questa classe, &egrae; sempre possibile 43. ridefinire tramite polimorfismo il metodo ToString. 44. In questo caso ne scriveremo non una, ma due versioni, 45. quindi deve essere dichiarato sia Overrides, perchè 46. sovrascrive System.Object.ToString, sia Overloads, 47. perchè è una versione alternativa di 48. quella che andremo a scrivere tra poco 49. Public Overloads Overrides Function ToString() As String 50. Return CompleteName 51. End Function 52. 53. Questa versione accetta un parametro stringa che assume 54. la funzione di stringa di formato: il metodo restituirà 55. la frase immessa, sostituendo {F} con FirstName e {L} con 56. LastName. In questa versione è sufficiente 57. Overloads, dato che non esiste un metodo ToString che 58. accetti un parametro stringa in System.Object e perciò 59. non lo potremmo modificare 60. Public Overloads Function ToString(ByVal FormatString As String) _ 61. As String 62. Dim Temp As String = FormatString 63. Sostituisce {F} con FirstName 64. Temp = Temp.Replace("{F}", _FirstName) 65. Sostituisce {L} con LastName 66. Temp = Temp.Replace("{L}", _LastName) 67. 68. Return Temp 69. End Function 70. 71. Sub New(ByVal FirstName As String, ByVal LastName As String, _ 72. ByVal BirthDay As Date) 73. Me.FirstName = FirstName 74. Me.LastName = LastName 75. Me._BirthDay = BirthDay 76. End Sub 77. End Class 78. 79. Sub Main() 80. Dim P As New Person("Mario", "Rossi", Date.Parse("17/07/67")) 81. 82. Console.WriteLine(P.ToString) 83. > Mario Rossi 84. 85. vbCrLf è una costante che rappresenta il carattere 86. "a capo" 87. Console.WriteLine(P.ToString("Nome: {F}" & vbCrLf & "Cognome: {L}")) 88. > Nome: Mario 89. > Cognome: Rossi 90. 91. Console.ReadKey() 92. End Sub 93. End ModuleCome mostr ato dallesempio, quando il membr o di cui si vogliono definir e var ianti è sottoposto anche a polimor fismo, ènecessar io specificar e la keyw or d Over loads, poiché, in caso contr ar io, il compilator e r intr accer ebbe quello stessomembr o come diver so e, non potendo esister e membr i con lo stesso nome, pr odur r ebbe un er r or e.
  • A32. Gestione degli erroriFino ad or a, nello scr iver e il codice degli esempi, ho sempr e (o quasi sempr e) supposto che lutente inser isse daticoer 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, cer ano solo due modi per evitar e che il pr ogr amma andasse in cr asho 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 daaffr ontar e.Ahim�, non è sempr e possibile agir e in questo modo: ci sono cer ti casi in cui né luno né laltr o metodo sono efficaci. Edi questo posso for nir e subito un esempio lampante: ammettiamo di aver scr itto un pr ogr amma che esegua ladivisione tr a due numer i. Molto banale come codice. Chiediamo allutente 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 ilr isultato. Ma... se lutente 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 solover 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 abbiamogià r ilevato come la funzione Par se sia di vedute r istr ette. In definitiva, non abbiamo alcun modo di contr ollar e pr imase il dato immesso o no sia r ealmente coer ente con ciò che stiamo chiedendo allutente. Possiamo saper e se il dato nonè coer ente solo quando si ver ifica ler r or e, ma in questo caso non possiamo per metter ci che il pr ogr amma vada incr ash per una semplice distr azione. Dovr emo, quindi, ges tire ler r or e.In .NET quelli che finor a ho chiamato "er r or i" si dicono, più pr opr iamente, Eccezio ni e sono anchesse r appr esentate dauna classe: la classe base di tutte le eccezioni è System.Ex ception, da cui der ivano tutte le var ianti per le specificheeccezioni (ad esempio divisione per zer o, file inesistente, for mato non valido, ecceter a...). Accanto a queste, esisteanche uno specifico costr utto che ser ve per gestir le, e pr ende il nome di Tr y. Ecco la sua sintassi: 1. Try 2. Codice che potrebbe generare leccezione 3. Catch [Variabile] As [Tipo Eccezione] 4. Gestisce leccezione [Tipo Eccezione] 5. End TryTr a Tr y e Catch viene scr itto il codice incr iminato, che potr ebbe eventualmente gener ar e ler r or e che noi stiamotentando di r intr acciar e e gestir e. Nello specifico, quando accade un avvenimento del gener e, si dice che il codice"lancia" uneccezione, 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 unsassolino - un pacchetto di infor mazioni che ci dice tutto sul per chè e sul per come è stato gener ato quello specificoer r or e. Or a, se questo sassolino viene inter cettato da qualcosa (dal blocco catch), possiamo evitar e danni collater ali,ma se niente blocca la sua cor sa, ahimé, dovr emmo r ipagar e i vetr i r otti a qualcuno. Ecco un esempio: 01. Module Module1 02. Sub Main() 03. Dim a, b As Single 04. ok controlla se a e b sono coerenti 05. Dim ok As Boolean = False 06. 07. Do 08. Tenta di leggere i numeri da tastiera 09. Try 10. Console.WriteLine("Inserire due numeri non nulli: ") 11. a = Console.ReadLine 12. b = Console.ReadLine 13. Se il codice arriva fino a questo punto, significa 14. che non si sono verificate eccezioni 15. ok = True 16.
  • Catch Ex As InvalidCastException 17. Se, invece, il programma arriva in questo blocco, 18. vuol dire che abbiamo "preso" (catch) uneccezione 19. di tipo InvalidCastException, che è stata 20. "lanciata" dal codice precedente. Tutti i dati 21. relativi a quella eccezione sono ora conservati 22. nella variabile Ex. 23. Possiamo accedervi oppure no, come in questo caso, 24. ma sono in ogni caso informazioni utili, come 25. vedremo fra poco 26. Console.WriteLine("I dati inseriti non sono numeri!") 27. I dati non sono coerenti, quindi ok = False 28. ok = False 29. End Try 30. Richiede gli stessi dati fino a che non si tratta 31. di due numeri 32. Loop Until ok 33. 34. Esegue il controllo su b e poi effettua la divisione 35. If b <> 0 Then 36. Console.WriteLine("{0} / {1} = {2}", a, b, a / b) 37. Else 38. Console.WriteLine("Divisione impossibile!") 39. End If 40. 41. Console.ReadKey() 42. End Sub 43. End ModuleOr a potr este anche chieder vi "Come faccio a saper e quale classe r appr esenta quale eccezione?". Beh, in gener e, simette un blocco Tr y dopo aver notato il ver ificar si deller r or e e quindi dopo aver letto il messaggio di er r or e checontiene anche il nome delleccezione. In alter nativa si può specificar e come tipo semplicemente Ex ception, ed in quelcaso ver r anno cattur ate tutte le eccezioni gener ate, di qualsiasi tipo. Ecco una var iante dellesempio pr ecedente: 01. Module Module1 02. Sub Main() 03. a e b sono interi short, ossia possono assumere 04. valori da -32768 a +32767 05. Dim a, b As Int16 06. Dim ok As Boolean = False 07. 08. Do 09. Try 10. Console.WriteLine("Inserire due numeri non nulli: ") 11. a = Console.ReadLine 12. b = Console.ReadLine 13. ok = True 14. Catch Ex As Exception 15. Catturiamo una qualsiasi eccezione e stampiamo il 16. messaggio 17. ad essa relativo. Il messaggio è contenuto nella 18. proprietà Message delloggetto Ex. 19. Console.WriteLine(Ex.Message) 20. ok = False 21. End Try 22. Loop Until ok 23. 24. If b <> 0 Then 25. Console.WriteLine("{0} / {1} = {2}", a, b, a / b) 26. Else 27. Console.WriteLine("Divisione impossibile!") 28. End If 29. 30. Console.ReadKey() 31. End Sub 32. End ModulePr ovando ad inser ir e un numer o tr oppo gr ande o tr oppo piccolo si otter r à "Over flow di unoper azione ar itmetica.";inser endo una str inga non conver tibile in numer o si otter r à "Cast non valido dalla str inga [str inga] al tipo Shor t".
  • Questa ver sione, quindi, cattur a e gestisce ogni possibile eccezione.Ricor date che è possibile usar e anche più clausole Catch in un unico blocco Tr y, ad esempio una per ogni eccezionediver sa.Clausola FinallyIl costr utto Tr y è costituito da un blocco Tr y e da una o più clausole Catch. Tuttavia, opzionalmente, è possibilespecificar e anche unulter ior e clausola, che deve esser e posta dopo tutti i Catch: Finally. Finally dà inizio ad un altr oblocco di codice che viene s empre eseguito, sia che si gener i uneccezione, sia che non se ne gener i alcuna. Il codice ivicontenuto viene eseguito comunque dopo il tr y e il catch. Ad esempio, assumiamo di aver e questo blocco di codice, conalcune istr uzioni di cui non ci inter essa la natur a: mar chiamo le istr uzioni con delle letter e e ipotizziamo che la Dgener i uneccezione: 01. Try 02. A 03. B 04. C 05. D 06. E 07. F 08. Catch Ex As Exception 09. G 10. H 11. Finally 12. I 13. L 14. End TryLe istr uzioni eseguite sar anno: 01. A 02. B 03. C 04. Eccezione: salta nel blocco Catch 05. G 06. H 07. Alla fine esegue comunque il Finally 08. I 09. LLanc iare unec c ezione e c reare ec c ezioni personalizzateAmmettiamo or a di aver bisogno di uneccezione che r appr esenti una par ticolar e cir costanza che si ver ifica solo nlenostr o pr ogr amma, e di cui non esiste un cor r ispettivo tr a le eccezioni pr edefinite del Fr amew or k. Dovr emo scr iver euna nuova eccezione. Per far ciò, bisogna semplicemente dichiar ar e una nuova classe che er editi dalla classe Ex eption: 01. Module Module1 02. Questa classe rappresenta lerrore lanciato quando una 03. password imessa è sbagliata. Per convenzione, tutte le 04. classi che rappresentano uneccezione devono terminare 05. con la parola "Exception" 06. Class IncorrectPasswordException 07. Inherits System.Exception Eredita da Exception 08. 09. Queste proprietà ridefiniscono quelle della classe 10. Exception tramite polimorfismo, perciò sono 11. dichiarate Overrides 12. 13. Sovrascrive il messaggio di errore 14. Public Overrides ReadOnly Property Message() As String 15. Get 16.
  • Return "La password inserita � sbagliata!" 17. End Get 18. End Property 19. 20. Modifica il link di aiuto 21. Public Overrides Property HelpLink() As String 22. Get 23. Return "http://totem.altervista.org" 24. End Get 25. Set(ByVal Value As String) 26. MyBase.HelpLink = value 27. End Set 28. End Property 29. 30. Il resto dei membri di Exception sono molto importanti 31. e vengono inizializzati con dati prelevati tramite 32. Reflection (ultimo argomento di questa sezione), perciò 33. è conveniente non modificare altro. Potete 34. semmai aggiungere qualche membro 35. End Class 36. 37. Sub Main() 38. Dim Pass As String = "b7dha90" 39. Dim NewPass As String 40. 41. Try 42. Console.WriteLine("Inserire la password:") 43. NewPass = Console.ReadLine 44. If NewPass <> Pass Then 45. Lancia leccezione usando la keyword Throw 46. Throw New IncorrectPasswordException 47. End If 48. Catch IPE As IncorrectPasswordException 49. Visualizza il messaggio 50. Console.WriteLine(IPE.Message) 51. E il link daiuto 52. Console.WriteLine("Help: " & IPE.HelpLink) 53. End Try 54. 55. Console.ReadKey() 56. End Sub 57. End ModuleCome si è visto nellesempio, lanciar e uneccezione è molto semplice: basta scr iver e Thr ow , seguito da un oggettoEx ception valido. In questo caso abbiamo cr eato loggetto Incor r ectPassw or dEx ception nello stessa linea di codice in cuilabbiamo lanciato.
  • A33. DistruttoriAvver 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 cheper 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 venivaimpostata su un oggetto COM, il contator e veniva aumentato di 1, mentr e quando quella var iabile veniva distr utta o sene cambiava il valor e, il contator e scendeva di ununità. Quando tale valor e r aggiungeva lo zer o, gli oggetti venivanodistr utti. Er ano pr esenti alcuni pr oblemi di cor r uzione della memor ia, per ò: ad esempio se due oggetti si puntavanovicendevolmente ma non er ano utilizzati dallapplicazione, essi non venivano distr utti (r ifer imento cir colar e).Il meccanismo di gestione della memor ia con il .NET Fr amew or k è molto diver so, e or a vediamo come oper a.Garbage Collec tionQuesto è il nome del pr ocesso sul quale si basa la gestione della memor ia del Fr amew or k. Quando lapplicazione tenta dicr ear e un nuovo oggetto e lo spazio disponibile nellheap managed scar seggia, viene messo in moto questo meccanismo,attr aver so lattivazione del Gar bage Collector . Per pr ima cosa vengono visitati tutti gli oggetti pr esenti nello heap: sece nè uno che non è r aggiungibile dallapplicazione, questo viene distr utto. Il pr ocesso è molto sofisticato, in quanto è ingr ado di r ilevar e anche dipendenze indir ette, come classi non r aggiungibili dir ettamente, r efer enziate da altr e classiche sono r aggiungibili dir ettamente; r iesce anche a r isolver e il pr oblema opposto, quello del r ifer imento cir colar e. Seuno o più oggetti non vengono distr utti per chè sono necessar i al pr ogr amma per funzionar e, si dice che essi sonosopr avvissuti a una Gar bage Collection e appar tengono alla gener azione 1, mentr e quelli inizializzati che non hannosubito ancor a nessun pr ocesso di r accolta della memor ia sono di gener azione 0. Lindice gener azionale vieneincr ementato di uno fino ad un massimo di 2. Questi ultimi oggetti sono sopr avvissuti a molti contr olli, il che significache continuano a esser e utilizzati nello stesso modo: per ciò il Gar bage Collector li sposta in una posizione inizialedellheap managed, in modo che si dovr anno eseguir e meno oper azioni di spostamento della memor ia in seguito. Lastessa cosa vale per le gener azioni successive. Questo sistema assicur a che ci sia sempr e spazio liber o, ma nongar antisce che ogni oggetto logicamente distr utto lo sia anche fisicamente: se per quegli oggetti che allocano solomemor ia il pr oblema è r elativo, per altr i che utilizzano file e r isor se ester ne, invece, diventa più complicato. Ilcompito 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 chequando loggetto venga distr utto lo siano cor r ettamente anche le r isor se ad esso associate. Bisogna quindi far eeseguir 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 lar accolta della memor ia. Come già detto, non è possibile saper e quando un oggetto logicamente distr utto lo sar à anchefisicamente, quindi Finalize potr ebbe esser e eseguito anche diver si secondi, o minuti, o addir ittur a or e, dopo che siastato annullato ogni r ifer imento alloggetto. Come seconda clausola impor tante, è necessar io non acceder e m ai adoggetti ester ni in una pr ocedur a Finalize: dato che il GC (acr onimo di gar bage collector ) può distr ugger e gli oggetti inqualsiasi or dine, non si può esser e sicur i che loggetto a cui si sta facendo r ifer imento esista ancor a o sia già statodistr 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 anchessi istanze di System.Type, che definisce le car atter istiche di ciascun tipo,
  • sono soggetti alla GC alla fine del pr ogr amma). Per saper e se il pr ocesso di distr uzione è stato avviato dalla chiusur adel pr ogr amma si può r ichiamar e una semplice pr opr ietà booleana, Envir onment.HasShutdow nStar ted. Peresemplificar e i concetti, in questo par agr afo far ò uso delloggetto singleton GC, che r appr esenta il Gar bage Collector ,per mettendo di avviar e for zatamente la r accolta della memor ia e altr e cose: questo no n deve mai esser e fatto inunapplicazione r eale, poichè potr ebbe compr ometter ne le pr estazioni. 01. Module Module1 02. Class Oggetto 03. Sub New() 04. Console.WriteLine("Un oggetto sta per essere creato.") 05. End Sub 06. La procedura Finalize è definita in System.Object, quindi, 07. per ridefinirla dobbiamo usare il polimorfismo. Inoltre 08. deve essere dichiarata Protected, poichè non può 09. essere richiamata da altro ente se non dal GC e allo 10. stesso tempo è ereditabile 11. Protected Overrides Sub Finalize() 12. Console.WriteLine("Un oggetto sta per essere distrutto.") 13. Blocca il programma per 4 secondi circa, consentendoci 14. di vedere cosa viene scritto a schermo 15. System.Threading.Thread.CurrentThread.Sleep(4000) 16. End Sub 17. End Class 18. 19. Sub Main() 20. Dim O As New Oggetto 21. Console.WriteLine("Oggetto = Nothing") 22. Console.WriteLine("Lapplicazione sta per terminare.") 23. End Sub 24. End ModuleLoutput sar à: 1. Un oggetto sta per essere creato. 2. Oggetto = Nothing 3. Lapplicazione sta per terminare. 4. Un oggetto sta per essere distrutto.Come si vede, loggetto viene distr utto do po il ter mine dellapplicazione (siamo for tunati che Console è ancor a "in vita"pr ima della distr uzione): questo significa che cer a abbastanza spazio disponibile da non avviar e la GC, che quindi èstata r imandata fino alla fine del pr ogr amma. Ripr oviamo invece in questo modo: 01. Sub Main() 02. Dim O As New Oggetto 03. O = Nothing 04. Console.WriteLine("Oggetto = Nothing") 05. 06. NON PROVATECI A CASA! 07. Forza una garbage collection 08. GC.Collect() 09. Attende che tutti i metodi Finalize siano stati eseguiti 10. GC.WaitForPendingFinalizers() 11. 12. Console.WriteLine("Lapplicazione sta per terminare.") 13. Console.ReadKey() 14. End SubCiò che appar ir à sullo scher mo è: 1. Un oggetto sta per essere creato. 2. Oggetto = Nothing 3. Un oggetto sta per essere distrutto. 4. Lapplicazione sta per terminare.Si vede che lor dine delle ultime due azioni è stato cambiato a causa delle GC avviata anzi tempo pr ima del ter mine delpr ogr amma.
  • Anche se ci siamo diver titi con Finalize, questo metodo deve esser e definito solo se str ettamente necessar io, peralcune r agioni. La pr ima è che il GC impiega non uno, ma due cicli per finalizzar e un oggetto in cui è stata definitaFinalize dal pr ogr ammator e. Il motivo consiste nella possibilità che venga usata la cosiddetta r esur r ezio nedello g g etto : in questa tecnica, ad una var iabile globale viene assegnato il r ifer imento alla classe stessa usando Me;dato che in questo modo cè ancor a un r ifer imento valido alloggetto, questo non deve venir e distr utto. Tuttavia, perr ilevar e questo fenomeno, il GC impiega due cicli e si r ischia di occupar e memor ia inutile. Inoltr e, sempr e per questacausa, 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 lamemor ia (ossia connessioni a database, files, immagini, pennelli, oggetti di sistema, ecceter a...) manualmente, appenapr ima di impostar e il r ifer imento a Nothing. In questo modo non si dovr à aspettar e una successiva GC affinchè siar ilasciato tutto cor r ettamente. Dispose non è un metodo definito da tutti gli oggetti, e per ciò ogni classe che intendedefinir lo deve implementar e linter 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 lagor mento delleinter facce. 01. Class Oggetto 02. Implementa linterfaccia IDisposable 03. Implements IDisposable 04. File da scrivere: 05. Dim W As IO.StreamWriter 06. 07. Sub New() 08. Inizializza loggetto 09. W = New IO.StreamWriter("C:test.txt") 10. End Sub 11. 12. Public Sub Dispose() Implements IDisposable.Dispose 13. Chiude il file 14. W.Close() 15. End Sub 16. End ClassInvocando il metodo Dispose di Oggetto, è possibile chiuder e il file ed evitar e che venga lasciato aper to. Il Vb.NETfor nisce un costr utto, valido per tutti gli oggetti che implementano linter faccia IDisposable, che si assicur a dir ichiamar e il metodo Dispose e impostar e il r ifer imento a Nothing automaticamente dopo luso. La sintassi è questa: 1. Using [Oggetto] 2. Codice da eseguire 3. End Using 4. 5. Che corrisponde a scrivere: 6. Codice da eseguire 7. [Oggetto].Dispose() 8. [Oggetto] = NothingPer convenzione, se una classe implementa uninter faccia IDisposable e contiene altr e classi nidificate o altr i oggetti, ilsuo metodo Dispose deve r ichiamar e il Dispose di tutti gli oggetti inter ni, almeno per quelli che ce lhanno. Altr aconvenzione è che se viene r ichiamata Dispose da un oggetto già distr utto logicamente, deve gener ar si leccezioneObjectDisposedEx ception.Usare Dispose e FinalizeCi sono alcune cir costanze che r ichiedono luso di una sola delle due, altr e che non le r ichiedono e altr e ancor a chedovr ebber o r cihieder le entr ambe. Segue una piccola lista di sugger imenti su come metter e in pr atica questi
  • meccanismi: Nè Dispose, nè Finalize: la classe impiega solo la memor ia come unica r isor sa o, se ne impiegate altr e, le r ilascia pr ima di ter minar e le pr opr ie oper azioni. Solo Dispose: la classe impiega r isor se facendo r ifer imento ad altr i oggetti .NET e si vuole for nir e al chiamante la possibilità di r ilasciar e tali r isor se il pr ima possibile. Dispose e Finalize: la classe impiega dir ettamente una r isor sa, ad esempio invocando un metodo di una libr er ia unmanaged, che r ichiede un r ilascio esplicito; in più si vuole for nir e al client la possibilità di deallocar e manualmente gli oggetti. Solo Finalize: si deve eseguir e un cer to codice pr ima della distr uzione.A questo punto ci si deve pr eoccupar e di due pr oblemi che possono pr esentar si: Finalize può esser e chiamato anchedopo che loggetto è stato distr utto e le sue r isor se deallocate con Dispose, quindi potr ebbe tantar e di distr ugger e unoggetto inesistente; il codice che viene eseguito in Finalize potr ebbe far r ifer imento a oggetti inesistenti. Leconvenzioni per mettono di aggir ar e il pr oblema facendo uso di ver sioni in over load di Dispose e di una var iabilepr ivata a livello di classe. La var iabile booleana Disposed ha il compito di memor izzar e se loggetto è stato distr utto: inquesto modo eviter emo di r ipeter e il codice in Finalize. Il metodo in over load di Dispose accetta un par ametr o di tipobooleano, di solito chiamato Disposing, che indica se loggetto sta subendo un pr ocesso di distr uzione manuale o difinalizzazione: pr ocedendo con questo metodo si è cer ti di r ichiamar e eventuali altr i oggetti nel caso non ci siafinalizzazione. Il codice seguente implementa una semplicissima classe FileWr iter e, tr amite messaggi a scher mo,visualizza quando e come loggetto viene r imosso dalla memor ia: 001. Module Module1 002. Class FileWriter 003. Implements IDisposable 004. 005. Private Writer As IO.StreamWriter 006. Indica se loggetto è già stato distrutto con Dispose 007. Private Disposed As Boolean 008. Indica se il file è aperto 009. Private Opened As Boolean 010. 011. Sub New() 012. Disposed = False 013. Opened = False 014. Console.WriteLine("FileWriter sta per essere creato.") 015. Questa procedura comunica al GC di non richiamare più 016. il metodo Finalize per questo oggetto. Scriviamo ciò 017. perchè se file non viene esplicitamente aperto con 018. Open non cè alcun bisogno di chiuderlo 019. GC.SuppressFinalize(Me) 020. End Sub 021. 022. Apre il file 023. Public Sub Open(ByVal FileName As String) 024. Writer = New IO.StreamWriter(FileName) 025. Opened = True 026. Console.WriteLine("FileWriter sta per essere aperto.") 027. Registra loggetto per eseguire Finalize: ora il file 028. è aperto e può quindi essere chiuso 029. GC.ReRegisterForFinalize(Me) 030. End Sub 031. 032. Scrive del testo nel file 033. Public Sub Write(ByVal Text As String) 034. If Opened Then 035. Writer.Write(Text) 036. End If 037. End Sub 038. 039. Una procedura analoga a Open aiuta a impostare meglio 040. loggetto e non fa altro che richiamare Dispose: è 041. più una questione di completezza 042.
  • Public Sub Close() 043. Dispose() 044. End Sub 045. 046. Questa versione è in overload perchè laltra viene 047. chiamata solo dallutente (è Public), mentre questa 048. implementa tutto il codice che è necessario eseguire 049. per rilasciare le risorse. 050. Il parametro Disposing indica se loggetto sta per 051. essere distrutto, quindi manualmente, o finalizzato, 052. quindi nel processo di GC: nel secondo caso altri oggetti 053. che questa classe utilizza potrebbero non esistere più, 054. perciò si deve controllare se è possibile 055. invocarli correttamente 056. Protected Overridable Overloads Sub Dispose(ByVal Disposing _ 057. As Boolean) 058. Esegue il codice solo se loggetto esiste ancora 059. If Disposed Then 060. Se è distrutto, esce dalla procedura 061. Exit Sub 062. End If 063. 064. If Disposing Then 065. Qui possiamo chiamare altri oggetti con la 066. sicurezza che esistano ancora 067. Console.WriteLine("FileWriter sta per essere distrutto.") 068. Else 069. Console.WriteLine("FileWriter sta per essere finalizzato.") 070. End If 071. 072. Chiude il file 073. Writer.Close() 074. 075. Disposed = True 076. Opened = False 077. End Sub 078. 079. Public Overloads Sub Dispose() Implements IDisposable.Dispose 080. Loggetto è stato distrutto 081. Dispose(True) 082. Quindi non deve più essere finalizzato 083. GC.SuppressFinalize(Me) 084. End Sub 085. 086. Protected Overrides Sub Finalize() 087. Processo di finalizzazione: 088. Dispose(False) 089. End Sub 090. End Class 091. 092. Sub Main() 093. Dim F As New FileWriter 094. Questo blocco mostra lesecuzione di Dispose 095. F.Open("C:test.txt") 096. F.Write("Ciao") 097. F.Close() 098. 099. Questo mostra lesecuzione di Finalize 100. F = New FileWriter 101. F.Open("C:test2.txt") 102. F = Nothing 103. 104. GC.Collect() 105. GC.WaitForPendingFinalizers() 106. 107. Console.ReadKey() 108. End Sub 109. End ModuleLoutput: 1. FileWriter sta per essere creato. 2.
  • FileWriter sta per essere aperto.3. FileWriter sta per essere distrutto.4. FileWriter sta per essere creato.5. FileWriter sta per essere aperto.6. FileWriter sta per essere finalizzato.
  • A34. I DelegateCon il ter mine Deleg ate si indica un par ticolar e tipo di dato che è in gr ado di "contener e" un metodo, ossia unapr ocedur a o una funzione. Ho messo di pr oposito le vir golette sul ver bo "contener e", poiché non è pr opr iamenteesatto, ma ser ve per r ender e più incisiva la definizione. Come esistono tipi di dato per gli inter i, i decimali, le date, lestr inghe, gli oggetti, ne esistono anche per i metodi, anche se può sembr ar e un po str ano. Per chi avesse studiatoaltr i linguaggi pr ima di appr occiar si al VB.NET, possiamo assimilar e i Delegate ai tipi pr ocedur ali del Pascal o aipuntator i a funzione del C. Ad ogni modo, i delegate sono legger mente diver si da questi ultimi e pr esentano alcunitr 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). Daltr 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 unaltr 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 lavvio 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 ilcompor tamento dei delegate.Dic hiarazione di un delegateUn nuovo tipo delegate viene dichiar ato con questa sintassi: 1. Delegate [Sub/Function] [Nome]([Elenco parametri])Appar e subito chiar o il legame con i metodi data la for tissima somiglianza della sintassi con quella usata per definir e,appunto, un metodo. Notate che in questo caso si specifica solo la signatur e (tipo e quantità dei par ametr i) e lacategor ia (pr ocedur a o funzione) del delegate, mentr e il [Nome] indica il nome del nuovo tipo cr eato (così come il nomedi una nuova classe o una nuova str uttur a), ma non vi è tr accia del "cor po" del delegate. Un delegate, infatti, non hacor po, per chè, se invocato da un oggetto, esegue i metodi che esso stesso contiene, e quindi esegue il codice contenutonei lor o cor pi. Da questo momento in poi, potr emo usar e nel codice questo nuovo tipo per immagazzinar e inter imetodi con le stesse car atter istiche appena definite. Dato che si tr atta di un tipo r efer ence, per ò, bisogna ancheinizializzar e loggetto con un costr uttor e... Qui dovr ebbe sor ger e spontaneamente un dubbio: dove e come si dichiar ail 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 leclassi, 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 cuipor r e un ipotetico costr uttor e. La r ealtà è che si usa sem pr e il costr uttor e di default, ossia quello pr edefinito, che
  • viene automaticamente cr eato allatto stesso della dichiar azione, anche se noi non r iusciamo a veder lo. Questocostr uttor e accetta sempr e e solo un par ametr o: un oggetto di tipo indeter minato r estituito da uno specialeoper ator e, Addr essOf. Questo è un oper ator e unar io che accetta come oper ando il metodo di cui ottener e l"indir izzo": 1. AddressOf [NomeMetodo]Ciò che Addr essOf r estituisce non è molto chiar o: la sua descr izione dice espr essamente che viene r estituito un oggettodelegate (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. Malasciamo 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 unmetodo con la signatur e specificata. Ecco un esempio: 01. Module Module1 02. Dichiarazione di un tipo delegate Sub che accetta un parametro 03. di tipo stringa. 04. Delegate Sub Display(ByVal Message As String) 05. 06. Una procedura dimostrativa 07. Sub Write1(ByVal S As String) 08. Console.WriteLine("1: " & S) 09. End Sub 10. 11. Unaltra procedura dimostrativa 12. Sub Write2(ByVal S As String) 13. Console.WriteLine("2: " & S) 14. End Sub 15. 16. Sub Main() 17. Variabile D di tipo Display, ossia il nuovo tipo 18. delegate appena definito allinizio del modulo 19. Dim D As Display 20. 21. Inizializa D con un nuovo oggetto delegate contenente 22. un riferimento al metodo Console.WriteLine 23. D = New Display(AddressOf Console.WriteLine) 24. 25. Invoca il metodo referenziato da D: in questo caso 26. equivarrebbe a scrivere Console.WriteLine("Ciao") 27. D("Ciao") 28. 29. Reinizializza D, assegnandogli lindirizzo di Write1 30. D = New Display(AddressOf Write1) 31. è come chiamare Write1("Ciao") 32. D("Ciao") 33. 34. Modo alternativo per inizializzare un delegate: si omette 35. New e si usa solo AddressOf. Questo genera una conversione 36. implicita che dà errore di cast nel caso in cui Write1 37. non sia compatibile con la signature del delegate 38. D = AddressOf Write2 39. D("Ciao") 40. 41. Notare che D può contenere metodi di istanza 42. (come Console.WriteLine) e metodi statici (come Write1 43. e Write2) 44. 45. Console.ReadKey() 46. End Sub 47. End ModuleLa signatur e di un delegate no n può contener e par ametr i indefiniti (Par amAr r ay) od opzionali (Optional), tuttavia imetodi memor izzati in un oggetto di tipo delegate possono aver e par ametr i di questo tipo. Eccone un esempio: 001. Module Module1 002.
  • Tipo delegate che può contenere riferimenti a funzioni Single003. che accettino un parametro di tipo array di Single004. Delegate Function ProcessData(ByVal Data() As Single) As Single005. Tipo delegate che può contenere riferimenti a procedure006. che accettino due parametri, un array di Single e un Boolean007. Delegate Sub PrintData(ByVal Data() As Single, ByVal ReverseOrder As Boolean)008.009. Funzione che calcola la media di alcuni valori. Notare che010. lunico parametro è indefinito, in quanto011. dichiarato come ParamArray012. Function CalculateAverage(ByVal ParamArray Data() As Single) As Single013. Dim Total As Single = 0014.015. For I As Int32 = 0 To Data.Length - 1016. Total += Data(I)017. Next018.019. Return (Total / Data.Length)020. End Function021.022. Funzione che calcola la varianza di alcuni valori. Notare che023. anche in questo caso il parametro è indefinito024. Function CalculateVariance(ByVal ParamArray Data() As Single) As Single025. Dim Average As Single = CalculateAverage(Data)026. Dim Result As Single = 0027.028. For I As Int32 = 0 To Data.Length - 1029. Result += (Data(I) - Average) ^ 2030. Next031.032. Return (Result / Data.Length)033. End Function034.035. Procedura che stampa i valori di un array in ordine normale036. o inverso. Notare che il secondo parametro è opzionale037. Sub PrintNormal(ByVal Data() As Single, _038. Optional ByVal ReverseOrder As Boolean = False)039. If ReverseOrder Then040. For I As Int32 = Data.Length - 1 To 0 Step -1041. Console.WriteLine(Data(I))042. Next043. Else044. For I As Int32 = 0 To Data.Length - 1045. Console.WriteLine(Data(I))046. Next047. End If048. End Sub049.050. Procedura che stampa i valori di un array nella forma:051. "I+1) Data(I)"052. Notare che anche in questo caso il secondo parametro053. è opzionale054. Sub PrintIndexed(ByVal Data() As Single, _055. Optional ByVal ReverseOrder As Boolean = False)056. If ReverseOrder Then057. For I As Int32 = Data.Length - 1 To 0 Step -1058. Console.WriteLine("{0}) {1}", Data.Length - I, Data(I))059. Next060. Else061. For I As Int32 = 0 To Data.Length - 1062. Console.WriteLine("{0}) {1}", (I + 1), Data(I))063. Next064. End If065. End Sub066.067. Sub Main()068. Dim Process As ProcessData069. Dim Print As PrintData070. Dim Data() As Single071. Dim Len As Int32072. Dim Cmd As Char073.074.
  • Console.WriteLine("Quanti valori inserire?") 075. Len = Console.ReadLine 076. 077. ReDim Data(Len - 1) 078. For I As Int32 = 1 To Len 079. Console.Write("Inserire il valore " & I & ": ") 080. Data(I - 1) = Console.ReadLine 081. Next 082. 083. Console.Clear() 084. 085. Console.WriteLine("Scegliere loperazione da eseguire: ") 086. Console.WriteLine("m - Calcola la media dei valori;") 087. Console.WriteLine("v - Calcola la varianza dei valori;") 088. Cmd = Console.ReadKey().KeyChar 089. Select Case Cmd 090. Case "m" 091. Process = New ProcessData(AddressOf CalculateAverage) 092. Case "v" 093. Process = New ProcessData(AddressOf CalculateVariance) 094. Case Else 095. Console.WriteLine("Comando non valido!") 096. Exit Sub 097. End Select 098. Console.WriteLine() 099. Console.WriteLine("Scegliere il metodo di stampa: ") 100. Console.WriteLine("s - Stampa i valori;") 101. Console.WriteLine("i - Stampa i valori con il numero ordinale a fianco.") 102. Cmd = Console.ReadKey().KeyChar 103. Select Case Cmd 104. Case "s" 105. Print = New PrintData(AddressOf PrintNormal) 106. Case "i" 107. Print = New PrintData(AddressOf PrintIndexed) 108. Case Else 109. Console.WriteLine("Comando non valido!") 110. Exit Sub 111. End Select 112. 113. Console.Clear() 114. 115. Console.WriteLine("Valori:") 116. Eccoci arrivati al punto. Come detto prima, i delegate 117. non possono definire una signature che comprenda parametri 118. opzionali o indefiniti, ma si 119. può aggirare questa limitazione semplicemente dichiarando 120. un array di valori al posto del ParamArray (in quanto si 121. tratta comunque di due vettori) e lo stesso parametro 122. non opzionale al posto del parametro opzionale. 123. Linconveniente, in questo ultimo caso, è che il 124. parametro, pur essendo opzionale va sempre specificato 125. quando il metodo viene richiamato attraverso un oggetto 126. delegate. Questo escamotage permette di aumentare la 127. portata dei delegate, includendo anche metodi che 128. possono essere stati scritti tempo prima in unaltra 129. parte inaccessibile del codice: così 130. non è necessario riscriverli! 131. Print(Data, False) 132. Console.WriteLine("Risultato:") 133. Console.WriteLine(Process(Data)) 134. 135. Console.ReadKey() 136. End Sub 137. 138. End ModuleUn esempio più signific ativoI delegate sono par ticolar mente utili per r ispar miar e spazio nel codice. Tr amite i delegate, infatti, possiamo usar e lo
  • stesso metodo per eseguir e più compiti differ enti. Dato che una var iabile delegate contiene un r ifr iento ad un metodoqualsiasi, semplicemente cambiando questo r ifer imento possiamo eseguir e codici diver si r ichiamando la stessavar iabile. E come se potessimo "innestar e" del codice sempr e diver so su un substr ato costante. Ecco un esempiopiccolo, ma significativo: 01. Module Module2 02. Nome del file da cercare 03. Dim File As String 04. 05. Questo delegate referenzia una funzione che accetta un 06. parametro stringa e restituisce un valore booleano 07. Delegate Function IsMyFile(ByVal FileName As String) As Boolean 08. 09. Funzione 1, stampa il contenuto del file a schermo 10. Function PrintFile(ByVal FileName As String) As Boolean 11. Io.Path.GetFileName(F) restituisce solo il nome del 12. singolo file F, togliendo il percorso delle cartelle 13. If IO.Path.GetFileName(FileName) = File Then 14. IO.File.ReadAllText(F) restituisce il testo contenuto 15. nel file F in una sola operazione 16. Console.WriteLine(IO.File.ReadAllText(FileName)) 17. Return True 18. End If 19. Return False 20. End Function 21. 22. Funzione 2, copia il file sul desktop 23. Function CopyFile(ByVal FileName As String) As Boolean 24. If IO.Path.GetFileName(FileName) = File Then 25. IO.File.Copy(S, D) copia il file S nel file D: 26. se D non esiste viene creato, se esiste viene 27. sovrascritto 28. IO.File.Copy(FileName, _ 29. My.Computer.FileSystem.SpecialDirectories.Desktop & _ 30. "" & File) 31. Return True 32. End If 33. Return False 34. End Function 35. 36. Procedura ricorsiva che cerca il file 37. Function SearchFile(ByVal Dir As String, ByVal IsOK As IsMyFile) _ 38. As Boolean 39. Ottiene tutte le sottodirectory 40. Dim Dirs() As String = IO.Directory.GetDirectories(Dir) 41. Ottiene tutti i files 42. Dim Files() As String = IO.Directory.GetFiles(Dir) 43. 44. Analizza ogni file per vedere se è quello cercato 45. For Each F As String In Files 46. È il file cercato, basta cercare 47. If IsOK(F) Then 48. Termina la funzione e restituisce Vero, cosicché 49. anche nel for sulle cartelle si termini 50. la ricerca 51. Return True 52. End If 53. Next 54. 55. Analizza tutte le sottocartelle 56. For Each D As String In Dirs 57. If SearchFile(D, IsOK) Then 58. Termina ricorsivamente la ricerca 59. Return True 60. End If 61. Next 62. End Function 63. 64. Sub Main() 65. Dim Dir As String 66.
  • 67. Console.WriteLine("Inserire il nome file da cercare:") 68. File = Console.ReadLine 69. 70. Console.WriteLine("Inserire la cartella in cui cercare:") 71. Dir = Console.ReadLine 72. 73. Cerca il file e lo scrive a schermo 74. SearchFile(Dir, AddressOf PrintFile) 75. 76. Cerca il file e lo copia sul desktop 77. SearchFile(Dir, AddressOf CopyFile) 78. 79. Console.ReadKey() 80. End Sub 81. End ModuleNel sor gente si vede che si usano pochissime r ighe per far compier e due oper azioni molto differ enti alla stessapr ocedur a. In altr e condizioni, un aspir ante pr ogr ammator e che non conoscesse i delegate avr ebbe scr itto duepr ocedur e inter e, spr ecando più spazio, e condannandosi, inoltr e, a r iscr iver e la stessa cosa per ogni futur a var iante.
  • A35. I Delegate MulticastAl 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. Lunico modo per far lo è r ichiamar e il metodostatico Combine della classe System.Delegate (ossia la classe base di tutti i delegate). Combine espone anche un over loadche per mette di unir e molti delegate alla volta, specificandoli tr amite un Par amAr r ay. Dato che un delegate multicastcontiene più r ifer imenti a metodi distinti, si par la di inv o catio n list (lista di invocazione) quando ci si r ifer isceallinsieme di tutti i metodi memor izzati in un delegate multicast. Ecco un semplice esempio: 01. Module Module2 02. Vedi esempio precedente 03. Sub Main() 04. Dim Dir As String 05. Dim D As IsMyFile 06. 07. Console.WriteLine("Inserire il nome file da cercare:") 08. File = Console.ReadLine 09. 10. Console.WriteLine("Inserire la cartella in cui cercare:") 11. Dir = Console.ReadLine 12. 13. Crea un delegate multicast, unendo PrintFile e CopyFile. 14. Da notare che in questa espressione è necessario usare 15. delle vere e proprie variabili delegate, poiché 16. loperatore AddressOf da solo non è valido in questo caso 17. D = System.Delegate.Combine(New IsMyFile(AddressOf PrintFile), _ 18. New IsMyFile(AddressOf CopyFile)) 19. Per la cronaca, Combine è un metodo factory 20. 21. Ora il file trovato viene sia visualizzato che copiato 22. sul desktop 23. SearchFile(Dir, D) 24. 25. Se si vuole rimuovere uno o più riferimenti a metodi del 26. delegate multicast si deve utilizzare il metodo statico Remove: 27. D = System.Delegate.Remove(D, New IsMyFile(AddressOf CopyFile)) 28. Ora D farà visualizzare solamente il file trovato 29. 30. Console.ReadKey() 31. End Sub 32. End ModuleLa 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.Nellesempio, 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). Comeabbiamo 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 allaltr a non si per de alcun dato, ma si gener elizza soltanto il valor e r appr esentato; eseguir e ilpassaggio inver so, invece, ossia assegnar e una classe base a una der ivata, può r isultar e in qualche str ano er r or eper chè i membr i in più della classe der ivata sono vuoti. Nel caso dei delegate, che sono oggetti immutabili, e che quindinon espongono pr opr ietà modificabili, questo non è un pr oblema, ma il compilator e questo non lo sa. Per esser e sicur i,è meglio utilizzar e un oper ator e di cast come Dir ectCast: 1. DirectCast(System.Delegate.Combine(A, B), IsMyFile)N.B.: Quando un delegate multicast contiene delle funzioni e viene r ichiamato, il valor e r estituito è quello della pr ima
  • funzione memor izzata.Ecco or a un altr o esempio molto ar ticolato sui delegate multicast: 001. Questo esempio si basa completamente sulla manipolazione 002. di file e cartelle, argomento non ancora affrontato. Se volete, 003. potete dare uno sguardo ai capitoli relativi nelle parti 004. successive della guida, oppure potete anche limitarvi a leggere 005. i commenti, che spiegano tutto ciò che accade. 006. Module Module1 007. In questo esempio eseguiremo delle operazioni su file con i delegate. 008. Nel menù sarà possibile scegliere quali operazioni 009. eseguire (una o tutte insieme) e sotto quali condizioni modificare 010. un file. 011. Il delegate FileFilter rappresenta una funzione che restituisce 012. True se la condizione è soddisfatta. Le condizioni 013. sono racchiuse in un delegate multicast che contiene più 014. funzioni di questo tipo 015. Delegate Function FileFilter(ByVal FileName As String) As Boolean 016. Il prossimo delegate rappresenta unoperazione su un file 017. Delegate Sub MassFileOperation(ByVal FileName As String) 018. AskForData è un delegate del tipo più semplice. 019. Servirà per reperire le informazioni necessarie ad 020. eseguire le operazioni (ad esempio, se si sceglie di copiare 021. tutti i file di una cartella, si dovrà anche scegliere 022. dove copiare questi file). 023. Delegate Sub AskForData() 024. 025. Queste variabili globali rappresentano le informazioni necesarie 026. per lo svolgimento delle operazioni o la verifica delle condizioni. 027. 028. Stringa di formato per rinominare i file 029. Dim RenameFormat As String 030. Posizione di un file nella cartella 031. Dim FileIndex As Int32 032. Directory in cui copiare i file 033. Dim CopyDirectory As String 034. File in cui scrivere. Il tipo StreamWriter permette di scrivere 035. facilmente stringhe su un file usando WriteLine come in Console 036. Dim LogFile As IO.StreamWriter 037. Limitazioni sulla data di creazione del file 038. Dim CreationDateFrom, CreationDateTo As Date 039. Limitazioni sulla data di ultimo accesso al file 040. Dim LastAccessDateFrom, LastAccessDateTo As Date 041. Limitazioni sulla dimensione 042. Dim SizeFrom, SizeTo As Int64 043. 044. Rinomina un file 045. Sub Rename(ByVal Path As String) 046. Ne prende il nome semplice, senza estensione 047. Dim Name As String = IO.Path.GetFileNameWithoutExtension(Path) 048. Apre un oggetto contenente le informazioni sul file 049. di percorso Path 050. Dim Info As New IO.FileInfo(Path) 051. 052. Formatta il nome secondo la stringa di formato RenameFormat 053. Name = String.Format(RenameFormat, _ 054. Name, FileIndex, Info.Length, Info.LastAccessTime, Info.CreationTime) 055. E aggiunge ancora lestensione al nome modificato 056. Name &= IO.Path.GetExtension(Path) 057. Copia il vecchio file nella stessa cartella, ma con il nuovo nome 058. IO.File.Copy(Path, IO.Path.GetDirectoryName(Path) & "" & Name) 059. Elimina il vecchio file 060. IO.File.Delete(Path) 061. 062. Aumenta lindice di uno 063. FileIndex += 1 064. End Sub 065. 066. Funzione che richiede i dati necessari per far funzionare 067. il metodo Rename 068. Sub InputRenameFormat() 069.
  • Console.WriteLine("Immettere una stringa di formato valida per rinominare i file.")070. Console.WriteLine("I parametri sono:")071. Console.WriteLine("0 = Nome originale del file;")072. Console.WriteLine("1 = Posizione del file nella cartella, in base 0;")073. Console.WriteLine("2 = Dimensione del file, in bytes;")074. Console.WriteLine("3 = Data dellultimo accesso;")075. Console.WriteLine("4 = Data di creazione.")076. RenameFormat = Console.ReadLine077. End Sub078.079. Elimina un file di percorso Path080. Sub Delete(ByVal Path As String)081. IO.File.Delete(Path)082. End Sub083.084. Copia il file da Path alla nuova cartella085. Sub Copy(ByVal Path As String)086. IO.File.Copy(Path, CopyDirectory & "" & IO.Path.GetFileName(Path))087. End Sub088.089. Richiede una cartella valida in cui copiare i file. Se non esiste,090. la crea091. Sub InputCopyDirectory()092. Console.WriteLine("Inserire una cartella valida in cui copiare i file:")093. CopyDirectory = Console.ReadLine094. If Not IO.Directory.Exists(CopyDirectory) Then095. IO.Directory.CreateDirectory(CopyDirectory)096. End If097. End Sub098.099. Scrive il nome del file sul file aperto100. Sub Archive(ByVal Path As String)101. LogFile.WriteLine(IO.Path.GetFileName(Path))102. End Sub103.104. Chiede il nome di un file su cui scrivere tutte le informazioni105. Sub InputLogFile()106. Console.WriteLine("Inserire il percorso del file su cui scrivere:")107. LogFile = New IO.StreamWriter(Console.ReadLine)108. End Sub109.110. Verifica che la data di creazione del file cada tra i limiti fissati111. Function IsCreationDateValid(ByVal Path As String) As Boolean112. Dim Info As New IO.FileInfo(Path)113. Return (Info.CreationTime >= CreationDateFrom) And (Info.CreationTime >= CreationDateTo)114. End Function115.116. Richiede di immettere una limitazione temporale per considerare117. solo certi file118. Sub InputCreationDates()119. Console.WriteLine("Verranno considerati solo i file con data di creazione:")120. Console.Write("Da: ")121. CreationDateFrom = Date.Parse(Console.ReadLine)122. Console.Write("A: ")123. CreationDateTo = Date.Parse(Console.ReadLine)124. End Sub125.126. Verifica che la data di ultimo accesso al file cada tra i limiti fissati127. Function IsLastAccessDateValid(ByVal Path As String) As Boolean128. Dim Info As New IO.FileInfo(Path)129. Return (Info.LastAccessTime >= LastAccessDateFrom) And (Info.LastAccessTime >= LastAccessDateTo)130. End Function131.132. Richiede di immettere una limitazione temporale per considerare133. solo certi file134. Sub InputLastAccessDates()135. Console.WriteLine("Verranno considerati solo i file con data di creazione:")136. Console.Write("Da: ")137. LastAccessDateFrom = Date.Parse(Console.ReadLine)138. Console.Write("A: ")139.
  • LastAccessDateTo = Date.Parse(Console.ReadLine)140. End Sub141.142. Verifica che la dimensione del file sia coerente coi limiti fissati143. Function IsSizeValid(ByVal Path As String) As Boolean144. Dim Info As New IO.FileInfo(Path)145. Return (Info.Length >= SizeFrom) And (Info.Length >= SizeTo)146. End Function147.148. Richiede di specificare dei limiti dimensionali per i file149. Sub InputSizeLimit()150. Console.WriteLine("Verranno considerati solo i file con dimensione compresa:")151. Console.Write("Tra (bytes):")152. SizeFrom = Console.ReadLine153. Console.Write("E (bytes):")154. SizeTo = Console.ReadLine155. End Sub156.157. Classe che rappresenta unoperazione eseguibile su file158. Class Operation159. Private _Description As String160. Private _Execute As MassFileOperation161. Private _RequireData As AskForData162. Private _Enabled As Boolean163.164. Descrizione165. Public Property Description() As String166. Get167. Return _Description168. End Get169. Set(ByVal value As String)170. _Description = value171. End Set172. End Property173.174. Variabile che contiene loggetto delegate associato175. a questa operazione, ossia un riferimento a una delle Sub176. definite poco sopra177. Public Property Execute() As MassFileOperation178. Get179. Return _Execute180. End Get181. Set(ByVal value As MassFileOperation)182. _Execute = value183. End Set184. End Property185.186. Variabile che contiene loggetto delegate che serve187. per reperire informazioni necessarie ad eseguire188. loperazione, ossia un riferimento a una delle sub189. di Input definite poco sopra. E Nothing quando190. non serve nessun dato ausiliario (come nel caso191. di Delete)192. Public Property RequireData() As AskForData193. Get194. Return _RequireData195. End Get196. Set(ByVal value As AskForData)197. _RequireData = value198. End Set199. End Property200.201. Determina se loperazione va eseguita oppure no202. Public Property Enabled() As Boolean203. Get204. Return _Enabled205. End Get206. Set(ByVal value As Boolean)207. _Enabled = value208. End Set209. End Property210.211.
  • Sub New(ByVal Description As String, _212. ByVal ExecuteMethod As MassFileOperation, _213. ByVal RequireDataMethod As AskForData)214. Me.Description = Description215. Me.Execute = ExecuteMethod216. Me.RequireData = RequireDataMethod217. Me.Enabled = False218. End Sub219. End Class220.221. Classe che rappresenta una condizione a cui sottoporre222. i file nella cartella: verranno elaborati solo quelli che223. soddisfano tutte le condizioni224. Class Condition225. Private _Description As String226. Private _Verify As FileFilter227. Private _RequireData As AskForData228. Private _Enabled As Boolean229.230. Public Property Description() As String231. Get232. Return _Description233. End Get234. Set(ByVal value As String)235. _Description = value236. End Set237. End Property238.239. Contiene un oggetto delegate associato a una delle240. precedenti funzioni241. Public Property Verify() As FileFilter242. Get243. Return _Verify244. End Get245. Set(ByVal value As FileFilter)246. _Verify = value247. End Set248. End Property249.250. Public Property RequireData() As AskForData251. Get252. Return _RequireData253. End Get254. Set(ByVal value As AskForData)255. _RequireData = value256. End Set257. End Property258.259. Public Property Enabled() As Boolean260. Get261. Return _Enabled262. End Get263. Set(ByVal value As Boolean)264. _Enabled = value265. End Set266. End Property267.268. Sub New(ByVal Description As String, _269. ByVal VerifyMethod As FileFilter, _270. ByVal RequireDataMethod As AskForData)271. Me.Description = Description272. Me.Verify = VerifyMethod273. Me.RequireData = RequireDataMethod274. End Sub275. End Class276.277. Sub Main()278. Contiene tutte le operazioni da eseguire: sarà, quindi, un279. delegate multicast280. Dim DoOperations As MassFileOperation281. Contiene tutte le condizioni da verificare282. Dim VerifyConditions As FileFilter283.
  • Indica la cartella di cui analizzare i file284. Dim Folder As String285. Hashtable di caratteri-Operation o carattri-Condition. Il286. carattere indica quale tasto è necessario287. premere per attivare/disattivare loperazione/condizione288. Dim Operations As New Hashtable289. Dim Conditions As New Hashtable290. Dim Cmd As Char291.292. Aggiunge le operazioni esistenti. La c messa dopo la stringa293. indica che la costante digitata è un carattere e non una294. stringa. Il sistema non riesce a distinguere tra stringhe di295. lunghezza 1 e caratteri, al contrario di come accade in C296. With Operations297. .Add("r"c, New Operation("Rinomina tutti i file nella cartella;", _298. New MassFileOperation(AddressOf Rename), _299. New AskForData(AddressOf InputRenameFormat)))300. .Add("c"c, New Operation("Copia tutti i file nella cartella in unaltra cartella;", _301. New MassFileOperation(AddressOf Copy), _302. New AskForData(AddressOf InputCopyDirectory)))303. .Add("a"c, New Operation("Scrive il nome di tutti i file nella cartella su un file;", _304. New MassFileOperation(AddressOf Archive), _305. New AskForData(AddressOf InputLogFile)))306. .Add("d"c, New Operation("Cancella tutti i file nella cartella;", _307. New MassFileOperation(AddressOf Delete), _308. Nothing))309. End With310.311. Aggiunge le condizioni esistenti312. With Conditions313. .Add("r"c, New Condition("Seleziona i file da elaborare in base alla data di creazione;", _314. New FileFilter(AddressOf IsCreationDateValid), _315. New AskForData(AddressOf InputCreationDates)))316. .Add("l"c, New Condition("Seleziona i file da elaborare in base allultimo accesso;", _317. New FileFilter(AddressOf IsLastAccessDateValid), _318. New AskForData(AddressOf InputLastAccessDates)))319. .Add("s"c, New Condition("Seleziona i file da elaborare in base alla dimensione;", _320. New FileFilter(AddressOf IsSizeValid), _321. New AskForData(AddressOf InputSizeLimit)))322. End With323.324. Console.WriteLine("Modifica in massa di file ---")325. Console.WriteLine()326.327. Do328. Console.WriteLine("Immetti il percorso della cartella su cui operare:")329. Folder = Console.ReadLine330. Loop Until IO.Directory.Exists(Folder)331.332. Do333. Console.Clear()334. Console.WriteLine("Premere la lettera corrispondente per selezionare la voce.")335. Console.WriteLine("Premere e per procedere.")336. Console.WriteLine()337. For Each Key As Char In Operations.Keys338. Disegna sullo schermo una casella di spunta, piena:339. [X]340. se loperazione è attivata, altrimenti vuota:341. [ ]342. Console.Write("[")343. If Operations(Key).Enabled = True Then344. Console.Write("X")345. Else346. Console.Write(" ")347. End If348. Console.Write("] ")349. Scrive quindi il carattere da premere e vi associa la descrizione350.
  • Console.Write(Key)351. Console.Write(" - ")352. Console.WriteLine(Operations(Key).Description)353. Next354. Cmd = Console.ReadKey().KeyChar355. If Operations.ContainsKey(Cmd) Then356. Operations(Cmd).Enabled = Not Operations(Cmd).Enabled357. End If358. Loop Until Cmd = "e"c359.360. Do361. Console.Clear()362. Console.WriteLine("Premere la lettera corrispondente per selezionare la voce.")363. Console.WriteLine("Premere e per procedere.")364. Console.WriteLine()365. For Each Key As Char In Conditions.Keys366. Console.Write("[")367. If Conditions(Key).Enabled = True Then368. Console.Write("X")369. Else370. Console.Write(" ")371. End If372. Console.Write("] ")373. Console.Write(Key)374. Console.Write(" - ")375. Console.WriteLine(Conditions(Key).Description)376. Next377. Cmd = Console.ReadKey().KeyChar378. If Conditions.ContainsKey(Cmd) Then379. Conditions(Cmd).Enabled = Not Conditions(Cmd).Enabled380. End If381. Loop Until Cmd = "e"c382.383. Console.Clear()384. Console.WriteLine("Acquisizione informazioni")385. Console.WriteLine()386.387. Cicla su tutte le operazioni presenti nellHashtable.388. For Each Op As Operation In Operations.Values389. Se loperazione è attivata...390. If (Op.Enabled) Then391. Se richiede dati ausiliari, invoca il delegate memorizzato392. nella proprietà RequireData. Invoke è un metodo393. di istanza che invoca i metodi contenuti nel delegate.394. Si può anche scrivere:395. Op.RequireData()()396. Dove la prima coppia di parentesi indica che la proprietà397. non è indicizzata e la seconda, in questo caso, specifica398. che il metodo sotteso dal delegate non richiede parametri.399. È più comprensibile la prima forma400. If Op.RequireData IsNot Nothing Then401. Op.RequireData.Invoke()402. End If403. Se DoOperations non contiene ancora nulla, vi inserisce Op.Execute404. If DoOperations Is Nothing Then405. DoOperations = Op.Execute406. Else407. Altrimenti, combina gli oggetti delegate già memorizzati408. con il nuovo409. DoOperations = System.Delegate.Combine(DoOperations, Op.Execute)410. End If411. End If412. Next413.414. For Each C As Condition In Conditions.Values415. If C.Enabled Then416. If C.RequireData IsNot Nothing Then417. C.RequireData.Invoke()418. End If419. If VerifyConditions Is Nothing Then420. VerifyConditions = C.Verify421. Else422.
  • VerifyConditions = System.Delegate.Combine(VerifyConditions, C.Verify) 423. End If 424. End If 425. Next 426. 427. FileIndex = 0 428. For Each File As String In IO.Directory.GetFiles(Folder) 429. Ok indica se il file ha passato le condizioni 430. Dim Ok As Boolean = True 431. Se ci sono condizioni da applicare, le verifica 432. If VerifyConditions IsNot Nothing Then 433. Dato che nel caso di delegate multicast contenenti 434. rifermenti a funzione, il valore restituito è 435. solo quello della prima funzione e a noi interessano 436. <b>tutti</b> i valori restituiti, dobbiamo enumerare 437. ogni singolo oggetto delegate presente nel 438. delegate multicast e invocarlo singolarmente. 439. Ci viene in aiuto il metodo di istanza GetInvocationList, 440. che restituisce un array di delegate singoli. 441. For Each C As FileFilter In VerifyConditions.GetInvocationList() 442. Tutte le condizioni attive devono essere verificate, 443. quindi bisogna usare un And 444. Ok = Ok And C(File) 445. Next 446. End If 447. Se le condizioni sono verificate, esegue le operazioni 448. If Ok Then 449. Try 450. DoOperations(File) 451. Catch Ex As Exception 452. Console.WriteLine("Impossibile eseguire loperazione: " & Ex.Message) 453. End Try 454. End If 455. Next 456. Chiude il file di log se era aperto 457. If LogFile IsNot Nothing Then 458. LogFile.Close() 459. End If 460. 461. Console.WriteLine("Operazioni eseguite con successo!") 462. Console.ReadKey() 463. End Sub 464. 465. End ModuleQuesto esempio molto ar tificioso è solo un assaggio delle potenzialità dei delegate (noter ete che ci sono anche molticonflitti, ad esempio se si seleziona sia copia che elimina, i file potr ebber o esser e cancellati pr ima della copia a secondadellor dine di invocazione). Vedr emo fr a poco come utilizzar e alcuni delegate piuttosto comuni messi a disposizione dalFr amew or k, e scopr ir emo nella sezione B che i delegate sono il meccanismo fondamentale alla base di tutto il sistemadegli ev enti.Alc uni membri importanti per i delegate multic astLa classe System.Delegate espone alcuni metodi statici pubblici, molti dei quali sono davver o utili quando si tr atta didelegate 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 distanza 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 nellunica var iabile Remove(A, B) : r imuove loggetto 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 lultimo elemento dellinvocation list di B viene r imosso da quella di A
  • RemoveAll(A, B) : r imuove tutte le occor r enze degli elementi pr esenti nellinvocation list di B da quella di A. Sisuppone che sia A che B siano multicast
  • A36. Classi Astratte, Sigillate e ParzialiClassi AstratteLe classi astr atte sono speciali classi che esistono con il solo scopo di esser e er editate da altr e classi: non possonoesser e usate da sole, non espongono costr uttor i e alcuni lor o metodi sono pr ivi di un cor po. Queste sonocar atter istiche molto peculiar i, e anche abbastanza str ane, che, tuttavia, nascondono un potenziale segr eto. Sequalcuno dei miei venticinque lettor i avesse avuto loccasione di osser var e qualcuno dei miei sor genti, avr ebbe notatoche in più di un occasione ho fatto uso di classi mar cate con la keyw or d MustInher it. Questa è la par ola r iser vata che siusa per r ender e as tratta una classe. Lutilizzo pr incipale delle classi astr atte è quello di for nir e un o s cheletro o un abas e di as trazion e per altr e classi. Pr endiamo come esempio uno dei miei pr ogr ammi, che potete tr ovar e nellasezione dow nload, Totem Char ting: ci r ifer ir emo al file Char t.vb. In questo sor gente, la pr ima classe che incontr ate èdefinita come segue: 1. <Serializable()> _ 2. Public MustInherit Class ChartPer or a lasciamo per der e ciò che viene compr eso tr a le par entesi angolar i e focalizziamoci sulla dichiar azione nuda ecr uda. Quella che avete visto è pr opr io la dichiar azione di una classe astr atta, dove MustInher it significa appunto "deveer 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 daccor do con me nellasser ir e che ognigr afico può aver e una legenda e può contemplar e un insieme di dati limitato per cui esista un massimo: ne concludiamoche i due metodi in questione ser vono a tutti i gr afici ed è cor r etto che siano stati definiti allinter no del cor po diChar t. Ma or a andiamo un po più in su e tr oviamo questa singolar e dichiar azione di metodo: 1. Public MustOverride Sub Draw()Non cè il cor po del metodo! Aiuto! Lhanno r ubato! No... Si dà il caso che nelle classi astr atte possano esister e anchemetodi 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 ilmetodo Dr aw , ma cè un piccolo inconveniente. Dato che non esiste un solo tipo di gr afico - ce ne sono molti, e nelcodice di Totem Char ting vengono contemplati solo gli istogr ammi, gli ar eaogr ammi e i gr afici a disper sione - nonpossiamo 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 classider ivate (nella fattispecie, Histogr am, PieChar t, LinesChar t, Disper sionChar t). Questo è pr opr io lutilizzo delle classiastr atte: definir e un ar chetipo, uno schema, sulla base del quale le classi che lo er editer anno dovr anno modellar e ilpr opr io compor tamento. Altr a osser vazione: le classi astr atte, come dice il nome stesso, sono utilizzate perr appr esentar e concetti astr atti, che non possono concr etamente esser e istanziati: ad esempio, non ha senso unoggetto di tipo Char t, per chè non esiste un gr afico gener ico pr ivo di qualsiasi car atter istica, ma esiste solo declinatoin 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 diaccesso e aller editar ietà e sono utilizzabili tutti i meccanismi già illustr ati, compr eso lover loading; infatti, hodichiar ato due metodi Pr otected per chè ser vir anno alle classi der ivate. Inoltr e, una classe astr atta può ancheer editar e da unaltr a classe astr atta: in questo caso, tutti i metodi mar cati con MustOver r ide dovr anno subir e una diqueste sor ti: Esser e modificati tr amite polimor fismo, definendone, quindi, il cor po; Esser e r idichiar ati MustOver r ide, r imandandone ancor a la definizione.Nel secondo caso, si r imanda ancor a la definizione di un cor po valido alla "discendenza", ma cè un piccolo ar tifizio da
  • adottar e: eccone una dimostr azione nel pr ossimo esempio: 001. Module Module1 002. 003. Classe astratta che rappresenta un risolutore di equazioni. 004. Dato che di equazioni ce ne possono essere molte tipologie 005. differenti, non ha senso rendere questa classe istanziabile. 006. Provando a scrivere qualcosa come: 007. Dim Eq As New EquationSolver() 008. Vi verrà comunicato un errore, in quanto le classi 009. astratte sono per loro natura non istanziabili 010. MustInherit Class EquationSolver 011. Per lo stesso discorso fatto prima, se non conosciamo come 012. è fatta lequazione che questo tipo contiene non 013. possiamo neppure tentare di risolverla. Perciò 014. ci limitiamo a dichiarare una funzione Solve come MustOverride. 015. Notate che il tipo restituito è un array di Single, 016. in quanto le soluzioni saranno spesso più di una. 017. Public MustOverride Function Solve() As Single() 018. End Class 019. 020. La prossima classe rappresenta un risolutore di equazioni 021. polinomiali. Dato che la tipologia è ben definita, 022. avremmo potuto anche <i>non</i> rendere astratta la classe 023. e, nella funzione Solve, utilizzare un Select Case per 024. controllare il grado dellequazione. Ad ogni modo, è 025. utile vedere come si comporta lerediterietà attraverso 026. più classi astratte. 027. Inoltre, ci ritornerà molto utile in seguito disporre 028. di questa classe astratta intermedia 029. MustInherit Class PolynomialEquationSolver 030. Inherits EquationSolver 031. 032. Private _Coefficients() As Single 033. 034. Array di Single che contiene i coefficienti dei 035. termini di i-esimo grado allinterno dellequazione. 036. Lelemento 0 dellarray indica il coefficiente del 037. termine a grado massimo. 038. Public Property Coefficients() As Single() 039. Get 040. Return _Coefficients 041. End Get 042. Set(ByVal value As Single()) 043. _Coefficients = value 044. End Set 045. End Property 046. 047. Ecco quello a cui volevo arrivare. Se un metodo astratto 048. lo si vuole mantenere tale anche nella classe derivata, 049. non basta scrivere: 050. MustOverride Function Solve() As Single() 051. Percè in questo caso verrebbe interpretato come 052. un membro che non centra niente con MyBase.Solve, 053. e si genererebbe un errore in quanto stiamo tentando 054. di dichiarare un nuovo membro con lo stesso nome 055. di un membro della classe base. 056. Per questo motivo, dobbiamo comunque usare il polimorfismo 057. come se si trattasse di un normale metodo e dichiararlo 058. Overrides. In aggiunta a questo, deve anche essere 059. astratto, e perciò aggiungiamo MustOverride: 060. Public MustOverride Overrides Function Solve() As Single() 061. 062. Anche in questo caso usiamo il polimorfismo, ma ci riferiamo 063. alla semplice funzione ToString, derivata dalla classe base 064. di tutte le entità esistenti, System.Object. 065. Questa si limita a restituire una stringa che rappresenta 066. lequazione a partire dai suoi coefficienti. Ad esempio: 067. 3x^2 + 2x^1 + 4x^0 = 0 068. Potete modificare il codice per eliminare le forme ridondanti 069. x^1 e x^0. 070. Public Overrides Function ToString() As String 071.
  • Dim Result As String = ""072.073. For I As Int16 = 0 To Me.Coefficients.Length - 1074. If I > 0 Then075. Result &= " + "076. End If077. Result &= String.Format("{0}x^{1}", _078. Me.Coefficients(I), Me.Coefficients.Length - 1 - I)079. Next080.081. Result &= " = 0"082.083. Return Result084. End Function085. End Class086.087. Rappresenta un risolutore di equazioni non polinomiali.088. La classe non è astratta, ma non presenta alcun codice.089. Per risolvere questo tipo di equazioni, è necessario090. sapere qualche cosa in più rispetto al punto in cui siamo091. arrivati, perciò mi limiterò a lasciare in bianco092. Class NonPolynomialEquationSolver093. Inherits EquationSolver094.095. Public Overrides Function Solve() As Single()096. Return Nothing097. End Function098. End Class099.100. Rappresenta un risolutore di equazioni di primo grado. Eredita101. da PolynomialEquationSolver poichè, ovviamente, si102. tratta di equazioni polinomiali. In più, definisce103. le proprietà a e b che sono utili per inserire i104. coefficienti. Infatti, lequazione standard è:105. ax + b = 0106. Class LinearEquationSolver107. Inherits PolynomialEquationSolver108.109. Public Property a() As Single110. Get111. Return Me.Coefficients(0)112. End Get113. Set(ByVal value As Single)114. Me.Coefficients(0) = value115. End Set116. End Property117.118. Public Property b() As Single119. Get120. Return Me.Coefficients(1)121. End Get122. Set(ByVal value As Single)123. Me.Coefficients(1) = value124. End Set125. End Property126.127. Sappiamo già quanti sono i coefficienti, dato128. che si tratta di equazioni lineari, quindi ridimensioniamo129. larray il prima possibile.130. Sub New()131. ReDim Me.Coefficients(1)132. End Sub133.134. Funzione Overrides che sovrascrive il metodo astratto della135. classe base. Avrete notato che quando scrivete:136. Inherits PolynomialEquationSolver137. e premete invio, questa funzione viene aggiunta automaticamente138. al codice. Questa è unutile feature dellambiente139. di sviluppo140. Public Overrides Function Solve() As Single()141. If a <> 0 Then142. Return New Single() {-b / a}143.
  • Else144. Return Nothing145. End If146. End Function147. End Class148.149. Risolutore di equazioni di secondo grado:150. ax<sup>2</sup> + bx + c = 0151. Class QuadraticEquationSolver152. Inherits LinearEquationSolver153.154. Public Property c() As Single155. Get156. Return Me.Coefficients(2)157. End Get158. Set(ByVal value As Single)159. Me.Coefficients(2) = value160. End Set161. End Property162.163. Sub New()164. ReDim Me.Coefficients(2)165. End Sub166.167. Public Overrides Function Solve() As Single()168. If b ^ 2 - 4 * a * c >= 0 Then169. Return New Single() { _170. (-b - Math.Sqrt(b ^ 2 - 4 * a * c)) / 2, _171. (-b + Math.Sqrt(b ^ 2 - 4 * a * c)) / 2}172. Else173. Return Nothing174. End If175. End Function176. End Class177.178. Risolutore di equazioni di grado superiore al secondo. So179. che avrei potuto inserire anche una classe relativa180. alle cubiche, ma dato che si tratta di un esempio, vediamo181. di accorciare il codice...182. Comunque, dato che non esiste formula risolutiva per183. le equazioni di grado superiore al quarto (e già,184. ci mancava unaltra classe!), usiamo in questo caso185. un semplice ed intuitivo metodo di approssimazione degli186. zeri, il metodo dicotomico o di bisezione (che vi può187. essere utile per risolvere un esercizio delleserciziario)188. Class HighDegreeEquationSolver189. Inherits PolynomialEquationSolver190.191. Private _Epsilon As Single192. Private _IntervalLowerBound, _IntervalUpperBound As Single193.194. Errore desiderato: lalgoritmo si fermerà una volta195. raggiunta una precisione inferiore a Epsilon196. Public Property Epsilon() As Single197. Get198. Return _Epsilon199. End Get200. Set(ByVal value As Single)201. _Epsilon = value202. End Set203. End Property204.205. Limite inferiore dellintervallo in cui cercare la soluzione206. Public Property IntervalLowerBound() As Single207. Get208. Return _IntervalLowerBound209. End Get210. Set(ByVal value As Single)211. _IntervalLowerBound = value212. End Set213. End Property214.215.
  • Limite superiore dellintervallo in cui cercare la soluzione216. Public Property IntervalUpperBound() As Single217. Get218. Return _IntervalUpperBound219. End Get220. Set(ByVal value As Single)221. _IntervalUpperBound = value222. End Set223. End Property224.225.226. Valuta la funzione polinomiale. Dati i coefficienti immessi,227. noi disponiamo del polinomio p(x), quindi possiamo calcolare228. i valori che esso assume per ogni x229. Private Function EvaluateFunction(ByVal x As Single) As Single230. Dim Result As Single = 0231.232. For I As Int16 = 0 To Me.Coefficients.Length - 1233. Result += Me.Coefficients(I) * x ^ (Me.Coefficients.Length - 1 - I)234. Next235.236. Return Result237. End Function238.239. Public Overrides Function Solve() As Single()240. Dim a, b, c As Single241. Dim fa, fb, fc As Single242. Dim Interval As Single = 100243. Dim I As Int16 = 0244. Dim Result As Single245.246. a = IntervalLowerBound247. b = IntervalUpperBound248.249. Non esiste uno zero tra a e b se f(a) e f(b) hanno250. lo stesso segno251. If EvaluateFunction(a) * EvaluateFunction(b) > 0 Then252. Return Nothing253. End If254.255. Do256. c è il punto medio tra a e b257. c = (a + b) / 2258. Calcola f(a), f(b) ed f(c)259. fa = EvaluateFunction(a)260. fb = EvaluateFunction(b)261. fc = EvaluateFunction(c)262.263. Se uno tra f(a), f(b) e f(c) vale zero, allora abbiamo264. trovato una soluzione perfetta, senza errori, ed265. usciamo direttamente dal ciclo266. If fa = 0 Then267. c = a268. Exit Do269. End If270. If fb = 0 Then271. c = b272. Exit Do273. End If274. If fc = 0 Then275. Exit Do276. End If277.278. Altrimenti, controlliamo quale coppia di valori scelti279. tra f(a), f(b) ed f(c) ha segni discorsi: lo zero si troverà280. tra le ascisse di questi281. If fa * fc < 0 Then282. b = c283. Else284. a = c285. End If286. Loop Until Math.Abs(a - b) < Me.Epsilon287.
  • Cicla finchè lampiezza dellintervallo non è288. sufficientemente piccola, quindi assume come zero più289. probabile il punto medio tra a e b:290. Result = c291.292. Return New Single() {Result}293. End Function294. End Class295.296.297. Sub Main()298. Contiene un generico risolutore di equazioni. Non sappiamo ancora299. quale tipologia di equazione dovremo risolvere, ma sappiamo per300. certo che lo dovremo fare, ed EquationSolver è la classe301. base di tutti i risolutori che espone il metodo Solve.302. Dim Eq As EquationSolver303. Dim x() As Single304. Dim Cmd As Char305.306. Console.WriteLine("Scegli una tipologia di equazione: ")307. Console.WriteLine(" l - lineare;")308. Console.WriteLine(" q - quadratica;")309. Console.WriteLine(" h - di grado superiore al secondo;")310. Console.WriteLine(" e - non polinomiale;")311. Cmd = Console.ReadKey().KeyChar312. Console.Clear()313.314. If Cmd <> "e" Then315. Ancora, sappiamo che si tratta di unequazione polinomiale316. ma non di quale grado317. Dim Poly As PolynomialEquationSolver318.319. Ottiene i dati relativi a ciascuna equazione320. Select Case Cmd321. Case "l"322. Dim Linear As New LinearEquationSolver()323. Poly = Linear324. Case "q"325. Dim Quadratic As New QuadraticEquationSolver()326. Poly = Quadratic327. Case "h"328. Dim High As New HighDegreeEquationSolver()329. Dim CoefNumber As Int16330. Console.WriteLine("Inserire il numero di coefficienti: ")331. CoefNumber = Console.ReadLine332. ReDim High.Coefficients(CoefNumber - 1)333. Console.WriteLine("Inserire i limti dellintervallo in cui cercare gli zeri:")334. High.IntervalLowerBound = Console.ReadLine335. High.IntervalUpperBound = Console.ReadLine336. Console.WriteLine("Inserire la precisione (epsilon):")337. High.Epsilon = Console.ReadLine338. Poly = High339. End Select340.341. A questo punto la variabile Poly contiene sicuramente un oggetto342. (LinearEquationSolver, QuadraticEquationSolver oppure343. HighDegreeEquationSolver), anche se non sappiamo quale. Tuttavia,344. tutti questi sono pur sempre polinomiali e perciò tutti345. hanno bisogno di sapere i coefficienti del polinomio.346. Ecco che allora possiamo usare Poly con sicurezza percè347. sicuramente contiene un oggetto e la proprietà Coefficients348. è stata definita proprio nella classe PolynomialEquationSolver.349. <b>N.B.: ricordate tutto quello che abbiamo detto sullassegnamento350. di un oggetto di classe derivata a uno di classe base!</b>351. Console.WriteLine("Inserire i coefficienti: ")352. For I As Int16 = 1 To Poly.Coefficients.Length - 1353. Console.Write("a{0} = ", Poly.Coefficients.Length - I)354. Poly.Coefficients(I - 1) = Console.ReadLine355. Next356.357. Assegnamo Poly a Eq. Osservate che siamo andati via via dal358.
  • caso più particolare al più generale: 359. - Abbiamo creato un oggetto specifico per un certo grado 360. di unequazione polinomiale (Linear, Quadratic, High); 361. - Abbiamo messo quelloggetto in uno che si riferisce 362. genericamente a tutti i polinomi; 363. - Infine, abbiamo posto questultimo in uno ancora più 364. generale che si riferisce a tutte le equazioni; 365. Questo percorso porta da oggetto molto specifici e ricchi di membri 366. (tante proprietà e tanti metodi), a tipi molto generali 367. e poveri di membri (nel caso di Eq, un solo metodo). 368. Eq = Poly 369. Else 370. Inseriamo in Eq un nuovo oggetto per risolvere equazioni non 371. polinomiali, anche se il codice è al momento vuoto 372. Eq = New NonPolynomialEquationSolver 373. Console.WriteLine("Non implementato") 374. End If 375. 376. Risolviamo lequazione. Richiamare la funzione Solve da un oggetto 377. EquationSolver potrebbe non dirvi nulla, ma ricordate che dentro Eq 378. è memorizzato un oggetto più specifico in cui 379. è stata definita la funzione Solve(). Per questo motivo, 380. anche se Eq è di tipo classe base, purtuttavia contiene 381. al proprio interno un oggetto di tipo classe derivata, ed 382. è questo che conta: viene usato il metodo Solve della classe 383. derivata. 384. Se ci pensate bene, vi verrà più spontaneo capire, 385. poiché noi, ora, stiamo guardando ATTRAVERSO il tipo 386. EquationSolver un oggetto di altro tipo. È come osservare 387. attraverso filtri via via sempre più fitti (cfr 388. immagine seguente) 389. x = Eq.Solve() 390. 391. If x IsNot Nothing Then 392. Console.WriteLine("Soluzioni trovate: ") 393. For Each s As Single In x 394. Console.WriteLine(s) 395. Next 396. Else 397. Console.WriteLine("Nessuna soluzione") 398. End If 399. 400. Console.ReadKey() 401. End Sub 402. End ModuleEccovi unimmagine dellultimo commento:
  • Il piano r osso è loggetto che r ealmente cè in memor ia (ad esempio, Linear EquationSolver ); il piano blu con tr eaper tur e è ciò che r iusciamo a veder e quando loggetto viene memor izzato in una classe astr attaPolynomialEquationSolver ; il piano blu iniziale, invece, è ciò a cui possiamo acceder e attr aver so un EquationSolver : ilfascio 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 lopposto di quelle astr atte, ossia non possono mai esser e er editate. Si dichiar anocon la keyw or d NotInher itable: 1. NotInheritable Class Example 2. ... 3. End ClassAllo stesso modo, penser ete voi, i membr i che non possono subir e over loading sar anno mar cati con qualcosa tipoNotOver 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 ametodi già modificati tr amite polimor fismo, ossia Over r ides. 01. Class A 02. Sub DoSomething() 03. ... 04. End Sub 05. End Class 06. 07. Class B 08. Inherits A 09. 10. Questa procedura sovrascrive la precedente versione 11. di DoSomething dichiarata in A, ma preclude a tutte le 12. classi derivate da B la possibilità di fare lo stesso 13. NotOverridable Overrides Sub DoSomething() 14. ... 15. End Sub 16. End ClassInoltr e, le classi sigillate no n possono mai espor r e membr i sigillati, anche per chè tutti i lor o membr i lo sonoimplicitamente (se una classe non può esser e er editata, ovviamente non si potr anno r idefinir e i membr i conpolimor fismo).Classi ParzialiUna classe si dice par ziale quando il suo cor po è suddiviso su più files. Si tr atta solamento di unutilità pr atica che hapoco a che veder e con la pr ogr ammazione ad oggetti. Mi sembr ava, per ò, or dinato espor r e tutte le keyw or d associatealle classi in un solo capitolo. Semplicemente, una classe par ziale si dichiar a in questo modo: 1. Partial Class [Nome] 2. ... 3. End ClassÈ sufficiente dichiar ar e una classe come par ziale per chè il compilator e associ, in fase di assemblaggio, tutte le classicon lo stesso nome in file diver si a quella definizione. Ad esempio: 01. Nel file Codice1.vb : 02. Partial Class A 03. Sub One() 04.
  • ...05. End Sub06. End Class07.08. Nel file Codice2.vb09. Class A10. Sub Two()11. ...12. End Sub13. End Class14.15. Nel file Codice3.vb16. Class A17. Sub Three()18. ...19. End Sub20. End Class21.22. Tutte le classi A vengono compilate come ununica classe23. perchè una possiede la keyword Partial:24. Class A25. Sub One()26. ...27. End Sub28.29. Sub Two()30. ...31. End Sub32.33. Sub Three()34. ...35. End Sub36. End Class
  • A37. Le InterfacceSc opo delle Interfac c eLe inter facce sono unentità davver o singolar e allinter no del .NET Fr amew or k. La lor o funzione è assimilabile a quelladelle classi astr atte, ma il modo con cui esse la svolgono è molto diver so da ciò che abbiamo visto nel capitolopr ecedente. Il pr incipale scopo di uninter faccia è definir e lo scheletr o di una classe; potr ebbe esser e scher zosamenteassimilata alla r icetta con cui si pr epar a un dolce. Quello che linter faccia X fa, ad esempio, consiste nel dir e che percostr 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 funzioneGetSomething senza par ametr i che r estituisce una str inga e una pr ocedur a DoSomething con un singolo par ametr oDouble. Tutte le classi che avr anno intenzione di seguir e i pr ecetti di X (in ger go im plem entar e X) dovr anno definir e,allo stesso modo, quella pr opr ietà di quel tipo e quei metodi con quelle specifiche signatur e (il nome ha impor tanzar 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. Siaper luno che per laltr o, ho detto che è possibile eseguir e uniter azione con il costr utto For Each: 01. Dim Ar() As Int32 = {1, 2, 3, 4, 5, 6} 02. Dim Al As New ArrayList 03. 04. For I As Int32 = 1 To 40 05. Al.Add(I) 06. Next 07. 08. Stampa i valori di Ar: 09. For Each K As Int32 In Ar 10. Console.WriteLine(K) 11. Next 12. Stampa i valori di Al 13. For Each K As Int32 In Al 14. Console.WriteLine(K) 15. NextMa il sistema come fa a saper e che Ar e Al sono degli insiemi di valor i? Dopotutto, il lor o nome è significativo solo pernoi 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 diAr r ay e Ar r ayList, definito dai pr ogr ammator i che hanno scr itto il Fr amew or k, è intelligibile solo agli uomini, per chèal computer non comunica nulla sullo scopo per il quale è stato scr itto. Allor a, siamo al punto di par tenza: nelle classiAr 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 eche, quindi, sono iter abili; e, anche se in qualche str ano modo lelabor ator e lo potesse capir e, non "sapr ebbe" (in quantoentità non senziente) come far per estr ar r e singoli dati e dar celi uno in fila allaltr o. Ecco che entr ano in scena leinter facce: tutte le classi che r appr esentano un insieme o una collezione di elementi implemen tan o linter facciaIEnumer 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 lenumer 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 loggetto posto dopo la clausola "In" implementilinter faccia IEnumer able. Quindi r ichiama il metodo Reset per por si sul pr imo elemento, poi deposita in K il valor eesposto 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 ilmetodo MoveNex t per avanzar e al pr ossimo elemento. Il For Each "è sicur o" dellesistenza di questi membr i per chèlinter 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 unaclasse. Per questo motivo, il lor o nomi ter minano spesso in "-able", come ad esempio IEnumer able, IEquatable,ICompr able, che ci dicono "- è enumer abile", "- è eguagliabile", "- è compar abile", "è ... qualcosa".
  • Dic hiarazione e implementazioneLa sintassi usata per dichiar ar e uninter faccia è la seguente: 1. Interface [Nome] 2. Membri 3. End InterfaceI 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 equeste 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 linter faccia ha il solo scopo didefinir e quali membr i una classe debba implementar e: per questo motivo, non se ne può scr iver e il cor po, dato chespetta espr essamente alle classi implementanti, e non ci si pr eoccupa dello specificator e di accesso, dato che si staspecificando solo il "cosa" e non il "come". Ecco alcuni semplici esempi di dichiar azioni: 01. Questa interfaccia dal nome improbabile indica che 02. la classe che la implementa rappresenta qualcosa di 03. "identificabile" e per questo espone una proprietà Integer Id 04. e una funzione ToString. Id e ToString, infatti, sono gli 05. elementi più utili per identificare qualcosa, prima in 06. base a un codice univoco e poi grazie ad una rappresentazione 07. comprensibile dalluomo 08. Interface IIdentifiable 09. ReadOnly Property Id() As Int32 10. Function ToString() As String 11. End Interface 12. 13. La prossima interfaccia, invece, indica qualcosa di resettabile 14. e obbliga le classi implementanti a esporre il metodo Reset 15. e la proprietà DefaultValue, che dovrebbe rappresentare 16. il valore di default delloggetto. Dato che non sappiamo ora 17. quali classi implementeranno questa interfaccia, dobbiamo 18. per forza usare un tipo generico come Object per rappresentare 19. un valore reference. Vedremo come aggirare questo ostacolo 20. fra un po, con i Generics 21. Interface IResettable 22. Property DefaultValue() As Object 23. Sub Reset() 24. End Interface 25. 26. Come avete visto, i nomi di interfaccia iniziano per convenzione 27. con la lettera I maiuscolaOr a che sappiamo come dichiar ar e uninter faccia, dobbiamo scopr ir e come usar la. Per implementar e uninter faccia inuna classe, si usa questa sintassi: 1. Class Example 2. Implements [Nome Interfaccia] 3. 4. [Membro] Implements [Nome Interfaccia].[Membro] 5. End ClassSi capisce meglio con un esempio: 01. Module Module1 02. Interface IIdentifiable 03. ReadOnly Property Id() As Int32 04. Function ToString() As String 05. End Interface 06. 07.
  • Rappresenta un pacco da spedire08. Class Pack09. Implementa linterfaccia IIdentifiable, in quanto un pacco10. dovrebbe poter essere ben identificato11. Implements IIdentifiable12.13. Notate bene che linterfaccia ci obbliga a definire una14. proprietà, ma non ci obbliga a definire un campo15. ad essa associato16. Private _Id As Int3217. Private _Destination As String18. Private _Dimensions(2) As Single19.20. La classe definisce una proprietà id di tipo Integer21. e la associa allomonima presente nellinterfaccia in22. questione. Il legame tra questa proprietà Id e quella23. presenta nellinterfaccia è dato solamente dalla24. clausola (si chiama così in gergo) "Implements",25. la quale avvisa il sistema che il vincolo imposto26. è stato soddisfatto.27. N.B.: il fatto che il nome di questa proprietà sia uguale28. a quella definita in IIdentifiable non significa nulla.29. Avremmo potuto benissimo chiamarla "Pippo" e associarla30. a Id tramite il codice "Implements IIdentifiable.Id", ma31. ovviamente sarebbe stata una palese idiozia XD32. Public ReadOnly Property Id() As Integer Implements IIdentifiable.Id33. Get34. Return _Id35. End Get36. End Property37.38. Destinazione del pacco.39. Il fatto che linterfaccia ci obblighi a definire quei due40. membri non significa che non possiamo definirne altri41. Public Property Destination() As String42. Get43. Return _Destination44. End Get45. Set(ByVal value As String)46. _Destination = value47. End Set48. End Property49.50. Piccolo ripasso delle proprietà indicizzate e51. della gestione degli errori52. Public Property Dimensions(ByVal Index As Int32) As Single53. Get54. If (Index >= 0) And (Index < 3) Then55. Return _Dimensions(Index)56. Else57. Throw New IndexOutOfRangeException()58. End If59. End Get60. Set(ByVal value As Single)61. If (Index >= 0) And (Index < 3) Then62. _Dimensions(Index) = value63. Else64. Throw New IndexOutOfRangeException()65. End If66. End Set67. End Property68.69. Public Overrides Function ToString() As String Implements IIdentifiable.ToString70. Return String.Format("{0}: Pacco {1}x{2}x{3}, Destinazione: {4}", _71. Me.Id, Me.Dimensions(0), Me.Dimensions(1), _72. Me.Dimensions(2), Me.Destination)73. End Function74. End Class75.76. Sub Main()77. ...78. End Sub79.
  • End ModuleOr a che abbiamo implementato linter faccia nella classe Pack, tuttavia, non sappiamo che far cene. Siamo a conoscenzadel fatto che gli oggetti Pack sar anno sicur amente identificabili, ma nulla di più. Ritor niamo, allor a, allesempio delpr 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? Siapplica a qualsiasi collezione o insieme, non impor ta di quale natur a o per quali scopi, non impor ta nemmeno il codiceche sottende allenumer azione: limpor tante è che una vastissima gamma di oggetti possano esser e r icondotti ad unsolo ar chetipo (io ne ho nominati solo due, ma ce ne sono a iosa). Allo stesso modo, potr emo usar e IIdentifiable permanipolar e una gr an quantità di dati di natur a differ ente. Ad esempio, il codice di sopr a potr ebbe esser e sviluppatoper cr ear e un sistema di gestione di un ufficio postale. Eccone un esempio: 001. Module Module1 002. 003. Interface IIdentifiable 004. ReadOnly Property Id() As Int32 005. Function ToString() As String 006. End Interface 007. 008. Class Pack 009. Implements IIdentifiable 010. 011. Private _Id As Int32 012. Private _Destination As String 013. Private _Dimensions(2) As Single 014. 015. Public ReadOnly Property Id() As Integer Implements IIdentifiable.Id 016. Get 017. Return _Id 018. End Get 019. End Property 020. 021. Public Property Destination() As String 022. Get 023. Return _Destination 024. End Get 025. Set(ByVal value As String) 026. _Destination = value 027. End Set 028. End Property 029. 030. Public Property Dimensions(ByVal Index As Int32) As Single 031. Get 032. If (Index >= 0) And (Index < 3) Then 033. Return _Dimensions(Index) 034. Else 035. Throw New IndexOutOfRangeException() 036. End If 037. End Get 038. Set(ByVal value As Single) 039. If (Index >= 0) And (Index < 3) Then 040. _Dimensions(Index) = value 041. Else 042. Throw New IndexOutOfRangeException() 043. End If 044. End Set 045. End Property 046. 047. Sub New(ByVal Id As Int32) 048. _Id = Id 049. End Sub 050. 051. Public Overrides Function ToString() As String Implements IIdentifiable.ToString 052. Return String.Format("{0:0000}: Pacco {1}x{2}x{3}, Destinazione: {4}", _ 053. Me.Id, Me.Dimensions(0), Me.Dimensions(1), _ 054. Me.Dimensions(2), Me.Destination) 055. End Function 056. End Class 057. 058.
  • Class Telegram059. Implements IIdentifiable060.061. Private _Id As Int32062. Private _Recipient As String063. Private _Message As String064.065. Public ReadOnly Property Id() As Integer Implements IIdentifiable.Id066. Get067. Return _Id068. End Get069. End Property070.071. Public Property Recipient() As String072. Get073. Return _Recipient074. End Get075. Set(ByVal value As String)076. _Recipient = value077. End Set078. End Property079.080. Public Property Message() As String081. Get082. Return _Message083. End Get084. Set(ByVal value As String)085. _Message = value086. End Set087. End Property088.089. Sub New(ByVal Id As Int32)090. _Id = Id091. End Sub092.093. Public Overrides Function ToString() As String Implements IIdentifiable.ToString094. Return String.Format("{0:0000}: Telegramma per {1} ; Messaggio = {2}", _095. Me.Id, Me.Recipient, Me.Message)096. End Function097. End Class098.099. Class MoneyOrder100. Implements IIdentifiable101.102. Private _Id As Int32103. Private _Recipient As String104. Private _Money As Single105.106. Public ReadOnly Property Id() As Integer Implements IIdentifiable.Id107. Get108. Return _Id109. End Get110. End Property111.112. Public Property Recipient() As String113. Get114. Return _Recipient115. End Get116. Set(ByVal value As String)117. _Recipient = value118. End Set119. End Property120.121. Public Property Money() As Single122. Get123. Return _Money124. End Get125. Set(ByVal value As Single)126. _Money = value127. End Set128. End Property129.130.
  • Sub New(ByVal Id As Int32)131. _Id = Id132. End Sub133.134. Public Overrides Function ToString() As String Implements IIdentifiable.ToString135. Return String.Format("{0:0000}: Vaglia postale per {1} ; Ammontare = {2}€", _136. Me.Id, Me.Recipient, Me.Money)137. End Function138. End Class139.140. Classe che elabora dati di tipo IIdentifiable, ossia qualsiasi141. oggetto che implementi tale interfaccia142. Class PostalProcessor143. Tanto per tenersi allenati coi delegate, ecco una144. funzione delegate che funge da filtro per i vari id145. Public Delegate Function IdSelector(ByVal Id As Int32) As Boolean146.147. Private _StorageCapacity As Int32148. Private _NextId As Int32 = 0149. Un array di interfacce. Quando una variabile viene150. dichiarata come di tipo interfaccia, ciò151. che può contenere è qualsiasi oggetto152. che implementi quellinterfaccia. Per lo stesso153. discorso fatto nel capitolo precedente, noi154. possiamo vedere <i>attraverso</i> linterfaccia155. solo quei membri che essa espone direttamente, anche156. se il contenuto vero e proprio è qualcosa157. di più158. Private Storage() As IIdentifiable159.160. Capacità del magazzino. Assumeremo che tutti161. gli oggetti rappresentati dalle classi Pack, Telegram162. e MoneyOrder vadano in un magazzino immaginario che,163. improbabilmente, riserva un solo posto per ogni164. singolo elemento165. Public Property StorageCapacity() As Int32166. Get167. Return _StorageCapacity168. End Get169. Set(ByVal value As Int32)170. _StorageCapacity = value171. ReDim Preserve Storage(value)172. End Set173. End Property174.175. Modifica od ottiene un riferimento allIndex-esimo176. oggetto nellarray Storage177. Public Property Item(ByVal Index As Int32) As IIdentifiable178. Get179. If (Index >= 0) And (Index < Storage.Length) Then180. Return Me.Storage(Index)181. Else182. Throw New IndexOutOfRangeException()183. End If184. End Get185. Set(ByVal value As IIdentifiable)186. If (Index >= 0) And (Index < Storage.Length) Then187. Me.Storage(Index) = value188. Else189. Throw New IndexOutOfRangeException()190. End If191. End Set192. End Property193.194. Restituisce la prima posizione libera nellarray195. Storage. Anche se in questo esempio non labbiamo196. contemplato, gli elementi possono anche essere rimossi197. e quindi lasciare un posto libero nellarray198. Public ReadOnly Property FirstPlaceAvailable() As Int32199. Get200. For I As Int32 = 0 To Me.Storage.Length - 1201. If Me.Storage(I) Is Nothing Then202.
  • Return I203. End If204. Next205. Return (-1)206. End Get207. End Property208.209. Tutti gli oggetti che inizializzeremo avranno bisogno210. di un id: ce lo fornisce la stessa classe Processor211. tramite questa proprietà che si autoincrementa212. Public ReadOnly Property NextId() As Int32213. Get214. _NextId += 1215. Return _NextId216. End Get217. End Property218.219.220. Due possibili costruttori: uno che accetta un insieme221. già formato di elementi...222. Public Sub New(ByVal Items() As IIdentifiable)223. Me.Storage = Items224. _SorageCapacity = Items.Length225. End Sub226.227. ... e uno che accetta solo la capacità del magazzino228. Public Sub New(ByVal Capacity As Int32)229. Me.StorageCapacity = Capacity230. End Sub231.232. Stampa a schermo tutti gli elementi che la funzione233. contenuta nel parametro Selector di tipo delegate234. considera validi (ossia tutti quelli per cui235. Selector.Invoke restituisce True)236. Public Sub PrintByFilter(ByVal Selector As IdSelector)237. For Each K As IIdentifiable In Storage238. If K Is Nothing Then239. Continue For240. End If241. If Selector.Invoke(K.Id) Then242. Console.WriteLine(K.ToString())243. End If244. Next245. End Sub246.247. Stampa loggetto con Id specificato248. Public Sub PrintById(ByVal Id As Int32)249. For Each K As IIdentifiable In Storage250. If K Is Nothing Then251. Continue For252. End If253. If K.Id = Id Then254. Console.WriteLine(K.ToString())255. Exit For256. End If257. Next258. End Sub259.260. Cerca tutti gli elementi che contemplano allinterno261. della propria descrizione la stringa Str e li262. restituisce come array di Id263. Public Function SearchItems(ByVal Str As String) As Int32()264. Dim Temp As New ArrayList265.266. For Each K As IIdentifiable In Storage267. If K Is Nothing Then268. Continue For269. End If270. If K.ToString().Contains(Str) Then271. Temp.Add(K.Id)272. End If273. Next274.
  • 275. Dim Result(Temp.Count - 1) As Int32276. For I As Int32 = 0 To Temp.Count - 1277. Result(I) = Temp(I)278. Next279.280. Temp.Clear()281. Temp = Nothing282.283. Return Result284. End Function285. End Class286.287. Private Processor As New PostalProcessor(10)288. Private Cmd As Char289. Private IdFrom, IdTo As Int32290.291. Function SelectId(ByVal Id As Int32) As Boolean292. Return (Id >= IdFrom) And (Id <= IdTo)293. End Function294.295. Sub InsertItems(ByVal Place As Int32)296. Console.WriteLine("Scegliere la tipologia di oggetto:")297. Console.WriteLine(" p - pacco;")298. Console.WriteLine(" t - telegramma;")299. Console.WriteLine(" v - vaglia postale;")300. Cmd = Console.ReadKey().KeyChar301.302. Console.Clear()303. Select Case Cmd304. Case "p"305. Dim P As New Pack(Processor.NextId)306. Console.WriteLine("Pacco - Id:{0:0000}", P.Id)307. Console.Write("Destinazione: ")308. P.Destination = Console.ReadLine309. Console.Write("Larghezza: ")310. P.Dimensions(0) = Console.ReadLine311. Console.Write("Lunghezza: ")312. P.Dimensions(1) = Console.ReadLine313. Console.Write("Altezza: ")314. P.Dimensions(2) = Console.ReadLine315. Processor.Item(Place) = P316. Case "t"317. Dim T As New Telegram(Processor.NextId)318. Console.WriteLine("Telegramma - Id:{0:0000}", T.Id)319. Console.Write("Destinatario: ")320. T.Recipient = Console.ReadLine321. Console.Write("Messaggio: ")322. T.Message = Console.ReadLine323. Processor.Item(Place) = T324. Case "v"325. Dim M As New MoneyOrder(Processor.NextId)326. Console.WriteLine("Vaglia - Id:{0:0000}", M.Id)327. Console.Write("Beneficiario: ")328. M.Recipient = Console.ReadLine329. Console.Write("Somma: ")330. M.Money = Console.ReadLine331. Processor.Item(Place) = M332. Case Else333. Console.WriteLine("Comando non riconosciuto.")334. Console.ReadKey()335. Exit Sub336. End Select337.338. Console.WriteLine("Inserimento eseguito!")339. Console.ReadKey()340. End Sub341.342. Sub ProcessData()343. Console.WriteLine("Selezionare loperazione:")344. Console.WriteLine(" c - cerca;")345. Console.WriteLine(" v - visualizza;")346.
  • Cmd = Console.ReadKey().KeyChar347.348. Console.Clear()349. Select Case Cmd350. Case "c"351. Dim Str As String352. Console.WriteLine("Inserire la parola da cercare:")353. Str = Console.ReadLine354.355. Dim Ids() As Int32 = Processor.SearchItems(Str)356. Console.WriteLine("Trovati {0} elementi. Visualizzare? (y/n)", Ids.Length)357. Cmd = Console.ReadKey().KeyChar358. Console.WriteLine()359.360. If Cmd = "y" Then361. For Each Id As Int32 In Ids362. Processor.PrintById(Id)363. Next364. End If365. Case "v"366. Console.WriteLine("Visualizzare gli elementi")367. Console.Write("Da Id: ")368. IdFrom = Console.ReadLine369. Console.Write("A Id: ")370. IdTo = Console.ReadLine371. Processor.PrintByFilter(AddressOf SelectId)372. Case Else373. Console.WriteLine("Comando sconosciuto.")374. End Select375.376. Console.ReadKey()377. End Sub378.379. Sub Main()380. Do381. Console.WriteLine("Gestione ufficio")382. Console.WriteLine()383. Console.WriteLine("Selezionare loperazione da effettuare:")384. Console.WriteLine(" i - inserimento oggetti;")385. Console.WriteLine(" m - modifica capacità magazzino;")386. Console.WriteLine(" p - processa i dati;")387. Console.WriteLine(" e - esci.")388. Cmd = Console.ReadKey().KeyChar389.390. Console.Clear()391. Select Case Cmd392. Case "i"393. Dim Index As Int32 = Processor.FirstPlaceAvailable394.395. Console.WriteLine("Inserimento oggetti in magazzino")396. Console.WriteLine()397.398. If Index > -1 Then399. InsertItems(Index)400. Else401. Console.WriteLine("Non cè più spazio in magazzino!")402. Console.ReadKey()403. End If404. Case "m"405. Console.WriteLine("Attuale capacità: " & Processor.StorageCapacity)406. Console.WriteLine("Inserire una nuova dimensione: ")407. Processor.StorageCapacity = Console.ReadLine408. Console.WriteLine("Operazione effettuata.")409. Console.ReadKey()410. Case "p"411. ProcessData()412. End Select413. Console.Clear()414. Loop Until Cmd = "e"415. End Sub416. End Module
  • Avevo in mente di definir e anche unaltr 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 alledimensioni per il pacco, in base al numer o di par ole per il telegr amma e in base allammontar e inviato per il vaglia),bastasse r ichiamar e una funzione attr aver so linter faccia per ottener e il r isultato. Poi ho consider ato che un esempiodi 400 r ighe er a già abbastanza. Ad ogni modo, user ò adesso quelidea in uno spezzone tr atto dal pr ogr amma appenascr itto per mostr ar e luso di inter facce multiple: 01. Module Module1 02. ... 03. 04. Interface IPayable 05. Function CalculateSendCost() As Single 06. End Interface 07. 08. Class Telegram 09. Nel caso di più interfacce, le si separa con la virgola 10. Implements IIdentifiable, IPayable 11. 12. ... 13. 14. Public Function CalculateSendCost() As Single Implements IPayable.CalculateSendCost 15. Come vedremo nel capitolo dedicato alle stringhe, 16. la funzione Split(c) spezza la stringa in tante 17. parti, divise dal carattere c, e le restituisce 18. sottoforma di array. In questo caso, tutte le sottostringhe 19. separate da uno spazio sono allincirca tante 20. quanto il numero di parole nella frase 21. Select Case Me.Message.Split(" ").Length 22. Case Is <= 20 23. Return 4.39 24. Case Is <= 50 25. Return 6.7 26. Case Is <= 100 27. Return 10.3 28. Case Is <= 200 29. Return 19.6 30. Case Is <= 500 31. Return 39.75 32. End Select 33. End Function 34. End Class 35. 36. ... 37. End ClassDefinizione di tipi in uninterfac c iaCosì come è possibile dichiar ar e una nuova classe allinter no di unaltr a, o una str uttur a in una classe, o uninter faccia inuna classe, o una str uttur a in una str uttur a, o tutte le altr e possibili combinazioni, è anche possibile dichiar ar e unnuovo tipo in uninter faccia. In questo caso, solo le classi che implementer anno quellinter faccia sar anno in gr ado diusar e quel tipo. Ad esempio: 01. Interface ISaveable 02. Structure FileInfo 03. Assumiamo per brevità che queste variabili Public 04. siano in realtà proprietà 05. Public Path As String 06. FileAttribues è un enumeratore su bit che contiene 07. informazioni sugli attributi di un file (nascosto, a sola 08. lettura, archivio, compresso, eccetera...) 09. Public Attributes As FileAttributes 10. End Structure 11. Property SaveInfo() As FileInfo 12. Sub Save() 13.
  • End Interface 14. 15. Class A 16. Private _SaveInfo As ISaveable.FileInfo SBAGLIATO! 17. ... 18. End Class 19. 20. 21. Class B 22. Implements ISaveable 23. 24. Private _SaveInfo As ISaveable.FileInfo GIUSTO 25. 26. ... 27. End ClassEreditarietà, polimorfismo e overloading per le interfac c eAnche le inter facce possono er editar e da unaltr a inter faccia base. In questo caso, dato che in uninter faccia non sipossono usar e specificator i di accesso, la classe der ivata acquisisce tutti i membr i di quella base: 1. Interface A 2. Property PropA() As Int32 3. End Interface 4. 5. Interface B 6. Inherits A 7. Sub SubB() 8. End InterfaceNon 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 lover loading come si fa di consueto: non ci sono differ enze significative in questo ambito.Perc hè preferire uninterfac c ia a una c lasse astrattaLa differ enza sostanziale tr a una classe astr atta e uninter faccia è che la pr ima definisce les s en za di un oggetto (checosa è), mentr e la seconda ne indica il comportamen to (che cosa fa). Inoltr e una classe astr atta è in gr ado di definir emembr i che ver r anno acquisiti dalla classe der ivata, e quindi dichiar a delle funzionalità di base er editabili da tutti idiscendenti; linter faccia, al contr ar io, "or dina" a chi la implementa di definir e un cer to membr o con una cer tafunzione, ma non for nisce alcun codice di base, né alcuna dir ettiva su come un dato compito debba esser e svolto. Eccoche, sulla base di queste osser vazioni, possiamo individuar e alcune casistiche in cui sia meglio luna o laltr a: Quando esistono compor tamenti comuni : inter facce Quando esistono classi non r iconducibili ad alcun ar chetipo o classe base: inter facce Quando tutte le classi hanno fondamentalmente la stessa essenza : classe astr atta Quando tutte le classi, assimilabili ad un unico ar chetipo, hanno bisogno di implementar e la stessa funzionalità o gli stessi membr i : classe astr atta
  • A38. Utilizzo delle Interfacce - Parte ILaspetto più inter essante e sicur amente più utile delle inter facce è che il lor o utilizzo è fondamentale per luso di alcunicostr utti par ticolar i, quali il For Each e lUsing, e per molte altr e funzioni e pr ocedur e che inter vengono nella gestionedelle 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 lapplicazione in una manier a solida e r obusta.IComparable e IComparerUn oggetto che implementa ICompar able comunica implicitamente al .NET Fr amew or k che può esser e confr ontato conaltr i oggetti, stabilendo se uno di essi è maggior e, minor e o uguale allaltr o e abilitando in questo modo lor dinamentoautomatico attr aver so il metodo Sor t di una collection. Infatti, tale metodo confr onta uno ad uno ogni elemento di unacollezione 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 dinecr escente o decr escente. Compar eTo è una funzione di istanza che implementa ICompar able.Compar eTo e ha deir isultati pr edefiniti: r estituisce 1 se loggetto passato come par ametr o è minor e delloggetto dalla quale viener ichiamata, 0 se è uguale e -1 se è maggior e. Ad esempio, questo semplice pr ogr amma illustr a il funzionamento diCompar eTo e Sor t: 01. Module Module1 02. Sub Main() 03. Dim A As Int32 04. 05. Console.WriteLine("Inserisci un numero intero:") 06. A = Console.ReadLine 07. 08. Tutti i tipi di base espongono il metodo CompareTo, poichè 09. tutti implementano linterfaccia IComparable: 10. If A.CompareTo(10) = 1 Then 11. Console.WriteLine(A & " è maggiore di 10") 12. ElseIf A.CompareTo(10) = 0 Then 13. Console.WriteLine(A & " è uguale a 10") 14. Else 15. Console.WriteLine(A & " è minore di 10") 16. End If 17. 18. Il fatto che i tipi di base siano confrontabili implica 19. che si possano ordinare tramite il metodo Sort di una 20. qualsiasi collezione o array di elementi 21. Dim B() As Int32 = {1, 5, 2, 8, 10, 56} 22. Ordina larray 23. Array.Sort(B) 24. E visualizza i numeri in ordine crescente 25. For I As Int16 = 0 To UBound(B) 26. Console.WriteLine(B(I)) 27. Next 28. 29. Anche String espone questo metodo, quindi si può ordinare 30. alfabeticamente un insieme di stringhe: 31. Dim C As New ArrayList 32. C.Add("Banana") 33. C.Add("Zanzara") 34. C.Add("Anello") 35. C.Add("Computer") 36. Ordina linsieme 37. C.Sort() 38. For I As Int16 = 0 To C.Count - 1 39. Console.WriteLine(C(I)) 40. Next 41. 42. Console.ReadKey() 43.
  • End Sub 44. End ModuleDopo aver immesso un input, ad esempio 8, avr emo la seguente scher mata: Inserire un numero intero: 8 8 è minore di 10 1 2 5 8 10 56 Anello Banana Computer ZanzaraCome si osser va, tutti gli elementi sono stati or dinati cor r ettamente. Or a che abbiamo visto la potenza diICompar able, vediamo di capir e come implementar la. Lesempio che pr ender ò come r ifer imento or a pone una sempliceclasse 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 ilnome completo: 01. Module Module1 02. Class Person 03. Implements IComparable 04. Private _FirstName, _LastName As String 05. Private ReadOnly _BirthDay As Date 06. 07. Public Property FirstName() As String 08. Get 09. Return _FirstName 10. End Get 11. Set(ByVal Value As String) 12. If Value <> "" Then 13. _FirstName = Value 14. End If 15. End Set 16. End Property 17. 18. Public Property LastName() As String 19. Get 20. Return _LastName 21. End Get 22. Set(ByVal Value As String) 23. If Value <> "" Then 24. _LastName = Value 25. End If 26. End Set 27. End Property 28. 29. Public ReadOnly Property BirthDay() As Date 30. Get 31. Return _BirthDay 32. End Get 33. End Property 34. 35. Public ReadOnly Property CompleteName() As String 36. Get 37. Return _FirstName & " " & _LastName 38. End Get 39. End Property 40. 41. Per definizione, purtroppo, CompareTo deve sempre usare 42. un parametro di tipo Object: risolveremo questo problema 43.
  • più in là utilizzando i Generics 44. Public Function CompareTo(ByVal obj As Object) As Integer _ 45. Implements IComparable.CompareTo 46. Un oggetto non-nothing (questo) è sempre maggiore di 47. un oggetto Nothing (ossia obj) 48. If obj Is Nothing Then 49. Return 1 50. End If 51. Tenta di convertire obj in Person 52. Dim P As Person = DirectCast(obj, Person) 53. E restituisce il risultato delloperazione di 54. comparazione tra stringhe dei rispettivi nomi 55. Return String.Compare(Me.CompleteName, P.CompleteName) 56. End Function 57. 58. Sub New(ByVal FirstName As String, ByVal LastName As String, _ 59. ByVal BirthDay As Date) 60. Me.FirstName = FirstName 61. Me.LastName = LastName 62. Me._BirthDay = BirthDay 63. End Sub 64. End Class 65. 66. Sub Main() 67. Crea un array di oggetti Person 68. Dim Persons() As Person = _ 69. {New Person("Marcello", "Rossi", Date.Parse("10/10/1992")), _ 70. New Person("Guido", "Bianchi", Date.Parse("01/12/1980")), _ 71. New Person("Bianca", "Brega", Date.Parse("23/06/1960")), _ 72. New Person("Antonio", "Felice", Date.Parse("16/01/1930"))} 73. 74. E li ordina, avvalendosi di IComparable.CompareTo 75. Array.Sort(Persons) 76. 77. For I As Int16 = 0 To UBound(Persons) 78. Console.WriteLine(Persons(I).CompleteName) 79. Next 80. 81. Console.ReadKey() 82. End Sub 83. End ModuleDato 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 diCompar eTo, poichè devono aver e la stessa signatur e, e cr ear e due metodi che or dinino lar r ay sar ebbe scomodo: è quiche entr a in gioco linter faccia ICompar er . Essa r appr esenta un oggetto che deve eseguir e la compar azione tr a duealtr i oggetti, facendo quindi da tramite nellor dinamento. Dato che Sor t accetta in una delle sue ver sioni un oggettoICompar er , è possibile or dinar e una lista di elementi con qualsiasi cr iter io si voglia semplicemente cambiando ilpar ametr o. Ad esempio, in questo sor gente scr ivo una classe Bir thDayCompar er che per mette di or dinar e oggettiPer son in base allanno di nascita: 01. Module Module2 02. Questa classe fornisce un metodo per comparare oggetti Person 03. utilizzando la proprietà BirthDay. 04. Per convenzione, classi che implementano IComparer dovrebbero 05. avere un suffisso "Comparer" nel nome. 06. Altra osservazione: se ci sono molte interfacce il cui nome 07. termina in "-able", definendo una caratteristica delloggetto 08. che le implementa (ad es.: un oggetto enumerabile, 09. comparabile, distruggibile, ecc...), ce ne sono altrettante 10. che terminano in "-er", indicando, invece, un oggetto 11. che "fa" qualcosa di specifico. 12. Nel nostro esempio, oggetti di tipo BirthDayComparer 13. hanno il solo scopo di comparare altre oggetti 14. Class BirthDayComparer 15. Implementa linterfaccia 16. Implements IComparer 17. 18.
  • Anche questa funzione deve usare parametri object 19. Public Function Compare(ByVal x As Object, ByVal y As Object) _ 20. As Integer Implements System.Collections.IComparer.Compare 21. Se entrambi gli oggetti sono Nothing, allora sono 22. uguali 23. If x Is Nothing And y Is Nothing Then 24. Return 0 25. ElseIf x Is Nothing Then 26. Se x è Nothing, y è maggiore 27. Return -1 28. ElseIf y Is Nothing Then 29. Se y è Nothing, x è maggiore 30. Return 1 31. Else 32. Dim P1 As Person = DirectCast(x, Person) 33. Dim P2 As Person = DirectCast(y, Person) 34. Compara le date 35. Return Date.Compare(P1.BirthDay, P2.BirthDay) 36. End If 37. End Function 38. End Class 39. 40. Sub Main() 41. Dim Persons() As Person = _ 42. {New Person("Marcello", "Rossi", Date.Parse("10/10/1992")), _ 43. New Person("Guido", "Bianchi", Date.Parse("01/12/1980")), _ 44. New Person("Bianca", "Brega", Date.Parse("23/06/1960")), _ 45. New Person("Antonio", "Felice", Date.Parse("16/01/1930"))} 46. 47. Ordina gli elementi utilizzando il nuovo oggetto 48. inizializato in linea BirthDayComparer 49. Array.Sort(Persons, New BirthDayComparer()) 50. 51. For I As Int16 = 0 To UBound(Persons) 52. Console.WriteLine(Persons(I).CompleteName) 53. Next 54. 55. Console.ReadKey() 56. End Sub 57. End ModuleUsando questo meccanismo è possibile or dinar e qualsiasi tipo di lista o collezione finor a analizzata (tr anne Sor tedList,che si or dina automaticamente), in modo semplice e veloce, par ticolar mente utile nellambito delle liste visuali acolonne, 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 poidistr ugger lo in poche r ighe di codice. Ogni classe che espone il metodo Dispose deve obbligator iamente implementar eanche linter faccia IDisposable, la quale comunica implicitamente che essa ha questa car atter istica. Dato che già moltiesempi sono stati fatti sullar gomento distr uttor i, eviter ò di tr attar e nuovamente Dispose in questo capitolo.
  • A39. Utilizzo delle Interfacce - Parte IIIEnumerable e IEnumeratorUna classe che implementa IEnumer able diventa enum er abile agli occhi del .NET Fr amew or k: ciò significa che si puòusar e su di essa un costr utto For Each per scor r er ne tutti gli elementi. Di solito questo tipo di classe r appr esenta unacollezione di elementi e per questo motivo il suo nome, secondo le convenzioni, dovr ebbe ter minar e in "Collection". Unmotivo per costr uir e una nuova collezione al posto di usar e le classiche liste può consister e nel voler definir e nuovimetodi o pr opr ietà per modificar la. Ad esempio, si potr ebbe scr iver e una nuova classe Per sonCollection che per mettedi r aggr uppar e ed enumer ar e le per sone ivi contenute e magar i calcolar e anche letà media. 01. Module Module1 02. Class PersonCollection 03. Implements IEnumerable 04. La lista delle persone 05. Private _Persons As New ArrayList 06. 07. Teoricamente, si dovrebbero ridefinire tutti i metodi 08. di una collection comune, ma per mancanza di spazio, 09. accontentiamoci 10. Public ReadOnly Property Persons() As ArrayList 11. Get 12. Return _Persons 13. End Get 14. End Property 15. 16. Restituisce letà media. TimeSpan è una struttura che si 17. ottiene sottraendo fra loro due oggetti date e indica un 18. intervallo di tempo 19. Public ReadOnly Property AverageAge() As String 20. Get 21. Variabile temporanea 22. Dim Temp As TimeSpan 23. Somma tutte le età 24. For Each P As Person In _Persons 25. Temp = Temp.Add(Date.Now - P.BirthDay) 26. Next 27. Divide per il numero di persone 28. Temp = TimeSpan.FromSeconds(Temp.TotalSeconds / _Persons.Count) 29. 30. Dato che TimeSpan può contenere al massimo 31. giorni e non mesi o anni, dobbiamo fare qualche 32. calcolo 33. Dim Years As Int32 34. Gli anni, ossia il numero dei giorni fratto 365 35. Divisione intera 36. Years = Temp.TotalDays 365 37. Sottrae gli anni: da notare che 38. (Temp.TotalDays 365) * 365) non è un passaggio 39. inutile. Infatti, per determinare il numero di 40. giorni che rimangono, bisogna prendere la 41. differenza tra il numero totale di giorni e 42. il multiplo più vicino di 365 43. Temp = _ 44. Temp.Subtract(TimeSpan.FromDays((Temp.TotalDays 365) * 365)) 45. 46. Return Years & " anni e " & CInt(Temp.TotalDays) & " giorni" 47. End Get 48. End Property 49. 50. La funzione GetEnumerator restituisce un oggetto di tipo 51. IEnumerator che vedremo fra breve: esso permette di 52. scorrere ogni elemento ordinatamente, dallinizio 53. alla fine. In questo caso, poichè non abbiamo ancora 54. analizzato questa interfaccia, ci limitiamo a restituisce 55.
  • lIEnumerator predefinito per un ArrayList 56. Public Function GetEnumerator() As IEnumerator _ 57. Implements IEnumerable.GetEnumerator 58. Return _Persons.GetEnumerator 59. End Function 60. End Class 61. 62. Sub Main() 63. Dim Persons As New PersonCollection 64. With Persons.Persons 65. .Add(New Person("Marcello", "Rossi", Date.Parse("10/10/1992"))) 66. .Add(New Person("Guido", "Bianchi", Date.Parse("01/12/1980"))) 67. .Add(New Person("Bianca", "Brega", Date.Parse("23/06/1960"))) 68. .Add(New Person("Antonio", "Felice", Date.Parse("16/01/1930"))) 69. End With 70. 71. For Each P As Person In Persons 72. Console.WriteLine(P.CompleteName) 73. Next 74. Console.WriteLine("Età media: " & Persons.AverageAge) 75. > 41 anni e 253 giorni 76. 77. Console.ReadKey() 78. End Sub 79. End ModuleCome si vede dallesempio, è lecito usar e Per sonCollection nel costr utto For Each: liter azione viene svolta dal pr imoelemento inser ito allultimo, poichè lIEnumer ator dellAr r ayList oper a in questo modo. Tuttavia, cr eando una diver saclasse che implementa IEnumer ator si può scor r er e la collezione in qualsiasi modo: dal più giovane al più vecchio, alpr imo allultimo, dallultimo al pr imo, a caso, saltandone alcuni, a seconda dellor a di cr eazione ecceter a. Quindi inquesto 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 definitinellinter faccia: MoveNex t è una funzione che r estituisce Tr ue se esiste un elemento successivo nella collezione (e inquesto caso lo imposta come elemento cor r ente), altr imenti False; Cur r ent è una pr opr ietà ReadOnly di tipo Object cher estituisce lelemento cor r ente; Reset è una pr ocedur a senza par ametr i che r esetta il contator e e fa iniziar e il ciclodaccapo. Questultimo metodo non viene mai utilizzato, ma nellesempio che segue ne scr iver ò comunque il cor po: 001. Module Module1 002. Class PersonCollection 003. Implements IEnumerable 004. La lista delle persone 005. Private _Persons As New ArrayList 006. 007. Questa classe ha il compito di scorrere ordinatamente gli 008. elementi della lista, dal più vecchio al più giovane 009. Private Class PersonAgeEnumerator 010. Implements IEnumerator 011. 012. Per enumerare gli elementi, la classe ha bisogno di un 013. riferimento ad essi: perciò si deve dichiarare ancora 014. un nuovo ArrayList di Person. Questo passaggio è 015. facoltativo nelle classi nidificate come questa, ma è 016. obbligatorio in tutti gli altri casi 017. Private Persons As New ArrayList 018. Per scorrere la collezione, si userà un comune indice 019. Private Index As Int32 020. 021. Essendo una normalissima classe, è lecito definire un 022. costruttore, che in questo caso inizializza la 023. collezione 024. Sub New(ByVal Persons As ArrayList) 025. Ricordate: poichè ArrayList deriva da Object, è 026. un tipo reference. Assegnare Persons a Me.Persons 027. equivale ad assegnarne lindirizzo e quindi ogni 028. modifica su questo arraylist privato si rifletterà 029. su quello passato come parametro. Si può 030. evitare questo problema clonando la lista 031.
  • Me.Persons = Persons.Clone032. MoveNext viene richiamato prima di usare Current,033. quindi Index verrà incrementata subito.034. Per farla diventare 0 al primo ciclo la si035. deve impostare a -1036. Index = -1037.038. Dato che lenumeratore deve scorrere la lista039. secondo lanno di nascita, bisogna prima ordinarla040. Me.Persons.Sort(New BirthDayComparer)041. End Sub042.043. Restituisce lelemento corrente044. Public ReadOnly Property Current() As Object _045. Implements System.Collections.IEnumerator.Current046. Get047. Return Persons(Index)048. End Get049. End Property050.051. Restituisce True se esiste lelemento successivo e lo052. imposta, altrimenti False053. Public Function MoveNext() As Boolean _054. Implements System.Collections.IEnumerator.MoveNext055. If Index = Persons.Count - 1 Then056. Return False057. Else058. Index += 1059. Return True060. End If061. End Function062.063. Resetta il ciclo064. Public Sub Reset() _065. Implements System.Collections.IEnumerator.Reset066. Index = -1067. End Sub068. End Class069.070. Public ReadOnly Property Persons() As ArrayList071. Get072. Return _Persons073. End Get074. End Property075.076. Public ReadOnly Property AverageAge() As String077. Get078. Dim Temp As TimeSpan079. For Each P As Person In _Persons080. Temp = Temp.Add(Date.Now - P.BirthDay)081. Next082. Temp = TimeSpan.FromSeconds(Temp.TotalSeconds / _Persons.Count)083.084. Dim Years As Int32085. Years = Temp.TotalDays 365086. Temp = _087. Temp.Subtract(TimeSpan.FromDays((Temp.TotalDays 365) * 365))088. Return Years & " anni e " & CInt(Temp.TotalDays) & " giorni"089. End Get090. End Property091.092. La funzione GetEnumerator restituisce ora un oggetto di093. tipo IEnumerator che abbiamo definito in una classe094. nidificata e il ciclo For Each scorrerà quindi095. dal più vecchio al più giovane096. Public Function GetEnumerator() As IEnumerator _097. Implements IEnumerable.GetEnumerator098. Return New PersonAgeEnumerator(_Persons)099. End Function100. End Class101.102. Sub Main()103.
  • Dim Persons As New PersonCollection 104. With Persons.Persons 105. .Add(New Person("Marcello", "Rossi", Date.Parse("10/10/1992"))) 106. .Add(New Person("Guido", "Bianchi", Date.Parse("01/12/1980"))) 107. .Add(New Person("Bianca", "Brega", Date.Parse("23/06/1960"))) 108. .Add(New Person("Antonio", "Felice", Date.Parse("16/01/1930"))) 109. End With 110. 111. Enumera ora per data di nascita, ma senza modificare 112. lordine degli elementi 113. For Each P As Person In Persons 114. Console.WriteLine(P.BirthDay.ToShortDateString & ", " & _ 115. P.CompleteName) 116. Next 117. 118. Stampa la prima persona, dimostrando che lordine 119. della lista è intatto 120. Console.WriteLine(Persons.Persons(0).CompleteName) 121. 122. Console.ReadKey() 123. End Sub 124. End ModuleICloneableCome si è visto nellesempio appena scr itto, si pr esentano alcune difficoltà nel manipolar e oggetti di tipo Refer ence, inquanto lassegnazione di questi cr eer ebbe due istanze che puntano allo stesso oggetto piuttosto che due oggettidistinti. È in questo tipo di casi che il metodo Clone e linter faccia ICloneable assumono un gr an valor e. Il pr imoper mette di eseguir e una copia delloggetto, cr eando un nuo v o og g etto a tutti gli effetti, totalmente disgiunto daquello di par tenza: questo per mette di non intaccar ne accidentalmente lintegr ità. Una dimostr azione: 01. Module Esempio 02. Sub Main() 03. Il tipo ArrayList espone il metodo Clone 04. Dim S1 As New ArrayList 05. Dim S2 As New ArrayList 06. 07. S2 = S1 08. 09. Verifica che S1 e S2 puntano lo stesso oggetto 10. Console.WriteLine(S1 Is S2) 11. > True 12. 13. Clona loggetto 14. S2 = S1.Clone 15. Verifica che ora S2 referenzia un oggetto differente, 16. ma di valore identico a S1 17. Console.WriteLine(S1 Is S2) 18. > False 19. 20. Console.ReadKey() 21. End Sub 22. End ModuleLinter faccia, invece, come accadeva per IEnumer able e ICompar able, indica al .NET Fr amew or k che loggetto è clonabile.Questo codice mostr a la funzione Close alloper a: 01. Module Module1 02. Class UnOggetto 03. Implements ICloneable 04. Private _Campo As Int32 05. 06. Public Property Campo() As Int32 07. Get 08. Return _Campo 09. End Get 10.
  • Set(ByVal Value As Int32) 11. _Campo = Value 12. End Set 13. End Property 14. 15. Restituisce una copia delloggetto 16. Public Function Clone() As Object Implements ICloneable.Clone 17. La funzione Protected MemberwiseClone, ereditata da 18. Object, esegue una copia superficiale delloggetto, 19. come spiegherò fra poco: è quello che 20. serve in questo caso 21. Return Me.MemberwiseClone 22. End Function 23. 24. Loperatore = permette di definire de due oggetti hanno un 25. valore uguale 26. Shared Operator =(ByVal O1 As UnOggetto, ByVal O2 As UnOggetto) As _ 27. Boolean 28. Return O1.Campo = O2.Campo 29. End Operator 30. 31. Shared Operator <>(ByVal O1 As UnOggetto, ByVal O2 As UnOggetto) As _ 32. Boolean 33. Return Not (O1 = O2) 34. End Operator 35. End Class 36. 37. Sub Main() 38. Dim O1 As New UnOggetto 39. Dim O2 As UnOggetto = O1.Clone 40. 41. I due oggetti NON sono lo stesso oggetto: il secondo 42. è solo una copia, disgiunta da O1 43. Console.WriteLine(O1 Is O2) 44. > False 45. 46. Tuttavia hanno lo stesso identico valore 47. Console.WriteLine(O1 = O2) 48. > True 49. 50. Console.ReadKey() 51. End Sub 52. End ModuleOr a, è impor tante distinguer e due tipi di copia: quella Shallo w e quella Deep. La pr ima cr ea una copia super ficialedelloggetto, ossia si limita a clonar e tutti i campi. La seconda, invece, è in gr ado di eseguir e questa oper azione anchesu 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 nocontiene 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 puntasempr e allo stesso oggetto, mentr e una copia Deep cloner à anche Childr en. Si nota meglio con un gr afico: le fr eccever di indicano oggetti clonati, mentr e la fr eccia ar ancio si r ifer isce allo stesso oggetto.
  • Non è possibile specificar e nella dichiar azione di Clone quale tipo di copia ver r à eseguita, quindi tutto viene lasciatoallar 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 atipizzazio ne debo le, ossia ser ve un oper ator e di cast per conver tir lo nel tipo desider ato; per cr ear ne una ver sionea tipizzazio ne fo r te è necessar io scr iver e una funzione che r estituisca, ad esempio, un tipo Per son. Questultimaver sione avr à il nome Clone, mentr e quella che implementa ICloneable.Clone() avr à un nome differ ente, come CloneMe().
  • A40. Le librerie di classiCer 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 totipo di infor mazioni. In questi casi, si scr ive una libr er ia di classi, ossia un insieme, appunto, di namespace, classi e tipiche ser vono ad un deter minato scopo. Potete tr ovar e un esempio tr a i sor genti della sezione Dow nload: mi r ifer isco aMp3 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 (perulter ior i infor mazioni sullar gomento, consultar e la sezione FFS). Con quel pr ogetto non ho voluto scr iver e unpr ogr amma che svolgesse quei compiti, per chè da solo sar ebe stato poco utile, ma piuttosto metter e a disposizioneanche agli altr i pr ogr ammator i un modo semplice per manipolar e quel tipo di infor mazioni. Così facendo, uno potr ebbeusar 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 ittoappositamente 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 sodal solito: 1. Class Class1 2. 3. End ClassNoter 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, masolo 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 dalpr ogr amma, estr aendone solo le classi: 001. Namespace PostalManagement 002. 003. Public Interface IIdentifiable 004. ReadOnly Property Id() As Int32 005. Function ToString() As String 006. End Interface 007. 008. Public Class Pack 009. Implements IIdentifiable 010. 011. Private _Id As Int32 012. Private _Destination As String 013. Private _Dimensions(2) As Single 014. 015. Public ReadOnly Property Id() As Integer Implements IIdentifiable.Id 016. Get 017. Return _Id 018. End Get 019. End Property 020. 021. Public Property Destination() As String 022. Get 023. Return _Destination 024. End Get 025. Set(ByVal value As String) 026. _Destination = value 027. End Set 028. End Property 029. 030. Public Property Dimensions(ByVal Index As Int32) As Single 031. Get 032.
  • If (Index >= 0) And (Index < 3) Then033. Return _Dimensions(Index)034. Else035. Throw New IndexOutOfRangeException()036. End If037. End Get038. Set(ByVal value As Single)039. If (Index >= 0) And (Index < 3) Then040. _Dimensions(Index) = value041. Else042. Throw New IndexOutOfRangeException()043. End If044. End Set045. End Property046.047. Public Sub New(ByVal Id As Int32)048. _Id = Id049. End Sub050.051. Public Overrides Function ToString() As String Implements IIdentifiable.ToString052. Return String.Format("{0:0000}: Pacco {1}x{2}x{3}, Destinazione: {4}", _053. Me.Id, Me.Dimensions(0), Me.Dimensions(1), _054. Me.Dimensions(2), Me.Destination)055. End Function056. End Class057.058. Public Class Telegram059. Implements IIdentifiable060.061. Private _Id As Int32062. Private _Recipient As String063. Private _Message As String064.065. Public ReadOnly Property Id() As Integer Implements IIdentifiable.Id066. Get067. Return _Id068. End Get069. End Property070.071. Public Property Recipient() As String072. Get073. Return _Recipient074. End Get075. Set(ByVal value As String)076. _Recipient = value077. End Set078. End Property079.080. Public Property Message() As String081. Get082. Return _Message083. End Get084. Set(ByVal value As String)085. _Message = value086. End Set087. End Property088.089. Public Sub New(ByVal Id As Int32)090. _Id = Id091. End Sub092.093. Public Overrides Function ToString() As String Implements IIdentifiable.ToString094. Return String.Format("{0:0000}: Telegramma per {1} ; Messaggio = {2}", _095. Me.Id, Me.Recipient, Me.Message)096. End Function097. End Class098.099. Public Class MoneyOrder100. Implements IIdentifiable101.102. Private _Id As Int32103. Private _Recipient As String104.
  • Private _Money As Single105.106. Public ReadOnly Property Id() As Integer Implements IIdentifiable.Id107. Get108. Return _Id109. End Get110. End Property111.112. Public Property Recipient() As String113. Get114. Return _Recipient115. End Get116. Set(ByVal value As String)117. _Recipient = value118. End Set119. End Property120.121. Public Property Money() As Single122. Get123. Return _Money124. End Get125. Set(ByVal value As Single)126. _Money = value127. End Set128. End Property129.130. Public Sub New(ByVal Id As Int32)131. _Id = Id132. End Sub133.134. Public Overrides Function ToString() As String Implements IIdentifiable.ToString135. Return String.Format("{0:0000}: Vaglia postale per {1} ; Ammontare = {2}€", _136. Me.Id, Me.Recipient, Me.Money)137. End Function138. End Class139.140. Public Class PostalProcessor141. Public Delegate Function IdSelector(ByVal Id As Int32) As Boolean142.143. Private _StorageCapacity As Int32144. Private _NextId As Int32 = 0145. Private Storage() As IIdentifiable146.147. Public Property StorageCapacity() As Int32148. Get149. Return _StorageCapacity150. End Get151. Set(ByVal value As Int32)152. _StorageCapacity = value153. ReDim Preserve Storage(value)154. End Set155. End Property156.157. Public Property Item(ByVal Index As Int32) As IIdentifiable158. Get159. If (Index >= 0) And (Index < Storage.Length) Then160. Return Me.Storage(Index)161. Else162. Throw New IndexOutOfRangeException()163. End If164. End Get165. Set(ByVal value As IIdentifiable)166. If (Index >= 0) And (Index < Storage.Length) Then167. Me.Storage(Index) = value168. Else169. Throw New IndexOutOfRangeException()170. End If171. End Set172. End Property173.174. Public ReadOnly Property FirstPlaceAvailable() As Int32175. Get176.
  • For I As Int32 = 0 To Me.Storage.Length - 1177. If Me.Storage(I) Is Nothing Then178. Return I179. End If180. Next181. Return (-1)182. End Get183. End Property184.185. Public ReadOnly Property NextId() As Int32186. Get187. _NextId += 1188. Return _NextId189. End Get190. End Property191.192.193. Public Sub New(ByVal Items() As IIdentifiable)194. Me.Storage = Items195. _StorageCapacity = Items.Length196. End Sub197.198. Public Sub New(ByVal Capacity As Int32)199. Me.StorageCapacity = Capacity200. End Sub201.202. Public Sub PrintByFilter(ByVal Selector As IdSelector)203. For Each K As IIdentifiable In Storage204. If K Is Nothing Then205. Continue For206. End If207. If Selector.Invoke(K.Id) Then208. Console.WriteLine(K.ToString())209. End If210. Next211. End Sub212.213. Public Sub PrintById(ByVal Id As Int32)214. For Each K As IIdentifiable In Storage215. If K Is Nothing Then216. Continue For217. End If218. If K.Id = Id Then219. Console.WriteLine(K.ToString())220. Exit For221. End If222. Next223. End Sub224.225. Public Function SearchItems(ByVal Str As String) As Int32()226. Dim Temp As New ArrayList227.228. For Each K As IIdentifiable In Storage229. If K Is Nothing Then230. Continue For231. End If232. If K.ToString().Contains(Str) Then233. Temp.Add(K.Id)234. End If235. Next236.237. Dim Result(Temp.Count - 1) As Int32238. For I As Int32 = 0 To Temp.Count - 1239. Result(I) = Temp(I)240. Next241.242. Temp.Clear()243. Temp = Nothing244.245. Return Result246. End Function247. End Class248.
  • 249. End NamespaceNotate che ho r acchiuso tutto in un namespace e ho anche messo lo scope Public a tutti i membr i non pr ivati. Se nonavessi messo Public, infatti, i membr i senza scope sar ebber o stati automaticamente mar cati con Fr iend. Suppongo vir 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 equindi chi la user à non potr à acceder vi. Ovviamente, dato che il tutto si basa sullinter faccia IIdentifiable non potevopr ecluder ne laccesso agli utenti della libr er ia, e allo stesso modo i costr uttor i senza Public sar ebber o stati inaccessibilie non si sar ebbe potuto istanziar e alcun oggetto. Ecco che concludiamo la lista di tutti gli specificator i di accesso conPr otected Fr iend: un membr o dichiar ato Pr otected Fr iend sar à accessibile solo ai membr i delle classi der ivateappar tenenti allo stesso assembly. Per r icapitolar vi tutti gli scope, ecco uno schema dove le fr ecce ver di indicano gliunici accessi consentiti:Importare la libreria in un altro progettoUna volta compilata la libr er ia, al posto delleseguibile, nella sottocar tella binRelease del vostr o pr ogetto, si tr over à unfile con estensione *.dll. Per usar e le classi contenute in questa libr er ia (o r ifer im ento , nome tecnico che si confondespesso 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 ache mostr a tutti gli elementi del pr ogetto) cliccate col pulsante destr o sul nome del pr ogetto e selezionate "AddRefer ence" ("Aggiungi r ifer imento"):
  • Quindi r ecatevi fino alla car tella della libr er ia cr eata, selezionate il file e pr emete OK (nellesempio cè una delle libr er ieche ho scr itto e che potete tr ovar e nella sezione Dow nload):or a il r ifer imento è stato aggiunto al pr ogetto, ma non potete ancor a usar e le classi della libr er ia. Pr ima dovete "dir e"al compilator e che nel codice che sta per esser e letto potr este far e r ifer imento ad esse. Questo si fa "impor tando" ilnamespace, con il codice: 1. Imports [Nome Libreria].[Nome Namespace]Io ho chiamato la libr er ia con lo stesso nome del namespace, ma potete usar e anche nomi diver si, poiché in una libr er iaci possono esser e tanti namespace differ enti: 1. Imports PostalManagement.PostalManagementImpor ts è una "dir ettiva", ossia non costituisce codice eseguibile, ma infor ma il compilator e che alcune classi delsor gente potr ebber o appar tener e a questo namespace (omettendo questa r iga, dovr ete scr iver e ogni voltaPostalManagement.Pack, ad esempio, per usar e la classe Pack, per chè altr imenti il compilator e non sar ebbe in gr ado di
  • tr ovar e il name Pack nel contesto cor r ente). Ecco un esempio: 01. Imports PostalManagement.PostalManagement 02. 03. Module Module1 04. 05. Sub Main() 06. Dim P As New PostalProcessor(10) 07. Dim Pk As New Pack(P.NextId) 08. 09. P.Item(P.FirstPlaceAvailable) = Pk 10. ... 11. End Sub 12. 13. End Moduleche equivale a: 01. Module Module1 02. 03. Sub Main() 04. Dim P As New PostalManagement.PostalManagement.PostalProcessor(10) 05. Dim Pk As New PostalManagement.PostalManagement.Pack(P.NextId) 06. 07. P.Item(P.FirstPlaceAvailable) = Pk 08. ... 09. End Sub 10. 11. End ModuleNella scheda ".NET" che vedete nella seconda immagine di sopr a, ci sono molte libr er ie facenti par te del Fr amew or k cheuser emo nelle pr ossime sezioni della guida.
  • A41. I Generics - Parte IPanoramic a sui Generic sI 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 lesistenza, dor a in poi non potr ete far ne a meno. Cominciamo colfar e un par agone per esemplificar e il concetto di gener ics. Ammettiamo di dichiar ar e una var iabile I di tipo Int32: inquesta var iabile potr emo immagazzinar e qualsiasi infor mazione che consista di un numer o inter o r appr esentabile su32 bit. Possiamo dir e, quindi, che il tipo Int32 costituisce unastr azione di tutti i numer i inter i esistenti da-2147483648 a +2147483647. Analogamente un tipo g ener ic può assumer e come valor e un altr o tipo e, quindi, astr aetutti 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 ir ichiesti, dobbiamo anche "dir e" di quale tipo essi siano (ossia immetter e in T non un valor e ma un tipo): in questomodo, definendo un solo metodo, potr emo eseguir e somme tr a inter i, decimali, str inghe, date, file, classi, ecceter a...In VB.NET, loper azione di specificar e un tipo per un entità gener ic si attua con questa sintassi: 1. [NomeEntità](Of [NomeTipo])Dato i gener ics di possono applicar e ad ogni entità del .NET (metodi, classi, pr opr ietà, str uttur e, inter facce, delegate,ecceter a...), ho scr itto solo "NomeEntità" per indicar e il nome del tar get a cui si applicano. Il pr ossimo esempio mostr acome 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 equalsiasi tipo di dato, memor izzando, quindi, var iabili di tipo Object. Come già detto allinizio del cor so, luso di Objectcompor ta molti r ischi sia a livello di pr estazioni, dovute alle continue oper azioni di box ing e unbox ing (e le gar bagecollection che ne conseguono, data la cr eazione di molti oggetti tempor anei), sia a livello di cor r ettezza del codice. Unesempio 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 siincontr a un r ecor d che non è del tipo specificato, ad esempio: 01. Dim A As New ArrayList 02. A.Add(2) 03. A.Add(3) 04. A.Add("C") 05. A run-time, sarà lanciata uneccezione inerente il cast 06. poichè la stringa "C" non è del tipo specificato 07. nel blocco For Each 08. For Each V As Int32 In A 09. Console.WriteLine(V) 10. NextInfatti, se lapplicazione dovesse er r oneamente inser ir e una str inga al posto di un numer o inter o, non ver r ebbegener ato nessun er r or e, ma si ver ificher ebbe uneccezione successivamente. Altr a pr oblematica legata alluso dicollezioni a tipizzazione debole (ossia che r egistr ano gener ici oggetti Object, come lAr r ayList, lHashTable o laSor tedList) è dovuta al fatto che sia necessar ia una conver sione esplicita di tipo nelluso dei suoi elementi, almeno nellamaggior anza dei casi. La soluzione adottata da un pr ogr ammator e che non conoscesse i gener ics per r isolver e taliinconvenienti sar ebbe quella di cr ear e una nuova lista, ex novo, er editandola da un tipo base come CollectionBase er idefinendone tutti i metodi (Add, Remove, Index Of ecc...). Luso dei Gener ics, invece, r ende molto più veloce e menoinsidiosa la scr ittur a di un codice r obusto e solido nellambito non solo delle collezioni, ma di molti altr i ar gomenti. Eccoun esempio di come implementar e una soluzione basata sui Gener ics: 01. La lista accetta solo oggetti di tipo Int32: per questo motivo 02. si genera uneccezione quando si tenta di inserirvi elementi di 03. tipo diverso e la velocità di elaborazione aumenta! 04. Dim A As New List(Of Int32) 05.
  • A.Add(1) 06. A.Add(4) 07. A.Add(8) 08. A.Add("C") <- Impossibile 09. For Each V As Int32 In A 10. Console.WriteLine(V) 11. NextE questa è una dimostr azione dellincr emento delle pr estazioni: 01. Module Module1 02. Sub Main() 03. Dim TipDebole As New ArrayList 04. Dim TipForte As New List(Of Int32) 05. Dim S As New Stopwatch 06. 07. Cronometra le operazioni su ArrayList 08. S.Start() 09. For I As Int32 = 1 To 1000000 10. TipDebole.Add(I) 11. Next 12. S.Stop() 13. Console.WriteLine(S.ElapsedMilliseconds & _ 14. " millisecondi per ArrayList!") 15. 16. Cronometra le operazioni su List 17. S.Reset() 18. S.Start() 19. For I As Int32 = 1 To 1000000 20. TipForte.Add(I) 21. Next 22. S.Stop() 23. Console.WriteLine(S.ElapsedMilliseconds & _ 24. " millisecondi per List(Of T)!") 25. 26. Console.ReadKey() 27. End Sub 28. End ModuleSul mio computer por tatile lAr r ayList impiega 197ms, mentr e List 33ms: i Gener ics incr ementano la velocità di 6volte!Oltr e a List, esistono anche altr e collezioni gener ic, ossia Dictionar y e Sor tedDictionar y: tutti questi sono la ver sione atipizzazione for te delle nor mali collezioni già viste. Ma or a vediamo come scr iver e nuove classi e metodi gener ic.Generic s 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 à allatto di dichiar azione di un oggetto r appr esentato da questa classe. Il fattoche essa sia di tipo gener ico indica che anche i suoi membr i, molto pr obabilmente, avr anno lo stesso tipo: più nellospecifico, potr ebber o esser ci campi di tipo T e metodi che lavor ano su oggetti di tipo T. Se nessuna di queste duecondizioni è ver ificata, allor a non ha senso scr iver e una classe gener ics. Ma iniziamo col veder e un semplice esempio: 001. Module Module1 002. Collezione generica che contiene un qualsiasi tipo T di 003. oggetto. T si dice "tipo generic aperto" 004. Class Collection(Of T) 005. Per ora limitiamoci a dichiarare un array interno 006. alla classe. 007. Vedremo in seguito che è possibile ereditare da 008. una collezione generics già esistente. 009. Notate che la variabile è di tipo T: una volta che 010. abbiamo dichiarato la classe come generics su un tipo T, 011.
  • è come se avessimo "dichiarato" lesistenza di T012. come tipo fittizio.013. Private _Values() As T014.015. Restituisce lIndex-esimo elemento di Values (anchesso016. è di tipo T)017. Public Property Values(ByVal Index As Int32) As T018. Get019. If (Index >= 0) And (Index < _Values.Length) Then020. Return _Values(Index)021. Else022. Throw New IndexOutOfRangeException()023. End If024. End Get025. Set(ByVal value As T)026. If (Index >= 0) And (Index < _Values.Length) Then027. _Values(Index) = value028. Else029. Throw New IndexOutOfRangeException()030. End If031. End Set032. End Property033.034. Proprietà che restituiscono il primo e lultimo035. elemento della collezione036. Public ReadOnly Property First() As T037. Get038. Return _Values(0)039. End Get040. End Property041.042. Public ReadOnly Property Last() As T043. Get044. Return _Values(_Values.Length - 1)045. End Get046. End Property047.048. Stampa tutti i valori presenti nella collezione a schermo.049. Su un tipo generic è sempre possibile usare050. loperatore Is (ed il suo corrispettivo IsNot) e051. confrontarlo con Nothing. Se si tratta di un tipo value052. luguaglianza con Nothing sarà sempre falsa.053. Public Sub PrintAll()054. For Each V As T In _Values055. If V IsNot Nothing Then056. Console.WriteLine(V.ToString())057. End If058. Next059. End Sub060.061. Inizializza la collezione con Count elementi, tutti del062. valore DefaultValue063. Sub New(ByVal Count As Int32, ByVal DefaultValue As T)064. If Count < 1 Then065. Throw New ArgumentOutOfRangeException()066. End If067.068. ReDim _Values(Count - 1)069.070. For I As Int32 = 0 To _Values.Length - 1071. _Values(I) = DefaultValue072. Next073. End Sub074.075. End Class076.077. Sub Main()078. Dichiara quattro variabili contenenti quattro nuovi079. oggetti Collection. Ognuno di questi, però,080. è specifico per un solo tipo che decidiamo081. noi durante la dichiarazione. String, Int32, Date082. e Person, ossia i tipi che stiamo inserendo nel tipo083.
  • generico T, si dicono "tipi generic collegati", 084. poiché collegano il tipo fittizio T con un 085. reale tipo esistente 086. Dim Strings As New Collection(Of String)(10, "null") 087. Dim Integers As New Collection(Of Int32)(5, 12) 088. Dim Dates As New Collection(Of Date)(7, Date.Now) 089. Dim Persons As New Collection(Of Person)(10, Nothing) 090. 091. Strings.Values(0) = "primo" 092. Integers.Values(3) = 45 093. Dates.Values(6) = New Date(2009, 1, 1) 094. Persons.Values(3) = New Person("Mario", "Rossi", Dates.Last) 095. 096. Strings.PrintAll() 097. Integers.PrintAll() 098. Dates.PrintAll() 099. Persons.PrintAll() 100. 101. Console.ReadKey() 102. End Sub 103. 104. End ModuleOgnuna della quattr o var iabili del sor gente contiene un oggetto di tipo Collection, ma tali oggetti non sono dello stessotipo, poiché ognuno espone un differ ente tipo gener ics collegato. Quindi, nonostante si tr atti sempr e della stessa classeCollection, Collection(Of Int32) e Collection(Of Str ing) sono a tutti gli effetti due tipi diver si: è come se esistesser o dueclassi in cui T è sostituito in una da Int32 e nellaltr a da Str ing. Per dimostr ar e la lor o diver sità, basta scr iver e: 1. Console.WriteLine(Strings.GetType() Is Integers.GetType()) 2. Output : FalseMetodi Generic s e tipi generic s c ollegati implic itiSe si decide di scr iver e un solo metodo gener ics, e di focalizzar e su di esso lattenzione, solo accanto al suo nomeappar ir à la dichiar azione di un tipo gener ics aper to, con la consueta clausola "(Of T)". Anche se finor a ho usato comenome solamente T, nulla vieta di specificar e un altr o identificator e valido (ad esempio Pippo): tuttavia, è convenzioneche il nome dei tipi gener ics aper ti sia Tn (con n numer o inter o, ad esempio T1, T2, T3, eccetr a...) o, in caso contr ar io,che inizi almeno con la letter a T (ad esempio TSize, TClass, ecceter a...). 1. Sub [NomeProcedura](Of T)([Parametri]) 2. ... 3. End Sub 4. 5. Function [NomeFunzione](Of T)([Parametri]) As [TipoRestituito] 6. ... 7. End FunctionEcco un semplice esempio: 01. Module Module1 02. 03. Scambia i valori di due variabili, passate 04. per indirizzo 05. Public Sub Swap(Of T)(ByRef Arg1 As T, ByRef Arg2 As T) 06. Dim Temp As T = Arg1 07. Arg1 = Arg2 08. Arg2 = Temp 09. End Sub 10. 11. Sub Main() 12. Dim X, Y As Double 13. Dim Z As Single 14. Dim A, B As String 15. 16. X = 90.0 17.
  • Y = 67.58 18. Z = 23.01 19. A = "Ciao" 20. B = "Mondo" 21. 22. Nelle prossime chiamate, Swap non presenta un 23. tipo generics collegato: il tipo viene dedotto dai 24. tipi degli argomenti 25. 26. X e Y sono Double, quindi richiama il metodo con 27. T = Double 28. Swap(X, Y) 29. A e B sono String, quindi richiama il metodo con 30. T = String 31. Swap(A, B) 32. 33. Qui viene generato un errore: nonostante Z sia 34. convertibile in Double implicitamente senza perdita 35. di dati, il suo tipo non corrisponde a quello di X, 36. dato che cè un solo T, che può assumere 37. un solo valore-tipo. Per questo è necessario 38. utilizzare una scappatoia 39. Swap(Z, X) 40. 41. Soluzione 1: si esplicita il tipo generic collegato 42. Swap(Of Double)(Z, X) 43. Soluzione 2: si converte Z in double esplicitamente 44. Swap(CDbl(Z), X) 45. 46. Console.ReadKey() 47. End Sub 48. End ModuleGeneric s multipliQuando, anziché un solo tipo gener ics, se ne specificano due o più, si par la di genr ics multipli. La dichiar azione avvieneallo stesso modo di come abbiamo visto pr ecedentemente e i tipi vengono separ ati da una vir gola: 01. Module Module2 02. Una relazione qualsiasi fra due oggetti di tipo indeterminato 03. Public Class Relation(Of T1, T2) 04. Private Obj1 As T1 05. Private Obj2 As T2 06. 07. Public ReadOnly Property FirstObject() As T1 08. Get 09. Return Obj1 10. End Get 11. End Property 12. 13. Public ReadOnly Property SecondObject() As T2 14. Get 15. Return Obj2 16. End Get 17. End Property 18. 19. Sub New(ByVal Obj1 As T1, ByVal Obj2 As T2) 20. Me.Obj1 = Obj1 21. Me.Obj2 = Obj2 22. End Sub 23. End Class 24. 25. Sub Main() 26. Crea una relazione fra uno studente e un insegnante, 27. utilizzando le classi create nei capitoli precedenti 28. Dim R As Relation(Of Student, Teacher) 29. Dim S As New Student("Pinco", "Pallino", Date.Parse("25/06/1990"), _ 30. "Liceo Scientifico N. Copernico", 4) 31. Dim T As New Teacher("Mario", "Rossi", Date.Parse("01/07/1950"), _ 32.
  • "Matematica") 33. 34. Crea una nuova relazione tra lo studente e linsegnante 35. R = New Relation(Of Student, Teacher)(S, T) 36. Console.WriteLine(R.FirstObject.CompleteName) 37. Console.WriteLine(R.SecondObject.CompleteName) 38. 39. Console.ReadKey() 40. End Sub 41. End ModuleNotate che è anche possibile cr ear e una r elazione tr a due r elazioni (e la cosa diventa complicata): 01. Dim S As New Student("Pinco", "Pallino", Date.Parse("25/06/1990"), "Liceo Scientifico N. Copernico", 4) 02. Dim T As New Teacher("Mario", "Rossi", Date.Parse("01/07/1950"), "Matematica") 03. Dim StudentTeacherRelation As Relation(Of Student, Teacher) 04. Dim StudentClassRelation As Relation(Of Student, String) 05. Dim Relations As Relation(Of Relation(Of Student, Teacher), Relation(Of Student, String)) 06. 07. StudentTeacherRelation = New Relation(Of Student, Teacher)(S, T) 08. StudentClassRelation = New Relation(Of Student, String)(S, "5A") 09. Relations = New Relation(Of Relation(Of Student, Teacher), Relation(Of Student, String)) (StudentTeacherRelation, StudentClassRelation) 10. 11. Relations.FirstObject.FirstObject 12. > Student "Pinco Pallino" 13. Relations.FirstObject.SecondObject 14. > Teacher "Mario Rossi" 15. Relations.SecondObject.FirstObject 16. > Student "Pinco Pallino" 17. Relations.SecondObject.SecondObject 18. > String "5A"Alc une regole per luso dei Generic s Si può sempr e assegnar e Nothing a una var iabile di tipo gener ics. Nel caso il tipo gener ics collegato sia r efer ence, alla var iabile ver r à assegnato nor malmente Nothing; in caso contr ar io, essa assumer à il valor e di default per il tipo; Non si può er editar e da un tipo gener ic aper to: 1. Class Example(Of T) 2. Inherits T 3. SBAGLIATO 4. End Class Tuttavia si può er editar e da una classe gener ics specificando come tipo gener ics collegato lo stesso tipo aper to: 1. Class Example(Of T) 2. Inherits List(Of T) 3. CORRETTO 4. End Class Allo stesso modo, non si può implementar e T come se fosse uninter faccia: 1. Class Example(Of T) 2. Implements T 3. SBAGLIATO 4. End Class Ma si può implementar e uninter faccia gener ics di tipo T: 1. Class Example(Of T) 2. Implements IEnumerable(Of T) 3. CORRETTO 4.
  • End ClassEntità con lo stesso nome ma con gener ics aper ti differ enti sono consider ate in over load. Per tanto, è lecitoscr iver e: 1. Sub Example(Of T)(ByVal A As T) 2. ... 3. End Sub 4. 5. Sub Example(Of T1, T2)(ByVal A As T1) 6. ... 7. End Sub
  • A42. I Generics - Parte IIInterfac c e Generic sPr oviamo or a a scr iver e qualche inter faccia gener ics per veder ne il compor tamento. Ripr endiamo linter facciaICompar er , che indica qualcosa con il compito di compar ar e oggetti: esiste anche la sua cor r ispettiva gener ics, ossiaICompar er (Of T). Non fa nessun differ enza il compor tamento di questultima: lunica cosa che cambia è il tipo deglioggetti da compar ar e. 01. Module Module1 02. Questa classe implementa un comaparatore di oggetti Student 03. in base al loro anno di corso 04. Class StudentByGradeComparer 05. Implements IComparer(Of Student) 06. 07. Come potete osservare, in questo metodo non viene eseguito 08. nessun tipo di cast, poiché linterfaccia IComparer(Of T) 09. prevede un metodo Compare a tipizzazione forte. Dato che 10. abbiamo specificato come tipo generic collegato Student, 11. anche il tipo a cui IComparer si riferisce sarà 12. Student. Possiamo accedere alle proprietà di x e y 13. senza nessun late binding (per ulteriori informazioni, 14. vedere i capitoli sulla reflection) 15. Public Function Compare(ByVal x As Student, ByVal y As Student) As Integer Implements IComparer(Of Student).Compare 16. Return x.Grade.CompareTo(y.Grade) 17. End Function 18. End Class 19. 20. Sub Main() 21. Crea un nuovo array di oggeti Student 22. Dim S(2) As Student 23. 24. Inizializza ogni oggetto 25. S(0) = New Student("Mario", "Rossi", New Date(1993, 2, 3), "Liceo Classico Ugo Foscolo", 2) 26. S(1) = New Student("Luigi", "Bianchi", New Date(1991, 6, 27), "Liceo Scientifico Fermi", 4) 27. S(2) = New Student("Carlo", "Verdi", New Date(1992, 5, 12), "ITIS Cardano", 1) 28. 29. Ordina larray con il comparer specificato 30. Array.Sort(S, New StudentByGradeComparer()) 31. 32. Stampa il profilo di ogni studente: vedrete che essi sono 33. in effetti ordinati in base allanno di corso 34. For Each St As Student In S 35. Console.WriteLine(St.Profile) 36. Next 37. Console.ReadKey() 38. End Sub 39. 40. End ModuleI V inc oliI tipi gener ics sono molto utili, ma spesso sono un po tr oppo... "gener ici" XD Faccio un esempio. Ammettiamo di aver eun metodo gener ics (Of T) che accetta due par ametr i A e B. Pr oviamo a scr iver e: 1. If A = B Then ...LIDE ci comunica subito un er r or e: "Oper ator = is not definited for type T and T." In effetti, poiché T può esser e un
  • qualsiasi tipo, non possiamo neanche saper e se questo tipo implementi loper ator e uguale =. In questo caso, vogliamoimpor r e come condizione, ossia come v inco lo , che, per usar e il metodo in questione, il tipo gener ic collegato debbaobbligator iamente espor r e un modo per saper e se due oggetti di quel tipo sono uguali. Come si r ende in codice? Se fatemente locale sulle inter facce, r icor der ete che una classe r appr esenta un concetto con deter minate car atter istiche seimplementa deter minate inter facce. Dovr emo, quindi, tr ovar e uninter faccia che r appr esenta l"eguagliabilità":linter faccia in questione è IEquatable(Of T). Per poter saper e se due oggetti T sono uguali, quindi, T dovr à esser e unqualsiasi 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 lanalizzar e il pr imo di cui abbiamo par lato.V inc olo di Interfac c iaIl vincolo di inter faccia è indubbiamente uno dei più utili e usati accanto a quello di er editar ietà. Esso impone che il tipogener ic collegato implementi linter faccia specificata. Dato che dopo limposizione del vincolo sappiamo per ipotesi che iltipo T espor r à sicur amente tutti i membr i di quellinter faccia, possiamo r ichiamar e tali membr i da tutte le var iabili ditipo T. La sintassi è molto semplice: 1. (Of T As [Interfaccia])Ecco un esempio: 001. Module Module1 002. 003. Questa classe rappresenta una collezione di 004. elementi che possono essere comparati. Per questo 005. motivo, il tipo T espone un vincolo di interfaccia 006. che obbliga tutti i tipi generics collegati ad 007. implementare tale interfaccia. 008. Notate bene che in questo caso particolare ho usato 009. un generics doppio, poiché il vincolo non 010. si riferisce a IComparable, ma a IComparable(Of T). 011. Daltra parte, è abbastanza ovvio che se 012. una collezione contiene un solo tipo di dato, 013. basterà che la comparazione sia possibile 014. solo attraverso oggetti di quel tipo 015. Class ComparableCollection(Of T As IComparable(Of T)) 016. Ereditiamo direttamente da List(Of T), acquisendone 017. automaticamente tutti i membri base e le caratteristiche. 018. In questo modo, godremo di due grandi vantaggi: 019. - non dovremo definire tutti i metodi per aggiungere, 020. rimuovere o cercare elementi, in quanto vengono tutti 021. ereditati dalla classe base List; 022. - non dovremo neanche implementare linterfaccia 023. IEnumerable(Of T), poiché la classe base la 024. implementa di per sé. 025. Inherits List(Of T) 026. 027. Dato che gli oggetti contenuti in oggetti di 028. questo tipo sono per certo comparabili, possiamo 029. trovarne il massimo ed il minimo. 030. 031. Trova il massimo elemento 032. Public ReadOnly Property Max() As T 033. Get 034.
  • If Me.Count > 0 Then035. Dim Result As T = Me(0)036.037. For Each Element As T In Me038. Ricordate che A.CompareTo(B) restituisce039. 1 se A > B040. If Element.CompareTo(Result) = 1 Then041. Result = Element042. End If043. Next044.045. Return Result046. Else047. Return Nothing048. End If049. End Get050. End Property051.052. Trova il minimo elemento053. Public ReadOnly Property Min() As T054. Get055. If Me.Count > 0 Then056. Dim Result As T = Me(0)057.058. For Each Element As T In Me059. If Element.CompareTo(Result) = -1 Then060. Result = Element061. End If062. Next063.064. Return Result065. Else066. Return Nothing067. End If068. End Get069. End Property070.071. Trova tutti gli elementi uguali ad A e ne restituisce072. gli indici073. Public Function FindEquals(ByVal A As T) As Int32()074. Dim Result As New List(Of Int32)075.076. For I As Int32 = 0 To Me.Count - 1077. If Me(I).CompareTo(A) = 0 Then078. Result.Add(I)079. End If080. Next081.082. Converte la lista di interi in un array di interi083. con gli stessi elementi084. Return Result.ToArray()085. End Function086.087. End Class088.089. Sub Main()090. Tre collezioni, una di interi, una di stringhe e091. una di date092. Dim A As New ComparableCollection(Of Int32)093. Dim B As New ComparableCollection(Of String)094. Dim C As New ComparableCollection(Of Date)095.096. A.AddRange(New Int32() {4, 19, 6, 90, 57, 46, 4, 56, 4})097. B.AddRange(New String() {"acca", "casa", "zen", "rullo", "casa"})098. C.AddRange(New Date() {New Date(2008, 1, 1), New Date(1999, 12, 31), New Date(2100, 4, 12)})099.100. Console.WriteLine(A.Min())101. > 4102. Console.WriteLine(A.Max())103. > 90104. Console.WriteLine(B.Min())105.
  • > acca 106. Console.WriteLine(B.Max()) 107. > zen 108. Console.WriteLine(C.Min().ToShortDateString) 109. > 31/12/1999 110. Console.WriteLine(C.Max().ToShortDateString) 111. > 12/4/2100 112. 113. Trova la posizione degli elementi uguali a 4 114. Dim AEqs() As Int32 = A.FindEquals(4) 115. > 0 6 8 116. Dim BEqs() As Int32 = B.FindEquals("casa") 117. > 1 4 118. 119. Console.ReadKey() 120. End Sub 121. 122. End ModuleV inc olo di ereditarietàHa la stessa sintassi del vincolo di inter faccia, con la sola differ enza che al posto dellinter faccia si specifica la classe dallaquale il tipo gener ics collegato deve er editar e. I vantaggi sono pr aticamente uguali a quelli offer ti dal vincolo diinter 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 aabbastanza r idondante, ma cè una caso par ticolar e che mi piacer ebbe sottolinear e. Mi r ifer isco al caso in cui al postodella classe base viene specificato un altr o tipo gener ic (aper to), e di questo, data la non immediatezza dicompr ensione, posso dar e un veloce esempio: 1. Class IsARelation(Of T, U As T) 2. Public Base As T 3. Public Derived As U 4. End ClassQuesta classe r appr esenta una r elazione is-a ("è un"), quella famosa r elazione che avevo intr odotto come esempio unaquar antina di capitoli fa dur ante i pr imi par agr afi di spiegazione. Questa r elazione è r appr esentata par ticolar mentebene, 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 equesto 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 eche sia solo der ivato e non lo stesso tipo). Ad esempio, potr ebbe esser e valido un oggetto del gener e: 1. Dim P As Person 2. Dim S As Student 3. ... 4. Dim A As New IsARelation(Of Person, Student)(P, S)V inc oli 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 chesia un tipo value. Le sintassi sono le seguenti: 1. (Of T As Class) 2. (Of T As Structure)Questi due vincoli non sono molto usati, a dir e il ver o, e la lor o utilità non è così mar cata e lampante come appar e peri pr imi due vincoli analizzati. Cer to, possiamo evitar e alcuni compor tamenti str ani dovuti ai tipi r efer ence, osfr uttar e alcune car atter istiche dei tipi value, ma nulla di più. Ecco un esempio dei possibili vantaggi:
  • Vincolo di classe: Possiamo assegnar e Nothing con la sicur ezza di distr ugger e loggetto 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 loper 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 dellassegnamento 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 unaster isco su questo capitolo.V inc olo NewQuesto vincolo impone al tipo gener ic collegato di espor r e almeno un costr uttor e senza par ametr i. Par ticolar menteutile quando si devono inizializzar e dei valor i gener ics: 01. Module Module1 02. 03. Con molta fantasia, il vincolo New si dichiara postponendo 04. "As New" al tipo generic aperto. 05. Function CreateArray(Of T As New)(ByVal Count As Int32) As T() 06. Dim Result(Count - 1) As T 07. 08. For I As Int32 = 0 To Count - 1 09. Possiamo usare il costruttore perchè il 10. vincolo ce lo assicura 11. Result(I) = New T() 12. Next 13. 14. Return Result 15. End Function 16. 17. Sub Main() 18. Crea 10 flussi di dati in memoria. Non abbiamo 19. mai usato questa classe perchè rientra in 20. un argomento che tratterò più avanti, ma 21. è una classe particolarmente utile e versatile 22. che trova applicazioni in molte situazioni. 23. Avere un bel metodo generics che ne crea 10 in una 24. volta è una gran comodità. 25. Ovviamente possiamo fare la stessa cosa con tutti 26. i tipi che espongono almeno un New senza parametri 27. Dim Streams As IO.MemoryStream() = CreateArray(Of IO.MemoryStream)(10) 28. 29. ... 30. End Sub 31. 32. End ModuleV inc oli 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 lacombinazione di due o più vincoli semplici di quelli appena visti. La sintassi di un vincolo multiplo è legger mente diver sae pr evede che tutti i vincoli siano r aggr uppati in una copia di par entesi gr affe e separ ati da vir gole: 1. (Of T As {Vincolo1, Vincolo2, ...})
  • Ecco un esempio: 01. Module Module1 02. 03. Classe che filtra dati di qualsiasi natura 04. Class DataFilter(Of T) 05. Delegate Function FilterData(ByVal Data As T) As Boolean 06. 07. La signature chilometrica è fatta apposta per 08. farvi impazzire XD Vediamo le parti una per una: 09. - TSerach: deve essere un tipo uguale a T o derivato 10. da T, in quanto stiamo elaborando elementi di tipo T; 11. inoltre deve anche essere clonabile, poiché 12. salveremo solo una copia dei valor trovati. 13. Questo implica che TSearch sia un tipo reference, e che 14. quindi lo sia anche T: questa complicazione è solo 15. per mostrare dei vincoli multipli e potete anche 16. rimuoverla se vi pare; 17. - TList: deve essere un tipo reference, esporre un 18. costruttore senza parametri ed implementare 19. linterfaccia IList(Of TSearch), ossia deve 20. essere una lista; 21. - ResultList: lista in cui riporre i risultati (passata 22. per indirizzo); 23. - Filter: delegate che punta alla funzione usata per 24. selezionare i valori; 25. - Data: paramarray contenente i valori da filtrare. 26. Sub Filter(Of TSearch As {ICloneable, T}, TList As {IList(Of TSearch), New, Class}) _ 27. (ByRef ResultList As TList, ByVal Filter As FilterData, ByVal ParamArray Data() As TSearch) 28. 29. Se la lista è Nothing, la inizializza. 30. Notare che non avremmo potuto compararla a Nothing 31. senza il vincolo Class, né inizializzarla 32. senza il vincolo New 33. If ResultList Is Nothing Then 34. ResultList = New TList() 35. End If 36. 37. Itera sugli elementi di data 38. For Each Element As TSearch In Data 39. E aggiunge una copia di quelli che 40. soddisfano la condizione 41. If Filter.Invoke(Element) Then 42. Aggiunge una copia dellelemento alla lista. 43. Anche in questo non avremmo potuto richiamare 44. Add senza il vincolo interfaccia su IList, né 45. clonare Element senza il vincolo interfaccia ICloneable 46. ResultList.Add(Element.Clone()) 47. End If 48. Next 49. End Sub 50. End Class 51. 52. Controlla se la stringa A è palindroma 53. Function IsPalindrome(ByVal A As String) As Boolean 54. Dim Result As Boolean = True 55. 56. For I As Int32 = 0 To (A.Length / 2) - 1 57. If A.Chars(I) <> A.Chars(A.Length - 1 - I) Then 58. Result = False 59. Exit For 60. End If 61. Next 62. 63. Return Result 64. End Function 65. 66. Sub Main() 67. Dim DF As New DataFilter(Of String) 68. Lista di stringhe: notare che la variabile non 69. contiene nessun oggetto perchè non abbiamo usato New. 70.
  • Serve per mostrare che verrà inizializzata71. da DF.Filter.72. Dim L As List(Of String)73.74. Analizza le stringhe passate, trova quelle palindrome75. e le pone in L76. DF.Filter(L, AddressOf IsPalindrome, _77. "casa", "pane", "anna", "banana", "tenet", "radar")78.79. For Each R As String In L80. Console.WriteLine(R)81. Next82.83. Console.ReadKey()84. End Sub85.86. End Module
  • A43. I tipi NullableI tipi Nullable costituiscono una utile applicazione dei gener ics alla gestione dei database. Infatti, quando si lavor a condei database, capita molto spesso di tr ovar e alcune celle vuote, ossia il cui valor e non è stato impostato. In questo caso,loggetto che media tr a il database e il pr ogr amma - oggetto che analizzer emo solo nella sezione C - pone in tali celleuno speciale valor e che significa "non contiene nulla". Questo valor e è par i a DBNull.Value, una costante staticapr eimpostata di tipo DBNull, appunto. Essendo un tipo r efer ence, lassegnar e il valor e di una cella a una var iabile valuepuò 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 unaconver sione. Compor tamenti del gener e costr ingono (anzi, costr ingevano) i pr ogr ammator i a scr iver e una quantitàeccessiva di costr utti di contr ollo del tipo: 01. If Cell.Value IsNot DBNull.Value Then 02. Variable = Cell.Value 03. Else 04. Variable = 0 05. Per impostare il valore di default, bisognava ripetere 06. questi If tante volte quanti erano i tipi in gioco, poiché 07. non cera modo di assegnare un valore Null a tutti 08. in un solo colpo 09. End IfTuttavia, con lavvento dei gener ics, nella ver sione 2005 del linguaggio, questi pr oblemi sono stati ar ginati, almeno inpar te e almeno per chi conosce i tipi nullable. Questi speciali tipi sono str uttur e gener ics che possono anche accettar evalor i r efer ence come Nothing: ovviamente, dato che i pr oblemi insor gono solo quando si tr atta di tipi value, i tipigener ics collegati che è lecito specificar e quando si usa nullable devono esser e tipi value (quindi cè un vincolo distr uttur a).Ci sono due sintassi molto diver se per dichiar ar e tipi nullable, una esplicita e una implicita: 1. Dichiarazione esplicita: 2. Dim [Nome] As Nullable(Of [Tipo]) 3. 4. Dichiarazione implicita: 5. Dim [Nome] As [Tipo]?La seconda si attua postponendo un punto inter r ogativo al nome del tipo: una sintassi molto br eve e concisa chetuttavia può anche sfuggir e facilmente allocchio. Una volta dichiar ata, una var iabile nullable può esser e usata come unacomunissima var iabile del tipo gener ic collegato specificato. Essa, tuttavia, espone alcuni membr i in più r ispetto ainor mali tipi value, nella fattispecie: HasValue : pr opr ietà r eadonly che r estituisce Tr ue se loggetto contiene un valor e; Value : pr opr ietà r eadonly che r estituisce il valor e delloggetto, nel caso esista; GetValueOr Default() : funzione che r estituisce Value se loggetto 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 loggetto non contiene nulla, viene r estituito X al posto del valor e di default.Ecco un esempio: 01. Module Module1 02. 03. Sub Main() 04. Tre variabili di tipo value dichiarate come 05. nullable nei due modi diversi consentiti 06. Dim Number As Integer? 07. Dim Data As Nullable(Of Date) 08.
  • Dim Cost As Double? 09. Dim Sent As Nullable(Of Boolean) 10. 11. Ammettiamo di star controllando un database: 12. questo array di oggetti rappresenta il contenuto 13. di una riga 14. Dim RowValues() As Object = {DBNull.Value, New Date(2009, 7, 1), 67.99, DBNull.Value} 15. 16. Con un solo ciclo trasforma tutti i DBNull.Value 17. in Nothing, poiché i nullable supportano solo 18. Nothing come valore nullo 19. For I As Int16 = 0 To RowValues.Length - 1 20. If RowValues(I) Is DBNull.Value Then 21. RowValues(I) = Nothing 22. End If 23. Next 24. 25. Assegna alle variabili i valori contenuti nellarray: 26. non ci sono mai problemi in questo codice, poiché, 27. trattandosi di tipi nullable, questi oggetti possono 28. accettare anche valori Nothing. In questo esempio, 29. Number e Sent riceveranno un Nothing come valore: la 30. loro proprietà HasValue varrà False. 31. Number = RowValues(0) 32. Data = RowValues(1) 33. Cost = RowValues(2) 34. Sent = RowValues(3) 35. 36. Scrive a schermo il valore di ogni variabile, se ne 37. contiene uno, oppure il valore di default se non 38. contiene alcun valore. 39. Console.WriteLine("{0} {1} {2} {3}", _ 40. Number.GetValueOrDefault, _ 41. Data.GetValueOrDefault, _ 42. Cost.GetValueOrDefault, _ 43. Sent.GetValueOrDefault) 44. 45. Provando a stampare una variabile nullable priva 46. di valore senza usare la funzione GetValueOrDefault, 47. semplicemente non stamperete niente: 48. Console.WriteLine(Number) 49. Non stampa niente e va a capo. 50. 51. Console.ReadKey() 52. End Sub 53. 54. End ModuleLogic a 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). Usandouna var iabile booleana nullable come oper ando per gli oper ator i logici, si otter r anno r isultati diver si a seconda cheessa abbia o non abbia un valor e. Le nuove combinazioni che possono esser e eseguite si vanno ad aggiunger e a quellegià esistenti per cr ear e un nuovo tipo di logica elementar e, detta, appunto, "logica booleana a tr e valor i". Essa seguequesto 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
  • Tr ue Xor Null NullFalse Xor Null Null
  • A44. La Reflection - Parte ICon il ter mine gener ale di r eflection si intendono tutte le classi del Fr amew or k che per mettono di acceder e omanipolar e assembly e moduli. A ssem bly Lassembly è lunità logica più piccola su cui si basa il Fr amew or k .NET. Un assembly altr o non è che un pr ogr amma o una libr er ia di classi (compilati in .NET). Il Fr amew or k stesso è composto da una tr entina di assembly pr incipali che costituiscono le libr er ie di classi più impor tanti per 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 evidenteche mediante r eflection noi scr iviamo del codice che analizza altr o codice, anche se compilato: è una specie diour 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 dallIDE e dal Fr amew or k stesso, anche se spesso questi meccanismi si svolgono "dietr o le quinte" e vengonomascher ati per non far li appar ir e evidenti. Alcuni esempi sono la ser ializzazione, di cui mi occuper ò in seguito, ed illate binding. Late Binding Lazione 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 unastr azione fondamentale affinché il pr ogr ammator e possa compr ender e ciò che sta scr itto nel codice: nessuno r iuscir ebbe a capir e alcunché se al posto dei nomi di var iabile ci fosser o degli indir izzi di memor ia a otto cifr e. Ebbene, esistono due tipi di binding: quello statico o "ear ly", e quello dinam ico o "late". Il pr imo viene effetuato pr ima che il pr ogr amma sia eseguito, ed è quello che per mette al compilator e di tr adur r e in linguaggio inter medio le istr uzioni scr itte in for ma testuale dal pr ogr ammator e. Quando assegnamo un nome ad una var iabile, o r ichiamiamo un metodo da un oggetto stiamo attuando un ear ly binding: sappiamo che quellidentificator 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 quelloggetto 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 distanza 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. Questultima è 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 untipo: ad esempio, cè un oggetto Type per Str ing, uno per Per son, uno per Integer , e via dicendo. Risulta logico che nonpossiamo cr ear e un oggetto Type, per chè non sar ebbe associato ad alcun tipo e non avr ebbe motivo di esister e:possiamo, al contr ar io, ottener e un oggetto Type già esistente.
  • I ContestiPr ima di iniziar e a veder e come analizzar e un assembly, dobbiamo fer mar ci un attimo a capir e come funziona ilsistema oper ativo a livello un po più basso del nor male. Questo ci sar à utile per sceglier e una modalità di accessoallassembly 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 unadeter minata r isor sa dellelabor ator e e di for nir e per essa unastr azione, ossia una visione semplificata ed estesa. Ilpr 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 esecuzioner acchiudendoli in ar ee di memor ia separ ate, i pr ocessi appunto. Un pr ocesso r appr esenta un "pr ogr amma inesecuzione" e non contiene solo il semplice codice eseguibile, ma, oltr e a questo, mantiene tutti i dati iner enti alfunzionamento del pr ogr amma, ivi compr esi var iabili, collegamenti a r isor se ester ne, stato della CPU, ecceter a... Oltr ead 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 aciascuno, r endendo impossibile per un pr ocesso modificar e i dati di un altr o pr ocesso, causando, in questo modo, unpossibile cr ash di entr ambi i pr ogr ammi o del sistema stesso. Questa politica di coor dinamento, quindi, r ende sicur a eisolata lesecuzione di un pr ogr amma. Il CLR del .NET, tuttavia, aggiunge unulter ior e suddivisione, basata sui dom iniapplicativ i o A ppDo m ain o co ntesti di esecuzio ne. Allinter no di un singolo pr ocesso possono esister e più dominiapplicativi, i quali sono tr a lor o isolati come se fosser o due pr ocessi differ enti: in questo modo, un assemblyappar 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: lunica differ enza sta nelfatto 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 ocessidiffer enti, ma attr aver so i quali è molto più semplice la comunicazione e lo scambio di dati. Un semplice esempio lopotr ete tr ovar e osser vando il Task Manager di Window s quando ci sono due finestr e di Fir eFox aper te allo stessotempo: noter e che vi è un solo pr ocesso fir efox .ex e associato.
  • Caric are un assemblyUn assembly è r appr esentato dalla classe System.Reflection.Assembly. Tutte le oper azioni effettuabili su di esso sonoesposte mediante metodi della classe assembly. Pr imi fr a tutti, spiccano i metodi per il car icamento, che si distinguonodagli altr i per la lor o copiosa quantità. Esistono, infatti, ben sette metodi statici per car icar e od ottener e unr 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 allassembly che è in esecuzione e dal quale questa chiamata a funzione viene lanciata. In poche par ole, loggetto 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 allassembly 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 allassembly car icato. "Nome" può
  • anche esser e il no m e co m pleto dellassembly, 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 amew or 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, questultima 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 amew or k è in gr ado di r isolver ne le dipendenze solo nel caso in cui queste siano pr esenti nella car tella pr incipale dellapplicazione; Assembly.LoadFile("File") Agisce in modo analogo a LoadFr om, ma lassembly viene car icato in un contesto di esecuzione differ ente, e il Fr amew or 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 allassembly 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 allassembly 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 inmemor ia, ossia non diventano par te attiva dal dominio applicativo, pur tuttavia vengono posti in un altr o contestospeciale, detto co ntesto di ispezio ne. Questultimo è unico per ogni pr ocesso e condiviso da tutti gli AppDomainpr esenti nel pr ocesso.Nome dellassembly 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 ieinfor mazioni. Ecco una br eve lista delle pr opr ietà e dei metodi più significativi: Fullname : r estituisce il nome completo dellassembly, specificando nome, cultur a, ver sione e token della chiave pubblica; CodeBase : nel caso lassembly 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 dellassembly; GlobalAssemblyChace : pr opr ietà che value Tr ue nel caso lassembly sia stato car icato dalla GAC; G lo bal A ssem bly Cache (G A C) La car tella fisica in cui vengono depositati tutti gli assembly pubblici. Per assembly pubblico, infatti, sintende ogni assembly accessibile da ogni applicazione su una deter minata macchina. Gli assembly pubblici sono, solitamente, tutti quelli di base del Fr amew or k .NET, ma è possibile aggiunger ne altr i con deter minati comandi. La GAC di Window s è di solito posizionata in C:WINDOWSassembly e contiene tutte le libr er ie base del Fr amew or k. Ecco per chè basta specificar e il nome dellassembly pubblico per car icar lo (la car tella è nota a pr ior i).
  • ReflectionOnly : r estituisce Tr ue se lassembly è stato car icato per soli scopi di analisi (r eflection); GetName() : r estituisce un oggetto AssemblyName associato allassembly cor r ente; GetTypes() : r estituisce un ar r ay di Type che definiscono ogni tipo dichiar ato allinter no dellassembly.Pr ima di ter minar e il capitolo, esaminiamo le par ticolar ità del nome dellassembly. In gener e il no m e co m pleto di unassembly 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 nellassembly.La ver sione è un numer o di ver sione a quattr o par ti, divise solitamente, in or dine, come segue: Major (numer o diver sione pr incipale) , Minor (numer o di ver sione minor e, secondar io), Revision (numer o della r evisione a cui si è giuntiper questa ver sione), Build (numer o di compilazioni eseguite per questa r evisione). Il numer o di ver sione indica disolito la ver sione del Fr amew or k per cui lassembly è stato scr itto: se state usando VB2005, tutte le ver sioni sar annouguali 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 lassembly: di solito è semplicmente "neutr al", neutr ale, ma nel caso in cui sia differ ente, influenza alcuniaspetti secondar i come la r appr esentazione dei numer i (sepr ator i decimali e delle migliaia), dellor ar io, i simboli divaluta, ecceter a... Il token della chiave pubblica è un insieme di otto bytes che identifica univocamente la chiavepubblica (è una sua ver sione "abbr eviata"), la quale identifica univocamente lassembly. Viene usato il token e non tuttala chiave per questioni di lunghezza. Ecco un esempio che ottiene questi dati: 01. Module Module1 02. 03. Sub Main() 04. Carica un assembly per soli scopi di analisi. 05. mscorlib è lassembly più importante di 06. tutto il Framework, da cui deriva pressochè ogni 07. cosa. Data la sua importanza, non ha dipendenze, 08. perciò non ci saranno problemi nel risolverle. 09. Se volete caricare un altro assembly, dovrete usare 10. uno dei metodi in grado di risolvere le dipendenze. 11. Dim Asm As Assembly = Assembly.ReflectionOnlyLoad("mscorlib") 12. Dim Name As AssemblyName = Asm.GetName 13. 14. Console.WriteLine(Asm.FullName) 15. Console.WriteLine("Nome: " & Name.Name) 16. Console.WriteLine("Versione: " & Name.Version.ToString) 17. Console.WriteLine("Cultura: " & Name.CultureInfo.Name) 18. 19. Il formato X indica di scrivere un numero usando la 20. codifica esadecimale. X2 impone di occupare sempre almeno 21. due posti: se cè una sola cifra, viene inserito 22. uno zero. 23. Console.Write("Public Key: ") 24. For Each B As Byte In Name.GetPublicKey() 25. Console.Write("{0:X2}", B) 26. Next 27. Console.WriteLine() 28. 29. Console.Write("Public Key token: ") 30. For Each B As Byte In Name.GetPublicKeyToken 31. Console.Write("{0:X2}", B) 32. Next 33. Console.WriteLine() 34. 35. Console.WriteLine("Processore: " & _ 36. Name.ProcessorArchitecture.ToString) 37. 38. Console.ReadKey() 39. 40. End Sub 41. 42. End Module
  • Con quello che abbiamo visto finor a si potr ebbe scr iver e una pr ocedur a che enumer i tutti gli assembly pr esenti nelcontesto cor r ente: 01. Sub EnumerateAssemblies() 02. Dim Asm As Assembly 03. Dim Name As AssemblyName 04. 05. AppDomain è una variabile globale, oggetto singleton, da cui 06. si possono trarre informazioni sullAppDomain corrente o 07. crearne degli altri. 08. For Each Asm In AppDomain.CurrentDomain.GetAssemblies 09. Name = Asm.GetName 10. Console.WriteLine("Nome: " & Name.Name) 11. Console.WriteLine("Versione: " & Name.Version.ToString) 12. Console.Write("Public Key Token: ") 13. For Each B As Byte In Name.GetPublicKeyToken 14. Console.Write(Hex(B)) 15. Next 16. Console.WriteLine() 17. Console.WriteLine() 18. Next 19. End Sub
  • A45. La Reflection - Parte IILa c lasse Sy stem.Ty peLa classe Type è una classe davver o par ticolar e, poiché r appr esenta un tipo. Con tipo indichiamo tutte le possibilitipologie di dato esistenti: tipi base, enumer ator i, str uttur e, classi e delegate. Per ogni tipo contemplato, esiste uncor r ispettivo oggetto Type che lo r appr esenta: avevo detto allinizio della guida, infatti, che ogni cosa in .NET è unoggetto, ed i tipi non fanno eccezione. Vi sor pr ender ebbe saper e tutto ciò che può esser e r appr esentato da una classee fr a poco vi sveler ò un segr eto... Ma per or a concentr iamoci su Type. Questi oggetti r appr esentanti un tipo - chepossiamo chiamar e per br evità OT (non è un ter mine tecnico) - vengono cr eati dur ante la fase di inizializzazione delpr ogr amma e ne esiste una e una sola copia per ogni singolo tipo allinter no di un singolo AppDomain. Ciò significa chedue contesti applicativi differ enti avr anno due OT diver si per r appr esentar e lo stesso tipo, ma non analizzer emoquesta peculiar e casistica. Ci limiter emo, invece, a studiar e gli OT allinter no di un solo dominio applicativo, coincidentecon il nostr o pr ogr amma.Come per gli assembly, esistono molteplici modi per ottener e un OT: Tr amite loper ator e GetType(Tipo); Tr amite la funzione distanza GetType(); Tr amite la funzione condivisa Type.GetType("nometipo").Ecco un semplice esempio di come funzionano questi metodi: 01. Module Module1 02. 03. Sub Main() 04. Ottiene un OT per il tipo double tramite 05. loperatore GetType 06. Dim DoubleType As Type = GetType(Double) 07. Console.WriteLine(DoubleType.FullName) 08. 09. Ottiene un OT per il tipo string tramite 10. la funzione statica Type.GetType. Essa richiede 11. come parametro il nome (possibilmente completo) 12. del tipo. Nel caso il nome non corrisponda a 13. nessun tipo, verrà restituito Nothing 14. Dim StringType As Type = Type.GetType("System.String") 15. Console.WriteLine(StringType.FullName) 16. 17. Ottiene un OT per il tipo ArrayList tramite 18. la funzione distanza GetType. Da notare che, 19. mentre le precedenti usavano come punto 20. di partenza direttamente un tipo (o il suo nome), 21. questa richiede un oggetto di quel tipo. 22. Dim A As New ArrayList 23. Dim ArrayListType As Type = A.GetType() 24. Console.WriteLine(ArrayListType.FullName) 25. 26. Console.ReadKey() 27. End Sub 28. 29. End ModuleOr a che ho esemplificato come ottener e un OT, vor r ei mostr ar e lunicità di OT ottenuti in modi differ enti: anche seusassimo 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 allostesso oggetto, poiché il tipo Str ing è unico: 01. Module Module1 02. 03. Sub Main() 04.
  • Dim Type1 As Type = GetType(String) 05. Dim Type2 As Type = Type.GetType("System.String") 06. Dim Type3 As Type = "Ciao".GetType() 07. 08. Console.WriteLine(Type1 Is Type2) 09. > True 10. Console.WriteLine(Type2 Is Type3) 11. > True 12. 13. Gli OT contenuti in Type1, Type2 e Type3 14. SONO lo stesso oggetto 15. 16. Console.ReadKey() 17. End Sub 18. 19. End ModuleQuesto non vale per il tipo System.Type stesso, poiché il metodo distanza 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 disoffer 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 ditutto il Fr amew or k. Di questa massa enor me di infor mazioni, ve ne è un sottoinsieme che per mette di saper e in chemodo il tipo è stato dichiar ato e quali sono le sue car atter istiche pr incipali. Possiamo r icavar e, ad esempio, glispecificator i di accesso, gli eventuali modificator i, possiamo saper e se si tr atta di una classe, un enumer ator e, unastr 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 cheimplementa, 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 lassembly a cui il tipo appar tiene (ossia in cui è stato dichiar ato); AssemblyQualifiedName : r estituisce il nome dellassembly 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 allinter 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 è uninter 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 lambito 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 allinter no di un namespace, gli unici specificator i possibili sono Public e Fr iend (gli altr i si adottano solo allinter no di una classe); IsPointer : deter mina se è un puntator e;
  • 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 unassembly e scr ive a scher mo tutti i tipi ivi definiti: 01. Module Module1 02. 03. Sub EnumerateTypes(ByVal Asm As Assembly) 04. Dim Category As String 05. 06. GetTypes restituisce un array di Type che 07. indicano tutti i tipi definiti allinterno 08. dellassembly Asm 09. For Each T As Type In Asm.GetTypes 10. If T.IsClass Then 11. Category = "Class" 12. ElseIf T.IsInterface Then 13. Category = "Interface" 14. ElseIf T.IsEnum Then 15. Category = "Enumerator" 16. ElseIf T.IsValueType Then 17. Category = "Structure" 18. ElseIf T.IsPrimitive Then 19. Category = "Base Type" 20. End If 21. Console.WriteLine("{0} ({1})", T.Name, Category) 22. Next 23. End Sub 24. 25. Sub Main() 26. Ottiene un riferimento allassembly in esecuzione, 27. quindi al programma. Non otterrete molti tipi 28. usando questo codice, a meno che il resto del 29. modulo non sia pieno di codice vario come nel 30. mio caso XD 31. Dim Asm As Assembly = Assembly.GetExecutingAssembly() 32. 33. EnumerateTypes(Asm) 34. 35. Console.ReadKey() 36. End Sub 37. 38. End ModuleIl nostro pic c olo segretoPr ima di pr oceder e con lenumer azione dei membr i, vor r ei mostr ar e che in r ealtà tutti i tipi sono classi, soltanto conr egole "speciali" di er editar ietà e di sintassi. Questo codice r intr accia tutte le classi basi di un tipo, costr uendonelalber o di er editar ietà fino alla r adice (che sar à ovviamente System.Object): 01. Module Module1 02. 03. Analizza lalbero di ereditarietà di un tipo 04. Sub AnalyzeInheritance(ByVal T As Type) 05. La proprietà BaseType restituisce la classe 06. base da cui T è derivata 07. If T.BaseType IsNot Nothing Then 08. Console.WriteLine("> " & T.BaseType.FullName) 09.
  • AnalyzeInheritance(T.BaseType) 10. End If 11. End Sub 12. 13. Enum Status 14. Enabled 15. Disabled 16. Standby 17. End Enum 18. 19. Structure Example 20. Dim A As Int32 21. End Structure 22. 23. Delegate Sub Sample() 24. 25. Sub Main() 26. Console.WriteLine("Integer:") 27. AnalyzeInheritance(GetType(Integer)) 28. Console.WriteLine() 29. 30. Console.WriteLine("Enum Status:") 31. AnalyzeInheritance(GetType(Status)) 32. Console.WriteLine() 33. 34. Console.WriteLine("Structure Example:") 35. AnalyzeInheritance(GetType(Example)) 36. Console.WriteLine() 37. 38. Console.WriteLine("Delegate Sample:") 39. AnalyzeInheritance(GetType(Sample)) 40. Console.WriteLine() 41. 42. Console.ReadKey() 43. End Sub 44. 45. End ModuleLoutput mostr a che il tipo Integer e la str uttur a Ex ample der ivano entr ambi da System.ValueType, che a sua voltader 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 lenumer ator e der iva indir ettamente da tale classe, anche se mostr a un passaggioin più, attr aver so il tipo System.Enum. Allo stesso modo, il delegate Sample der iva dalla classe DelegateMulticast, laquale 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 ei 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 iesposti 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 laclasse in cui queste infor mazioni vengono poste è Member Info, del namespace System.Reflection. Dato che ci sonodiver se categor ie di membr i, esistono altr ettante classi der ivate da Member Info che ci r accontano una stor ia tuttadiver sa a seconda di cosa stiamo guar dando: MethodInfo contiene infor mazioni su un metodo, Pr oper tyInfo su unapr opr ietà, Par amter Info su un par ametr o, FieldInfo su un campo e via dicendo. Fr a le molteplici funzioni esposte daType, 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;
  • 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: 01. Module Module1 02. Sub Main() 03. Dim T As Type = GetType(String) 04. 05. Elenca tutti i membri di String 06. For Each M As MemberInfo In T.GetMembers 07. La proprietà MemberType restituisce un enumeratore che 08. specifica di che tipo sia il membro, se una proprietà, 09. un metodo, un costruttore, eccetera... 10. Console.WriteLine(M.MemberType.ToString & " " & M.Name) 11. Next 12. 13. Console.ReadKey() 14. End Sub 15. End ModuleEseguendo il codice appena pr oposto, potr ete notar e che a scher mo appaiono tutti i membr i di Str ing, ma molti sonor ipetuti: questo si ver ifica per chè i metodi che possiedono delle var ianti in over load vengono r ipor tati tante voltequante sono le var ianti; natur alemnte, ogni oggetto MethodInfo sar à diver so dagli altr i per le infor mazioni sullaquantità e sul tipo di par ametr i passati a tale metodo. Accanto a questa str anezza, noter ete, poi, che per ognipr opr ietà ci sono due metodi definiti come get_NomePr opr ietà e set_NomePr opr ietà: questi metodi vengono cr eatiautomaticamente quando il codice di una pr opr ietà viene compilato, e vengono eseguiti al momento di impostar e odottener 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 . Stiamocominciando ad entr ar e nel mondo dellInter mediate Language, il linguaggio inter medio simil-macchina in cui vengonoconver titi i sor genti una volta compilati. Di fatto, noi stiamo eseguendo il pr ocesso inver so della compilazione, ossia ladeco m pilazio ne. Alcune infor mazioni vengono manipolate nel passaggio da codice a IL, e quando si tor na indietr o, lesi vede in altr o modo, ma tutta linfor mazione necessar ia è ancor a contenuta lì dentr o. Non esiste, tuttavia, una classegià 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 diconoscer e "a pezzi" tutta linfor 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 diun 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:M em ber Info Declar ingType : la classe che dichiar a questo membr o; Member Type : categor ia del membr o; Name : il nome del membr o;
  • ReflectedType : il tipo usato per ottener e un r ifer imento a questo membr o tr amite r eflection;M etho dInfo 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 loggetto 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 GetRaw CostantValue() : 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;Pr o per tyInfo 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à.Ev entInfo (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 devento; GetRaiseMethod() : r estituisce un r ifer imento al metodo che viene r ichiamato quando si scatena levento; GetRemoveMethod() : r estituisce un r ifer imento al metodo usato per r imuover e gli handler devento; IsMulticast : indica se levento è gestito tr amite un delegate multicast. 001. Module Module1 002. Analizza il metodo rappresentato dalloggetto MI 003. Sub AnalyzeMethod(ByVal MI As MethodInfo) 004. Il nome 005. Dim Name As String 006. Il nome completo, con scpecificatori di accesso, 007. modificatori, signature e tipo restituito. Per 008. ulteriori informazioni sul tipo StringBuilder, 009. vedere il capitolo "Magie con le stringhe" 010.
  • Dim CompleteName As New System.Text.StringBuilder011. Lo specificatore di accesso012. Dim Scope As String013. Gli eventuali modificatori014. Dim Modifier As String015. La categoria: Sub o Function016. Dim Category As String017. La signature del metodo, che andremo a costruire018. Dim Signature As New System.Text.StringBuilder019.020. Di solito, tutti i metodi hanno un tipo restituito,021. poiché, in analogia con la sintassi del C#, una022. procedura è una funzione che restituisce Void,023. ossia niente. Per questo bisogna controllare anche il024. nome del tipo di ReturnParameter025. If MI.ReturnParameter IsNot Nothing AndAlso _026. MI.ReturnType.FullName <> "System.Void" Then027. Category = "Function"028. Else029. Category = "Sub"030. End If031.032. If MI.IsConstructor Then033. Name = "New"034. Else035. Name = MI.Name036. End If037.038. If MI.IsAssembly Then039. Scope = "Friend"040. ElseIf MI.IsFamily Then041. Scope = "Protected"042. ElseIf MI.IsFamilyOrAssembly Then043. Scope = "Protected Friend"044. ElseIf MI.IsPrivate Then045. Scope = "Private"046. Else047. Scope = "Public"048. End If049.050. If MI.IsFinal Then051. Vorrei far notare una sottigliezza. Se il metodo è052. Final, ossia NotOverridable, significa che non può053. essere modificato nelle classi derivate. Ma tutti i054. membri non dichiarati esplicitamente Overridable055. non sono modificabili nelle classi derivate. Quindi,056. definire un metodo senza modificatori polimorfici057. (come quelli che seguono qua in basso), equivale a058. definirlo NotOverridable. Perciò non si059. aggiunge nessun modificatore in questo caso060. ElseIf MI.IsAbstract Then061. Modifier = "MustOverride"062. ElseIf MI.IsVirtual Then063. Modifier = "Overridable"064. ElseIf MI.GetBaseDefinition IsNot Nothing AndAlso _065. MI IsNot MI.GetBaseDefinition Then066. Modifier = "Overrides"067. End If068.069. If MI.IsStatic Then070. If Modifier <> "" Then071. Modifier = "Shared " & Modifier072. Else073. Modifier = "Shared"074. End If075. End If076.077. Inizia la signature con una parentesi tonda aperta.078. Append aggiunge una stringa a Signature079. Signature.Append("(")080. For Each P As ParameterInfo In MI.GetParameters081. Se P è un parametro successivo al primo, lo separa dal082.
  • precedente con una virgola083. If P.Position > 0 Then084. Signature.Append(", ")085. End If086.087. Se P è passato per valore, ci vuole ByVal, altrimenti088. ByRef. IsByRef è un membro di Type, ma viene089. usato solo quando il tipo in questione indica il tipo090. di un parametro091. If P.ParameterType.IsByRef Then092. Signature.Append("ByRef ")093. Else094. Signature.Append("ByVal ")095. End If096.097. Se P è opzionale, ci vuole la keyword Optional098. If P.IsOptional Then099. Signature.Append("Optional ")100. End If101.102.103. Signature.Append(P.Name)104. If P.ParameterType.IsArray Then105. Signature.Append("()")106. End If107. Dato che la sintassi del nome è in stile C#, al108. posto delle parentesi tonde in un array ci sono delle109. quadre: rimediamo110. Signature.Append(" As " & P.ParameterType.Name.Replace("[]",""))111.112. Si ricordi che i parametri optional hanno un valore113. di default114. If P.IsOptional Then115. Signature.Append(" = " & P.DefaultValue.ToString)116. End If117. Next118. Signature.Append(")")119.120. If MI.ReturnParameter IsNot Nothing AndAlso _121. MI.ReturnType.FullName <> "System.Void" Then122. Signature.Append(" As " & MI.ReturnType.Name)123. End If124.125. Ed ecco il nome completo126. CompleteName.AppendFormat("{0} {1} {2} {3}{4}", Scope, Modifier, _127. Category, Name, Signature.ToString)128. Console.WriteLine(CompleteName.ToString)129. Console.WriteLine()130.131. Ora ci occupiamo del corpo132. Dim MB As MethodBody = MI.GetMethodBody133.134. If MB Is Nothing Then135. Exit Sub136. End If137.138. Massima memoria occupata sullo stack139. Console.WriteLine("Massima memoria stack : {0} bytes", _140. MB.MaxStackSize)141. Console.WriteLine()142.143. Variabili locali (LocalVariableInfo è una variante di144. FieldInfo)145. Console.WriteLine("Variabili locali:")146. For Each L As LocalVariableInfo In MB.LocalVariables147. Dato che non si può ottenere il nome, ci si deve148. accontentare di un indice149. Console.WriteLine(" Var({0}) As {1}", L.LocalIndex, _150. L.LocalType.Name)151. Next152. Console.WriteLine()153.154.
  • Gestione delle eccezioni 155. Console.WriteLine("Gestori di eccezioni:") 156. For Each Ex As ExceptionHandlingClause In MB.ExceptionHandlingClauses 157. Tipo di clausola: distingue tra filtro (When), 158. clausola (Catch) o un blocco Finally 159. Console.WriteLine(" Tipo : {0}", Ex.Flags.ToString) 160. Se si tratta di un blocco Catch, ne specifica la 161. natura 162. If Ex.Flags = ExceptionHandlingClauseOptions.Clause Then 163. Console.WriteLine(" Catch Ex As " & Ex.CatchType.Name) 164. End If 165. Offset, ossia posizione in bytes nel Try, del gestore 166. Console.WriteLine(" Offset : {0}", Ex.HandlerOffset) 167. Lunghezza, in bytes, del codice eseguibile del gestore 168. Console.WriteLine(" Lunghezza : {0}", Ex.HandlerLength) 169. Console.WriteLine() 170. Next 171. End Sub 172. 173. Sub Test(ByVal Num As Int32, ByVal S As String) 174. Dim T As Date 175. Dim V As String 176. 177. Try 178. Console.WriteLine("Prova 1, 2, 3") 179. Catch Ex As ArithmeticException 180. Console.WriteLine("Errore 1") 181. Catch Ex As ArgumentException 182. Console.WriteLine("Errore 2") 183. Finally 184. Console.WriteLine("Ciao") 185. End Try 186. End Sub 187. 188. Sub Main() 189. Dim T As Type = GetType(Module1) 190. Dim Methods() As MethodInfo = T.GetMethods 191. Dim Index As Int16 192. 193. Console.WriteLine("Inserire un numero tra i seguenti per analizzare il metodo corrispondente:") 194. Console.WriteLine() 195. For I As Int16 = 0 To Methods.Length - 1 196. Console.WriteLine("{0} - {1}", I, Methods(I).Name) 197. Next 198. Console.WriteLine() 199. Index = Console.ReadLine 200. 201. If Index >= 0 And Index &rt; Methods.Length Then 202. AnalyzeMethod(Methods(Index)) 203. End If 204. 205. Console.ReadKey() 206. End Sub 207. 208. End ModuleAnalizzando 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
  • 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
  • A46. La Reflection - Parte IIIReflec tion dei generic sI 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 Typeche r appr esenta un tipo gener ic non ha lo stesso nome di quando è stato dichiar ato nel codice, ma possiede una for macontr atta e diver sa. Ad esempio, ammettendo che lassembly che stiamo analizzando contenga questa classe: 1. Class Example(Of T, K) 2. ... 3. End Classquando tr over emo loggetto Type che la r appr esenta dur ante lenumer azione dei tipi, scopr ir emo che il suo nome èmolto str ano. Sar à molto simile a questa str inga: Example`2In 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 ipr ovenienti da AppDomain diver si, si capisce che in r ealtà sono pr opr io lo stesso tipo, poiché la ver a differ enza stasolo nel nome e nella quantità di par ametr i gener ics (lidentificator e di questi ultimi, infatti, essendo solo unsegnaposto, è ininfluente). Nonostante lassenza di dettagli, ci sono delle pr opr ietà che ci per mettono di r ecuper ar e ilnome 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 unaltr 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 n on collegati). Ad esempio: 01. Module Module1 02. 03. Dichiaro questa classe e la prossima variabile come 04. pubblici perchè se fossero Friend bisognerebbe 05. usare un overload troppo lungo di GetField e 06. GetNestedTypes specificando ci cercare i membri non 07. pubblici. Di default, le funzioni di ricerca operano 08. solo su membri pubblici 09. 10. Public Class Example(Of T) 11. 12. End Class 13. 14. Public E As Example(Of Int32) 15. 16. Sub Main() 17. Ottiene il tipo di questo modulo 18. Dim ModuleType As Type = GetType(Module1) 19. 20. Enumera tutti i tipi presenti nel modulo fino a 21. trovare la classe Example. Ho usato un for perchè 22. non si può usare GetType (in qualsiasi 23. sua versione) su una classe generics senza specificare 24. un tipo generics collegato, cosa che noi non 25. vogliamo affatto. Per ottenere il riferimento a 26. Example(Of T) bisogna per forza usare una funzione 27. che restituisca tutti i tipi esistenti e poi 28. cercarlo tra questi. 29. For Each T As Type In ModuleType.GetNestedTypes() 30.
  • If T.Name.StartsWith("Example") Then 31. Console.WriteLine("{0} - IsGenericTypeDefinition: {1}", _ 32. T.Name, T.IsGenericTypeDefinition) 33. End If 34. Next 35. 36. Ottiene un riferimento al campo E dichiarayo sopra 37. Dim EField As FieldInfo = ModuleType.GetField("E") 38. E ne ottiene il tipo 39. Dim EType As Type = EField.FieldType 40. 41. Console.WriteLine("{0} - IsGenericTypeDefinition: {1}", EType.Name, EType.IsGenericTypeDefinition) 42. 43. Console.ReadKey() 44. End Sub 45. 46. End Module A scher mo appar ir à lo stesso nome due volte, ma in un caso IsGener icTypeDefinition sar à Tr ue e nellaltr o False. Questo per chè il tipo della var iabile E è sì dichiar ato come gener ic, ma allatto pr atico lavor a su un solo tipo: Int32; per ciò non si tr atta di una defin izion e 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. Nellesempio 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: 01. Module Module1 02. 03. Sub EnumerateGenerics(ByVal Asm As Assembly) 04. For Each T As Type In Asm.GetTypes 05. Controlla se si tratta di un tipo contenente 06. tipi generics aperti 07. If T.IsGenericTypeDefinition Then 08. Ottiene il nome semplice di quel tipo (la 09. versione completa è troppo lunga XD) 10. Dim Name As String = T.Name 11. 12. Controlla che il nome contenga laccento tonico. 13. Infatti, possono esistere casi in cui la 14. propietà IsGeneircTypeDefinition è vera, 15. ma non ci troviamo di fronte a un tipo la cui 16. signature contenga effettivamente tipi generics. 17. Ne darò un esempio dopo... 18. If Not Name.Contains("`") Then 19. Continue For 20. End If 21. 22. Ottiene una stringa in cui elimina tutti i 23. caratteri a partire dallindice delaccento 24. Name = T.Name.Remove(T.Name.IndexOf("`")) 25. E poi gli aggiunge un "(Of ", per far vedere che 26. si sta iniziando una dichiarazione generic 27. Name &= "(Of " 28. Quindi aggiunge tutti gli argomenti generic 29. For Each GenT As Type In T.GetGenericArguments 30. Se il parametro non è il primo, lo separa dal 31. precedente con una virgola. 32. If GenT.GenericParameterPosition > 0 Then 33. Name &= ", " 34. End If 35. Quindi vi aggiunge il nome 36. Name &= GenT.Name 37.
  • Next 38. E chiude la parentesi 39. Name &= ")" 40. Console.WriteLine(Name) 41. End If 42. Next 43. End Sub 44. 45. Notate che la classe Type espone molte proprietà che 46. si possono usare solo in determinati casi. Ad esempio, in 47. questo codice è lecito richiamare GenericParametrPosition 48. poiché sappiamo a priori che quel Type indica un tipo 49. generic in una signature generic. Ma un in un qualsiasi OT 50. non ha alcun senso usare tale proprietà! 51. 52. Sub Main() 53. Ottiene un riferimento allassembly corrente 54. Dim Asm As Assembly = Assembly.GetExecutingAssembly() 55. 56. EnumerateGenerics(Asm) 57. 58. Console.ReadKey() 59. End Sub 60. 61. End ModuleEcco 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 allif posto nel ciclo enumer ativo, vor r ei far notar e che IsGener icTypeDefinition r estituisce tr ue se r intr accianel tipo un r ifer imento ad un tipo gener ic aper to, indipendentemente che questo sia dichiar ato nel tipo o da unaltr apar te. Ad esempio: 1. Class Example(Of T) 2. Delegate Sub DoSomething(ByVal Data As T) 3. ... 4. End ClassLenumer azione r aggiunge anche DoSomething, poiché è anchesso un tipo, anche se nidificato, accessibile a tutti imembr i dellassembly (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é ilgener ics è stato dichiar ato a livello di classe.Ecco un altr o esempio, ma sui tipi gener ic collegati: 01. Module Module1 02. 03. Enumera solo i campi generic di un tipo 04. Sub EnumerateGenericFieldMembers(ByVal T As Type) 05. For Each F As FieldInfo In T.GetFields() 06. If F.FieldType.IsGenericType Then 07. Dim Name As String = F.FieldType.Name 08. Dim I As Int16 = 0 09. 10. If Not Name.Contains("`") Then 11. Continue For 12. End If 13. 14. Name = Name.Remove(Name.IndexOf("`")) 15. Name &= "(Of " 16. For Each GenP As Type In F.FieldType.GetGenericArguments 17.
  • Dato che non si stanno analizzando dei 18. parametri generic, non si può utilizzare 19. la proprietà GenericParameterPosition 20. If I > 0 Then 21. Name &= ", " 22. End If 23. Name &= GenP.Name 24. I += 1 25. Next 26. Name &= ")" 27. Console.WriteLine("Dim {0} As {1}", F.Name, Name) 28. End If 29. Next 30. End Sub 31. 32. Public L As New List(Of Integer) 33. Public I As Int32? 34. 35. Sub Main() 36. EnumerateGenericFieldMembers(GetType(Module1)) 37. 38. Console.ReadKey() 39. End Sub 40. End ModuleLuso della Reflec tionFino 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 nonuser 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 objectbr ow ser ), or a sappiamo quali infor mazioni possiamo r aggiunger e e come pr ender le. Questo è impor tante sopr attuttoquando si lavor a con assembly che vengono car icati dinamicamente, ad esempio in un sistema di plug-ins, comemostr er ò fr a poco. Per dar vi un assaggio della potenza della r eflection, ho scr itto un semplice codice che per mette diacceder 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à: 01. Module Module1 02. 03. Stampa tutte le informazioni ricavabili dalle 04. proprietà di un dato oggetto O. Indent è solo 05. una variabile dappoggio per la formattazione, in modo 06. da indentare bene le righe nel caso i valori delle 07. proprietà siano altri oggetti. 08. Public Sub PrintInfo(ByVal O As Object, ByVal Indent As String) 09. Ottiene il tipo di O 10. Dim T As Type = O.GetType() 11. 12. Console.WriteLine("{0}Object of type {1}", Indent, T.Name) 13. Enumera tutte le proprietà 14. For Each Prop As PropertyInfo In T.GetProperties() 15. Ottiene il tipo restituito dalla proprietà 16. Dim PropType As Type = Prop.PropertyType() 17. 18. Se si tratta di una proprietà parametrica, 19. la salta: in questo esempio non volevo dilungarmi, 20. ma potete completare il codice se desiderate. 21. If Prop.GetIndexParameters().Count > 0 Then 22. Continue For 23. End If 24. 25. Se è un di tipo base o una stringa (giacché le 26. stringhe non sono tipo base ma reference), ne stampa 27. direttamente il valore a schermo 28. If (PropType.IsPrimitive) Or (PropType Is GetType(String)) Then 29. Console.WriteLine("{0} {1} = {2}", _ 30.
  • Indent, Prop.Name, Prop.GetValue(O, Nothing)) 31. 32. Altrimenti, se si tratta di un oggetto, lo analizza a 33. sua volta 34. ElseIf PropType.IsClass Then 35. Console.WriteLine("{0} {1} = ", Indent, Prop.Name) 36. PrintInfo(Prop.GetValue(O, Nothing), Indent & " ") 37. End If 38. Next 39. Console.WriteLine() 40. End Sub 41. 42. Sub Main() 43. Crea alcuni oggetti vari 44. Dim P As New Person("Mario", "Rossi", New Date(1982, 3, 17)) 45. Dim T As New Teacher("Luigi", "Bianchi", New Date(1879, 8, 21), "Storia") 46. Dim R As New Relation(Of Person, Teacher)(P, T) 47. Dim Q As New List(Of Int32) 48. Dim K As New Text.StringBuilder() 49. 50. Ne stampa le proprietà, senza sapere nulla a priori 51. sulla natura degli oggetti. 52. Notate che i nomi generics rimangono con laccento... 53. PrintInfo(P, "") 54. PrintInfo(T, "") 55. PrintInfo(R, "") 56. PrintInfo(Q, "") 57. PrintInfo(K, "") 58. 59. Console.ReadKey() 60. End Sub 61. End ModuleLoutput 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
  • Object of type StringBuilder Capacity = 16 MaxCapacity = 2147483647 Length = 0Per scr iver e questo codice mi sono basato sul metodo GetValue esposto dalla classe Pr oper tyInfo. Esso per mette diottener e il valor e che la pr opr ietà r appr esentata dalloggetto Pr oper tyInfo da cui viene invocato possiede nelloggettospecificato come par ametr o. In gener ale, GetValue accetta due par ametr i: il pr imo è loggetto da cui estr ar r e il valor edella 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à. Comeavete visto, ho enumer ato solo pr opr ietà non par ametr iche e per ciò non cer 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, ossiase 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. Module Module1 02. 03. Non riscrivo PrintInfo, ma considero che stia 04. ancora in questo modulo 05. 06. Sub Main() 07. Dim P As New Person("Mario", "Rossi", New Date(1982, 3, 17)) 08. Dim T As New Teacher("Luigi", "Bianchi", New Date(1879, 8, 21), "Storia") 09. Dim R As New Relation(Of Person, Teacher)(P, T) 10. Dim Q As New List(Of Int32) 11. Dim K As New Text.StringBuilder() 12. Dim Objects() As Object = {P, T, R, Q, K} 13. Dim Cmd As Int32 14. 15. Console.WriteLine("Oggetti nella collezione: ") 16. For I As Int32 = 0 To Objects.Length - 1 17. Console.WriteLine("{0} - Istanza di {1}", _ 18. I, Objects(I).GetType().Name) 19. Next 20. Console.WriteLine("Inserire il numero corrispondente alloggetto da modificare: ") 21. Cmd = Console.ReadLine 22. 23. If Cmd < 0 Or Cmd > Objects.Length - 1 Then 24. Console.WriteLine("Nessun oggetto corrispondente!") 25. Exit Sub 26. End If 27. 28. Dim Selected As Object = Objects(Cmd) 29. Dim SelectedType As Type = Selected.GetType() 30. Dim Properties As New List(Of PropertyInfo) 31. 32. For Each Prop As PropertyInfo In SelectedType.GetProperties() 33. If (Prop.PropertyType.IsPrimitive Or Prop.PropertyType Is GetType(String)) And _ 34. Prop.CanWrite Then 35. Properties.Add(Prop) 36. End If 37. Next 38. 39. Console.Clear() 40. Console.WriteLine("Proprietà delloggetto:") 41. For I As Int32 = 0 To Properties.Count - 1 42. Console.WriteLine("{0} - {1}", _ 43. I, Properties(I).Name) 44. Next 45. Console.WriteLine("Inserire il numero corrispondente alla proprietà da modificare:") 46. Cmd = Console.ReadLine 47. 48. If Cmd < 0 Or Cmd > Objects.Length - 1 Then 49. Console.WriteLine("Nessuna proprietà corrispondente!") 50. Exit Sub 51.
  • End If 52. 53. Dim SelectedProp As PropertyInfo = Properties(Cmd) 54. Dim NewValue As Object 55. 56. Console.Clear() 57. Console.WriteLine("Nuovo valore: ") 58. NewValue = Console.ReadLine 59. 60. Imposta il nuovo valore della proprietà. Noterete che 61. si ottiene un errore di cast con tutti i tipi che 62. non siano String. Questo accade poiché viene 63. eseguito un matching sul tipo degli argomenti: se essi 64. sono diversi, indipendentemente dal fatto che possano 65. essere convertiti luno nellaltro (al contrario di 66. quanto dice il testo dellerrore), viene sollevata 67. quelleccezione. Per aggirare il problema, si 68. dovrebbe eseguire un cast esplicito controllando prima 69. il tipo della proprietà: 70. If SelectedProp.PropertyType Is GetType(Int32) Then 71. NewValue = CType(NewValue, Int32) 72. ElseIf SelectedProp. ... 73. È il prezzo da pagare quando si lavora con 74. uno strumento così generale come la Reflection. 75. [Generalmente si conosce in anticipo il tipo] 76. SelectedProp.SetValue(Selected, NewValue, Nothing) 77. 78. Console.WriteLine("Proprietà modificata!") 79. 80. PrintInfo(Selected, "") 81. 82. Console.ReadKey() 83. End Sub 84. End ModuleChi ha letto anche la ver sione pr ecedente della guida, avr à notato che manca il codice per lassembly br ow ser , ossiaquel 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 opponoioso e labor ioso e tr oppo poco inter essante per r ipr opor lo anche qui, ma siete liber i di dar ci unocchiata (al r elativocapitolo della ver sione 2).
  • A47. La Reflection - Parte IVCompilazione di c odic e a runtimeBene, or a che sappiamo scr iver e del nor male codice per una qualsiasi applicazione e che sappiamo come analizzar ecodice 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 lesecuzionedalapplicazione stessa, gener ando così nuovi assembly per gli scopi più var i. Una volta mi sono ser vito in manier aintensiva 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 nellospecifico 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 . Essicontengono le classi che fanno al caso nostr o per il mestier e. Il pr ocesso di compilazione si svolge alltr aver so questefasi: 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 lassembly e salvar lo in memor ia, se tr attar e gli w ar ning come er r or i, ecceter a... Insomma, tutto quello che noi scegliamo tr amite linter faccia dellambiente di sviluppo o che ci tr oviamo già impostato gr azie allIDE 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, lassembly ver r à gener ato cor r ettamente; Se lassembly 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 alloutput 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 lassembly 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 lassembly 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 chepotr ebbe tor nar vi utile in futur o: 001. Module Module1 002. 003. Questa classe rappresenta una funzione matematica: 004. ne ho racchiuso il nome tra parentesi quadre poiché Function 005. è una keyword del linguaggio, ma in questo caso la si 006.
  • vuole usare come identificatore. In generale, si possono usare007. le parentesi quadre per trasformare ogni keyword in un normale008. nome.009. Class [Function]010. Private _Expression As String011. Private EvaluateFunction As MethodInfo012.013. Contiene lespressione matematica che costruisce il valore014. della funzione in base alla variabile x015. Public Property Expression() As String016. Get017. Return _Expression018. End Get019. Set(ByVal value As String)020. _Expression = value021. Me.CreateEvaluator()022. End Set023. End Property024.025. La prossima è la procedura centrale di tutto lesempio.026. Il suo compito consiste nel compilare una libreria di027. classi in cui è definita una funzione che, ricevuto028. come parametro un x decimale, ne restituisce il valore029. f(x). Il corpo di tale funzione varia in base030. allespressione immessa dallutente.031. Private Sub CreateEvaluator()032. Crea il codice della libreria: una sola classe033. contenente la sola funzione statica Evaluate. Gli {0}034. verranno sostituti con caratteri di "a capo", mentre035. in {1} verrà posta lespressione che produce036. il valore desiderato, ad esempio "x ^ 2 + 1".037. Dim Code As String = String.Format( _038. "Imports Microsoft.VisualBasic{0}" & _039. "Imports System{0}" & _040. "Imports System.Math{0}" & _041. "Public Class Evaluator{0}" & _042. " Public Shared Function Evaluate(ByVal X As Double) As Double{0}" & _043. " Return {1}{0}" & _044. " End Function{0}" & _045. "End Class", Environment.NewLine, Me.Expression)046.047. Crea un nuovo oggetto CompilerParameters, per048. contenere le informazioni relative alla compilazione049. Dim Parameters As New CompilerParameters050.051. With Parameters052. Indica se creare un eseguibile o una libreria di053. classi: in questo caso ci interessa la seconda,054. quindi impostiamo la proprietà su False055. .GenerateExecutable = False056.057. Gli warning vengono considerati come errori058. .TreatWarningsAsErrors = True059. Non vogliamo tenere alcun file temporaneo: ci060. interessa solo lassembly compilato061. .TempFiles.KeepFiles = False062. Lassembly verrà tenuto in memoria temporanea063. .GenerateInMemory = True064.065. I due riferimenti di cui abbiamo bisogno, che si066. trovano nella GAC (quindi basta specificarne il067. nome). In questo caso, si richiede anche068. lestensione (*.dll)069. .ReferencedAssemblies.Add("Microsoft.VisualBasic.dll")070. .ReferencedAssemblies.Add("System.dll")071. End With072.073. Crea un nuovo provider di compilazione074. Dim Provider As New VBCodeProvider075. E compila il codice seguendo i parametri di076. compilazione forniti dalloggetto Parameters. Il077. valore restituito dalla funzione078.
  • CompileAssemblyFromSource è di tipo079. CompilerResults e viene salvato in CompResults080. Dim CompResults As CompilerResults = _081. Provider.CompileAssemblyFromSource(Parameters, Code)082.083. Se ci sono errori, lancia uneccezione084. If CompResults.Errors.Count > 0 Then085. Throw New FormatException("Espressione non valida!")086. Else087. Altrimenti crea un riferimento allassembly di088. output. La proprietà CompiledAssembly di089. CompResults contiene un riferimento diretto a090. quellassembly, quindi ci è molto comoda.091. Dim Asm As Reflection.Assembly = CompResults.CompiledAssembly092. Dallassembly ottiene un OT che rappresenta093. lunico tipo ivi definito, e da questo ne094. estrae un MethodInfo con informazioni sul095. metodo Evaluate (lunico presente).096. Me.EvaluateFunction = _097. Asm.GetType("Evaluator").GetMethod("Evaluate")098. End If099. End Sub100.101. Per richiamare la funzione, basta invocare il metodo102. Evaluate estratto precedentemente. Al pari delle103. proprietà e dei campi, che possono essere letti o104. scritti dinamicamente, anche i metodi possono essere105. invocati dinamicamete attraverso MethodInfo. Si usa106. la funzione Invoke: il primo parametro da107. passare è loggetto da cui richiamare il metodo, mentre108. il secondo è un array di oggetti che indicano i109. parametri da passare a tale metodo.110. In questo caso, il primo parametro è Nothing poiché111. Evaluate è una funzione statica e non ha bisgno di nessuna112. istanza per essere richiamata.113. Public Function Apply(ByVal X As Double) As Double114. Return EvaluateFunction.Invoke(Nothing, New Object() {X})115. End Function116.117. End Class118.119. Sub Main()120. Dim F As New [Function]()121.122. Do123. Try124. Console.Clear()125. Console.WriteLine("Inserisci una funzione: ")126. Console.Write("f(x) = ")127. F.Expression = Console.ReadLine128. Exit Do129. Catch ex As Exception130. Console.WriteLine("Espressione non valida!")131. Console.ReadKey()132. End Try133. Loop134.135. Dim Input As String136. Dim X As Double137.138. Do139. Try140. Console.Clear()141. Console.WriteLine("Immettere stop per terminare.")142. Console.WriteLine("Il programma calcola il valore di f in X: ")143. Console.Write("x = ")144. Input = Console.ReadLine145. If Input <> "stop" Then146. X = CType(Input, Double)147. Console.WriteLine("f(x) = {0}", F.Apply(X))148. Console.ReadKey()149. Else150.
  • Exit Do 151. End If 152. Catch Ex As Exception 153. Console.WriteLine(Ex.Message) 154. Console.ReadKey() 155. End Try 156. Loop 157. End Sub 158. End ModuleIn questo esempio ho utilizzato solo alcuni dei membr i esposti dalle classi sopr a menzionate. Di seguito elenco tuttiquelli più r ilevanti, che potr ebber o ser vir vi in futur o: Co m piler Par am eter 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 dallIDE nellambito 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 nellassembly 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 lassembly gener ato su un suppor to per manente (disco fisso o altr e memor ie non volatili); IncludeDebugInfor mations : deter mina se includer e nelleseguibile 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 lIDE facendo il debug del codice stesso che compila altr o codice XD; MainClass : imposta il nome della classe pr incipale dellassembly. 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 nellapplicazione. 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 lapplicazione da compilar e sia di tipo w indow s 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 dellassembly 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 lassembly 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 w ar ning come se fosser o er r or i. Questo impedisce di por tar e a ter mine la compilazione quando ci sono degli w ar 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 w ar 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. Co m piler Results CompiledAssembly : r estituisce un oggetto Assembly in r ifer imento allassembly 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
  • identificar e il luogo ed il motivo deller r or e. Alcune sono: Line e Column (linea e colonna deller r or e), IsWar ning (se è un w ar ning o un er r or e), Er r or Number (numer o identificativo deller r or e), Er r or Tex t (testo deller 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 lesecuzione. Vi r icor do, infatti, che compilator e, editor di codice e debugger sono tr e pr ogr ammi differ enti: lambiente di sviluppo integr ato for nisce uninter 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 dellassembly 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 lagener azione di codice. Questo meccanismo per mette di assemblar e una collezione di oggetti ognuno dei qualir appr esenta unistr uzione di codice, e poi di gener ar e codice per un qualsiasi linguaggio .NET. Sebbene sia unfunzionalità 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 unapplicazionew indow s for ms. Tuttavia siamo nella sezione A e qui si fa solo teor ia, per ciò, pur tr oppo, dovr ete sor bir vi questoentusiasmante esempio come applicazione console.Ammettiamo che unimpr esa abbia un softw ar e di gestione dei suoi mater iali, e che abbastanza spesso acquisti nuovetipologie di pr odotti o semilavor ati. Gli addetti al magazzino dovr anno intr odur r e i dati dei nuovi oggetti, ma per farciò, è necessar io un pr ogr amma adatto per gestir e quel tipo di oggetti: in questo modo, ogni volta, ser ve unpr ogr amma nuovo. Lesempio che segue è una possibile soluzione a questo pr oblema: il pr ogr amma pr incipale r ichiededi 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. Module Module1 002. 003. Classe che rappresenta il nuovo tipo di dato, ed 004. espone una funzione per scriverne il codice 005. Class TypeCreator 006. Private _Fields As Dictionary(Of String, String) 007. Private _Name As String 008. 009. Fields è un dizionario che contiene come 010. chiavi i nomi delle proprietà da definire 011. nel nuovo tipo e come valori il loro tipi 012. Public ReadOnly Property Fields() As Dictionary(Of String, String) 013. Get 014. Return _Fields 015. End Get 016. End Property 017. 018. Nome del nuovo tipo 019. Public Property Name() As String 020. Get 021. Return _Name 022. End Get 023. Set(ByVal value As String) 024. _Name = value 025. End Set 026. End Property 027. 028. Public Sub New() 029. _Fields = New Dictionary(Of String, String) 030. End Sub 031. 032.
  • Genera il codice della proprietà033. Public Function GenerateCode() As String034. Dim Code As New Text.StringBuilder()035.036. Code.AppendLine("Class " & Name)037. For Each Field As String In Me.Fields.Keys038. Code.AppendFormat("Private _{0} As {1}{2}", _039. Field, Me.Fields(Field), Environment.NewLine)040. Code.AppendFormat( _041. "Public Property {0} As {1}{2}" & _042. " Get{2}" & _043. " Return _{0}{2}" & _044. " End Get{2}" & _045. " Set(ByVal value As {1}){2}" & _046. " _{0} = value{2}" & _047. " End Set{2}" & _048. "End Property{2}", _049. Field, Me.Fields(Field), Environment.NewLine)050. Next051. Code.AppendLine("End Class")052.053. Return Code.ToString()054. End Function055.056. End Class057.058. Classe statica contenente la funzione per scrivere059. e generare il nuovo programma060. Class ProgramCreator061.062. Accetta come input il nuovo tipo di dato063. da gestire. Restituisce in output il percorso064. delleseguibile creato065. Public Shared Function CreateManagingProgram(ByVal T As TypeCreator) As String066. Dim Code As New Text.StringBuilder()067.068. Code.AppendLine("Imports System")069. Code.AppendLine("Imports System.Collections.Generic")070. Code.AppendLine("Module Module1")071. Code.AppendLine(T.GenerateCode())072.073. Code.AppendLine("Sub Main()")074. Code.AppendLine(" Dim Storage As New List(Of " & T.Name & ")")075. Code.AppendLine(" Dim Cmd As Char")076. Code.AppendLine(" Do")077. Code.AppendLine(" Console.Clear()")078. Code.AppendLine(" Console.WriteLine(""Inserimento di oggetti " & T.Name & """)")079. Code.AppendLine(" Console.WriteLine()")080. Code.AppendLine(" Console.Writeline(""Scegliere unoperazione: "")")081. Code.AppendLine(" Console.WriteLine("" i - inserimento;"")")082. Code.AppendLine(" Console.WriteLine("" e - elenca;"")")083. Code.AppendLine(" Console.WriteLine("" u - uscita."")")084. Code.AppendLine(" Cmd = Console.ReadKey().KeyChar")085. Code.AppendLine(" Console.Clear()")086. Code.AppendLine(" Select Case Cmd")087. Code.AppendLine(" Case ""i"" ")088. Code.AppendLine(" Dim O As New " & T.Name & "()")089. Code.AppendLine(" Console.WriteLine(""Inserire i dati: "")")090. Legge ogni membro del nuovo tipo. Usa la CType091. per essere sicuri che tutto venga interpretato nel092. modo corretto.093. For Each Field As String In T.Fields.Keys094. Code.AppendFormat("Console.Write("" {0} = ""){1}", Field, Environment.NewLine)095. Code.AppendFormat("O.{0} = CType(Console.ReadLine(), {1}){2}", Field, T.Fields(Field), Environment.NewLine)096. Next097. Code.AppendLine(" Storage.Add(O)")098. Code.AppendLine(" Console.WriteLine(""Inserimento completato!"")")099. Code.AppendLine(" Case ""e"" ")100. Code.AppendLine(" For I As Int32 = 0 To Storage.Count - 1")101. Code.AppendLine(" Console.WriteLine(""{0:000} + "", I)")102.
  • Fa scrivere una linea per ogni proprietà103. delloggetto, mostrandone il valore104. For Each Field As String In T.Fields.Keys105. Code.AppendFormat("Console.WriteLine("" {0} = "" & Storage(I). {0}.ToString()){1}", Field, Environment.NewLine)106. Next107. Code.AppendLine(" Next")108. Code.AppendLine(" Console.ReadKey()")109. Code.AppendLine(" End Select")110. Code.AppendLine(" Loop Until Cmd = ""u""")111.112. Code.AppendLine("End Sub")113. Code.AppendLine("End Module")114.115. Dim Parameters As New CompilerParameters116.117. With Parameters118. .GenerateExecutable = True119. .TreatWarningsAsErrors = True120. .TempFiles.KeepFiles = False121. .GenerateInMemory = False122. .ReferencedAssemblies.Add("Microsoft.VisualBasic.dll")123. .ReferencedAssemblies.Add("System.dll")124. End With125.126. Dim Provider As New VBCodeProvider127. Dim CompResults As CompilerResults = _128. Provider.CompileAssemblyFromSource(Parameters, Code.ToString())129.130. If CompResults.Errors.Count = 0 Then131. Return CompResults.PathToAssembly132. Else133. For Each E As CompilerError In CompResults.Errors134. Stop135. Next136. Return Nothing137. End If138. End Function139.140. End Class141.142. Sub Main()143. Dim NewType As New TypeCreator()144. Dim I As Int16145. Dim Field, FieldType As String146.147. Console.WriteLine("Creazione di un tipo")148. Console.WriteLine()149.150. Console.Write("Nome del tipo = ")151. NewType.Name = Console.ReadLine152.153. Console.WriteLine("Inserisci il nome del campo e il suo tipo:")154.155. I = 1156. Do157. Console.Write("Nome campo {0}: ", I)158. Field = Console.ReadLine159.160. If String.IsNullOrEmpty(Field) Then161. Exit Do162. End If163.164. Console.Write("Tipo campo {0}: ", I)165. FieldType = Console.ReadLine166.167. Dovrete immettere il nome completo e con168. le maiuscole al posto giusto. Ad esempio:169. System.String170. e non string o system.String o STring.171. If Type.GetType(FieldType) Is Nothing Then172. Console.WriteLine("Il tipo {0} non esiste!", FieldType)173.
  • Console.ReadKey()174. Else175. NewType.Fields.Add(Field, FieldType)176. I += 1177. End If178. Loop179.180. Dim Path As String = ProgramCreator.CreateManagingProgram(NewType)181.182. If Not String.IsNullOrEmpty(Path) Then183. Console.WriteLine("Programma di gestione per il tipo {0} creato!", NewType.Name)184. Console.WriteLine("Avviarlo ora? y/n")185. If Console.ReadKey().KeyChar = "y" Then186. Process.Start(Path)187. End If188. End If189. End Sub190. End Module
  • A48. Gli AttributiCosa sono e a c osa 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 eentità. Tutte le infor mazioni contenute in un attr ibuto vanno sotto il nome di m etadati. Attr aver so i metadati, ilpr 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 lealtr e par ti del codice. Gli attr ibuti, quindi, non sono str umenti di "pr ogr ammazione attiva", poiché non fan n o nulla, madicon o semplicemente qualcosa; si avvicinano, per cer ti ver si, alla pr ogr ammazione dichiar ativa. Inoltr e, essi nonpossono 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 oaltr o; gli attr ibuti, semplicemente par lando, non "esistono" se non come par te invisibile di infor mazione attaccata aqualche altr o membr o. Sono come dei par assiti: hanno senso solo se attr ibuiti, appunto, a qualcosa. Per questo motivo,lunico modo per utilizzar e le infor mazioni che essi si por tano dietr o consiste nella Reflection.La sintassi con cui si asseg na un attr ibuto a un membr o (non "si dichiar a", né "si inizializza", ma "si assegna" aqualcosa) è questa: 1. <Attributo(Parametri)> [Entità]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 suapeculiar ità, che non si tr ova in tutti i linguaggi .NET. Le Common Language Specifications del Fr amew or k specificanoche un qualsiasi linguaggio, per esser e qualificato come .NET, deve dar e la possibilità di gestir e ar r ay a base 0. VB faquesto, ma espone anche quella par ticolar ità di pr ima che gli der iva dal VB classico: questa potenzialità è detta n onCLS-Complian t, ossia che non r ispetta le specifiche del Fr amew or k. Noi siamo liber i di usar la, ad esempio in unalibr er ia, ma dobbiamo avver tir e gli altr i che, qualor a usasser o un altr o linguaggio .NET, non potr ebber opr obabilmente usufr uir e di quella par te di codice. Lunico modo di far e ciò consiste nellassegnar e un attr ibutoCLSCompliant a quel membr o che non r ispetta le specifiche: 01. <CLSCompliant(False)> _ 02. Function CreateArray(Of T)(ByVal IndexFrom As Int32, ByVal IndexTo As Int32) As Array 03. Per creare un array a estremi variabili è necessario 04. usare una funzione della classe Array, ed è altrettanto 05. necessario dichiarare larray come di tipo Array, anche se 06. questo è solitamente sconsigliato. Per creare il 07. suddetto oggetto, bisogna passare alla funzione tre 08. parametri: 09. - il tipo degli oggetti che larray contiene; 10. - un array contenente le lunghezze di ogni rango dellarray; 11. - un array contenente gli indici iniziali di ogni rango. 12. Return Array.CreateInstance(GetType(T), New Int32() {IndexTo - IndexFrom}, New Int32() {IndexTo}) 13. End Function 14. 15. Sub Main() 16. Dim CustomArray As Array = CreateArray(Of Int32)(3, 9) 17. 18. CustomArray(3) = 1 19. ... 20. End SubOr 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 ebbeun War ning aggiuntivo indicano esplicitamente che il metodo Cr eateAr r ay non è CLS-Compliant, e per ciò non è sicur ousar lo.Allo stesso modo, un altr o esempio potr ebbe esser e lattr ibuto Obsolete. Specialmente quando si lavor a su gr andipr ogetti di cui esistono più ver sioni e lo sviluppo è in costante evoluzione, capita che vengano scr itte nuove ver sioni dimembr i o tipi già esistenti: quelle vecchie sar anno molto pr obabilmente mantenute per assicur ar e la compatibilità con
  • softw ar e datati, ma sar anno comunque mar cate con lattr ibuto obsolete. Ad esempio, con questa r iga di codice potr eteottener e lindir izzo IP del mio sito: 1. Dim IP As Net.IPHostEntry = System.Net.Dns.Resolve("www.totem.altervista.org")Tuttavia otter r ete un War ning che r ipor ta la seguente dicitur a: Public Shared Fun ction Res olve(hos tName As Strin g)As Sys tem.Net.IPHos tEn try is obs olete: Res olve is obs oleted for this type, pleas e us e GetHos tEn try in s tead.http://go.micros oft.com/fwlin k/?lin kid=14202 . Questo è un esempio di un metodo, esistente dalla ver sione 1.1 delFr amew or 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 aseconda che sia definita una cer ta costante di compilazione. Queste costanti sono impostabili in una finestr a dicompilazione avanzata che vedr emo solo più avanti. Tuttavia, quando lapplicazione è in modalità debug, è di defaultdefinita la costante DEBUG. 01. <Conditional("DEBUG")> _ 02. Sub WriteStatus() 03. Scriva a schermo la quantità di memoria usata, 04. senza aspettare la prossima garbage collection 05. Console.WriteLine("Memory: {0}", GC.GetTotalMemory(False)) 06. End Sub 07. 08. Crea un nuovo oggetto 09. Function CreateObject(Of T As New)() As T 10. Dim Result As New T 11. Richiama WriteStatus: questa chiamata viene IGNORATA 12. in qualsiasi caso, tranne quando siamo in debug. 13. WriteStatus() 14. Return Result 15. End Function 16. 17. Sub Main() 18. Dim k As Text.StringBuilder = _ 19. CreateObject(Of Text.StringBuilder)() 20. ... 21. End SubUsando 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 infattiuna 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 omodo. Un attr ibuto può esser e usato, quindi, nelle manier e più var ie e intr odur r ò nuovi attr ibuti molto impor tantinelle pr ossime sezioni. Questo capitolo non ha lo scopo di mostr ar e il funzionamento di ogni attr ibuti esistente, ma diinsegnar 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 unnuovo attr ibuto.Dic hiarare nuovi attributiFor malmente, un attr ibuto non è altr o che una classe der ivata da System.Attr ibute. Ci sono alcune convenzionir 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 dellattr ibuto è contener einfor mazione, è ovvio che possa contener e solo pr opr ietà, poiché non spetta a lui usar ne il valor e. Ecco un esempiosemplice con un attr ibuto senza pr opr ietà:
  • 001. In questo codice, cronometreremo dei metodi, per002. vedere quale è il più veloce!003. Module Module1004.005. Questo è un nuovo attributo completamente vuoto.006. Linformazione che trasporta consiste nel fatto stesso007. che esso sia applicato ad un membro.008. Nel metodo di cronometraggio, rintracceremo e useremo009. solo i metodi a cui sia stato assegnato questo attributo.010. Public Class TimeAttribute011. Inherits Attribute012.013. End Class014.015. I prossimi quattro metodi sono procedure di test. Ognuna016. esegue una certa operazione 100mila o 10 milioni di volte.017.018. <Time()> _019. Sub AppendString()020. Dim S As String = ""021. For I As Int32 = 1 To 100000022. S &= "a"023. Next024. S = Nothing025. End Sub026.027. <Time()> _028. Sub AppendBuilder()029. Dim S As New Text.StringBuilder()030. For I As Int32 = 1 To 100000031. S.Append("a")032. Next033. S = Nothing034. End Sub035.036. <Time()> _037. Sub SumInt32()038. Dim S As Int32039. For I As Int32 = 1 To 10000000040. S += 1041. Next042. End Sub043.044. <Time()> _045. Sub SumDouble()046. Dim S As Double047. For I As Int32 = 1 To 10000000048. S += 1.0049. Next050. End Sub051.052. Questa procedura analizza il tipo T e ne estrae tutti053. i metodi statici e senza parametri marcati con lattributo054. Time, quindi li esegue e li cronometra, poi riporta055. i risultati a schermo per ognuno.056. Vogliamo che i metodi siano statici e senza parametri057. per evitare di raccogliere tutte le informazioni per la058. funzione Invoke.059. Sub ReportTiming(ByVal T As Type)060. Dim Methods() As MethodInfo = T.GetMethods()061. Dim TimeType As Type = GetType(TimeAttribute)062. Dim TimeMethods As New List(Of MethodInfo)063.064. La funzione GetCustomAttributes accetta due parametri065. nel secondo overload: il primo è il tipo di066. attributo da cercare, mentre il secondo specifica se067. cercare tale attributo in tutto lalbero di068. ereditarietà del membro. Restituisce come069. risultato un array di oggetti contenenti gli attributi070. del tipo voluto. Vedremo fra poco come utilizzare071. questo array: per ora limitiamoci a vedere se non è072.
  • vuoto, ossia se il metodo è stato marcato con Time 073. For Each M As MethodInfo In Methods 074. If M.GetCustomAttributes(TimeType, False).Length > 0 And _ 075. M.GetParameters().Count = 0 And _ 076. M.IsStatic Then 077. TimeMethods.Add(M) 078. End If 079. Next 080. 081. Methods = Nothing 082. 083. La classe Stopwatch rappresenta un cronometro. Start 084. per farlo partire, Stop per fermarlo e Reset per 085. resettarlo a 0 secondi. 086. Dim Crono As New Stopwatch 087. 088. For Each M As MethodInfo In TimeMethods 089. Crono.Reset() 090. Crono.Start() 091. M.Invoke(Nothing, New Object() {}) 092. Crono.Stop() 093. Console.WriteLine("Method: {0}", M.Name) 094. Console.WriteLine(" Time: {0}ms", Crono.ElapsedMilliseconds) 095. Next 096. 097. TimeMethods.Clear() 098. TimeMethods = Nothing 099. End Sub 100. 101. Sub Main() 102. Dim This As Type = GetType(Module1) 103. 104. Non vi allarmate se il programma non stampa nulla 105. per qualche secondo. Il primo metodo è molto 106. lento XD 107. ReportTiming(This) 108. 109. Console.ReadKey() 110. End Sub 111. End ModuleEcco i r isultati del benchmar king (ter mine tecnico) sul mio por tatile: Method: AppendString Time: 4765ms Method: AppendBuilder Time: 2ms Method: SumInt32 Time: 27ms Method: SumDouble Time: 34msCome potete osser var e, concatenar e le str inghe con & è enor memente meno efficiente r ispetto allAppend della classeStr ingBuilder . Ecco per chè, quando si hanno molti dati testuali da elabor ar e, consiglio sempr e di usar e il secondometodo. 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 soloattr aver so la Reflection. Pr ima di pr oceder e, bisogna dir e che esiste uno speciale attr ibuto applicabile solo agliattr ibuti che definisce quali entità possano esser e mar cate con dato attr ibuto. Esso si chiama Attr ibuteUsage. Adesempio, nel codice pr ecedente, Time è stato scr itto con lintento di mar car e tutti i metodi che sar ebber o statisottoposti a benchmar king, ossia è "nato" per esser e applicato solo a metodi, e non a classi, enumer ator i, var iabili oaltr o. Tuttavia, per come labbiamo dichiar ato, un pr ogr ammator e può applicar lo a qualsiasi cosa. Per r estr inger e ilsuo campo dazione si dovr ebbe modificar e il sor gente come segue: 1. <AttributeUsage(AttributeTargets.Method)> _ 2.
  • Public Class TimeAttribute 3. Inherits Attribute 4. 5. End ClassAttr ibuteTar gets è un enumer ator e codificato a bit.Ma veniamo or a agli attr ibuti con par ametr i: 001. Module Module1 002. 003. UserInputAttribute specifica se una certa proprietà 004. debba essere valorizzata dallutente e se sia 005. obbligatoria o meno. Il costruttore impone un solo 006. argomento, IsUserScope, che deve essere per forza 007. specificato, altrimenti non sarebbe neanche valsa la 008. pena di usare lattributo, dato che questa è la 009. sua unica funzione. 010. Come specificato dalle convenzioni, la proprietà 011. impostata nel costruttore è ReadOnly, mentre 012. le altre (laltra) è normale. 013. <AttributeUsage(AttributeTargets.Property)> _ 014. Class UserInputAttribute 015. Inherits Attribute 016. 017. Private _IsUserScope As Boolean 018. Private _IsCompulsory As Boolean = False 019. 020. Public ReadOnly Property IsUserScope() As Boolean 021. Get 022. Return _IsUserScope 023. End Get 024. End Property 025. 026. Public Property IsCompulsory() As Boolean 027. Get 028. Return _IsCompulsory 029. End Get 030. Set(ByVal value As Boolean) 031. _IsCompulsory = value 032. End Set 033. End Property 034. 035. Sub New(ByVal IsUserScope As Boolean) 036. _IsUserScope = IsUserScope 037. End Sub 038. 039. End Class 040. 041. Cubo 042. Class Cube 043. Private _SideLength As Single 044. Private _Density As Single 045. Private _Cost As Single 046. 047. Se i parametri del costruttore vanno specificati 048. tra parentesi quando si assegna lattributo, allora 049. come si fa a impostare le altre proprietà 050. facoltative? Si usa un particolare operatore di 051. assegnamento ":=" e si impostano esplicitamente 052. i valori delle proprietà ad uno ad uno, 053. separati da virgole, ma sempre nelle parentesi. 054. <UserInput(True, IsCompulsory:=True)> _ 055. Public Property SideLength() As Single 056. Get 057. Return _SideLength 058. End Get 059. Set(ByVal value As Single) 060. _SideLength = value 061. End Set 062. End Property 063. 064.
  • <UserInput(True)> _065. Public Property Density() As Single066. Get067. Return _Density068. End Get069. Set(ByVal value As Single)070. _Density = value071. End Set072. End Property073.074. Cost non verrà chiesto allutente075. <UserInput(False)> _076. Public Property Cost() As Single077. Get078. Return _Cost079. End Get080. Set(ByVal value As Single)081. _Cost = value082. End Set083. End Property084.085. End Class086.087. Crea un oggetto di tipo T richiendendo allutente di088. impostare le proprietà marcate con UserInput089. in cui IsUserScope è True.090. Function GetInfo(ByVal T As Type) As Object091. Dim O As Object = T.Assembly.CreateInstance(T.FullName)092.093. For Each PI As PropertyInfo In T.GetProperties()094. If Not PI.CanWrite Then095. Continue For096. End If097.098. Dim Attributes As Object() = PI.GetCustomAttributes(GetType(UserInputAttribute), True)099.100. If Attributes.Count = 0 Then101. Continue For102. End If103.104. Ottiene il primo (e lunico) elemento dellarray,105. un oggetto di tipo UserInputAttribute che rappresenta106. lattributo assegnato e contiene tutte le informazioni107. passate, sottoforma di proprietà.108. Dim Attr As UserInputAttribute = Attributs(0)109.110. Se la proprietà non è richiesta allutente,111. allora continua il ciclo112. If Not Attr.IsUserScope Then113. Continue For114. End If115.116. Dim Value As Object = Nothing117. Se è obbligatoria, continua a richiederla118. fino a che lutente non immette un valore corretto.119. If Attr.IsCompulsory Then120. Do121. Try122. Console.Write("* {0} = ", PI.Name)123. Value = Convert.ChangeType(Console.ReadLine, PI.PropertyType)124. Catch Ex As Exception125. Value = Nothing126. Console.WriteLine(Ex.Message)127. End Try128. Loop Until Value IsNot Nothing129. Else130. Altrimenti la richiede una sola volta131. Try132. Console.Write("{0} = ", PI.Name)133. Value = Convert.ChangeType(Console.ReadLine, PI.PropertyType)134. Catch Ex As Exception135.
  • Value = Nothing 136. End Try 137. End If 138. If Value IsNot Nothing Then 139. PI.SetValue(O, Value, Nothing) 140. End If 141. Next 142. 143. Return O 144. End Function 145. 146. Sub Main() 147. Dim O As Object 148. 149. Console.WriteLine("Riempire i campi (* = obbligatorio):") 150. 151. O = GetInfo(GetType(Cube)) 152. 153. Stampa i valori con il metodo PrintInfo scritto qualche 154. capitolo fa 155. PrintInfo(O, "") 156. 157. Console.ReadKey() 158. End Sub 159. End ModuleVi lascio immaginar e cosa faccia il metodo Conver t.ChangeType...
  • A49. Modificare le opzioni di compilazioneEsistono più modi di influir e sulla compilazione di un sor gente e di modificar e il compor tamento del compilator e ver sole sue par ti. Alcuni di questi "modi" consistono nel modificar e le opzioni di compilazione, che si suddividuono in quattr ovoci pr incipali: Ex plicit, Compar e, Infer e Str ict. Per attivar e o disattivar e ognuna di esse, è possibile usar e delledir ettive speciali poste in testa al codice o acceder e alla finestr a dellambiente 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 sulletichetta 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 setr attar le come w ar 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: daltr a par te,questa è sempr e stata la pr assi che abbiamo adottato fin dallinizio del cor so e non ci sono par ticolar i motivi percombiar la. Quando lopzione è disattivata, ogni nome sconosciuto ver r à tr attato come una nuova var iabile e cr eato almomento. Ecco un esempio in cui disattivo Ex plicit da codice: 01. Option Explicit Off 02. 03. Module Module1 04. Sub Main() 05. La variabile Stringa non viene dichiarata, ma è 06. lecito usarla e non viene comunicato alcun errore 07. Stringa = "Ciao" 08. 09. Stessa cosa per la variabile I, che non è stata 10. dichiarata da nessuna parte 11. For I = 1 To 20 12. Console.WriteLine(Stringa) 13. Next 14. 15.
  • Console.ReadKey() 16. End Sub 17. End ModuleLe dir ettive per lattivazione/disattivazione di unopzione 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: 1. Option [Nome] On/OffAnche se è possibile disattivar e Ex plicit, è fortemen te s con s 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 possonocondannar vi a settimane di insonnia. Ad esempio: 01. Option Explicit Off 02. 03. Module Module1 04. Sub Main() 05. Stringa = "Ciao" 06. 07. For I = 1 To 20 08. If I > 10 Then 09. Strnga = I 10. End If 11. Console.WriteLine(Stringa) 12. Next 13. 14. Console.ReadKey() 15. End Sub 16. End ModuleIl codice dovr ebbe, nelle vostr e intenzioni, scr iver e "Ciao" solo 10 volte, e poi stampar e 11, 12, 13, ecceter a... Tuttaviaquesto non succede, per chè avete dimenticato una "i" e il compilator e non vi segnala nessun er r or e a causa delladir ettiva Option; non r icever ete neppur e un w ar ning del tipo "Unused local var iable", per chè la r iga in cui è pr esenteStr 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, lacompar azione tr a due str inghe viene effettuata confr ontando il valor e dei singoli bytes che la compongono e, per ciò, ilconfr onto diventa cas e-s en s itive (maiuscole e minuscole della stessa letter a sono consider ate differ enti). Se, alcontr ar io, è impostata su Tex t, viene compar ato solo il testo che le str inghe contengono, senza far e distinzioni suletter e maiuscole o minuscole (cas e-in s en s itive). Eccone un esempio: 01. Option Compare Text 02. Module Module1 03. Sub Main() 04. If "CIAO" = "ciao" Then 05. Console.WriteLine("Option Compare Text") 06. Else 07. Console.WriteLine("Option Compare Binary") 08. End If 09. Console.ReadKey() 10. End Sub 11. End ModuleScr ivendo "Binar y" al posto di "Tex t", otter r emo un messaggio differ ente a r untime!Option Stric tQuesta opzione si occupa di r egolar e le conver sioni implicite tr a tipi di dato differ enti. Quando è attiva, tutti i cast
  • impliciti vengono segnalati e consider ati come er r or i: non si può passar e, ad esempio, da Double a Integer o da unaclasse base a una der ivata: 01. Option Strict On 02. Module Module1 03. Sub Main() 04. Dim I As Int32 05. Dim P As Student 06. 07. Conversione implicita da Double a Int32: viene 08. segnalata come errore 09. I = 4.0 10. Conversione implicita da Person a Student: viene 11. segnalata come errore 12. P = New Person("Mario", "Rossi", New Date(1968, 9, 12)) 13. 14. Console.ReadKey() 15. End Sub 16. End ModulePer 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 sioniimplicite, 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 diinfer ir e il tipo di una var iabile senza tipo (ossia senza clausola As) analizzando i valor i che le vengono passati. Alliniziodella 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 unesempio: 01. Option Infer Off 02. 03. Module Module1 04. Sub Main() 05. Infer è disattivata: I viene considerata di 06. tipo Object 07. Dim I = 2 08. 09. Dato che I è Object, può contenere 10. qualsiasi cosa, e quindi questo codice non genera 11. alcun errore 12. I = "ciao" 13. 14. End Sub 15. End ModulePr ovando ad impostar e Infer su On, non otter r ete nessuna segnalazione dur ante la scr ittur a, ma appena il pr ogr ammasar à avviato, ver r à lanciata uneccezione di cast, poiché il tipo di I viene dedotto dal valor e assegnatole (2) e la fadiventar e, da quel momento in poi, una var iabile Integer a tutti gli effetti, e "ciao" non è conver tibile in inter o.
  • A50. Comprendere e implementare un algoritmoFor se sar ebbe stato oppor tuno tr attar e questo ar gomento moooolto pr ima nella guida piuttosto che alla fine dellasezione; non per niente, la compr ensione degli algor itmi è la pr ima cosa che viene r ichiesta in un qualsiasi cor so diinfor 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 delpr 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: In s ieme di is truzion i elemen tari un ivocamen te in terpretabili che, es eguite in un ordin e s tabilito, permetton o la s oluzion e di un problema in un n umero fin ito di pas s i.Vor r ei innanzitutto por r e lattenzione sulle pr ime par ole: "insieme di istr uzioni elementar i" (io aggiunger e "insiemefinito", per esser e r igor osi, ma mi sembr a abbastanza difficile for nir e un insieme infinito di istr uzioni :P).Sottolineiamo elem entar i: anche se i linguaggi .NET si tr ovano ad un livello molto distante dal pr ocessor e, che è ingr 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 gammadi "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. Imetodi? Non fanno altr o che r ichiamar e altr o codice in cui si cono istr uzioni elementar i. Dobbiamo impar ar e, quindi, afar e il meglio possibile con ciò ci cui disponiamo. Vi sar à utile, a questo pr oposito, disegnar e dei diagr ammi di flussoper i vostr i algor itmi.Inoltr e, r equsitio essenziale, è che ogni istr uzione sia unvicamente inter pr etabile, ossia che non possa esser ciambiguità con qualsiasi altr a istr uzione. Dopotutto, questa condizione viene soddisfatta dallelabor ator e stesso, percome è costr uito. Altr o aspetto impor tante è lor dine: eseguir e pr ima A e poi B o vicever sa è differ ente; molto spessoquesta inver sione può causar e er r or i anche gr avi. Infine, è necessar io che lalgor itmo r estituisca un r isultato in unnumer o finito di passi: e questo è abbastanza ovvio, ma se ne per de di vista limpor tanza, ad esempio, nelle funzionir 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 comeluomo pensa. Ad esempio: dovete scr iver e un pr ogr amma che contr olla se due str inghe sono luna lanagr ammadellaltr 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 imacon le letter e della seconda o vicever sa, ma, domanda classica, "come si tr aduce in codice"? Ossia, dobbiamo domandar cicome 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 unasemplice 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 laltr 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 questultimo 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
  • 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 nellaltr 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. Module Module1 02. 03. Sub Main() 04. Dim S1, S2 As String 05. Dim C1, C2 As Dictionary(Of Char, Int32) 06. Dim Result As Boolean = True 07. 08. Console.Write("Stringa 1: ") 09. S1 = Console.ReadLine 10. Console.Write("Stringa 2: ") 11. S2 = Console.ReadLine 12. 13. If S1.Length = S2.Length Then 14. C1 = New Dictionary(Of Char, Int32) 15. For I As Int16 = 0 To S1.Length - 1 16. If C1.ContainsKey(S1.Chars(I)) Then 17. C1(S1.Chars(I)) += 1 18. Else 19. C1.Add(S1.Chars(I), 1) 20. End If 21. Next 22. 23. C2 = New Dictionary(Of Char, Int32) 24. For I As Int16 = 0 To S2.Length - 1 25. If C2.ContainsKey(S2.Chars(I)) Then 26. C2(S2.Chars(I)) += 1 27. Else 28. C2.Add(S2.Chars(I), 1) 29. End If 30. Next 31. 32. If C1.Keys.Count = C2.Keys.Count Then 33. For Each C As Char In C1.Keys 34. If Not C2.ContainsKey(C) Then 35. Result = False 36. ElseIf C1(C) <> C2(C) Then 37. Result = False 38. End If 39. If Not Result Then 40. Exit For 41. End If 42. Next 43. Else 44. Result = False 45. End If 46. Else 47. Result = False 48. End If 49. 50. If Result Then 51. Console.WriteLine("Sono anagrammi!") 52.
  • Else53. Console.WriteLine("Non sono anagrammi!")54. End If55.56. Console.ReadKey()57. End Sub58. End Module
  • A51. Il miglior codiceIl fine giustifica i mezzi... beh non sempr e. In questo caso mi sto r ifer endo allo stile in cui il codice sor gente vienescr itto: infatti, si può ottener e un r isultato che allocchio dellutente del pr ogr amma sembr a buono, se non ottimo, mache visto da un pr ogr ammator e osser vando il codice non è per niente affidabile. Quando si pubblicano i pr opr ipr 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 netr 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 gentemolto 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 daseguir 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 ebbediventar e una r egola quando scr ivete pr ocedur e funzioni o anche solo pezzi di codice più complessi o cr eati da voi exnovo (il che li r ende sconosciuti agli occhi altr ui). Vi faccio un piccolo esempio: 1. X = Math.Sqrt((1 - (Y ^ 2 / B ^ 2)) * A ^ 2)Questo potr ebbe esser e qualsiasi cosa: non cè alcuna indicazione di cosa le letter e r appr esentino, nè del per chè vengaeffettuata pr opr io quelloper azione. Ripr oviamo in questo modo, con il sor gente commentato, e vediamo se capite acosa la for mula si r ifer isca: 01. Data lequazione di unellisse: 02. x^2 y^2. 03. --- + --- = 1 04. a^2 b^2 05. Ricava x: 06. x^2 / a^2 = 1 - (y^2 / b^2) 07. x^2 = (1 - (y^2 / b^2)) * a^2 08. x = sqrt((1 - (y^2 / b^2)) * a^2) 09. Prende la soluzione positiva: 10. X = Math.Sqrt((1 - (Y ^ 2 / B ^ 2)) * A ^ 2)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 allinter no della w indow s for m, i lor o nomi vengono gener ati automaticamentetr amite un indice, pr eceduto dal nome della classe a cui il contr ollo appar tiene, come, ad esempio, Button1 oTabContr ol2. Se per applicazioni veloci, che devono svolger e pochissime, semplici oper azioni e per le quali basta unafinestr 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. Amio par er e, invece, è necessar io adottar e uno stile ben pr eciso anche per i nomi. Dal canto mio, utilizzo sempr e comenome 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 ibutton, 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 nedescr ivano la funzione. Esemplificando, un pulsante che debba cr ear e un nuovo file si chiamer à btnNew File, una listache debba contener e degli indir izzi di posta sar à lstEmail: questultima notazione è detta "notazione ungher ese". A tal
  • pr oposito, vi voglio sugger ir e questar tico lo e vi invito caldam ente a legger lo.V ariabiliE 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 ealgebr 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 nomisignificativi. 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 tipopiù adatto da utilizzar e, se Integer , Byte, Double, Object o altr i. Per ciò bisogna anche pr evedr e quali sar anno i casi chesi 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 golamobile, ma quali? Più è alta la pr ecisioe da utilizzar e, più vi ser vir à spazio: se dovete r isolver e pr oblemi da liceouser 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 dellesistenza dei tipi Unsigned, che vi per mettono di ottener eunestensione 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 ilr ispettivo distr uttor e se ne hanno uno (Dispose).Il rasoio di Oc c amLa soluzione più semplice è quella esatta. E per questo mi r ifer isco allo scr iver e anche in ter mini di lunghezza dicodice. È inutile scr iver e funzioni lunghissime quando è possibile eguagliar le con pochissime r ighe di codice, piùcompatto, incisivo ed efficace. 01. Public Function Fattoriale(ByVal X as byte) As UInt64 02. If X = 1 Then 03. Return 1 04. Else 05. Dim T As UInt64 = 1 06. For I As Byte = 1 To X 07. T *= I 08. Next 09. Return T 10. End If 11. End Function 12. 13. Diventa: 14. 15. Public Function Fattoriale(ByVal X as byte) As UInt64 16. If X = 1 Then 17. Return 1 18. Else 19. Return X * Fattoriale(X - 1) 20. End If 21. End Function 01. Sub Copia(ByVal Da As String, ByVal A As String) 02. Dim R As <font class="keyword">New</font> IO.SreamReader(Da) 03. Dim W As <font class="keyword">New</font> IO.StreamWriter(A) 04.
  • 05. W.Write(R.ReadToEnd) 06. 07. R.Close() 08. W.Close() 09. End Sub 10. 11. Diventa 12. 13. IO.File.Copy(Da, A) 14. Spesso anche il non conoscere tutte le possibilità 15. si trasforma in uno spreco di tempo e spazioInc apsulamentoLincapsulamento è uno dei tr e fondamentali del par adigma di pr ogr ammazione ad Oggetti (gli altr i due sonopolimor fismo ed er editar ietà, che abbiamo già tr attato). Come r icor der ete, allinizio del cor so, ho scr itto che il vb.netpr esenta tr e aspetti peculiar i e ve li ho spiegati. Tuttavia essi non costituiscono il ver o par adigma di pr ogr ammazionead oggetti e questo mi è stato fatto notar e da Netar r ow , che r ingr azio :P. Tr atter ò quindi, in questo momento talear gomento. Nonostante il nome possa sugger ir e un concetto difficile, non è complicato. Scr iver e un pr ogr amma usandolincapsulamento significa str uttur ar lo in sezioni in modo tale che il cambiamento di una di esse non si r iper cuota sulfunzionamento delle altr e. Facendo lo stesso esempio che por ta Wikipedia, potr este usar e tr e var iabili x , y e z perdeter 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 nellamanier 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, comequelli 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 eil 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 didefinizioni dei tipi di no m enclatur a 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. Cam el 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 lindir izzo di una per sona addr ess (un solo nome). No tazio ne Ung her e se : 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 liniziale 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
  • Membr i pr otected : PascalCampi di enumer ator i : PascalMembr i pr ivati : CamelVar iabili locali : CamelPar ametr i : CamelNomi di contr olli : Ungher eseNomi costituiti da acr onimi: All se di 2 car atter i, altr imenti Pascal
  • B1. IDE: Uno sguardo approfonditoFino ad or a ci siamo ser viti dellambiente di sviluppo integr ato - in br eve, IDE - come di un mer o suppor to per losviluppo di semplici applicazioni console: ne abbiamo fatto uso in quanto dotato di editor di testo "intelligente", uncomodo debugger e un compilator e integr ato nelle funzionalità. E non potr ete negar e che anche solo per questo non neavr emmo potuto far e a meno. Tuttavia, iniziando a sviluppar e applicazioni dotate di GUI (Gr aphical User Inter face) afinestr e, lIDE diventa uno str umento ancor a più impor tante. Le sue numer ose featur es ci per mettono di "disegnar e" lefinestr 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 levar ie par ti dellapplicativo, ecceter a ecceter a... Pr ima di intr odur vi allambito Window s For ms, far ò una r apidapanor amica dellIDE, più appr ofondita di quella pr oposta allinizio.Primo impattoFate click su File > New Pr oject, scegliete "Window s For m Application" e, dopo aver scelto un qualsiasi nome per ilpr 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 dellIDE sar anno molto pr obabilmente diver si da quelliche uso io. Linter faccia dellambiente di sviluppo, comunque, è completamente customizzabile, dato che è anchessastr uttur ata a finestr e: potete aggiunger e, r imuover e, fonder e o divider e finestr e semplicemente tr ascinandole con ilmouse. Ecco un esempio di come manipolar e le par ti dellIDE 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
  • di acceder e ad ogni oper azione possibile allinter no dellIDE. 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 allinter no delleditor 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 lar 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 nellIDE; 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 lambiente di sviluppo, dal color e deltesto nelleditor , agli spazi usati per lindentazione, allautosalvataggio, ecceter a... (non vale la pena di analizzar e tuttele voci disponibili, per chè sono ver amente tr oppe!).
  • Solution ExplorerLa finestr a denominata "Solution Ex plor er " per mette di navigar e allinter no della soluzione cor r ente e veder ne le var iepar ti. Una soluzione è linsieme 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à perlelemento selezionato; il secondo visualizza tutti i files fisicamente esistenti nella car tella della soluzione, il ter zoaggior na il solution ex plor er (nel caso di files aggiunti dallester no dellIDE), mentr e quar to e quinto per mettono dipassar e dal codice al visual designer e vicever sa. Pr emendo il secondo pulsante, potr emo ossevar e che cè molto più diciò che appar e a pr ima vista:
  • La pr ima car tella contiene dei files che vanno a costr uir e uno dei namespace più utili in unapplicazione w indow s, M y,di cui ci occuper emo nella sezione C. La seconda car tella mostr a lelenco di tutti i r ifer imenti inclusi nel pr ogetto: ilnumer o e il tipo di assembly impor tati var ia a seconda della ver sione dellIDE e nella 2008 quelli elencati sono glielementi di default. Per i nostr i pr ogetti, solamente tr e sar anno di vitale impor tanza: System, System.Dr aw ing eSystem.Window s.For ms. Potete r imuover e gli altr i senza pr eoccupazione (questo ci far à r ispar miar e anche unmegabyte di RAM). La car tella bin contiene a sua volta una o due car telle (Debug e Release) in cui tr over ete ilpr ogr amma compilato o in modalità debug o in modalità r elease. obj, invece, è dedicato ai file che contengono il codiceoggetto, una ser ie di bytes molto simili al codice compilato, ma ancor a in attesa di esser e assemblati in un unicoeseguibile.Dopo tutti questi elementi, che per or a ci inter essano poco, tr oviamo la For m1, di default la pr ima finestr adellapplicazione. Possiamo notar e che esiste anche un altr o file, oltr e a For m1.vb (in cui è contenuto il codice chescr iviamo noi): For m1.Designer .vb. Questultimo sor gente è pr odotto automaticamente dal Designer e contieneistr uzioni che ser vono a inizializzar e e costr uir e linter faccia gr afica; in esso sono anche dichiar ati tutti i contr olli cheabbiamo 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 iadi 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, incui il codice "gr afico" viene tenuto nascosto e pr odotto dallIDE 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à dellelemento selezionato nel designer e per mette di modificar le dir ettamentedallambiente di sviluppo. Il box sottostante contiene anche una br eve descr izione della pr opr ietà selezionata.
  • Pr emendo il pulsante con licona 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).
  • B2. Gli EventiCosa sonoOr a che stiamo per entr ar e nel mondo della pr ogr ammazione visuale, è necessar io allontanar si da quello ster eotipo diapplicazione che ho usato fin dallinizio della guida. In questo nuovo contesto, "non esiste" una Sub Main (o, per megliodir e, esiste ma possiede una semplice funzione di inizializzazione): il codice da eseguir e, quindi, non viene posto in unsingolo blocco ed eseguito dallinizio alla fine seguendo un flusso ben definito. Piuttosto, esiste un oggetto standar d, laFor m - nome tecnico della finestr a - che viene cr eato allavvio dellapplicazione e che lutente può veder e e manipolar e asuo piacimento. Lappr occio cambia: il pr ogr ammator e non vincola il flusso di esecuzione, ma dice semplicemente alpr ogr amma "come compor tar si" in r eazione allinput dellutente. Ad esempio, viene pr emuto un cer to pulsante: bene, alclick esegui questo codice; viene inser ito un testo in una casella di testo: quando lutente digita un car atter e, eseguiquestaltr o codice, e così via... Il codice viene scr itto, quindi, per even ti. 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 dipr 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 tipodelegate, tale var iabile può contener e r ifer imenti a uno o più metodi, i quali vengono comunemente chiamati g esto r idev ento (o ev ents handler ). La pr ogr ammazione visuale, in sostanza, r ichiede di scr iver e tanti gestor i deventoquanti sono gli eventi che vogliamo gestir e e, quindi, tanti quanti possono esser e le azioni che il nostr o pr ogr ammaconsente allutente di eseguir e. Mediante queste definizioni, delineamo il compor tamento di tutta lapplicazione.Sintassi e invoc azione degli eventiLe facilitazioni che lIDE mette a disposizione per la scr ittur a dei gestor i devento por tano spesso i pr ogr ammator inovelli a non saper e cosa siano e come funzionino r ealmente gli eventi, anche a causa di una consider evole pr esenza ditutor 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 chequeste scor ciatie fanno più male che bene. Per questo motivo ho deciso di intr odur r e lar gomento quanto pr ima, permetter vi subito al cor r ente di come stanno le cose.Iniziamo con lintr odur r e la sintassi con cui si dichiar a un evento: 1. Event [Nome] As [Tipo]Dove [Nome] è il nome dellevento e [Tipo] il suo tipo. Data la natur a di ciò che staimo definendo, il tipo sar à sempr e untipo delegate. Possiamo sceglier e di utilizzar e un delegate già definito nelle libr er ie standar d del Fr amew or k - come ilclassico EventHandler - oppur e decider e di scr iver ne uno noi al momento. Scegliendo il secondo caso, tuttavia, si devonor 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" (quandonon ci sono r egole da seguir e, ognuno fa come meglio cr ede: vedi, ad esempio, i compilator i C). Ad ogni modo, senderr appr esenta loggetto che ha gener ato levento, mentr e e contiene tutte le infor mazioni r elative alle cir costanze in cui
  • questo evento si è ver ificato. Se e è di tipo EventAr gs, non contiene alcun membr o: il fatto che levento sia statogener ato è di per sé significativo. Ad esempio, per un ipotetico evento Click non avr emmo bisogno di conoscer enessunaltr a infor mazione: ci basta saper e che è stato fatto click col mouse. Invece, per levento KeyDow n (pr essione diun tasto sulla tastier a) sar ebbe inter essante saper e quale tasto è stato pr emuto, il codice associato ad esso edeventualmente il car atter e. Ma or a passiamo a un piccolo esempio sul pr imo caso, mantenendoci ancor a per qualcher iga in una Console Application: 001. Module Module1 002. 003. Questa classe rappresenta una collezione generica di 004. elementi che può essere ordinata con lalgoritmo 005. Bubble Sort già analizzato 006. Public Class BubbleCollection(Of T As IComparable) 007. Eredita tutti i membri pubblici e protected della classe 008. a tipizzazione forte List(Of T), il che consente di 009. disporre di tutti i metodi delle liste scrivendo 010. solo una linea di codice 011. Inherits List(Of T) 012. 013. Questo campo indica il numero di millisecondi impiegati 014. ad ordinare tutta la collezione 015. Private _TimeElapsed As Single = 0 016. 017. Public ReadOnly Property TimeElapsed() As Single 018. Get 019. Return _TimeElapsed 020. End Get 021. End Property 022. 023. Ecco gli eventi: 024. Il primo viene lanciato prima che inizi la procedura di 025. ordinamento, e per tale motivo è di tipo 026. CancelEventHandler. Questo delegate espone come 027. secondo parametro della signature una variabile "e" 028. al cui intero è disponibile una proprietà 029. Cancel che indica se cancellare oppure no loperazione. 030. Se si volesse cancellare loperazione sarebbe possibile 031. farlo nellevento BeforeSorting. 032. In genere, si usa il tipo CancelEventHandler o un suo 033. derivato ogni volta che bisogna gestire un evento 034. che inizia unoperazione annullabile. 035. Event BeforeSorting As System.ComponentModel.CancelEventHandler 036. Il secondo viene lanciato dopo aver terminato la procedura 037. di ordinamento e serve solo a notificare unazione 038. avvenuta. Il tipo è un semplicissimo EventHandler 039. Event AfterSorting As EventHandler 040. 041. Scambia lelemento alla posizione Index con il suo 042. successivo 043. Private Sub SwapInList(ByVal Index As Int32) 044. Dim Temp As T = Me(Index + 1) 045. Me.RemoveAt(Index + 1) 046. Me.Insert(Index, Temp) 047. End Sub 048. 049. In List(Of T) è già presente un metodo Sort, 050. perciò bisogna oscurarlo con Shadows (in quanto non 051. è sovrascrivibile con il polimorfismo) 052. Public Shadows Sub Sort() 053. Dim Occurrences As Int32 054. Dim J As Int32 055. Dim Time As New Stopwatch 056. Attenzione! non bisogna confondere EventHandlers con 057. EventArgs: il primo è un tipo delegate e costituisce 058. il tipo dellevento; il secondo è un normale tipo 059. reference e rappresenta tutti gli argomenti opzionali 060. inerenti alle operazioni svolte 061. Dim e As New System.ComponentModel.CancelEventArgs 062. 063.
  • Viene generato levento. RaiseEvent si occupa di 064. richiamare tutti i gestori devento memorizzati 065. nellevento BeforeSorting (che, ricordo, è un 066. delegate multicast). A tutti i gestori devento 067. vengono passati i parametri Me ed e. Al termine 068. di questa operazione, se un gestore devento ha 069. modificato una qualsiasi proprietà di e (e volendo, 070. anche di questoggetto), possiamo sfruttare tale 071. conoscenza per agire in modi diversi. 072. RaiseEvent BeforeSorting(Me, e) 073. In questo caso, se e.Cancel = True si 074. cancella loperazione 075. If e.Cancel Then 076. Exit Sub 077. End If 078. 079. Time.Start() 080. J = 0 081. Do 082. Occurrences = 0 083. For I As Int32 = 0 To Me.Count - 1 - J 084. If I = Me.Count - 1 Then 085. Continue For 086. End If 087. If Me(I).CompareTo(Me(I + 1)) = 1 Then 088. SwapInList(I) 089. Occurrences += 1 090. End If 091. Next 092. J += 1 093. Loop Until Occurrences = 0 094. Time.Stop() 095. _TimeElapsed = Time.ElapsedMilliseconds 096. 097. Qui genera semplicemente levento 098. RaiseEvent AfterSorting(Me, EventArgs.Empty) 099. End Sub 100. 101. End Class 102. 103. ... 104. 105. End ModuleQuesto codice mostr a anche luso dellistr uzione RaiseEvent, usata per gener ar e un evento. Essa non fa altr o chescor r er e tutta linvocation list di tale evento ed invocar e tutti i gestor i devento ivi contenuti. Le invocazioni sisvolgono, di nor ma, una dopo laltr 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 tantiinfor 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 elunica istanza di BubbleCollection deve esser e dichiar ata in modo diver so dal solito. Se nor malmente potr emmoscr iver e: 1. Dim Bubble As New BubbleCollection(Of Int32)in questo caso, non basta. Per poter usar e gli eventi di un oggetto, è necessar io comunicar lo esplicitamente alcompilator e usando la keyw or d WithEvents, da antepor r e (o sostituir e) a Dim: 1. WithEvents Bubble As New BubbleCollection(Of Int32)Infine, dobbiamo associar e dei gestor i devento ai due eventi che Bubble espone (NB: non è obbligator io associar ehandler a tutti gli eventi di un oggetto, ma basta far lo per quelli che ci inter essano). Per associar e un metodo a unevento e far lo diventar e gestor e di quellevento, si usa la clausola Handles, molto simile come sintassi alla clausolaImplements analizzata nei capitoli sulle inter facce. 1. Sub [Nome Gestore](ByVal sender As Object, ByVal e As [Tipo]) Handles [Oggetto].[Evento] 2. ... 3.
  • End SubI gestor i devento sono sempr e pr ocedur e e mai funzioni: questo è ovvio, poiché eseguono solo istr uzioni e nessunor ichiede alcun valor e in r itor no da lor o. Ecco lesempio completo: 001. Module Module1 002. 003. Public Class BubbleCollection(Of T As IComparable) 004. Inherits List(Of T) 005. 006. Private _TimeElapsed As Single = 0 007. 008. Public ReadOnly Property TimeElapsed() As Single 009. Get 010. Return _TimeElapsed 011. End Get 012. End Property 013. 014. Event BeforeSorting As System.ComponentModel.CancelEventHandler 015. Event AfterSorting As EventHandler 016. 017. Private Sub SwapInList(ByVal Index As Int32) 018. Dim Temp As T = Me(Index + 1) 019. Me.RemoveAt(Index + 1) 020. Me.Insert(Index, Temp) 021. End Sub 022. 023. Public Shadows Sub Sort() 024. Dim Occurrences As Int32 025. Dim J As Int32 026. Dim Time As New Stopwatch 027. Dim e As New System.ComponentModel.CancelEventArgs 028. 029. RaiseEvent BeforeSorting(Me, e) 030. If e.Cancel Then 031. Exit Sub 032. End If 033. 034. Time.Start() 035. J = 0 036. Do 037. Occurrences = 0 038. For I As Int32 = 0 To Me.Count - 1 - J 039. If I = Me.Count - 1 Then 040. Continue For 041. End If 042. If Me(I).CompareTo(Me(I + 1)) = 1 Then 043. SwapInList(I) 044. Occurrences += 1 045. End If 046. Next 047. J += 1 048. Loop Until Occurrences = 0 049. Time.Stop() 050. _TimeElapsed = Time.ElapsedMilliseconds 051. 052. Qui genera semplicemente levento 053. RaiseEvent AfterSorting(Me, EventArgs.Empty) 054. End Sub 055. 056. End Class 057. 058. Bubble è WithEvents poiché ne utilizzeremo 059. gli eventi 060. WithEvents Bubble As New BubbleCollection(Of Int32) 061. Sub Main() 062. Dim I As Int32 063. 064. Console.WriteLine("Inserire degli interi (0 per terminare):") 065. I = Console.ReadLine 066. Do While I <> 0 067.
  • Bubble.Add(I) 068. I = Console.ReadLine 069. Loop 070. 071. Il corpo di Main termina con lesecuzione di Sort, ma 072. il programma non finisce qui, poiché Sort 073. scatena due eventi, BeforeSorting e AfterSorting. 074. Questi comportano lesecuzione prima del metodo 075. Bubble_BeforeSorting e poi di Bubble_AfterSorting. 076. Vedrete bene il risultato eseguendo il programma 077. Bubble.Sort() 078. End Sub 079. 080. Private Sub Bubble_BeforeSorting(ByVal sender As Object, ByVal e As System.ComponentModel.CancelEventArgs) Handles Bubble.BeforeSorting 081. If Bubble.Count = 0 Then 082. e.Cancel = True 083. Console.WriteLine("Lista vuota!") 084. Console.ReadKey() 085. End If 086. End Sub 087. 088. Private Sub Bubble_AfterSorting(ByVal sender As Object, ByVal e As EventArgs) Handles Bubble.AfterSorting 089. Console.WriteLine("Lista ordinata:") 090. Scrive a schermo tutti gli elementi di Bubble 091. mediante un delegate generico. 092. Bubble.ForEach(AddressOf Console.WriteLine) 093. Console.WriteLine("Tempo impiegato: {0} ms", Bubble.TimeElapsed) 094. Console.ReadKey() 095. End Sub 096. 097. Handles significa "gestisce". In questo come in molti altri 098. casi, il codice è molto simile al linguaggio. 099. Ad esempio, traducendo in italiano si avrebbe: 100. Bubble_AfterSorting gestisce Bubble.AfterSorting 101. Il VB è molto chiaro nelle keywords 102. End ModuleAnche per i nomi dei gestor i devento cè questa convenzione: "[Oggetto che gener a levento]_[Evento gestito]".Ciò che abbiamo appena visto consente di eseguir e una sor ta di ear ly binding, ossia legar e levento a un gestor edur ante la scr ittur a del codice. Cè, par imenti, una tecnica par allela più simile al late binding, che consente di associar eun gestor e ad un evento dinamicamente. La sintassi è: 1. Add Handler = Aggiungi Gestore; molto intuitiva come keyword 2. AddHandler [Oggetto].[Evento], AddressOf [Gestore] 3. E per rimuovere il gestore dallinvocation list: 4. RemoveHandler [Oggetto].[Evento], AddressOf [Gestore]Il codice sopr a potr ebbe esser e stato modificato come segue: 01. Module Module1 02. 03. ... 04. 05. WithEvents Bubble As New BubbleCollection(Of Int32) 06. Sub Main() 07. Dim I As Int32 08. 09. AddHandler Bubble.BeforeSorting, AddressOf Bubble_BeforeSorting 10. AddHandler Bubble.AfterSorting, AddressOf Bubble_AfterSorting 11. 12. Console.WriteLine("Inserire degli interi (0 per terminare):") 13. I = Console.ReadLine 14. Do While I <> 0 15. Bubble.Add(I) 16. I = Console.ReadLine 17. Loop 18. 19. Il corpo di Main termina con lesecuzione di Sort, ma 20.
  • il programma non finisce qui, poiché Sort 21. scatena due eventi, BeforeSorting e AfterSorting. 22. Questi comportano lesecuzione prima del metodo 23. Bubble_BeforeSorting e poi di Bubble_AfterSorting. 24. Vedrete bene il risultato eseguendo il programma 25. Bubble.Sort() 26. End Sub 27. 28. Private Sub Bubble_BeforeSorting(ByVal sender As Object, ByVal e As System.ComponentModel.CancelEventArgs) 29. If Bubble.Count = 0 Then 30. e.Cancel = True 31. Console.WriteLine("Lista vuota!") 32. Console.ReadKey() 33. End If 34. End Sub 35. 36. Private Sub Bubble_AfterSorting(ByVal sender As Object, ByVal e As EventArgs) 37. Console.WriteLine("Lista ordinata:") 38. Bubble.ForEach(AddressOf Console.WriteLine) 39. Console.WriteLine("Tempo impiegato: {0} ms", Bubble.TimeElapsed) 40. Console.ReadKey() 41. End Sub 42. End ModuleOvviamente se usate questo metodo, non potr ete usar e allo stesso tempo anche Handles, o aggiunger este due volte lostesso gestor e!
  • B3. I ControlliLa base delle applic azioni W indow s FormSe gli eventi sono il pr incipale meccanismo con cui scr iver e unapplicazione visuale, i contr olli sono i pr incipali oggettida usar e. For malmente, un contr ollo non è altr o che una classe der ivata da System.Window s.For ms.Contr ol. In pr atica,esso r appr esenta un qualsiasi componente dellinter 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 UserInter face) per il vostr o applicativo, dovr ete necessar iamente conoscer e quali contr olli le libr er ie standar d vi mettono adisposizione (e questo avviene in tutti i linguaggi che suppor tino libr er ie visuali). Conoscer e un contr ollo significapr incipalmente saper e quali pr opr ietà, metodi ed eventi esso possiede e come usar li.Una volta aper to il pr ogetto Window s For m, tr over ete che lIDE ha cr eato per noi la pr ima For m, ossia la pr imafinestr a dellapplicazione. Essa sar à la pr ima ad esser e aper ta quando il pr ogr amma ver r à fatto cor r er e e, per ipr ossimi capitoli, sar à anche lunica che user emo. Lesecuzione ter mina automaticamente quando tale finestr a vienechiusa. Come avr ete visto, inoltr e, tr a le mer avigliose funzionalità del nostr o ambiente di sviluppo cè anche unar eagr afica - detta Designer - che ci per mette di veder e unantepr ima della For m e di modificar la o aggiunger ci nuovielementi. Per modificar e laspetto o il compor tamento della For m, è sufficiente modificar e le r elative pr opr ietà nellafinestr 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 dallatoolbox 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 dellIDE: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 epr 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: Allow Dr 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 : lelenco dei contr olli contenuti allinter 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 unoper azione di Dr ag and Dr op da questo contr ollo;
  • 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 lutente 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 lindice 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. Liter 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 nessunaltr 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 unaclasse 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 alcunimembr i che potr ebber o inter essar vi ad or a: Allow Tr 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, lapplicazione 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 licona mostr ata nellangolo 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 licona 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 lopacità della finestr a: 0 per r ender la invisibile, 1 per r ender la totalmente opaca (nor male);
  • Show () : visualizza la for m nel caso sia nascosta o comunque non attualmente visibile sullo scher mo; Show Dialog() : come Show (), ma la finestr a viene mostr ata in modalità Dialog. In questo modo, lutente può inter agir e solo con essa e con nessunaltr a for m del pr ogr amma fino a che questa non sia stata chiusa, confer mando una scelta o annullando loper azione. Restituisce come r isultato un valor e enumer ato che indica che azione lutente abbia compiuto; Show Icon : deter mina se visualizzar e licona 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; Window State : 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 annodi modificar e laspetto ed il compor tamento della for m, in quanto, allo stato attuale delle cose, non siete in gr ado digestir e e compr ender e il r esto delle funzionalità. Nel cor so di questa sezione, comunque, intr odur r ò via via nuovidettagli 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 c ontrollo 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 epiù usato meccanismo di inter azione: il pulsante. Esso viene r appr esentato dal contr ollo Button. Dopo aver aper to unnuovo pr ogetto Window s For m vuoto, tr ascinate un nuovo pulsante dalla toolbox sulla super ficie della finestr a eposizionatelo dove più vi aggr ada. Il nome di questo contr ollo sar à btnHello, ad esempio.Or a che abbiamo disposto lunico elemento della GUI, bisogna cr ear e un gestor e devento che si occupi di eseguir e delcodice quando lutente clicca sul pulsante. Per far e ciò, possiamo scr iver e il codice a mano o semplicemente far e doppioclick sul pulsante nel Designer e lIDE scr iver à automaticamente il codice associato. Questo succede per chè ogni contr olloha un "evento di default", ossia quellevento che viene usato più spesso: il doppio click su un elemento dellinter facciagr afica ci per mette di delegar e allambiente di sviluppo la stesur a del pr ototipo per la Sub che dovr emo cr ear e per taleevento. Nel caso di Button, levento più usato è Click. Il codice automaticamente gener ato sar à: 1. Private Sub btnHello_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnHello.Click 2. 3. End SubOr a, allinter no del cor po della pr ocedur a possiamo por r e ciò che vogliamo. In questo esempio, visualizzer emo ascher mo il messaggio "Hello, Wor ld!", ma in modo diver so dalle applicazioni console. In questo ambiente, si è soliti usar euna par ticolar e classe che ser ve per visualizzar e finestr e di avver timento. Tale classe è MessageBox e ha un solometodo statico, Show : 1. Public Class Form1 2. 3. Private Sub btnHello_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnHello.Click 4. MessageBox.Show("Hello, World!", "Esempio", MessageBoxButtons.OK, MessageBoxIcon.Information) 5. End Sub 6. 7. End ClassShow 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 amiteover loading). In questo caso, il secondo indica il titolo della finestr a di avviso, il ter zo i pulsanti visualizzati (un solopulsante "OK") ed il quar to licona mostr ata in fianco al messaggio (una "I" bianca su sfondo blu, che significa"Infor mazione").
  • B4. Label e TextBoxIn questo capitolo mi occuper ò di altr i due comunissimi contr olli: label (etichetta) e tex tbox (casella di testo). Lesempiodella lezione consiste nello scr iver e un pr ogr amma che, dato il r aggio, calcola lar ea del cer chio.LabelIl contr ollo Label ser ve per visualizzar e un qualsiasi messaggio o testo sulla super ficie della w indow s for m. Per questopr ogetto, occor r e aggiunger e una label allinter no del for m designer e impostar e il testo su "Intr odur r e il r aggio di uncer 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 lesecuzione del pr ogr amma. Solo due pr opr ietà mer itano di esser emenzionate: 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). Lallineamento non è effettivo se AutoSize = Tr ue.Dopo aver modificato le pr opr ietà della for m come nella lezione scor sa, linter 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 allutente diimmetter 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" allutente cosa digitar e nel caso le pr ime letter e pr emute cor r ispondano allinizio 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. Lenumer 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 (ununione di entr ambe le pr ecedenti opzioni); AutoCompleteSour ce : fonte dalla quale pr elevar e le par ole dellautocompletamento. I valor i pr edefiniti indicano
  • 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 limmissione 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 Low er (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 lutente 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; Passw or 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 passw or 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 lutente 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. Lenumer 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 c alc oloUltima cosa essenziale per concluder e il pr ogr amma è un pulsante che avvii il calcolo, altr imenti non si potr ebbe saper equando lutente ha finito limmissione e vuole conoscer e il r isultato. Dopo aver aggiunto il button btnAr ea, la finestr asar à simile a questa:Doppio click sul pulsante per apr ir e leditor di codice sullevento Click di btnAr ea: 01. Public Class Form1 02. 03. Private Sub btnArea_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnArea.Click 04. Dim Radius As Single = txtRadius.Text 05. Dim Area As Single 06. Area = Radius ^ 2 * Math.PI 07. MessageBox.Show("Larea del cerchio è " & Area & ".", Me.Text, MessageBoxButtons.OK, MessageBoxIcon.Information) 08. End Sub 09. 10. End ClassPr 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 implicitevengono eseguite automaticamente quando sono possibili e Option Str ict è disattivata.
  • Tuttavia, se lutente 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 leccezione gener ata. 01. Public Class Form1 02. 03. Private Sub btnArea_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnArea.Click 04. Try 05. Dim Radius As Single = txtRadius.Text 06. Dim Area As Single 07. Area = Radius ^ 2 * Math.PI 08. MessageBox.Show("Larea del cerchio è " & Area & ".", Me.Text, MessageBoxButtons.OK, MessageBoxIcon.Information) 09. Catch ICE As InvalidCastException 10. MessageBox.Show("Inserire un valore numerico valido!", Me.Text, MessageBoxButtons.OK, MessageBoxIcon.Error) 11. End Try 12. End Sub 13. 14. End ClassMa 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. Public Class Form1 02. 03. Private Sub btnArea_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnArea.Click 04. Try 05. Dim Radius As Single = txtRadius.Text 06. 07. If Radius <= 0 Then 08. Throw New ArgumentException() 09. End If 10. 11. Dim Area As Single 12. Area = Radius ^ 2 * Math.PI 13. MessageBox.Show("Larea del cerchio è " & Area & ".", Me.Text, MessageBoxButtons.OK, MessageBoxIcon.Information) 14. Catch ICE As InvalidCastException 15. MessageBox.Show("Inserire un valore numerico valido!", Me.Text, MessageBoxButtons.OK, MessageBoxIcon.Error) 16. Catch AE As ArgumentException 17. MessageBox.Show("Il raggio non può essere negativo o nullo!", Me.Text, MessageBoxButtons.OK, MessageBoxIcon.Exclamation) 18. End Try 19. End Sub 20. 21. End Class
  • B5. Input e Output su fileGli 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 didati. In .NET, tale flusso viene r appr esentato da una classe astr atta, System.IO.Str eam, che espone alcuni metodi peracceder e e manipolar e i dati ivi contenuti. Dato che si tr atta di una classe astr atta, non possiamo utilizzar ladir ettamente, poiché, appunto, r appr esenta un concetto astr atto non istanziabile. Come già spiegato nel capitolor 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 Netw or 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 dispositivovir tuale o fisico o da qualunque entità astr atta allinter no della macchina: ad esempio è possibile aver e uno str eamassociato 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 à unoppor tuna classe der ivata di Str eam studiata per adempier e a quello specificocompito.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 laccesso 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. Questultimo par ametr o è ditipo 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 aggiun gere 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 uneccezione; Open : apr e il file e si posiziona allinizio; OpenOr Cr eate : apr e il file, se esiste, e si posiziona allinizio; se non esiste, cr ea il file; Tr uncate : apr e il file, cancella tutto il suo contenuto, e si posiziona allinizio.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 louser 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. Questacondizione 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: inquesto 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
  • 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 eun buffer, ossia un ar r ay di bytes che conter r à tempor aneamente i dati letti o scr itti. Tutti i metodi dilettur a/scr ittur a binar i del Fr amew or 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 loper 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 dallar r ay buffer (a par tir e da index ). Ecco un esempio: 01. Module Module1 02. 03. Sub Main() 04. Dim File As IO.FileStream 05. Dim FileName As String 06. 07. Console.WriteLine("Inserire il percorso di un file:") 08. FileName = Console.ReadLine 09. 10. IO.File.Exists(path) restituisce True se il percorso 11. path indica un file esistente e False in caso contrario 12. If Not IO.File.Exists(FileName) Then 13. Console.WriteLine("Questo file non esiste!") 14. Console.ReadKey() 15. Exit Sub 16. End If 17. 18. Console.Clear() 19. 20. Apre il file specificato, posizionandosi allinizio 21. File = New IO.FileStream(FileName, IO.FileMode.Open) 22. 23. Dim Buffer() As Byte 24. Dim Number, ReadBytes As Int32 25. 26. Chiede allutente quanti bytes vuole leggere, e 27. memorizza tale numero in Number 28. Console.WriteLine("Quanti bytes leggere?") 29. Number = CType(Console.ReadLine, Int32) 30. Se Number è un numero positivo e non siamo ancora 31. arrivati alla fine del file, allora legge quei bytes. 32. La proprietà Position restituisce la posizione 33. corrente allinterno del file (a iniziare da 0), mentre 34. File.Length restituisce la lunghezza del file, in bytes. 35. Do While (Number > 0) And (File.Position < File.Length - 1) 36. Ridimensiona il buffer 37. ReDim Buffer(Number - 1) 38. Legge Number bytes e li mette in Buffer, a partire 39. dallinizio dellarray. Read è una funzione, e 40. restituisce come risultato il numero di bytes 41. effettivamente letti dallo stream. 42. ReadBytes = File.Read(Buffer, 0, Number) 43. 44. Console.WriteLine("Bytes letti:") 45. For I As Int32 = 0 To ReadBytes - 1 46. Console.Write("{0:000} ", Buffer(I)) 47.
  • Next 48. Console.WriteLine() 49. 50. Se abbiamo letto tanti bytes quanti ne erano stati 51. chiesti, allora non siamo ancora arrivati alla 52. fine del file. Richiede allutente un numero 53. If ReadBytes = Number Then 54. Console.WriteLine("Quanti bytes leggere?") 55. Number = CType(Console.ReadLine, Int32) 56. End If 57. Loop 58. 59. Controlla se si è raggiunta la fine del file. 60. Infatti, il ciclo potrebbe terminare anche se lutente 61. immettesse 0. 62. If File.Position >= File.Length - 1 Then 63. Console.WriteLine("Raggiunta fine del file!") 64. End If 65. 66. Chiude il file 67. File.Close() 68. 69. Console.ReadKey() 70. End Sub 71. 72. End ModuleBisogna 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 ancheStr eam, implementa linter faccia IDisposable e il metodo Close non è altr o che un modo indir etto per r ichiamar eDispose (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 ilNetw or kStr eam (che analizzer emo nella sezione dedicata al Web) associato alle r ichieste http, il quale non suppor ta leoper 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 perspostar si velocemente da una par te allaltr 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 iginche può assumer e tr e valor i: Begin (si r ifer isce allinizio 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: 1. Si sposta alla posizione 100 2. File.Seek(100, IO.SeekOrigin.Begin) 3. Si sposta di 250 bytes indietro rispetto alla posizione corrente 4. File.Seek(-250, IO.SeekOrigin.Current) 5. Si sposta a 100 bytes dalla fine del file 6. File.Seek(-100, IO.SeekOrigin.End)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: ifile testuali.Lettura/sc rittura di file testualiI file testuali sono così denominati per chè contengono solo testo, ossia bytes codifcabili in una delle codifiche standar ddei car atter i (ASCII, UTF-8, ecceter a...). Alcuni par ticolar i bytes vengono intepr etati in modi diver si, come ad esempiola tabulazione, che viene r appr esentata con uno spazio più lungo; altr i vengono tr alasciati nella visualizzazione esembr ano non esister e, ad esempio il NULL ter minator , che r appr esenta la fine di una str inga, oppur e lEOF (End OfFile); 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
  • 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 quellibinar i è il fatto di non poter legger e i singoli bytes per chè non ce nè necessità: quello che impor ta è linfor mazione cheil testo por ta al suo inter no. La classe usata per la lettur a è Str eamReader , mentr e quella per la scr ittur aStr eamWr iter : il costr uttor e di entr ambi accetta un unico par ametr o, ossia il per cor so del file in questione; esistonoanche altr i over loads dei costr uttor i, ma il più usato e quindi il più impor tante di tutti è quello appena citato. Ecco unpiccolo esempio di come utilizzar e tali classi in una semplice applicazione console: 01. Module Module1 02. Sub Main() 03. Dim File As String 04. Dim Mode As Char 05. 06. Console.WriteLine("Premere R per leggere un file, W per scriverne uno.") 07. Console.ReadKey restituisce un oggetto ConsoleKeyInfo, 08. al cui interno ci sono tre proprietà: Key, 09. enumeratore che definisce il codice del pulsante premuto; 10. KeyChar, il carattere corrispondente a quel pulsante; 11. Modifier, enumeratore che definisce i modificatori attivi, 12. ossia Ctrl, Shift e Alt. 13. Quello che serve ora è solo KeyChar 14. Mode = Console.ReadKey.KeyChar 15. Dato che potrebbe essere attivo il Bloc Num, ci si 16. assicura che Mode contenga un carattere maiuscolo 17. con la funzione statica ToUpper del tipo base Char 18. Mode = Char.ToUpper(Mode) 19. Pulisce lo schermo 20. Console.Clear() 21. 22. Select Case Mode 23. Case "R" 24. Console.WriteLine("Inserire il percorso del file da leggere:") 25. File = Console.ReadLine 26. 27. Cosntrolla che il file esista 28. If Not IO.File.Exists(File) Then 29. Se non esiste, visualizza un messggio ed esce 30. Console.WriteLine("Il file specificato non esiste!") 31. Console.ReadKey() 32. Exit Sub 33. End If 34. 35. Dim Reader As New IO.StreamReader(File) 36. 37. Legge ogni singola riga del file, fintanto che non 38. si è raggiunta la fine 39. Do While Not Reader.EndOfStream 40. Come Console.Readline, la funzione distanza 41. ReadLine restituisce una linea di testo 42. dal file 43. Console.WriteLine(Reader.ReadLine) 44. Loop 45. 46. Quindi chiude il file 47. Reader.Close() 48. Case "W" 49. Console.WriteLine("Inserire il percorso del file da creare:") 50. File = Console.ReadLine 51. 52. Dim Writer As New IO.StreamWriter(File) 53. Dim Line As String 54. 55. Console.WriteLine("Immettere il testo del file, " & _ 56. "premere due volte invio per terminare") 57. Fa immettere righe di testo fino a quando 58. si termina 59. Do 60. Line = Console.ReadLine 61. Come Console.WriteLine, la funzione distanza 62. WriteLine scrive una linea di testo sul file 63.
  • Writer.WriteLine(Line) 64. Loop While Line <> "" 65. 66. Chiude il file 67. Writer.Close() 68. Case Else 69. Console.WriteLine("Comando non valido!") 70. End Select 71. 72. Console.ReadKey() 73. End Sub 74. End ModuleOvviamente esistono anche i metodi Read e Wr ite, che scr ivono del testo senza mandar e a capo: inoltr e, Wr ite eWr 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 diConsole: questo per chè anche la console è uno str eam, capace di input e output allo stesso tempo. Per color o chepr ovengono dal C non sar à difficile r ichiamar e questo concetto.Lettura/sc rittura di file binariCome già accennato nel par agr afo pr ecedente, la distinzione tr a file binar i e testuali avviene tr amite linter pr etazionedei singoli bytes. Con questo tipo di file, cè una cor r ispondenza biunivoca tr a i bytes del file e i dati letti dalpr ogr amma: infatti, non a caso, lI/O viene gestito attr aver so un ar r ay di byte. Binar yWr iter e Binar yReaderespongono, 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, allinizio 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 manipolabiledallelabor ator e: mi r ifer isco, ad esempio, alla notazione in complemento a 2 per gli inter i e al for mato in vir golamobile per i r eali. Potete documentar vi su queste modalità di r appr esentazione dellinfor mazione altr ove: in questomomento 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ò checapisce 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 cuilavor ar e. Ecco un esempio: 01. Module Module1 02. 03. Sub Main() 04. Apre il file "prova.dat", creandolo o sovrascrivendolo 05. Dim File As New IO.FileStream("prova.dat", IO.FileMode.Create) 06. Writer è lo strumento che ci permette di scrivere 07. sullo stream File con codifica binaria 08. Dim Writer As New IO.BinaryWriter(File) 09. Dim Number As Int32 10. 11. Console.WriteLine("Inserisci 10 numeri da scrivere sul file:") 12. For I As Int32 = 1 To 10 13. Console.Write("{0}: ", I) 14. Number = CType(Console.ReadLine, Int32) 15. Writer.Write(Number) 16. Next 17. Writer.Close() 18. 19. Console.ReadKey() 20. End Sub 21. 22. End ModuleIo 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
  • 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 00 14 00 00 00 40 lf 00 00 39 4a 00 00 a7 fe ff ff 5a 00 00 00 16 00 00 00Ogni 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 conSingle, 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 moltifamosi for mati di file, come mp3, w av (vedi sezione FFS), zip, mpg, ecceter a...Esempio: steganografia su immaginiLa steganogr afia è lar te di nasconder e del testo allinter no di unimmagine. Per i più cur iosi, mi avventur er ò nellascr 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 linter faccia gr afica, con questi contr olli: Una Label, Label1, Tex t = "Intr odur r e il per cor so di unimmagine:" 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 lindtr 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. Public Class Form1 02. 03. Private Sub btnHide_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnHide.Click 04. If Not IO.File.Exists(txtPath.Text) Then 05. MessageBox.Show("File inesistente!", Me.Text, MessageBoxButtons.OK, MessageBoxIcon.Error) 06. Exit Sub 07. End If 08. 09. If IO.Path.GetExtension(txtPath.Text) <> ".jpg" Then 10. MessageBox.Show("Il file deve essere in formato JPEG!", Me.Text, MessageBoxButtons.OK, MessageBoxIcon.Exclamation) 11.
  • Exit Sub 12. End If 13. 14. Dim File As New IO.FileStream(txtPath.Text, IO.FileMode.Open) 15. Converte il testo digitato in una sequenza di bytes, 16. secondo gli standard della codifica UTF8 17. Dim TextBytes() As Byte = _ 18. System.Text.Encoding.UTF8.GetBytes(txtText.Text) 19. 20. Va alla fine del file 21. File.Seek(0, IO.SeekOrigin.End) 22. Scrive i bytes 23. File.Write(TextBytes, 0, TextBytes.Length) 24. File.Close() 25. 26. MessageBox.Show("Testo nascosto con successo!", Me.Text, MessageBoxButtons.OK, MessageBoxIcon.Information) 27. End Sub 28. 29. Private Sub btnRead_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnRead.Click 30. If Not IO.File.Exists(txtPath.Text) Then 31. MessageBox.Show("File inesistente!", Me.Text, MessageBoxButtons.OK, MessageBoxIcon.Error) 32. Exit Sub 33. End If 34. 35. If IO.Path.GetExtension(txtPath.Text) <> ".jpg" Then 36. MessageBox.Show("Il file deve essere in formato JPEG!", Me.Text, MessageBoxButtons.OK, MessageBoxIcon.Exclamation) 37. Exit Sub 38. End If 39. 40. Dim File As New IO.FileStream(txtPath.Text, IO.FileMode.Open) 41. Dim TextBytes() As Byte 42. Dim B1, B2 As Byte 43. 44. Legge un byte 45. B1 = File.ReadByte() 46. Do 47. Legge un altro byte 48. B2 = File.ReadByte() 49. Se i bytes formano la sequenza FF D9, si ferma. 50. In Visual Basic, in numeri esadecimali si scrivono 51. facendoli precedere da "&H" 52. If B1 = &HFF And B2 = &HD9 Then 53. Exit Do 54. End If 55. Passa il valore di B2 in B1 56. B1 = B2 57. Loop While (File.Position < File.Length - 1) 58. 59. ReDim TextBytes(File.Length - File.Position - 1) 60. Legge ciò che rimane dopo FF D9 61. File.Read(TextBytes, 0, TextBytes.Length) 62. File.Close() 63. 64. txtText.Text = System.Text.Encoding.UTF8.GetString(TextBytes) 65. End Sub 66. End ClassIl testo accodato può esser e r ilevato facilmente con un Hex Editor , per questo lo si dovr ebbe cr iptar e con unapassw or d: per ulter ior i infor mazioni sulla cr iptazione in .NET, veder e capitolo r ekativo.
  • B6. ListBox e ComboBoxQuesti contr olli sono liste con stile visuale pr opr io in gr ado di contener e elementi. La gestione di tali elementi è moltosimile a quella delle List gener ic o degli Ar r ayList. Lunica differ enza sta nel fatto che in questo caso, tutte le modifichevengono r ese visibili sullinter faccia e influiscono, quindi, su ciò che lutente può veder e. Una volta aggiunte allaw indow s for m, il lor o aspetto sar à simile a questo: ListBo x Co m bo BoxLe 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 laltezza 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:
  • ListBo x M ultiCo lum n Scr ollAlw aysVisible : 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 nellar 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 opDow nHeight : deter mina laltezza, in pix el, del menù a discesa Dr opDow nStyle : 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 opDow n (stile nor male come nellimmagine di esempio pr oposta a inizio capitolo, ma è possibile modificar e il testo dellelemento selezionato scr ivendo entr o la casella), Dr opDow nList (stile nor male, non è possibile modificar e lelemento selezionato in alcun modo, se non selezionandone un altr o). Questa è unimmagine di una combobox con Dr opDow nStyle = Simple: Co m bo Box Sim ple Dr o pDo w n 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 opDow nItems : 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 opDow nStyle non è impostata su Dr opDow nList, poichè tale stile impedisce di modificar e il contenuto della combobox tr amite tastier a, come già dettoPer entr ambe le liste: Dr aw Mode : deter mina la modalità con cui ogni elemento viene disegnato. Può assumer e tr e valor i: Nor mal, Ow ner Dr aw Fix ed e Ow ner 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 nellevento Dr aw Item. Per ulter ior i infor mazioni su questo pr ocedimento, veder e lar ticolo Fo nt e
  • 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 co n Integ r alHe ig 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 alfabeticamenteDetto ciò, è possibile pr oceder e con un semplice esempio. Il pr ogr amma che segue per mette di aggiunger e unqualsiasi testo ad una lista. Pr ima di iniziar e a scr iver e il codice, bisogna includer e nella w indow s for m questicontr 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. Public Class Form1 02. Private Sub cmdAdd_Click(ByVal sender As Object, _ 03. ByVal e As EventArgs) Handles cmdAdd.Click 04. Dim S As String 05. 06. Inputbox( 07. ByVal Prompt As Object, 08. ByVal Title As String, 09. ByVal DefaultResponse As String) 10. Visualizza una finestra con una label esplicativa 11. il cui testo è racchiuso in Prompt, con un titolo 12. Title e una textbox con un testo di default 13. DeafultResponse: una volta che lutente ha inserito 14. la stringa nella textbox e cliccato OK, la funzione 15. restituisce la stringa immessa 16. S = InputBox("Inserisci una stringa:", "Inserimento stringa", _ 17. "[Stringa]") 18. 19. Aggiunge la stringa alla lista 20. lstItems.Items.Add(S) 21. End Sub 22. 23.
  • Private Sub cmdRemove_Click(ByVal sender As Object, _ 24. ByVal e As EventArgs) Handles cmdRemove.Click 25. Se è selezionato un elemento... 26. If lstItems.SelectedIndex >= 0 Then 27. Lo elimina 28. lstItems.Items.RemoveAt(lstItems.SelectedIndex) 29. End If 30. End Sub 31. End ClassNon solo stringheNellesempio pr ecedente, ho mostr ato che è possibile aggiunger e agli elementi della listbox delle str inghe, edesse ver r anno visualizzate come testo sullinter faccia del contr ollo. Tuttavia, la pr opr ietà Items è di tipoObjectCollection, quindi può contener e un qualsiasi tipo di oggetto e non necessar iamente solo str inghe. Quelloche ci pr eoccupa, in questo caso, è ciò che viene mostr ato allutente qualor a noi inser issimo un oggetto nellalistbox : quale testo sar à visualizzato per lelemento? Ecco un esempio (un for m con una listbox e un pulsante): 01. Class Form1 02. 03. Class Item 04. Private Shared IDCounter As Int32 = 0 05. 06. Private _ID As Int32 07. Private _Description As String 08. 09. Public ReadOnly Property ID() As Int32 10. Get 11. Return _ID 12. End Get 13. End Property 14. 15. Public Property Description() As String 16. Get 17. Return _Description 18. End Get 19. Set(ByVal value As String) 20. _Description = value 21. End Set 22. End Property 23. 24. Sub New() 25. _ID = IDCounter 26. IDCounter += 1 27. End Sub 28. 29. End Class 30. 31. Private Sub btnDoSomething_Click(ByVal sender As Object, ByVal e As EventArgs) Handles btnDoSomething.Click 32. lstItems.Items.Add(New Item() With {.Description = "Asus Eee PC 900"}) 33. lstItems.Items.Add(New Item() With {.Description = "Hp Pavillion Dv6000"}) 34. End Sub 35. 36. End ClassUna volta pr emuto btnDoSomething, nella lista ver r anno aggiunti due oggetti, tuttavia la GUI della listboxvisualizzer à questi due elementi: WindowsApplication4.Form1+Item WindowsApplication4.Form1+ItemQuesto nel mio caso, poiché il pr ogetto (e quindi il namespace pr incipale) si chiama Window sApplication4. Da ciòsi può capir e che, in assenza daltr o, la listbox tenta di conver tir e loggetto in una str inga, ossia un dato
  • intellegibile alluomo: lunico modo per poter avviar e questa conver sione consiste nellutilizzar e il metodoToStr ing, il quale, tuttavia, non è stato r idefinito dalla classe Item e pr ovoca luso del suo omonimo der ivantedalla classe base Object. Questultimo, infatti, r estituisce il tipo delloggetto, che in questo caso è pr opr ioWindow sApplication4.For m+Item. Per modificar e il compor tamento del contr ollo, dobbiamo aggiunger e allaclasse un metodo ToStr ing, ad esempio così: 1. Class Item 2. ... 3. 4. Public Overrides Function ToString() As String 5. Return Me.Description 6. End Function 7. End ClassAvviando di nuovo lapplicazione, gli elementi visualizzati sulla lista sar anno: Asus Eee PC 900 Hp Pavillion Dv6000Esiste, 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 ilcodice della classe di cui stiamo usando istanze: ad esempio per chè si tr atta di una classe definita in un assemblydiver so. Possiamo specificar e nella pr opr ietà ListBox .DisplayMember il nome della pr opr ietà che deve ser vir e avisualizzar e lelemento nella lista. Nel nostr o caso, vogliamo visualizzar e la descr izione, quindi user emo questocodice: 1. lstItems.DisplayMember = "Description" 2. lstItems.Items.Add(New Item() With {.Description = "Asus Eee PC 900"}) 3. lstItems.Items.Add(New Item() With {.Description = "Hp Pavillion Dv6000"})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à delloggettodeve esser e r estituita quando si r ichiede il valor e di un elemento selezionato mediante la pr opr ietàSelectedValue.
  • B7. CheckBox e RadioButtonMentr e ListBox e ComboBox mir avano a r ender e visuale un insieme di elementi, questi due contr olli r appr esentanouna valor e Booleano: infatti possono esser e spuntati oppur e no.Chec kBoxLa 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, lunico 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 dellimmagine 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 iEcco 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 diquesto 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 aiutamoltissimo nel caso si debbano pr opor r e allutente 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 illegame Xor tr a più condizioni.
  • GroupBoxPar lando di contenitor i, non si può non far e menzione al Gr oupBox . T