Questo sito utilizza cookies, anche di terze parti, per mostrare pubblicità e servizi in linea con il tuo account. Leggi l'informativa sui cookies.
Username: Password: oppure
Guida al Visual Basic .NET - Gli Operatori

Guida al Visual Basic .NET

Capitolo 27° - Gli Operatori

<< Precedente Prossimo >>
Gli operatori sono speciali metodi che permettono di eseguire, appunto, operazioni tra due valori mediante l'uso di un simbolo (ad esempio, + per la somma, - per la differenza, eccetera...). Quando facciamo i calcoli, comunemente usando i tipi base numerici del Framework, come Int16 o Double, usiamo praticamente sempre degli operatori. Essi non sono nulla di "straordinario", nel senso che anche se non sembra, data la loro particolare sintassi, sono pur sempre definiti all'interno delle varie classi come normali membri (statici). Gli operatori, come i tipi base, del resto, non si sottraggono alla globale astrazione degli linguaggi orientati agli oggetti: tutto è sempre incasellato al posto giusto in una qualche classe. Ma questo lo vedremo più avanti quando parlerò della Reflection.
Sorvolando su questa breve parentesi idilliaca, torniamo all'aspetto più concreto di questo capitolo. Anche il programmatore ha la possibilità di definire nuovi operatori per i tipi che ha creato: ad esempio, può scrivere operatori che operino tra strutture e tra classi. In genere, si preferisce adottare gli operatori nel caso delle strutture poiché, essendo tipi value, si prestano meglio - come idea, più che altro - al fatto di subire operazioni tramite simboli. Venendo alla pratica, la sintassi generale di un operatore è la seguente:
Shared Operator [Simbolo]([Parametri]) As [Tipo Restituito]
    '... 
    Return [Risultato]
End Operator 
Come si vede, la sintassi è molto simile a quella usata per dichiarare una funzione, ad eccezione della keyword e dell'identificatore. Inoltre, per far sì che l'operatore sia non solo sintatticamente, ma anche semanticamente valido, devono essere soddisfatte queste condizioni:
  • L'operatore deve SEMPRE essere dichiarato come Shared, ossia statico. Infatti, l'operatore rientra nel dominio della classe in sé e per sé, appartiene al tipo, e non ad un'istanza in particolare. Infatti, l'operatore può essere usato per eseguire operazioni tra tutte le istanze possibili della classe. Anche se viene definito in una struttura, deve comunque essere Shared. Infatti, sebbene il concetto di struttura si presti di meno a questa "visione" un po' assiomatica del concetto di istanza, è pur sempre vero che possono esistere tante variabili diverse contenenti dati diversi, ma dello stesso tipo strutturato.
  • L'operatore può specificare al massimo due parametri (si dice unario se ne specifica uno, e binario se due), e di questi almeno uno DEVE essere dello stesso tipo in cui l'operatore è definito - tipicamente il primo dei due deve soddisfare questa seconda condizione. Questo risulta abbastanza ovvio: se avessimo una struttura Frazione, come fra poco mostrerò, a cosa servirebbe dichiararvi all'interno un operatore + definito tra due numeri interi? A parte il fatto che esiste già, è logico aspettarsi che, dentro un nuovo tipo, si descrivano le istruzioni necessarie ad operare con quel nuovo tipo, o al massimo ad attuare calcoli tra questo e i tipi già esistenti.
  • Il simbolo che contraddistingue l'operatore deve essere scelto tra quelli disponibili, di cui qui riporto un elenco con annessa descrizione della funzione che usualmente l'operatore ricopre:
    • + (somma)
    • - (differenza)
    • * (prodotto)
    • / (divisione)
    • (divisione intera)
    • ^ (potenza)
    • & (concatenazione)
    • = (uguaglianza)
    • > (maggiore)
    • < (minore)
    • >= (maggiore o uguale)
    • <= (minore o uguale)
    • >> (shift destro dei bit)
    • << (shift sinistro dei bit)
    • And (intersezione logica)
    • Or (unione logica)
    • Not (negazione logica)
    • Xor (aut logico)
    • Mod (resto della divisione intera)
    • Like (ricerca di un pattern: di solito il primo argomento indica dove cercare e il secondo cosa cercare)
    • IsTrue (è vero)
    • IsFalse (è falso)
    • CType (conversione da un tipo ad un altro)
    Sintatticamente parlando, nulla vieta di usare il simbolo And per fare una somma, ma sarebbe meglio attenersi alle normali norme di utilizzo riportate.
