Guida al Visual Basic .NET
Capitolo 14° - I Metodi Parte II
ByVal e ByRefNel capitolo precedente, tutti i parametri sono stati dichiaranti anteponendo al loro nome la keyword ByVal. Essa ha il compito di comunicare al programma che al parametro formale deve essere passata una copia del parametro attuale. Questo significa che qualsiasi codice sia scritto entro il corpo del metodo, ogni manipolazione e ogni operazione eseguita su quel parametro agisce, di fatto, su un'altra variabile, temporanea, e non sul parametro attuale fornito. Ecco un esempio:Module Module1 Sub Change(ByVal N As Int32) N = 30 End Sub Sub Main() Dim A As Int32 = 56 Change(A) Console.WriteLine(A) Console.ReadKey() End Sub End ModuleA schermo apparirà la scritta "56": A è una variabile di Main, che viene passata come parametro attuale alla procedura Change. In quest'ultima, N costituisce il parametro formale - il segnaposto - a cui, durante il passaggio dei parametri, viene attribuita un copia del valore di A. In definitiva, per N viene creata un'altra area di memoria, totalmente distinta, e per questo motivo ogni operazione eseguita su quest'ultima non cambia il valore di A. Di fatto, ByVal indica di trattare il parametro come un tipo value (passaggio per valore). Al contrario, ByRef indica di trattare il parametro come un tipo reference (passaggio per indirizzo). Questo significa che, durante il passaggio dei parametri, al parametro formale non viene attribuito come valore una coppia di quello attuale, ma bensì viene forzato a puntare alla sua stessa cella di memoria. In questa situazione, quindi, anche i tipi value come numeri, date e valori logici, si comportano come se fossero oggetti. Ecco lo stesso esempio di prima, con una piccola modifica: Module Module1 Sub Change(ByRef N As Int32) N = 30 End Sub Sub Main() Dim A As Int32 = 56 Change(A) Console.WriteLine(A) Console.ReadKey() End Sub End ModuleNel codice, la sola differenza consiste nella keyword ByRef, la quale, tuttavia, cambia radicalmente il risultato. Infatti, a schermo apparirà "30" e non "56". Dato che è stata applicata la clausola ByRef, N punta alla stessa area di memoria di A, quindi ogni alterazione perpetrata nel corpo del metodo sul parametro formale si ripercuote su quello attuale. A questo punto è molto importante sottolineare che i tipi reference si comportano SEMPRE allo stesso modo, anche se vengono inseriti nell'elenco dei parametri accompagnati da ByVal. Eccone una dimostrazione: Module Module1 Dim A As New Object Sub Test(ByVal N As Object) Console.WriteLine(N Is A) End Sub Sub Main() Test(A) Console.ReadKey() End Sub End ModuleSe ByVal modificasse il comportamento degli oggetti, allora N conterrebbe una copia di A, ossia un altro oggetto semplicemente uguale, ma non identico. Invece, a schermo appare la scritta "True", che significa "Vero", perciò N e A sono lo stesso oggetto, anche se N era preceduto da ByVal. La funzioniLe funzioni sono simili alle procedure, ma possiedono qualche caratteristica in più. La loro sintassi è la seguente:Function [name]([elenco parametri]) As [tipo] '... Return [risultato] End FunctionLa prima differenza che salta all'occhio è l'As che segue l'elenco dei parametri, come a suggerire che la funzione sia di un certo tipo. Ad essere precisi, quell'As non indica il tipo della funzione, ma piuttosto quello del suo risultato. Infatti, le funzioni restituiscono qualcosa alla fine del loro ciclo di elaborazione. Per questo motivo, prima del termine del suo corpo, deve essere posta almeno un'istruzione Return, seguita da un qualsiasi dato, la quale fornisce al chiamante il vero risultato di tutte le operazioni eseguite. Non è un errore scrivere funzioni prive dell'istruzione Return, ma non avrebbe comunque senso: si dovrebbe usare una procedura in quel caso. Ecco un esempio di funzione: Module Module1 'Questa funzione calcola la media di un insieme 'di numeri decimali passati come array Function Average(ByVal Values() As Single) As Single 'Total conterrà la somma totale di tutti 'gli elementi di Values Dim Total As Single = 0 'Usa un For Each per ottenere direttamente i valori 'presenti nell'array piuttosto che enumerarli 'attraverso un indice mediante un For normale For Each Value As Single In Values Total += Value Next 'Restituisce la media aritmetica, ossia il rapporto 'tra la somma totale e il numero di elementi Return (Total / Values.Length) End Function Sub Main(ByVal Args() As String) Dim Values() As Single = {1.1, 5.2, 9, 4, 8.34} 'Notare che in questo caso ho usato lo stesso nome 'per il parametro formale e attuale Console.WriteLine("Media: " & Average(Values)) Console.ReadKey() End Sub End ModuleE un altro esempio in cui ci sono più Return: Module Module1 Function Potenza(ByVal Base As Single, ByVal Esponente As Byte) As Double Dim X As Double = 1 If Esponente = 0 Then Return 1 Else If Esponente = 1 Then Return Base Else For i As Byte = 1 To Esponente X *= Base Next Return X End If End If End Function Sub Main() Dim F As Double Dim b As Single Dim e As Byte Console.WriteLine("Inserire base ed esponente:") b = Console.ReadLine e = Console.ReadLine F = Potenza(b, e) Console.WriteLine(b & " elevato a " & e & " vale " & F) Console.ReadKey() End Sub End ModuleIn quest'ultimo esempio, il corpo della funzione contiene ben tre Return, ma ognuno appartiene a un path di codice differente. Path significa "percorso" e la locuzione appena usata indica il flusso di elaborazione seguito dal programma per determinati valori di Base ed Esponente. Disegnando un diagramma di flusso della funzione, sarà facile capire come ci siano tre percorsi differenti, ossia quando l'esponente vale 0, quando vale 1 e quando è maggiore di 1. È sintatticamente lecito usare due Return nello stesso path, o addirittura uno dopo l'altro, ma non ha nessun senso logico: Return, infatti, non solo restituisce un risultato al chiamante, ma termina anche l'esecuzione della funzione. A questo proposito, bisogna dire che esiste anche lo statement (=istruzione) Exit Function, che forza il programma ad uscire immediatamente dal corpo della funzione: inutile dire che è abbastanza pericoloso da usare, poiché si corre il rischio di non restituire alcun risultato al chiamante, il che può tradursi in un errore in fase di esecuzione. Come ultima postilla vorrei aggiungere che, come per le varibili, non è strettamente necessario specificare il tipo del valore restituito dalla funzione, anche se è fortemente consigliato: in questo caso, il programma supporrà che si tratti del tipo Object. Usi particolari delle funzioniCi sono certe circostanze in cui le funzioni possono differire leggermente dal loro uso e dalla loro forma consueti. Di seguito sono elencati alcuni casi:
Variabili StaticLe variabili Static sono una particolare eccezione alle variabili locali/temporanee. Avevo chiaramente scritto pochi paragrafi fa che queste ultime esistono solo nel corpo del metodo, vengono create al momento dell'invocazione e distrutte al termine dell'esecuzione. Le Static, invece, possiedono soltanto le prime due caratteristiche: non vengono distrutte alla fine del corpo, ma il loro valore si conserva in memoria e rimane tale anche quando il flusso entra una seconda volta nel metodo. Ecco un esempio:Module Module1 Sub Test() Static B As Int32 = 0 B += 1 Console.WriteLine(B) End Sub Sub Main(ByVal Args() As String) For I As Int16 = 1 To 6 Test() Next Console.ReadKey() End Sub End ModuleIl programma stamperà a schermo, in successione, 1, 2, 3, 4, 5 e 6. Come volevasi dimostrare, nonostante B sia temporanea, mantiene il suo valore tra una chiamata e la successiva.
C#, TypeScript, java, php, EcmaScript (JavaScript), Spring, Hibernate, React, SASS/LESS, jade, python, scikit, node.js, redux, postgres, keras, kubernetes, docker, hexo, etc...
|