Share via


Instrucciones de uso de propiedades

Determina si es más adecuado utilizar una propiedad o un método en función de las necesidades. Para obtener más información sobre la elección entre propiedades o métodos, vea Propiedades y métodos.

Elija un nombre para la propiedad siguiendo las Instrucciones de nomenclatura de propiedades recomendadas.

Cuando tenga acceso a una propiedad mediante el descriptor de acceso set, conserve el valor de la propiedad antes de cambiarlo. De este modo, se garantiza que los datos no se perderán si el descriptor de acceso set inicia una excepción.

Problemas de estado de propiedades

Permita que las propiedades se establezcan en cualquier orden. Las propiedades deben ser independientes con relación a otras propiedades. Suele ocurrir a menudo que la función de un objeto no se activa hasta que el programador define un conjunto de propiedades específico; o hasta que el objeto tiene un estado determinado. Si el objeto no tiene el estado correcto, la función no se activa. Cuando el objeto tenga el estado correcto, la función se activará automáticamente sin necesidad de una llamada explícita. La semántica es la misma independientemente del orden en el que el programador establezca los valores de la propiedad, o de cómo consiga que el objeto tenga el estado activo.

Por ejemplo, un control TextBox puede contener dos propiedades relacionadas: DataSource y DataField. DataSource especifica el nombre de la tabla y DataField el nombre de la columna. Una vez especificadas las dos propiedades, el control puede enlazar automáticamente datos de la tabla a la propiedad Text del control. En el siguiente ejemplo de código se muestran las propiedades que se pueden establecer en cualquier orden.

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.

Las propiedades DataSource y DataField se pueden establecer en cualquier orden. Por tanto, el código anterior es equivalente a lo siguiente:

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.

También se puede establecer una propiedad como null (Nothing en Visual Basic) para indicar que no se especifica ningún valor.

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.

En el siguiente ejemplo de código se muestra cómo realizar el seguimiento del estado de la función de enlace de datos y activarla o desactivarla automáticamente en el momento adecuado.

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.
   }
}

En el ejemplo anterior, la siguiente expresión determina si el objeto está en un estado en el que la función de enlace de datos se activa automáticamente.

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

La activación automática se define mediante la creación de un método que determina si el objeto se puede activar en función de su estado actual y, a continuación, se activa si es necesario.

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);
}

Si tiene propiedades relacionadas, como DataSource y DataMember, considere la posibilidad de implementar ISupportInitialize (Interfaz). De este modo, el diseñador (o el usuario) podrá llamar a los métodos ISupportInitialize.BeginInit e ISupportInitialize.EndInit al establecer varias propiedades para que el componente pueda proceder a optimizar. En el ejemplo anterior, ISupportInitialize puede evitar los intentos innecesarios de obtener acceso a la base de datos hasta que se haya realizado correctamente la configuración.

La expresión que aparece en este método indica las partes del modelo de objetos que se deben examinar para exigir estas transiciones de estado. En este caso, afecta a las propiedades DataSource y DataField. Para obtener más información sobre la elección entre propiedades o métodos, vea Propiedades frente a métodos.

Provocar eventos PropertyChanged

Los componentes deben provocar eventos PropertyChanged, si se desea notificar a los consumidores cuando se cambia la propiedad del componente mediante programación. La convención de nomenclatura para un evento PropertyChanged es agregar el sufijo Changed al nombre de la propiedad, como en TextChanged. Por ejemplo, un control puede provocar un evento TextChanged cuando se cambia la propiedad de texto. Para provocar este evento, se puede utilizar la rutina auxiliar protegida Raise<Property>Changed. No obstante, es posible que la sobrecarga para provocar el evento PropertyChanged y agregar un elemento a la tabla hash no compense. En el siguiente ejemplo de código se muestra la implementación de una rutina auxiliar en un evento PropertyChanged.

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();
         }
      }
   }
}

El enlace de datos utiliza este modelo para permitir el enlace de la propiedad en ambos sentidos. Sin los eventos <Property>Changed y Raise<Property>Changed, el enlace de datos funciona en un sentido; si la base de datos cambia, la propiedad se actualiza. Cada propiedad que provoca el evento <Property>Changed debe proporcionar los metadatos que indican que la propiedad admite el enlace de datos.

Conviene provocar eventos de cambio/cambiados, si el valor de una propiedad cambia debido a operaciones externas. Estos eventos indican al programador que el valor de una propiedad está cambiando o ha cambiado como resultado de una operación y no mediante una llamada al método en el objeto.

La propiedad Text de un control Edit es un ejemplo claro. Mientras el usuario escribe información en el control, el valor de la propiedad cambia automáticamente. Se provoca un evento antes de que el valor de la propiedad cambie. No se pasa ni el valor antiguo ni el nuevo, y el programador puede cancelar el evento iniciando una excepción. El nombre del evento es el nombre de la propiedad seguido del sufijo Changing. En el siguiente ejemplo de código se muestra un evento de cambio.

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;
         }
      }
   }
}