Ed ecco un esempio:
Module Module1

    Public Structure Fraction
        'Numeratore e denominatore
        Private _Numerator, _Denumerator As Int32

        Public Property Numerator() As Int32
            Get
                Return _Numerator
            End Get
            Set(ByVal value As Int32)
                _Numerator = value
            End Set
        End Property

        Public Property Denumerator() As Int32
            Get
                Return _Denumerator
            End Get
            Set(ByVal value As Int32)
                If value <> 0 Then
                    _Denumerator = value
                Else
                    'Il denominatore non può mai essere 0
                    'Dovremmo lanciare un'eccezione, ma vedremo più
                    'avanti come si fa. Per ora lo impostiamo a uno
                    _Denumerator = 1
                End If
            End Set
        End Property

        'Costruttore con due parametri, che inizializza numeratore
        'e denominatore
        Sub New(ByVal N As Int32, ByVal D As Int32)
            Me.Numerator = N
            Me.Denumerator = D
        End Sub

        'Restituisce la Fraction sottoforma di stringa
        Function Show() As String
            Return Me.Numerator & " / " & Me.Denumerator
        End Function

        'Semplifica la Fraction
        Sub Semplify()
            Dim X As Int32

            'Prende X come il valore meno alto in modulo
            'e lo inserisce in X. X servirà per un
            'calcolo spicciolo del massimo comune divisore
            X = Math.Min(Math.Abs(Me.Numerator), Math.Abs(Me.Denumerator))

            'Prima di iniziare, per evitare errori, controlla
            'se numeratore e denominatore sono entrambi negativi:
            'in questo caso li divide per -1
            If (Me.Numerator < 0) And (Me.Denumerator < 0) Then
                Me.Numerator /= -1
                Me.Denumerator /= -1
            End If

            'E con un ciclo scova il valore più alto di X 
            'per cui sono divisibili sia numeratore che denominatore 
            '(massimo comune divisore) e li divide per quel numero.

            'Continua a decrementare X finché non trova un
            'valore per cui siano divisibili sia numeratore che
            'denominatore: dato che era partito dall'alto, questo
            'sarà indubbiamente il MCD
            Do Until ((Me.Numerator Mod X = 0) And (Me.Denumerator Mod X = 0))
                X -= 1
            Loop

            'Divide numeratore e denominatore per l'MCD
            Me.Numerator /= X
            Me.Denumerator /= X
        End Sub

        'Somma due frazioni e restituisce la somma
        Shared Operator +(ByVal F1 As Fraction, ByVal F2 As Fraction) _
            As Fraction
            Dim F3 As Fraction

            'Se i denumeratori sono uguali, si limita a sommare 
            'i numeratori
            If F1.Denumerator = F2.Denumerator Then
                F3.Denumerator = F1.Denumerator
                F3.Numerator = F1.Numerator + F2.Numerator
            Else
                'Altrimenti esegue tutta l'operazione
                'x   a   x*b + a*y
                '- + - = ---------
                'y   b      y*b
                F3.Denumerator = F1.Denumerator * F2.Denumerator
                F3.Numerator = F1.Numerator * F2.Denumerator + F2.Numerator * F1.Denumerator
            End If

            'Semplifica la Fraction
            F3.Semplify()
            Return F3
        End Operator

        'Sottrae due Fraction e restituisce la differenza
        Shared Operator -(ByVal F1 As Fraction, ByVal F2 As Fraction) _
            As Fraction
            'Somma l'opposto del secondo membro
            F2.Numerator = -F2.Numerator
            Return F1 + F2
        End Operator

        'Moltiplica due frazioni e restituisce il prodotto
        Shared Operator *(ByVal F1 As Fraction, ByVal F2 As Fraction) _
            As Fraction
            'Inizializza F3 con il numeratore pari al prodotto 
            'dei numeratori e il denominatore pari al prodotto dei 
            'denominatori
            Dim F3 As Fraction = New Fraction(F1.Numerator * F2.Numerator, _
                F1.Denumerator * F2.Denumerator)
            F3.Semplify()
            Return F3
        End Operator

        'Divide due frazioni e restituisce il quoziente
        Shared Operator /(ByVal F1 As Fraction, ByVal F2 As Fraction) _
            As Fraction
            'Inizializza F3 eseguendo l'operazione:
            'a   x   a   y
            '- / - = - * -
            'b   y   b   x
            Dim F3 As Fraction = New Fraction(F1.Numerator * F2.Denumerator, _
                F1.Denumerator * F2.Numerator)
            F3.Semplify()
            Return F3
        End Operator
    End Structure

    Sub Main()
        Dim A As New Fraction(8, 112)
        Dim B As New Fraction(3, 15)

        A.Semplify()
        B.Semplify()
        Console.WriteLine(A.Show())
        Console.WriteLine(B.Show())

        Dim C As Fraction = A + B
        Console.WriteLine("A + B = " & C.Show())

        Console.ReadKey()
    End Sub
