Casino online













Mercato forex






A47. Gli Attributi


Gli attributi sono un'entità di programmazione non molto conosciuta dai giovani programmatori, ma altresì davvero utile. Essi servono a definire quella parte di informazioni conosciuta sotto il nome di metadati: i metadati servono a specificare ulteriori dettagli riguardo a un membro di classe e al suo comportamento. Gli attributi non sono direttive che forniscono comandi e perciò non influiscono direttamente sul membro a cui sono applicati, ma sulla sua relazione con il resto dell'applicazione. C'è solo un modo per utilizzarli: la Reflection. Infatti non esiste altra via per individuarli, anche dallo stesso codice nel quale vengono scritti. La sintassi con cui si assegna un attributo è:
<[Attributo]([Parametri del costruttore])> _
[Membro] 
Ad esempio:
<Description("Descrizione del metodo")> _
Public Sub UnMetodo() 
L'attributo Description (appartenente al namespace System.ComponentModel) serve solamente a specificare una piccola descrizione del membro a cui è applicato, in questo caso un metodo, come se fosse una targhetta di riconoscimento. Attraverso questo procedimento, l'Object Browser scritto nel capitolo precedente potrebbe rilevare anche le descrizioni dei membri, se presenti. Dato che gli attributi sono molto specifici e servono in determinate situazioni che non si possono analizzare in questa sede, nel capitolo spiegherò come scrivere un nuovo attributo e utilizzarlo per modificare il comportamento del programma.
Tuttavia, all'occorrenza, ne userò qualcuno nei prossimi capitoli riguardo ad ambiti differenti, come i file XML, gli eventi, le proprietà dei controlli eccetera...


Scrivere attributi
Un attributo, a livello teorico, non è altro che una classe derivata da System.Attribute e il suo nome deve sempre terminare con la parola "Attribute". Ecco un esempio:
Module Module1
    'Per convenzione gli attributi possono esporre solo proprietà
    'Questa classe definisce il nome di una proprietà a run-time
    Class NameAttribute
        Inherits Attribute

        Private ReadOnly _Name As String

        Public ReadOnly Property Name() As String
            Get
                Return _Name
            End Get
        End Property

        'Il costruttore viene richiamato automaticamente quando
        'l'attributo sta per essere applicato a un membro
        Sub New(ByVal Name As String)
            _Name = Name
        End Sub
    End Class
    
    'Classe che rappresenta una canzone
    Class Song
        Private _Title, _Author As String

        'Quando il programma principale la leggerà, questa proprietà
        'sarà visualizzata con il nome "Titolo" anzichè "Title"
        'Si noti che è sufficiente la prima parte del nome dell'attributo,
        'in questo caso Name anzichè NameAttribute
        <Name("Titolo")> _
        Public Property Title() As String
            Get
                Return _Title
            End Get
            Set(ByVal Value As String)
                _Title = Value
            End Set
        End Property

        <Name("Autore")> _
        Public Property Author() As String
            Get
                Return _Author
            End Get
            Set(ByVal Value As String)
                _Author = Value
            End Set
        End Property
    End Class

    Sub Main()
        Dim S As New Song
        'Il Type associato a Song
        Dim SongType As Type = GetType(Song)
        'Il Type associato a NameAttribute, servirà in seguito
        Dim NameType As Type = GetType(NameAttribute)
        'In questo caso è necessario tutto il nome
        Dim Attr As NameAttribute

        Console.WriteLine("Completare i campi per l'oggetto Song:")
        Console.WriteLine()

        'Chiede all'utente di immettere tutti i campi disponibili 
        'in Song, rappresentati dalle sue proprietà
        For Each PI As PropertyInfo In SongType.GetProperties()
            'Tenta di ottenere l'attributo associato alla proprietà.
            'GetCustomAttribues è un metodo comune a tutti i tipi della 
            'Reflection che rappresentano membri di classe e restituisce 
            'un array di attributi di un dato tipo, opzionalmente 
            'cercando nella gerarchia di ereditarietà
            Attr = TryCast(PI.GetCustomAttributes(NameType, False)(0),  _ 
                NameAttribute)
            'L'operatore TryCast restituisce Nothing se fallisce la 
            'conversione perciò se Attr = Nothing, non c'è alcun 
            'attributo Name applicato alla proprietà
            If Attr Is Nothing Then
                'Continua il ciclo
                Continue For
            End If

            Dim Value As Object
            'Altrimenti, visualizza il nome e chiede di 
            'immettere un valore
            Console.Write("{0}: ", Attr.Name)
            'Legge il valore
            Value = Console.ReadLine
            'E lo assegna alla relativa proprietà
            'PropertyInfo e FieldInfo hanno due metodi opposti: 
            'GetValue e SetValue
            '- GetValue(O, I()) : O è l'oggetto in cui impostare la 
            'proprietà o il campo, mentre I è un array di oggetti che 
            'esprimono gli eventuali indici usati per identificare una 
            'proprietà
            ' - SetValue(O, V, I()) : come prima, solo che V rappresenta 
            'il valore da assegnare
            PI.SetValue(S, Value, Nothing)
        Next

        Console.ReadLine()
    End Sub