También se provoca un evento después de cambiar el valor de la propiedad. Este evento no se puede cancelar. El nombre del evento es el nombre de la propiedad seguido del sufijo Changed. También se debe provocar el evento PropertyChanged genérico. El modelo para provocar estos dos eventos es provocar el evento específico desde el método OnPropertyChanged. En el siguiente ejemplo se muestra el uso del método 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);
   }
}

En algunos casos el valor subyacente de una propiedad no se almacena como campo, esto complica el seguimiento de los cambios de valores. Al provocar el evento de cambio, busque todos los lugares en los que el valor de la propiedad puede cambiar y proporcione la capacidad de cancelar el evento. Por ejemplo, el control Edit del ejemplo anterior no es del todo exacto porque el valor Text está almacenado en realidad en el identificador de ventana (HWND). Para provocar el evento TextChanging, debe examinar los mensajes de Windows para determinar cuando puede cambiar el texto; y permitir que se inicie una excepción en OnTextChanging para cancelar el evento. Si es muy difícil proporcionar un evento de cambio, se puede admitir sólo el evento cambiado.

Propiedades frente a métodos

Los diseñadores de bibliotecas de clases deben decidir a menudo entre implementar un miembro de la clase como una propiedad o como un método. En general, los métodos representan acciones y las propiedades representan datos. Utilice las instrucciones siguientes para decidir entre estas opciones.

  • Utilice una propiedad cuando el miembro sea un miembro de datos lógico. En las siguientes declaraciones de miembro, Name es una propiedad porque es un miembro lógico de la clase.

    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;
       }
    
  • Utilice un método cuando:

    • La operación es una conversión, como Object.ToString.

    • La operación es bastante costosa y, por tanto, desea ponerse en contacto con el usuario para que tenga en cuenta que debe almacenar el resultado en la caché.

    • Obtiene el valor de una propiedad mediante el descriptor de acceso get y esta acción tiene efectos secundarios visibles.

    • Al llamar al miembro dos veces seguidas da lugar a resultados distintos.

    • El orden de ejecución es importante. Tenga en cuenta que las propiedades de un tipo se deben poder establecer y recuperar en cualquier orden.

    • El miembro es estático, pero devuelve un valor que se puede cambiar.

    • El miembro devuelve una matriz. Las propiedades que devuelven matrices pueden ser muy imprecisas. Por lo general, es necesario devolver una copia de la matriz interna para que el usuario no pueda cambiar el estado interno. Esto junto con el hecho de que el usuario puede creer fácilmente que es una propiedad indizada, conduce a un código ineficaz. En el siguiente ejemplo de código, cada llamada a la propiedad Methods crea una copia de la matriz. Como resultado, se crearán 2n+1 copias de la matriz en el siguiente bucle.

      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.
         }
      }
      

En el ejemplo siguiente se muestra el uso correcto de propiedades y métodos.

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 ();
}

Propiedades de sólo lectura y escritura

Se debe utilizar una propiedad de sólo lectura cuando el usuario no puede cambiar el miembro de datos lógicos de la propiedad. No utilice propiedades de sólo escritura.

Uso de propiedades indizadas

Nota   Las propiedades indizadas también se conocen como indizadores.

En las reglas siguientes se describen las instrucciones de utilización de propiedades indizadas:

  • Utilice una propiedad indizada cuando el miembro de datos lógico de la propiedad sea una matriz.

  • Considere la utilización sólo de valores enteros o cadenas para las propiedades indizadas. Si el diseño requiere otros tipos para las propiedades indizadas, reconsidere si representa un miembro de datos lógico. Si no es así, use un método.

  • Considere usar sólo un índice. Si el diseño requiere varios índices, reconsidere si representa un miembro de datos lógico. Si no es así, use un método.

  • Utilice sólo una propiedad indizada por clase y convierta esta propiedad en el valor predeterminado de esa clase. La compatibilidad con indizadores del lenguaje de programación C# hace que se cumpla esta regla.

  • No utilice propiedades indizadas no predeterminadas. C# no lo permite.

  • Asigne el nombre Item a una propiedad indizada. Por ejemplo, vea la Propiedad DataGrid.Item. Siga esta regla, a no ser que haya un nombre más obvio para los usuarios como la propiedad Chars de la clase String. En C#, los indizadores siempre adoptan el nombre Item.

  • No proporcione una propiedad indizada y un método que sean equivalentes semánticamente a dos o más métodos sobrecargados. En el siguiente ejemplo de código, se debe cambiar la propiedad Method al método GetMethod(string). Tenga en cuenta que esto no está permitido en 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) 
    

Vea también

Instrucciones de diseño para programadores de bibliotecas de clases | Instrucciones de nomenclatura de propiedades | Instrucciones de uso de miembros de clases