Istruzioni per l'uso delle proprietà

Determinare se una proprietà o un metodo è più adatto alle proprie esigenze. Per ulteriori dettagli sulla scelta tra proprietà e metodi, vedere Confronto tra proprietà e metodi.

Scegliere un nome per la proprietà in base alle convenzioni di denominazione per le proprietà consigliate.

Quando si accede a una proprietà mediante la funzione di accesso set, è necessario conservare il valore della proprietà prima di modificarlo, in modo da garantire che i dati non vengano persi se la funzione di accesso set genera un'eccezione.

Aspetti relativi allo stato delle proprietà

Consentire l'impostazione delle proprietà in qualsiasi ordine. Le proprietà non devono contenere informazioni sullo stato rispetto alle altre proprietà. Si verifica spesso il caso in cui una particolare funzionalità di un oggetto non avrà effetto finché lo sviluppatore non specifica un particolare insieme di proprietà o un oggetto non si trovi in un determinato stato. Finché l'oggetto non si trova nello stato corretto, la funzionalità non è attiva. Quando l'oggetto si trova nello stato corretto, la funzionalità viene attivata automaticamente senza richiedere una chiamata esplicita. La semantica è la stessa a prescindere dall'ordine in cui lo sviluppatore imposta i valori della proprietà o dalla modalità in cui lo sviluppatore ottiene lo stato attivo dell'oggetto.

È possibile, ad esempio, che un controllo TextBox, abbia due proprietà correlate, DataSource e DataField, che specificano rispettivamente il nome della tabella e il nome della colona. Una volta specificate le due proprietà, il controllo può associare automaticamente i dati della tabella alla proprietà Text del controllo. Nell'esempio di codice seguente vengono illustrate le proprietà che possono essere impostate in qualsiasi ordine.