End Module 
L'output sarà, ad esempio:
Completare i campi per l'oggetto Song:

Titolo : Sinfonia n.5 in Dom
Autore : Ludwig van Beethoven 
Prima di procedere nella seconda parte dell'esempio, è necessario dire una cosa: secondo i principi Microsoft, tutte le proprietà che sono impostate nel costruttore devono essere ReadOnly, mentre tutte le altre (opzionali), possono essere anche impostate manualmente. Potrebbe sorgere un dubbio: come si fa ad impostare manualmente una proprietà di attributo se non è possibile usarlo come variabile? In questo caso si utilizza una sintassi speciale, che ricorda un pò il Pascal, dove si assegna il valore con l'operatore :=:
<[Attributo]([Parametri costruttore], [Proprieta'] := [Valore])> _
[Membro] 
Ora bisogna aggiungere un altro membro a NameAttribute, che specifica se il campo sia obbligatorio oppure no: sarà una proprietà normale poichè non presenzia nei parametri del costruttore:
Module Module1
    Class NameAttribute
        Inherits Attribute

        Private ReadOnly _Name As String
        Private _IsCompulsory As Boolean = False

        Public ReadOnly Property Name() As String
            Get
                Return _Name
            End Get
        End Property

        Public Property IsCompulsory() As Boolean
            Get
                Return _IsCompulsory
            End Get
            Set(ByVal Value As Boolean)
                _IsCompulsory = Value
            End Set
        End Property

        Sub New(ByVal Name As String)
            _Name = Name
        End Sub
    End Class

    Class Song
        Private _Title, _Author As String

        'Il titolo è obbligatorio
        <Name("Titolo", IsCompulsory:=True)> _
        Public Property Title() As String
            Get
                Return _Title
            End Get
            Set(ByVal Value As String)
                _Title = Value
            End Set
        End Property

        'Se non specificato, IsCompulsory = False
        <Name("Autore")> _
        Public Property Author() As String
            Get
                Return _Author
            End Get
            Set(ByVal Value As String)
                _Author = Value
            End Set
        End Property
    End Class

    Sub Main()
        Dim S As New Song
        Dim SongType As Type = GetType(Song)
        Dim NameType As Type = GetType(NameAttribute)
        Dim Attr As NameAttribute

        Console.WriteLine("Completare i campi per l'oggetto Song:")
        Console.WriteLine("L'asterisco indica quelli obbligatori:")
        Console.WriteLine()

        For Each PI As PropertyInfo In SongType.GetProperties()
            Attr = TryCast(PI.GetCustomAttributes(NameType, False)(0), _
                NameAttribute)
            If Attr Is Nothing Then
                Continue For
            End If

            Dim Value As Object
            Console.Write(Attr.Name)
            If Attr.IsCompulsory Then
                'Asterisco se è obbligatorio
                Console.Write("*")
            End If
            Console.Write(": ")
            'Legge il valore
            Value = Console.ReadLine
            'Se Value è una stringa vuota, ma è obbligatoria, la richiede
            'all'inifnito, fino a quanto l'utente non immette un 
            'valore corretto
            If Attr.IsCompulsory AndAlso Value = "" Then
                While Value = ""
                    Console.WriteLine("Questa proprietà è obbligatoria!")
                    Console.Write("Reimmettere il valore corretto: ")
                    Value = Console.ReadLine
                End While
            End If
            PI.SetValue(S, Value, Nothing)
        Next

        Console.ReadLine()
    End Sub
End Module 






 

The Totem's Lair - Copyright (C) 2009
È vietata la riproduzione sia totale che parziale del sito.