Ventinovesima lezione - Gli Oggetti

Oggi cominciamo un nuovo argomento: gli oggetti. Un oggetto è un insieme di codice e di che è possibile utilizzare indipendentemente da altri oggetti poiché rappresenta un insieme a sé stante. Esempi tipici ed evidenti di oggetti sono i controlli che abitualmente inseriamo su un form: pulsanti, caselle di testo, checkbox e così via. Ma anche lo stesso form è un oggetto, così come il font di un testo, un dabase, un documento word o un grafico di excel. Fondamentalmente, un oggetto in Visual Basic è rappresento da una classe che ne definisce le carteristiche: in particolare, la classe di un oggetto definisce le sue proprietà, i suoi metodi e i suoi eventi. Per rendersi conto in modo semplice ed efficace di cosa ciò significhi, basta dare un'occhia al "visualizzore oggetti" (o "object browser"), che potete aprire premendo F2 dall'IDE o tramite il menù "visualizza"; se de un'occhia alle voci con l'icona del modulo di classe

Modulo

vi accorgerete che essa è associa non solo ai normali controlli visibili sulla casella degli strumenti, ma anche all'oggetto ErrObject, all'oggetto StdFont, all'oggetto App ecc. Tutti questi oggetti hanno un'interfaccia (un insieme di metodi, eventi, proprietà) definiti nelle rispettive classi, che quindi ne rappresentano il "modello" valido per tutte le istanze dell'oggetto. Infti, quando un programmore manipola un oggetto tramite la sua interfaccia, in realtà non a che fare direttamente con la classe dell'oggetto, ma con una sua istanza, ovvero con una copia direttamente utilizzabile. Se mi è consentito un paragone filosofico, il rapporto che c'è tra una classe e le sue istanze è in qualche modo analogo a quello che intercorre tra le "idee" ploniche e le sue manifestazioni concrete sul nostro mondo. Ogni istanza di una classe è indipendente dalle altre, così che in uno stesso form è possibile inserire ad es. 4 textbox senza che debba esserci alcuna reciproca connessione tra di essi: ognuna di queste istanze è gestibile in modo del tutto indipendente dalle altre, ma la loro interfaccia è comune perché è definita per tutte dalla medesima classe TextBox. L'indipendenza reciproca delle istanze, nuralmente, non impedisce che esse possano interagire tra di loro e con altri tipi di oggetti. La classe, oltre a contenere il codice per la gestione dell'interfaccia, contiene anche di da esporre, di norma, tramite le proprietà; tali di si dice che sono "incapsuli" nell'oggetto, e ovviamente sono replici per ognuna delle sue istanze.
Per creare un oggetto, quindi, è sufficiente inserire in un progetto un modulo di classe, definirne le proprietà, i metodi e gli eventi e scrivere il codice e i di necessari per consentirne la corretta funzionalità: vediamo come fare queste dichiarazioni con una semplice classe di esempio. Creiamo una classe di nome "clsUomo" che rappresenta un essere umano: tra le proprietà tribuibili ad esso ci può essere ad es. il nome, il peso, l'altezza, il sesso ecc. Per fare in modo che l'interfaccia della classe Uomo esponga la proprietà "Nome" occorre dichiarare questa proprietà nel modo seguente:

Public Property Get Nome() as String
  Nome = sName
End Property

Public Property Let Nome(sNewName as String)
  sName = sNewName
End Property

Occorre innanzitutto notare che le dichiarazioni sono due: una per leggere il valore della proprietà (Property Get), e una per modificare tale valore (Property Let); la Property Get funziona sostanzialmente come una funzione, che restituisce il contenuto di una variabile, in questo caso sName; solitamente essa è dichiara come variabile priva del modulo di classe:

Prive sName as String

