Guida al Visual Basic .NET
Capitolo 22° - Le Proprieta Parte III
Proprietà con parametriNei due capitoli precedenti, ho sempre scritto proprietà che semplicemente restituivano il valore di un campo, ossia il codice del blocco Get non era nulla di più di un semplice Return. Introduciamo ora, invece, la possibilità di ottenere informazioni diverse dalla stessa proprietà specificando un parametro, proprio come fosse un metodo. Avrete notato, infatti, che fin dal principio c'era una coppia di parentesi tonde vicino al nome della proprietà, ossia proprio la sintassi che si usa per dichiarare metodi senza parametri. Ecco un esempio:Module Module1 'Classe che rappresenta un estrattore di numeri 'casuali Class NumberExtractor Private _ExtractedNumbers() As Byte 'Generatore di numeri casuali. Random è una classe 'del namespace System Private Rnd As Random 'Questa proprietà ha un parametro, Index, che 'specifica a quale posizione dell'array si intende recarsi 'per prelevarne il valore. Nonostante l'array abbia solo 6 'elementi di tipo Byte, l'indice viene comunemente sempre 'indicato come intero a 32 bit. È una specie di 'convenzione, forse derivante dalla maggior facilità di 'elaborazione su macchine a 32 bit Public ReadOnly Property ExtractedNumbers(ByVal Index As Int32) As Byte Get If (Index >= 0) And (Index < _ExtractedNumbers.Length) Then Return _ExtractedNumbers(Index) Else Return 0 End If End Get End Property Public Sub New() 'Essendo di tipo reference, si deve creare un nuovo 'oggetto Random e assegnarlo a Rnd. La ragione per cui 'Rnd è un membro di classe consiste nel fatto 'che se fosse stata variabile temporanea del corpo 'della procedura ExtractNumbers, sarebbero usciti 'gli stessi numeri. Questo perchè la sequenza 'pseudocasuale creata dalla classe non cambia se 'non glielo si comunica espressamente usando un altro 'costruttore. Non tratterò questo argomento ora Rnd = New Random() ReDim _ExtractedNumbers(5) End Sub Public Sub ExtractNumbers() 'Estrae 6 numeri casuali tra 1 e 90 e li pone nell'array For I As Int32 = 0 To 5 _ExtractedNumbers(I) = Rnd.Next(1, 91) Next End Sub End Class Sub Main() Dim E As New NumberExtractor() E.ExtractNumbers() Console.WriteLine("Numeri estratti: ") For I As Int32 = 0 To 5 Console.Write(E.ExtractedNumbers(I) & " ") Next Console.ReadKey() End Sub End ModuleNotare che sarebbe stato logicamente equivalente creare una proprietà che restituisse tutto l'array, in questo modo: Public ReadOnly Property ExtractedNumbers() As Byte() Get Return _ExtractedNumbers End Get End PropertyMa non si sarebbe avuto alcun controllo sull'indice che l'utente avrebbe potuto usare: nel primo modo, invece, è possibile controllare l'indice usato e restituire 0 qualora esso non sia coerente con i limiti dell'array. La restituzione di elementi di una lista, tuttavia, è solo una delle possibilità che le proprietà parametriche offrono, e non c'è limite all'uso che se ne può fare. Nonostante ciò, è bene sottolineare che è meglio utilizzare una funzione o una procedura (poiché le proprietà di questo tipo possono anche non essere readonly, questo era solo un caso) qualora si debbano eseguire calcoli o elaborazioni non immediati, diciamo oltre le 20/30 righe di codice, ma anche di meno, a seconda della pesantezza delle operazioni. Fate conto che le proprietà debbano sempre essere il più leggere possibile, computazionalmente parlando: qualche costrutto di controllo come If o Select, qualche calcolo sul Return, ma nulla di più. Proprietà di defaultCon questo termine si indica la proprietà predefinita di una classe. Per esistere, essa deve soddisfare questi due requisiti:
Module Module1 Class NumberExtractor Private _ExtractedNumbers() As Byte Private Rnd As Random 'Una proprietà di default si dichiara come una 'normalissima proprietà, ma anteponendo allo specificatore 'di accesso la keyword Default Default Public ReadOnly Property ExtractedNumbers(ByVal Index As Int32) As Byte Get If (Index >= 0) And (Index < _ExtractedNumbers.Length) Then Return _ExtractedNumbers(Index) Else Return 0 End If End Get End Property Public Sub New() Rnd = New Random() ReDim _ExtractedNumbers(5) End Sub Public Sub ExtractNumbers() For I As Int32 = 0 To 5 _ExtractedNumbers(I) = Rnd.Next(1, 91) Next End Sub End Class Sub Main() Dim E As New NumberExtractor() E.ExtractNumbers() Console.WriteLine("Numeri estratti: ") For I As Int32 = 0 To 5 'Ecco l'utilità delle proprietà di default: si possono 'richiamare anche omettendone il nome. In questo caso 'E(I) è equivalente a scrivere E.ExtractedNumbers(I), 'ma poiché ExtractedNumbers è di default, 'viene desunta automaticamente Console.Write(E(I) & " ") Next Console.ReadKey() End Sub End ModuleDal codice salta subito all'occhio la motivazione dei due prerequisiti specificati inizialmente:
Dim S As String = "Ciao" Dim C As Char = S.Chars(1) ' > C = "i", poiché "i" è il carattere alla posizione 1 ' nella stringa SEbbene, Chars è una proprietà di default, ossia è possibile scrivere: Dim S As String = "Ciao" Dim C As Char = S(1) ' > C = "i" Get e Set con specificatori di accessoAnche se a prima vista potrebbe sembrare strano, sì, è possibile assegnare uno specificatore di accesso anche ai singoli blocchi Get e Set all'interno di una proprietà. Questa peculiare caratteristica viene sfruttata veramente poco, ma offre una grande flessibilità e un'altrettanto grande potenzialità di gestione. Limitando l'accesso ai singoli blocchi, è possibile rendere una proprietà ReadOnly solo per certe parti di codice e/o WriteOnly solo per altre parti, pur senza usare direttamente tali keywords. Ovviamente, per essere logicamente applicabili, gli specificatori di accesso dei blocchi interni devono essere più restrittivi di quello usato per contrassegnare la proprietà stessa. Infatti, se una proprietà è privata, ovviamente non potrà avere un blocco get pubblico. In genere, la gerarchia di restrittività segue questa lista, dove Public è il meno restrittivo e Private il più restrittivo:
Public Property A() As Byte Get '... End Get Private Set(ByVal value As Byte) '... End Set End PropertyLa proprietà A è sempre leggibile, ma modificabile solo all'interno della classe che la espone. In pratica, è come una normale proprietà per il codice interno alla classe, ma come una ReadOnly per quello esterno. È pur vero che in questo caso, si sarebbe potuto renderla direttamente ReadOnly e modificare direttamente il campo da essa avvolto invece che esporre un Set privato, ma sono punti di vista. Ad ogni modo, l'uso di scope diversificati permette di fare di tutto e di più ed è solo un caso che non mi sia venuto in mente un esempio più significativo. Mettiamo un po' d'ordine sulle keywordIn questi ultimi capitoli ho spiegato un bel po' di keyword diverse, e specialmente nelle proprietà può accadere di dover specificare molte keyword insieme. Ecco l'ordine corretto (anche se l'editor del nostro ambiente di sviluppo le riordina per noi nel caso dovessimo sbagliare):[Default] [ReadOnly/WriteOnly] [Public/Friend/Private/...] Property ...E ora quelle che conoscete sono ancora poche... provate voi a scrivere una proprietà del genere: Default Protected Friend Overridable Overloads ReadOnly Property A(ByVal Index As Int32) As Byte Get '... End Get End Property N.B.: ovviamente, tutto quello che si è detto fin'ora sulle proprietà nelle classi vale anche per le strutture!
C#, TypeScript, java, php, EcmaScript (JavaScript), Spring, Hibernate, React, SASS/LESS, jade, python, scikit, node.js, redux, postgres, keras, kubernetes, docker, hexo, etc...
|