Anatomia di un metodo
Il Framework .NET mette a disposizione dello sviluppatore un enorme numero di classi contenenti metodi davvero utili, già scritti e pronti all'uso, ma solo in pochi casi questi bastano a creare un'applicazione ben strutturata ed elegante. Per questo motivo, è possibile creare nuovi metodi - procedure o funzioni che siano - ed usarli comodamente nel programma. Per lo più, si crea un metodo per separare logicamente una certa parte di codice dal resto del sorgente: questo serve in primis a rendere il listato più leggibile, più consultabile e meno prolisso, ed inoltre ha la funzione di racchiudere sotto un unico nome (il nome del metodo) una serie più o meno grande di istruzioni.Un metodo è costituito essenzialmente da tre parti:
- Nome : un identificatore che si può usare in altre parti del programma per invocare il metodo, ossia per eseguire le istruzioni di cui esso consta;
- Elenco dei parametri : un elenco di variabili attraverso i quali il metodo può scambiare dati con il programma;
- Corpo : contiene il codice effettivo associato al metodo, quindi tutte le istruzioni e le operazioni che esso deve eseguire
Procedure senza parametri
Il caso più semplice di metodo consiste in una procedura senza parametri: essa costituisce, grosso modo, un sottoprogramma a sè stante, che può essere richiamato semplicemente scrivendone il nome. La sua sintassi è molto semplice:Sub [nome]() 'istruzioni End SubCredo che vi sia subito balzato agli occhi che questo è esattamente lo stesso modo in cui viene dichiarata la Sub Main: pertanto, ora posso dirlo, Main è un metodo e, nella maggior parte dei casi, una procedura senza parametri (ma si tratta solo di un caso particolare, come vedremo fra poco). Quando il programma inizia, Main è il primo metodo eseguito: al suo interno, ossia nel suo corpo, risiede il codice del programma. Inoltre, poiché facenti parti del novero delle entità presenti in una classe, i metodi sono membri di classe: devono, perciò, essere dichiarati a livello di classe. Con questa locuzione abbastanza comune nell'ambito della programmazione si intende l'atto di dichiarare qualcosa all'interno del corpo di una classe, ma fuori dal corpo di un qualsiasi suo membro. Ad esempio, la dichiarazione seguente è corretta:
Module Module1 Sub Esempio() 'istruzioni End Sub Sub Main() 'istruzioni End Sub End Modulementre la prossima è SBAGLIATA:
Module Module1 Sub Main() Sub Esempio() 'istruzioni End Sub 'istruzioni End Sub End ModuleAllo stesso modo, i metodi sono l'unica categoria, oltre alle proprietà e agli operatori, a poter contenere delle istruzioni: sono strumenti "attivi" di programmazione e solo loro possono eseguire istruzioni. Quindi astenetevi dallo scrivere un abominio del genere:
Module Module1 Sub Main() 'istruzioni End Sub Console.WriteLine() End SubE' totalmente e concettualmente sbagliato. Ma ora veniamo al dunque con un esempio:
Module Module1 'Dichiarazione di una procedura: il suo nome è "FindDay", il 'suo elenco di parametri è vuoto, e il suo corpo è 'rappresentato da tutto il codice compreso tra "Sub FindDay()" 'ed "End Sub". Sub FindDay() Dim StrDate As String Console.Write("Inserisci giorno (dd/mm/yyyy): ") StrDate = Console.ReadLine Dim D As Date 'La funzione Date.TryParse tenta di convertire la stringa 'StrDate in una variabile di tipo Date (che è un tipo 'base). Se ci riesce, ossia non ci sono errori nella 'data digitata, restituisce True e deposita il valore 'ottenuto in D; se, al contrario, non ci riesce, 'restituisce False e D resta vuota. 'Quando una data non viene inizializzata, dato che è un 'tipo value, contiene un valore predefinito, il primo 'Gennaio dell'anno 1 d.C. a mezzogiorno in punto. If Date.TryParse(StrDate, D) Then 'D.DayOfWeek contiene il giorno della settimana di D '(lunedì, martedì, eccetera...), ma in un 'formato speciale, l'Enumeratore, che vedremo nei 'prossimi capitoli. 'Il ".ToString()" converte questo valore in una 'stringa, ossia in un testo leggibile: i giorni della 'settimana, però, sono in inglese Console.WriteLine(D.DayOfWeek.ToString()) Else Console.WriteLine(StrDate & " non è una data valida!") End If End Sub 'Altra procedura, simile alla prima Sub CalculateDaysDifference() Dim StrDate1, StrDate2 As String Console.Write("Inserisci il primo giorno (dd/mm/yyyy): ") StrDate1 = Console.ReadLine Console.Write("Inserisci il secondo giorno (dd/mm/yyyy): ") StrDate2 = Console.ReadLine Dim Date1, Date2 As Date If Date.TryParse(StrDate1, Date1) And _ Date.TryParse(StrDate2, Date2) Then 'La differenza tra due date restituisce il tempo 'trascorso tra l'una e l'altra. In questo caso noi 'prendiamo solo i giorni Console.WriteLine((Date2 - Date1).Days) Else Console.WriteLine("Inserire due date valide!") End If End Sub Sub Main() 'Command è una variabile di tipo char (carattere) che 'conterrà una lettera indicante quale compito eseguire Dim Command As Char Do Console.Clear() Console.WriteLine("Qualche operazione con le date:") Console.WriteLine("- Premere F per sapere in che giorno " & _ "della settimana cade una certa data;") Console.WriteLine("- Premere D per calcolare la differenza tra due date;") Console.WriteLine("- Premere E per uscire.") 'Console.ReadKey() è la funzione che abbiamo sempre 'usato fin'ora per fermare il programma in attesa della 'pressione di un pulsante. Come vedremo fra breve, non 'è necessario usare il valore restituito da una 'funzione, ma in questo caso ci serve. Ciò che 'ReadKey restituisce è qualcosa che non ho ancora 'trattato. Per ora basti sapere che 'Console.ReadKey().KeyChar contiene l'ultimo carattere 'premuto sulla tastiera Command = Console.ReadKey().KeyChar 'Analizza il valore di Command Select Case Command Case "f" 'Invoca la procedura FindDay() FindDay() Case "d" 'Invoca la procedura CalculateDaysDifference() CalculateDaysDifference() Case "e" 'Esce dal ciclo Exit Do Case Else Console.WriteLine("Comando non riconosciuto!") End Select Console.ReadKey() Loop End Sub End ModuleIn questo primo caso, le due procedure dichiarate sono effettivamente sottoprogrammi a sé stanti: non hanno nulla in comune con il modulo (eccetto il semplice fatto di esserne membri), né con Main, ossia non scambiano alcun tipo di informazione con essi; sono come degli ingranaggi sigillati all'interno di una scatola chiusa. A questo riguardo, bisogna inserire una precisazione sulle variabili dichiarate ed usate all'interno di un metodo, qualsiasi esso sia. Esse si dicono locali o temporanee, poiché esistono solo all'interno del metodo e vengono distrutte quando il flusso di elaborazione ne raggiunge la fine. Anche sotto questo aspetto, si può notare come le procedure appena stilate siano particolarmente chiuse e restrittive. Tuttavia, si può benissimo far interagire un metodo con oggetti ed entità esterne, e questo approccio è decisamente più utile che non il semplice impacchettare ed etichettare blocchi di istruzioni in locazioni distinte. Nel prossimo esempio, la procedura attinge dati dal modulo, poiché in esso è dichiarata una variabile a livello di classe.
Module Module1 'Questa variabile è dichiarata a livello di classe '(o di modulo, in questo caso), perciò è accessibile 'a tutti i membri del modulo, sempre seguendo il discorso 'dei blocchi di codice fatto in precedenza Dim Total As Single = 0 'Legge un numero da tastiera e lo somma al totale Sub Sum() Dim Number As Single = Console.ReadLine Total += Number End Sub 'Legge un numero da tastiera e lo sottrae al totale Sub Subtract() Dim Number As Single = Console.ReadLine Total -= Number End Sub 'Legge un numero da tastiera e divide il totale per 'tale numero Sub Divide() Dim Number As Single = Console.ReadLine Total /= Number End Sub 'Legge un numero da tastiera e moltiplica il totale 'per tale numero Sub Multiply() Dim Number As Single = Console.ReadLine Total *= Number End Sub Sub Main() 'Questa variabile conterrà il simbolo matematico 'dell'operazione da eseguire Dim Operation As Char Do Console.Clear() Console.WriteLine("Risultato attuale: " & Total) Operation = Console.ReadKey().KeyChar Select Case Operation Case "+" Sum() Case "-" Subtract() Case "*" Multiply() Case "/" Divide() Case "e" Exit Do Case Else Console.WriteLine("Operatore non riconosciuto") Console.ReadKey() End Select Loop End Sub End Module
Procedure con parametri
Avviandoci verso l'interazione sempre maggiore del metodo con l'ambiente in cui esso esiste, troviamo le procedure con parametri. Al contrario delle precedenti, esse possono ricevere e scambiare dati con il chiamante: con quest'ultimo termine ci si riferisce alla generica entità all'interno della quale il metodo in questione è stato invocato. I parametri sono come delle variabili locali fittizie: esistono solo all'interno del corpo, ma non sono dichiarate in esso, bensì nell'elenco dei parametri. Tale elenco deve essere specificato dopo il nome del metodo, racchiuso da una coppia di parentesi tonde, e ogni suo elemento deve essere separato dagli altri da virgole.Sub [nome](ByVal [parametro1] As [tipo], ByVal [parametro2] As [tipo], ...) 'istruzioni End SubCome si vede, anche la dichiarazione è abbastanza simile a quella di una variabile, fatta eccezione per la parola riservata ByVal, di cui tra poco vedremo l'utilià. Per introdurre semplicemente l'argomento, facciamo subito un esempio, riscrivendo l'ultimo codice proposto nel paragrafo precedente con l'aggiunta dei parametri:
Module Module1 Dim Total As Single = 0 Sub Sum(ByVal Number As Single) Total += Number End Sub Sub Subtract(ByVal Number As Single) Total -= Number End Sub Sub Divide(ByVal Number As Single) Total /= Number End Sub Sub Multiply(ByVal Number As Single) Total *= Number End Sub Sub Main() 'Questa variabile conterrà il simbolo matematico 'dell'operazione da eseguire Dim Operation As Char Do Console.Clear() Console.WriteLine("Risultato attuale: " & Total) Operation = Console.ReadKey().KeyChar Select Case Operation 'Se si tratta di simboli accettabili Case "+", "-", "*", "/" 'Legge un numero da tastiera Dim N As Single = Console.ReadLine 'E a seconda dell'operazione, utilizza una 'procedura piuttosto che un'altra Select Case Operation Case "+" Sum(N) Case "-" Subtract(N) Case "*" Multiply(N) Case "/" Divide(N) End Select Case "e" Exit Do Case Else Console.WriteLine("Operatore non riconosciuto") Console.ReadKey() End Select Loop End Sub End ModuleRichiamando, ad esempio, Sum(N) si invoca la procedura Sum e si assegna al parametro Number il valore di N: quindi, Number viene sommato a Total e il ciclo continua. Number, perciò, è un "segnaposto", che riceve solo durante l'esecuzione un valore preciso, che può anche essere, come in questo caso, il contenuto di un'altra variabile. Nel gergo tecnico, Number - ossia, più in generale, l'identificatore dichiarato nell'elenco dei parametri - si dice parametro formale, mentre N - ossia ciò che viene concretamente passato al metodo - si dice parametro attuale. Non ho volutamente assegnato al parametro attuale lo stesso nome di quello formale, anche se è del tutto lecito farlo: ho agito in questo modo per far capire che non è necessario nessun legame particolare tra i due; l'unico vincolo che deve sussistere risiede nel fatto che parametro formale ed attuale abbiano lo stesso tipo. Quest'ultima asserzione, del resto, è abbastanza ovvia: se richiamassimo Sum("ciao") come farebbe il programma a sommare una stringa ("ciao") ad un numero (Total)?
Ora proviamo a modificare il codice precedente riassumendo tutte le operazioni in una sola procedura, a cui, però, vengono passati due parametri: il numero e l'operatore da usare.
Module Module1 Dim Total As Single = 0 Sub DoOperation(ByVal Number As Single, ByVal Op As Char) Select Case Op Case "+" Total += Number Case "-" Total -= Number Case "*" Total *= Number Case "/" Total /= Number End Select End Sub Sub Main() Dim Operation As Char Do Console.Clear() Console.WriteLine("Risultato attuale: " & Total) Operation = Console.ReadKey().KeyChar Select Case Operation Case "+", "-", "*", "/" Dim N As Single = Console.ReadLine 'A questa procedura vengono passati due 'parametri: il primo è il numero da 'aggiungere/sottrarre/moltiplicare/dividere 'a Total; il secondo è il simbolo 'matematico che rappresenta l'operazione 'da eseguire DoOperation(N, Operation) Case "e" Exit Do Case Else Console.WriteLine("Operatore non riconosciuto") Console.ReadKey() End Select Loop End Sub End Module
Passare parametri al programma da riga di comando
Come avevo accennato in precedenza, non è sempre vero che Main è una procedura senza parametri. Infatti, è possibile dichiarare Main in un altro modo, che le consente di ottenere informazioni quando il programma viene eseguito da riga di comando. In quest'ultimo caso, Main viene dichiarata con un solo argomento, un array di stringhe:Module Module1 Sub Main(ByVal Args() As String) If Args.Length = 0 Then 'Se la lunghezza dell'array è 0, significa che è vuoto 'e quindi non è stato passato nessun parametro a riga 'di comando. Scrive a schermo come utilizzare 'il programma Console.WriteLine("Utilizzo: nomeprogramma.exe tuonome") Else 'Args ha almeno un elemento. Potrebbe anche averne di 'più, ma a noi interessa solo il primo. 'Saluta l'utente con il nome passato da riga di comando Console.WriteLine("Ciao " & Args(0) & "!") End If Console.ReadKey() End Sub End ModulePer provarlo, potete usare cmd.exe, il prompt dei comandi. Io ho digitato:
CD "C:UsersTotemDocumentsVisual Studio 2008ProjectsConsoleApplication2inDebug" ConsoleApplication2.exe TotemLa prima istruzione per cambiare la directory di lavoro, la seconda l'invocazione vera e propria del programma, dove "Totem" è l'unico argomento passatogli: una volta premuto invio, apparirà il messaggio "Ciao Totem!". In alternativa, è possibile specificare gli argomenti passati nella casella di testo "Command line arguments" presente nella scheda Debug delle proprietà di progetto. Per accedere alle proprietà di progetto, cliccate col pulsante destro sul nome del progetto nella finestra a destra, quindi scegliete Properties e recatevi alla tabella Debug:
A cura di: Il Totem