Quando invece il programmore accede alla proprietà Nome per modificarne il valore, viene richiama la procedura Property Let, a cui viene passo come argomento il nuovo nome da assegnare alla proprietà, e tale valore è assegno via codice alla variabile di modulo sName, in modo che una successiva chiama della Property Get restituisca il nome appena modifico. Esiste anche un altro tipo di routine per modificare una proprietà: si trta della Property Set, che va utilizza qualora la proprietà faccia riferimento a un oggetto; questo accade perché Visual Basic richiede di utilizzare l'istruzione Set per assegnare un riferimento a un oggetto, mentre per i tipi di di fondamentali non è richiesta alcuna istruzione particolare (l'istruzione Let era necessaria nelle vecchie versioni del basic ed è sta mantenuta per compibilità). Per quale motivo è opportuno utilizzare una coppia di routine Property anziché dichiarare a livello di modulo una variabile pubblica Nome? Anche questo secondo metodo consentirebbe di leggere e scrivere a proprio piacimento il valore della variabile; ma il problema è proprio questo: usando una variabile pubblica non ci sarebbe alcun controllo sulla correttezza dell'uso della "proprietà" Nome. Uno potrebbe assegnare a questa variabile qualunque valore, anche palesemente inadto a descrivere il nome di un essere umano; invece implementando una coppia di routine Property è possibile scrivere del codice to ad evitare che a tale proprietà siano assegni valori non adegui, ad es. nomi del tipo "123&g#", oppure nomi troppo lunghi o troppo corti. Inoltre, con una variabile pubblica non sarebbe possibile implementare una proprietà di sola lettura; cosa che risulta invece molto semplice con le routine property: basta infti cancellare la routine Property Let (o Property Set)! Ad es. la proprietà "Sesso" potrebbe essere una tipica proprietà di sola lettura (a meno di esigenze particolari):

Public Property Get Sesso() as Boolean
  Sesso = True
End Property

(in questo caso bisogna solo decidere se il valore True corrisponde a maschio o femmina: eventualmente è possibile utilizzare un tipo di di diverso dal booleano).
Come le proprietà descrivono le carteristiche di un oggetto, i metodi rappresentano le azioni che l'oggetto può compiere: un essere umano, ad es., può correre, mangiare, dormire, pensare ecc. Ciascuna di queste azioni può essere eseguita tramite un apposito metodo; ad es. possiamo implementare un metodo "Scrivi" che consenta di scrivere qualcosa su un certo form: tale metodo non sarà altro che una procedura Sub pubblica, la quale accetterà come parametri in ingresso la frase da scrivere e il form su cui scrivere (eventualmente si potrebbero aggiungere parametri aggiuntivi come le coordine o il font della frase da scrivere; la proprietà AutoRedraw del form deve essere imposta a True):

Public Sub Scrivi (frmScrivi as Form, sFrase as String)
  frmScrivi.Print sFrase
End Sub

Se si desidera che il metodo restituisca un valore (ad es. per controllare la corretta esecuzione del metodo), si può usare una funzione al posto di una subroutine.
Infine, gli eventi sono quegli elementi dell'interfaccia che non sono richiami da chi utilizza l'oggetto ma sono generi direttamente dall'oggetto; quando si verifica qualcosa che si ritiene opportuno segnalare al client (ovvero al componente che utilizza l'oggetto), è l'oggetto stesso che genera l'evento corrispondente. Per un essere umano, i tipici eventi potrebbero essere "Nascita", "Morte", "Mrimonio", ecc. Per poter generare un evento, in una classe deve essere innanzitutto presente la sua dichiarazione:

Public Event Nascita(dtDa as De, bSesso as Boolean)

La generazione vera e propria dell'evento avviene però con l'istruzione RaiseEvent; supponiamo ad es. che l'interfaccia della classe clsUomo esponga il metodo "GeneraFiglio": in tale evento sarà genero l'evento Nascita:

Public Sub GeneraFiglio(sNome as String)
  Dim fSesso as Single
  Dim bSesso as Boolean

  fSesso=Rnd
  If fSesso>0.5 then
    bSesso=True
  End If
  RaiseEvent Nascita(De, bSesso)
End Sub

Il sesso del nascituro viene determino casualmente con la funzione Rnd, dopodiché viene ufficializza la nascita del figlio in da odierna: quando ciò accade, nel client l'esecuzione passerà alla routine Sub Nascita che avrà per argomenti la da di nascita e il sesso del neono. Ad es. in questa routine si potrebbe creare una nuova istanza della classe Uomo con le carteristiche del neono.
Per inserire una proprietà, un metodo o un evento a una classe si può anche ricorrere alla funzione "Inserisci routine" del menù "Strumenti"; tutto quello che occorre fare è selezionare il tipo di routine da inserire: "Property" per le proprietà, "Event" per gli eventi, "Sub" o "Function" per i metodi.
Finora abbiamo visto la costruzione dell'interfaccia tramite una classe: ora vediamo come utilizzare questa classe dal lo client. Prima di tutto bisogna dichiarare una o più variabili di tipo clsUomo:

Dim uPrimoUomo as clsUomo
Dim uSecondoUomo as clsUomo