End Module 


CType

CType è un particolare operatore che serve per convertire da un tipo di dato ad un altro. Non è ancora stato introdotto nei precedenti capitoli, ma ne parlerò più ampiamente in uno dei successivi. Scrivo comunque un paragrafo a questo riguardo per amor di completezza e utilità di consultazione.
Come è noto, CType può eseguire conversioni da e verso tipi conosciuti: la sua sintassi, tuttavia, potrebbe sviare dalla corretta dichiarazione. Infatti, nonostante CType accetti due parametri, la sua dichiarazione ne implica uno solo, ossia il tipo che si desidera convertire, in questo caso Fraction. Il secondo parametro è implicitamente indicato dal tipo di ritorno: se scrivessimo "CType(ByVal F As Fraction) As Double", questa istruzione genererebbe un CType in grado di convertire dal tipo Fraction al tipo Double nella maniera consueta in cui siamo abituati:
Dim F As Fraction
'...
Dim D As Double = CType(F, Double)  
La dichiarazione di una conversione verso Double genera automaticamente anche l'operatore CDbl, che si può usare tranquillamente al posto della versione completa di CType. Ora conviene porre l'accento sul come CType viene dichiarato: la sua sintassi non è speciale solo perchè può essere confuso da unario a binario, ma anche perchè deve dichiarare sempre se una conversione è Widening (di espansione, ossia senza perdita di dati) o Narrowing (di riduzione, con possibile perdita di dati). Per questo motivo si deve specificare una delle suddette keyword tra Shared e Operator. Ad esempio: Fraction rappresenta un numero razionale e, sebbene Double non rappresenti tutte le cifre di un possibile numero periodico, possiamo considerare che nel passaggio verso i Double non ci sia perdita di dati nè di precisione in modo rilevante. Possiamo quindi definire la conversione Widening:
Shared Widening Operator CType(ByVal F As Fraction) As Double
    Return F.Numerator / F.Denumerator
End Operator  
Invece, la conversione verso un numero intero implica non solo una perdita di precisione rilevante ma anche di dati, quindi la definiremo Narrowing:
Shared Narrowing Operator CType(ByVal F As Fraction) As Int32
    'Notare l'operatore  di divisione intera (per maggiori 
    'informazioni sulla divisione intera, vedere capitolo A6)
    Return F.Numerator  F.Denumerator
End Operator  


Operatori di confronto

Gli operatori di confronto godono anch'essi di una caratteristica particolare: devono sempre essere definiti in coppia, < con >, = con <>, <= con >=. Non può infatti esistere un modo per verificare se una variabile è minore di un altra e non se è maggiore. Se manca uno degli operatori complementari, il compilatore visualizzerà un messaggio di errore. Ovviamente, il tipo restituito dagli operatori di confronto sarà sempre Boolean, poiché una condizione può essere solo o vera o falsa.
Shared Operator <(ByVal F1 As Fraction, ByVal F2 As Fraction) As Boolean
    'Converte le frazioni in double e confronta questi valori
    Return (CType(F1, Double) < CType(F2, Double))
End Operator

Shared Operator >(ByVal F1 As Fraction, ByVal F2 As Fraction) As Boolean
    Return (CDbl(F1) > CDbl(F2))
End Operator

Shared Operator =(ByVal F1 As Fraction, ByVal F2 As Fraction) As Boolean
    Return (CDbl(F1) = CDbl(F2))
End Operator

Shared Operator <>(ByVal F1 As Fraction, ByVal F2 As Fraction) As Boolean
    'L'operatore "diverso" restituisce sempre un valore opposto
    'all'operatore "uguale"
    Return Not (F1 = F2)
End Operator  
È da notare che le espressioni come (a=b) o (a-c>b) restituiscano un valore booleano. Possono anche essere usate nelle espressioni, ma è sconsigliabile, in quanto il valore di True è spesse volte confuso: in VB.NET è -1, ma a runtime è 1, mentre negli altri linguaggi è sempre 1. Queste espressioni possono tuttavia essere assegnate con sicurezza ad altri valori booleani:
'...
a = 10
b = 20
Console.WriteLine("a è maggiore di b: " & (a > b))
'A schermo compare: "a è maggiore di b: False"  


<< Precedente Prossimo >>
A proposito dell'autore

Programmatore e analista .NET 2005/2008/2010 (in particolare C# e VB.NET), anche nell'implementazione Mono per Linux. Conoscenze approfondite di Pascal, PHP, XML, HTML 4.01/5, CSS 2.1/3, Javascript (e jQuery). Conoscenze buone di C, LUA, GML, Ruby, XNA, AJAX e Assembly 68000. Competenze basilari di C++, SQL, Hlsl, Java.