Dim t As New TextBox()
t.DataSource = "Publishers"
t.DataField = "AuthorID"
' The data-binding feature is now active.
[C#]
TextBox t = new TextBox();
t.DataSource = "Publishers";
t.DataField = "AuthorID";
// The data-binding feature is now active.

È possibile impostare le proprietà DataSource e DataField in qualsiasi ordine. Il codice riportato in precedenza equivale quindi al codice seguente:

Dim t As New TextBox()
t.DataField = "AuthorID"
t.DataSource = "Publishers"
' The data-binding feature is now active.

[C#]
TextBox t = new TextBox();
t.DataField = "AuthorID";
t.DataSource = "Publishers";
// The data-binding feature is now active.

È anche possibile impostare una proprietà su null (Nothing in Visual Basic) per indicare che il valore non è specificato.

Dim t As New TextBox()
t.DataField = "AuthorID"
t.DataSource = "Publishers"
' The data-binding feature is now active.
t.DataSource = Nothing
' The data-binding feature is now inactive.
[C#]
TextBox t = new TextBox();
t.DataField = "AuthorID";
t.DataSource = "Publishers";
// The data-binding feature is now active.
t.DataSource = null;
// The data-binding feature is now inactive.

Nell'esempio di codice seguente viene illustrato come tenere traccia dello stato della funzionalità di associazione dei dati e attivarla o disattivarla automaticamente al momento opportuno.

Public Class TextBox
   Private m_dataSource As String
   Private m_dataField As String
   Private m_active As Boolean

   Public Property DataSource() As String
      Get
         Return m_dataSource
      End Get
      Set
         If value <> m_dataSource Then
            ' Set the property value first, in case activate fails.
            m_dataSource = value
            ' Update active state.
            SetActive(( Not (m_dataSource Is Nothing) And Not (m_dataField Is Nothing)))
         End If
      End Set
   End Property
   Public Property DataField() As String
      Get
         Return m_dataField
      End Get
      Set
         If value <> m_dataField Then
            ' Set the property value first, in case activate fails.
            m_dataField = value
            ' Update active state.
            SetActive(( Not (m_dataSource Is Nothing) And Not (m_dataField Is Nothing)))
         End If
      End Set
   End Property
   Sub SetActive(m_value As Boolean)
      If value <> m_active Then
         If m_value Then
            Activate()
            Text = dataBase.Value(m_dataField)
         Else
            Deactivate()
            Text = ""
         End If
         ' Set active only if successful.
         m_active = value
      End If
   End Sub
   Sub Activate()
      ' Open database.
   End Sub
 
   Sub Deactivate()
      ' Close database.
   End Sub
End Class
[C#]
public class TextBox
{
   string dataSource;
   string dataField;
   bool active;

   public string DataSource
   {   
      get
      {
         return dataSource;
      }
      set
      {
         if (value != dataSource)
         {
            // Update active state.
            SetActive(value != null && dataField != null);
            dataSource = value;
         }
      }
      }
      
   public string DataField
   {
      get
      {
         return dataField;
      }
      set
      {
         if (value != dataField)
         {
            // Update active state.
            SetActive(dataSource != null && dataField != null);
            dataField = value;
         }
      }
   }   
   void SetActive(Boolean value)
   {
      if (value != active)
      {
         if (value)
         {
            Activate();
            Text = dataBase.Value(dataField);
         }
         else
         {
            Deactivate();
            Text = "";
         }
         // Set active only if successful.
         active = value; 
      }
   }
   void Activate()
   {
      // Open database.
   }
      
   void Deactivate()
   {
      // Close database.
   }
}

Nell'esempio precedente l'espressione riportata di seguito determina se l'oggetto si trova in uno stato in cui la funzionalità di associazione dei dati può essere attivata automaticamente.

(Not (value Is Nothing) And Not (m_dataField Is Nothing))
[C#]
value != null && dataField != null

È possibile impostare l'attivazione automatica creando un metodo che determina se l'oggetto può essere attivato dato lo stato corrente, quindi lo attiva in base alle necessità.

Sub UpdateActive(m_dataSource As String, m_dataField As String)
   SetActive(( Not (m_dataSource Is Nothing) And Not (m_dataField Is Nothing)))
End Sub
[C#]
void UpdateActive(string dataSource, string dataField)
{
   SetActive(dataSource != null && dataField != null);
}

In caso di assenza di proprietà correlate, ad esempio DataSource e DataMember, è necessario prendere in considerazione la possibilità di implementare l'interfaccia ISupportInitialize, in modo tale che il responsabile della progettazione o l'utente possano chiamare i metodi ISupportInitialize.BeginInit e ISupportInitialize.EndInit al momento dell'impostazione di più proprietà e consentire al componente di fornire ottimizzazioni. Nell'esempio precedente, ISupportInitialize può evitare che vengano eseguiti inutili tentativi di accesso al database fino al corretto completamento dell'installazione.

L'espressione presente nel metodo indica le parti del modello a oggetti che devono essere esaminate per applicare le transizioni degli stati. In questo caso sono interessate le proprietà DataSource e DataField. Per ulteriori informazioni sulla scelta tra proprietà e metodi, vedere Confronto tra proprietà e metodi.

Attivazione di eventi di proprietà modificata

I componenti devono generare eventi di proprietà modificata per notificare ai consumer quando la proprietà del componente cambia a livello di codice. La convenzione di denominazione per un evento di proprietà modificata prevede l'aggiunta del suffisso Changed al nome della proprietà, ad esempio TextChanged. Un controllo potrebbe, ad esempio, generare un evento TextChanged alla modifica della proprietà del testo. Per generare l'evento, è possibile utilizzare una routine di supporto protetta Raise<Property>Changed. È probabile tuttavia che non sia opportuno generare un evento di proprietà modificata per l'aggiunta di voci in una tabella hash. Nell'esempio di codice seguente viene illustrata l'implementazione di una routine di supporto per un evento di proprietà modificata.

Class Control
   Inherits Component
   Private m_text As String
   Public Property Text() As String
      Get
         Return m_text
      End Get
      Set
         If Not m_text.Equals(value) Then
            m_text = value
            RaiseTextChanged()
         End If
      End Set
   End Property
End Class
[C#]
class Control: Component
{
   string text;
   public string Text
   { 
      get
      { 
         return text; 
      }
      set
      {
         if (!text.Equals(value))
         {
            text = value;
            RaiseTextChanged();
         }
      }
   }
}

L'associazione dati utilizza tale modello per consentire l'associazione a due vie della proprietà. Senza gli eventi <Property>Changed e Raise<Property>Changed l'associazione dati viene eseguita in una sola direzione; se il database viene modificato, la proprietà viene aggiornata. Ciascuna proprietà che genera l'evento <Property>Changed deve fornire i metadati per indicare che la proprietà supporta l'associazione dati.

È consigliabile generare gli eventi in corso di modifica/modificati se il valore di una proprietà viene modificato a causa di forze esterne. Tali eventi indicano allo sviluppatore che il valore di una proprietà è in corso di modifica o è stato modificato al termine di un'operazione, anziché richiamando i metodi su un oggetto.

Un chiaro esempio in proposito è la proprietà Text di un controllo Edit. Quando un utente digita le informazioni nel controllo, il valore della proprietà viene modificato automaticamente. Prima che il valore della proprietà venga modificato, viene attivato un evento. Non viene passato il valore precedente o quello nuovo; lo sviluppatore ha quindi la possibilità di annullare l'evento attivando un'eccezione. Il nome dell'evento è il nome della proprietà seguito dal suffisso Changing. Nell'esempio di codice seguente viene illustrato un evento in corso di modifica.

Class Edit
   Inherits Control
   
   Public Property Text() As String
      Get
         Return m_text
      End Get
      Set
         If m_text <> value Then
            OnTextChanging(Event.Empty)
            m_text = value
         End If
      End Set
   End Property
End Class
[C#]
class Edit : Control 
{
   public string Text 
   { 
      get 
      { 
         return text; 
      }
      set 
      {
         if (text != value) 
         {
            OnTextChanging(Event.Empty);
            text = value;
         }
      }
   }
}

Viene attivato un evento anche dopo la modifica del valore della proprietà. L'evento non può essere annullato. Il nome dell'evento è il nome della proprietà seguito dal suffisso Changed. È necessario generare anche l'evento PropertyChanged generico. Il criterio per generare entrambi tali eventi consiste nel generare l'evento specifico dal metodo OnPropertyChanged. Nell'esempio seguente viene illustrato l'uso del metodo OnPropertyChanged.

Class Edit
   Inherits Control  
   Public Property Text() As String
      Get
         Return m_text
      End Get
      Set
         If m_text <> value Then
            OnTextChanging(Event.Empty)
            m_text = value
            RaisePropertyChangedEvent(Edit.ClassInfo. m_text)
         End If
      End Set
   End Property
   Protected Sub OnPropertyChanged(e As PropertyChangedEventArgs)
      If e.PropertyChanged.Equals(Edit.ClassInfo. m_text) Then
         OnTextChanged(Event.Empty)
      End If
      If Not (onPropertyChangedHandler Is Nothing) Then
         onPropertyChangedHandler(Me, e)
      End If
   End Sub
End Class
[C#]
class Edit : Control 
{
   public string Text 
   {
      get 
      { 
         return text; 
      }
      set 
      {
         if (text != value) 
         {
            OnTextChanging(Event.Empty);
            text = value;
            RaisePropertyChangedEvent(Edit.ClassInfo.text);
         }
      }
   }

   protected void OnPropertyChanged(PropertyChangedEventArgs e) 
   {
      if (e.PropertyChanged.Equals(Edit.ClassInfo.text))
         OnTextChanged(Event.Empty);
      if (onPropertyChangedHandler != null)
         onPropertyChangedHandler(this, e);
   }
}

In alcuni casi è possibile che il valore sottostante di una proprietà non sia archiviato come campo rendendo difficile tenere traccia delle modifiche del valore. Quando si genera l'evento in corso di modifica, individuare tutte le posizioni in cui il valore della proprietà può cambiare e consentire l'annullamento dell'evento. L'esempio sul controllo Edit precedente non è del tutto corretto, in quanto il valore Text è effettivamente archiviato nell'handle della finestra (HWND). Per generare l'evento TextChanging, è necessario esaminare i messaggi di Windows per determinare i casi in cui il testo potrebbe cambiare e consentire la generazione di un'eccezione in OnTextChanging per annullare l'evento. Qualora risultasse troppo difficile fornire un evento in corso di modifica, è opportuno supportare solo l'evento modificato.

Confronto tra proprietà e metodi

Durante la progettazione delle librerie di classi è spesso necessario decidere se implementare un membro di classe come proprietà o metodo. In generale, i metodi rappresentano azioni e le proprietà rappresentano dati. Per scegliere tra queste opzioni, leggere le istruzioni riportate di seguito.

  • Utilizzare una proprietà quando il membro è un membro di dati logico. Nelle seguenti dichiarazioni di membri Name è una proprietà in quanto è un membro di classe logico.

    Public Property Name As String
       Get
          Return m_name
       End Get
       Set
          m_name = value
       End Set 
    End Property
    [C#]
    public string Name
       get 
       {
          return name;
       }
       set 
       {
          name = value;
       }
    
  • Utilizzare un metodo nei casi indicati di seguito.

    • L'operazione è una conversione, ad esempio Object.ToString.

    • L'operazione è impegnativa e si desidera comunicare all'utente che sarebbe opportuno inserire il risultato nella cache.

    • Ottenere un valore della proprietà utilizzando la funzione di accesso get potrebbe avere un effetto secondario importante.

    • Richiamare il membro due volte in successione produce due risultati diversi.

    • L'ordine di esecuzione è importante. Si noti che deve essere possibile impostare e recuperare le proprietà di un tipo in qualsiasi ordine.

    • Il membro è static, ma restituisce un valore che può essere modificato.

    • Il membro restituisce una matrice. Le proprietà che restituiscono le matrici possono essere molto fuorvianti. In genere è necessario restituire una copia della matrice interna in modo che l'utente non possa modificare lo stato interno. Questa condizione, in aggiunta al fatto che un utente può facilmente presumere che si tratti di una proprietà indicizzata, determina un'inefficienza del codice. Nell'esempio di codice seguente ciascuna chiamata alla proprietà Methods crea una copia della matrice. Di conseguenza verranno create 2n+1 copie della matrice nel ciclo riportato di seguito.

      Dim type As Type = ' Get a type.
      Dim i As Integer
      For i = 0 To type.Methods.Length - 1 
         If type.Methods(i).Name.Equals("text") Then
            ' Perform some operation.
         End If 
      Next i
      [C#]
      Type type = // Get a type.
      for (int i = 0; i < type.Methods.Length; i++)
      {
         if (type.Methods[i].Name.Equals ("text"))
         {
            // Perform some operation.
         }
      }
      

Nell'esempio seguente viene illustrato l'uso corretto di proprietà e metodi.

Class Connection
   ' The following three members should be properties
   ' because they can be set in any order.   
   Property DNSName() As String
      ' Code for get and set accessors goes here.
   End Property
   Property UserName() As String
      ' Code for get and set accessors goes here.
   End Property
   Property Password() As String
      'Code for get and set accessors goes here.
   End Property
   ' The following member should be a method
   ' because the order of execution is important.
   ' This method cannot be executed until after the 
   ' properties have been set.   
   Function Execute() As Boolean
[C#]
class Connection
{
   // The following three members should be properties
   // because they can be set in any order.
   string DNSName {get{};set{};}
   string UserName {get{};set{};}
   string Password {get{};set{};}

   // The following member should be a method
   // because the order of execution is important.
   // This method cannot be executed until after the 
   // properties have been set.
   bool Execute ();
}

Proprietà di sola lettura e sola scrittura

Utilizzare una proprietà di sola lettura quando il membro di dati logico della proprietà non può essere modificato dall'utente. Non utilizzare le proprietà di sola scrittura.

Uso delle proprietà indicizzate

Nota   A un proprietà indicizzata è inoltre possibile fare riferimento come a un indicizzatore.

Le istruzioni per l'uso delle proprietà indicizzate sono definite dalle regole riportate di seguito.

  • Utilizzare una proprietà indicizzata quando il membro dei dati logico della proprietà è una matrice.

  • Provare a utilizzare solo valori integrali o stringhe per una proprietà indicizzata. Se la progettazione richiede altri tipi per la proprietà indicizzata, verificare che non si tratti di un membro dati logico. In caso contrario, utilizzare un metodo.

  • Provare a utilizzare un solo indice. Se la progettazione richiede più indici, verificare che non si tratti di un membro dati logico. In caso contrario, utilizzare un metodo.

  • Utilizzare solo una proprietà indicizzata per classe e impostarla come proprietà indicizzata predefinita per tale classe. Questa regola viene applicata dal supporto per l'indicizzatore del linguaggio di programmazione C#.

  • Non utilizzare proprietà indicizzate non predefinite. Questa operazione non è consentita in C#.

  • Assegnare il nome Item a una proprietà indicizzata. Per un esempio, vedere la proprietà DataGrid.Item. Seguire questa regola, a meno che non esista un nome più significativo per gli utenti, ad esempio la proprietà Chars sulla classe String. In C#, gli indicizzatori vengono sempre denominati Item.

  • Non fornire un metodo e una proprietà indicizzata che sono semanticamente equivalenti a due o più metodi di overload. Nell'esempio di codice seguente è necessario modificare la proprietà Method nel metodo GetMethod(string). Questa operazione non è consentita in C#.

    ' Change the MethodInfo Type.Method property to a method.
    Property Type.Method(name As String) As MethodInfo
    Function Type.GetMethod(name As String, ignoreCase As Boolean) As MethodInfo
    [C#]
    // Change the MethodInfo Type.Method property to a method.
    MethodInfo Type.Method[string name]
    MethodInfo Type.GetMethod (string name, Boolean ignoreCase)
    [Visual Basic]
    ' The MethodInfo Type.Method property is changed to
    ' the MethodInfo Type.GetMethod method.
    Function Type.GetMethod(name As String) As MethodInfo
    Function Type.GetMethod(name As String, ignoreCase As Boolean) As MethodInfo
    [C#]
    // The MethodInfo Type.Method property is changed to
    // the MethodInfo Type.GetMethod method.
    MethodInfo Type.GetMethod(string name)
    MethodInfo Type.GetMethod (string name, Boolean ignoreCase) 
    

Vedere anche

Istruzioni di progettazione per gli sviluppatori di librerie di classi | Convenzioni di denominazione per le proprietà | Istruzioni per l'uso dei membri di classe