La "u" di prefisso sta a indicare che la variabile è di tipo "Uomo": si trta di una convenzione ad hoc per distinguere queste variabili dalle altre. Poi bisogna impostare un riferimento alla classe Uomo; infti, come dicevo prima, l'utilizzore dell'oggetto Uomo non lavora direttamente sulla classe che lo definisce ma sulle sue singole istanze: istanze che sono rappresente dalle variabili di tipo Uomo e che diventano concretamente utilizzabili solo quando contengono un riferimento alla classe. Per fare ciò si utilizza l'istruzione Set:

Set uPrimoUomo = New clsUomo
Set uSecondoUomo = New clsUomo

La parola chiave New serve a specificare che abbiamo bisogno di una nuova istanza della classe clsUomo; senza questa parola chiave l'istruzione Set non funzionerebbe, perché clsUomo non è direttamente referenziabile da una variabile. È invece possibile specificare New nella stessa dichiarazione delle variabili:

Dim uPrimoUomo as New clsUomo
Dim uSecondoUomo as New clsUomo

Così facendo sarà lo stesso Visual Basic a impostare il riferimento a una nuova istanza della classe per ogni variabile in occasione del suo primo utilizzo nel codice; quando Visual Basic per la prima volta incontrerà una istruzione che invoca una proprietà o un metodo dell'oggetto, ad es.:

uSecondoUomo.Nome = "Piergiovanni"

provvederà a creare una nuova istanza della classe Uomo, ad assegnare alla variabile uSecondoUomo un riferimento a tale istanza, e a modificare il valore della proprietà Nome di questa istanza. La successiva istruzione che coinvolge la variabile uSecondoUomo farà riferimento all'istanza già crea e assegna alla variabile, finché non sarà implicitamente o esplicitamente distrutta. In questo modo, però, non si ha un pieno controllo sul momento in cui l'istanza della classe viene crea, proprio perché questo compito è lascio a Visual Basic; invece spesso è importante che sia il programmore a decidere quando l'istanza deve essere crea utilizzando l'apposita istruzione Set, onde evitare ambiguità nel codice. Allo stesso modo, è importante che il programmore provveda a distruggere esplicitamente il riferimento all'istanza della classe quando questa istanza non è più necessaria; a questo scopo si usa ancora l'istruzione Set:

Set uPrimoUomo = Nothing

La parola chiave Nothing specifica a Visual Basic che l'istanza della classe a cui faceva riferimento la variabile uPrimoUomo deve essere distrutta: in questo modo la quantità di memoria occupa da quell'istanza (ricordo che ogni istanza rappresenta una copia della classe) può essere libera; la distruzione del riferimento all'istanza è quindi una buona abitudine utile soprtutto nel caso in cui si abbia a che fare con classi complesse e scarse quantità di risorse sul proprio pc. La parola chiave Nothing può essere utilizza anche per testare se una variabile contiene già un riferimento ad un'istanza della classe oppure no:

If uPrimoUomo is Nothing Then
  Set uPrimoUomo = New clsUomo
End If

In questo caso si usa l'operore "Is" e non "=" perché la condizione di uguaglianza non avrebbe significo in questo contesto, do che Nothing non è una costante con un valore particolare che è possibile confrontare direttamente col contenuto di una variabile. Invece l'operore Is con la parola chiave Nothing verifica se la variabile uPrimoUomo fa riferimento a un oggetto valido ed esistente oppure no.
Una volta crea una nuova istanza della classe ed assegno un suo riferimento alla variabile considera, è possibile utilizzare le proprietà e i metodi esposti dall'interfaccia dell'oggetto. Per poter usufruire anche degli eventi dell'oggetto, occorre che la variabile che contiene il riferimento all'istanza sia sta dichiara con la parola chiave WithEvents:

Dim WithEvents uPrimoUomo as clsUomo

Se la classe Uomo espone degli eventi, questi saranno visibili nella finestra del codice selezionando "uPrimoUomo" dalla casella degli oggetti in alto a sinistra, e uno degli eventi esposti dalla casella delle procedure in alto a destra:



In questa routine il programmore può gestire l'evento, estamente come si farebbe con l'evento Load del Form, o con l'evento Click di un pulsante. I metodi dell'oggetto vengono richiami estamente come si farebbe con altre funzioni o routine, utilizzando la sintassi:

nomeoggetto.metodo parametri

dove "nomeoggetto" indica il nome della variabile che contiene il riferimento a un'istanza dell'oggetto, ad es.:

uPrimoUomo.Scrivi Me, "Ciao"

