|
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
Ma ora scendiamo un po' più nello specifico...
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 Sub
Credo 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 Module
mentre la prossima è SBAGLIATA:
Module Module1
Sub Main()
Sub Esempio()
'istruzioni
End Sub
'istruzioni
End Sub
End Module
Allo 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 Sub
E' 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 Module
In 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 Sub
Come 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 Module
Richiamando, 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 Module
Per provarlo, potete usare cmd.exe, il prompt dei comandi. Io ho digitato:
CD "C:UsersTotemDocumentsVisual Studio 2008ProjectsConsoleApplication2inDebug"
ConsoleApplication2.exe Totem
La 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:
|
|