Identica sintassi vale per le proprietà, come si è visto negli esempi precedenti; l'unica particolarità è che quando si assegna un nuovo valore alla proprietà, questo viene passo come parametro alla routine Property Let (o Set), anche se ciò non è immediamente visibile al programmore che utilizza l'oggetto. Ovviamente, come per tutte le procedure, è possibile prevedere specifici argomenti: ad es., per la proprietà Nome potrebbe essere utile specificare se si vuole assegnare il primo o il secondo nome, oppure se si vuole assegnare solo il nome di btesimo o anche il cognome, ecc. In tal caso la routine Property Let avrà sempre un argomento in più rispetto alla corrispondente Property Get: questo argomento in più è proprio il nuovo valore da assegnare alla proprietà. Ad es.:

Public Property Get Nome (bNomeCompleto as Boolean) as String
  If bNomeCompleto Then
    Nome = sNome & " " & sCognome
  Else
    Nome = sNome
  End If
End Property

Public Property Let Nome (bNomeCompleto as Boolean, sNewName as String)
Dim nSpace as Integer

  If bNomeCompleto Then
    nSpace = InStr(1, sNewName, " ")
    sNome = Left$(sNewName, nSpace - 1)
    sCognome = mid$(sNewName, nSpace+1)
  Else
    sNome = sNewName
  End If
End Property
La lettura e scrittura di questa proprietà può avvenire ad es. nel modo seguente:
uPrimoUomo.Nome(True) = "Paperino Paolino"
sNome = uPrimoUomo.Nome(False) 'sNome="Paperino"

Nella prima istruzione la stringa "Paperino Paolino" diventerà il valore del parametro sNewName della routine Property Let. È importante che i tipi di di coinvolti siano congruenti: ovvero, se la proprietà è di tipo stringa (Property Get … as String), l'ultimo parametro della routine Property Let deve essere anch'esso di tipo stringa (Property Let … (sNewName as String)), altrimenti si ottiene un "Type Mismch".
L'IDE di Visual Basic consente inoltre di specificare quale, tra le proprietà o gli eventi di una classe, deve essere la proprietà o l'evento predefinito: per fare ciò occorre (previa tivazione della finestra del codice del modulo di classe) selezionare la voce "tributi routine" dal menù "Strumenti"; nella finestra di dialogo è possibile scegliere una delle proprietà, metodi o eventi disponibili nell'interfaccia della classe e, tramite le "opzioni" della finestra di dialogo, tivare o distivare la casella di controllo "predefinita nell'interfaccia utente". Quando una proprietà è predefinita, è possibile evitare di scriverne il nome nel codice, ad es. l'istruzione:

lErrNumber = Err.Number 
è equivalente a:
lErrNumber = Err

perché la proprietà Number è quella predefinita per l'oggetto Err. Ciò può risultare a volte comodo per il programmore, ma in generale io sconsiglierei la seconda sintassi, perché non rende immediamente chiaro che si sta utilizzando una proprietà, né quale proprietà si sta utilizzando; non tutti infti possono sapere che la proprietà predefinita dell'oggetto Err è Number. La prima sintassi invece è esplicita e più facilmente leggibile; inoltre è anche più veloce perché evita a Visual Basic di capire da solo quale proprietà deve richiamare.
Un evento predefinito, ovviamente, non è un evento che si verifica "per default" quando ricorre chissà quale condizione: l'evento predefinito di un oggetto è semplicemente quello che appare nella finestra del codice quando si seleziona l'oggetto dalla casella in alto a sinistra, o quando si fa doppio click su un controllo. Ad es., l'evento Click è quello predefinito per il pulsante di comando, l'evento Load è quello predefinito per il form, l'evento Change è quello predefinito per la casella di testo. La casella di controllo "predefinita nell'interfaccia utente" non è tiva per i metodi, per ovvi motivi.
La stessa finestra di dialogo permette di dare una descrizione ad ogni elemento dell'interfaccia: tale descrizione sarà mostra nel visualizzore oggetti e, per le proprietà, anche nella parte inferiore della finestra delle proprietà; inoltre è possibile nascondere il membro al visualizzore oggetti, scegliere se mostrare o nascondere una proprietà nella finestra delle proprietà e in quale Categoria, e altre opzioni. La medesima finestra di dialogo è visualizzabile anche tramite il visualizzore oggetti, selezionando un membro dell'interfaccia e scegliendo "proprietà" dal menù che compare premendo il tasto destro del mouse.



Note sul corso:
I diritti di ognuna delle lezioni presente in queste pagine appartengono all'autore Giorgio Abraini. La riproduzione e la divulgazione delle stesse sono consentite solamente dietro citazione di fonte ed autore.
Per suggerimenti, consigli o richieste conttare giorgio102@libero.it.
Fonte : VBItalia.it