¿Variable privada vs propiedad?

41

Al establecer un valor en una variable dentro de una clase, la mayoría de las veces se nos presentan dos opciones:

private string myValue;
public string MyValue
{
   get { return myValue; }
   set { myValue = value; }
}

¿Existe una convención que determine cómo debemos asignar valores a las variables dentro de nuestras clases? Por ejemplo, si tengo un método dentro de la misma clase, debería asignarlo usando la propiedad o la variable privada. Lo he visto en ambos sentidos, por lo que me preguntaba si esta es una opción o si el rendimiento es un factor (menor, probablemente).

Eduardo
fuente

Respuestas:

23

Lo llevaría un paso más allá y lo llevaría a 3 casos. Aunque hay variaciones en cada uno, estas son las reglas que uso la mayoría de las veces cuando programo C #.

En los casos 2 y 3, vaya siempre al Accesor de propiedades (no a la variable de campo). Y en el caso 1, se salva incluso de tener que tomar esta decisión.

1.) Propiedad inmutable (pasada al constructor, o creada en tiempo de construcción). En este caso, uso una variable de campo, con una propiedad de solo lectura. Elijo esto sobre un setter privado, ya que un setter privado no garantiza la inmutabilidad.

public class Abc
{ 
  private readonly int foo;

  public Abc(int fooToUse){
    foo = fooToUse;
  }

  public int Foo { get{ return foo; } }
}

2.) variable POCO. Una variable simple que puede obtener / establecer en cualquier ámbito público / privado. En este caso, simplemente usaría una propiedad automática.

public class Abc
{ 
  public int Foo {get; set;}
}

3.) Propiedades de enlace de ViewModel. Para las clases que admiten INotifyPropertyChanged, creo que necesita una variable de campo de respaldo privado.

public class Abc : INotifyPropertyChanged
{
  private int foo;

  public int Foo
  {
    get { return foo; }
    set { foo = value;  OnPropertyChanged("foo"); }
  }
}
Sheldon Warkentin
fuente
2
+1 para el ejemplo MVVM. En realidad, eso fue lo que provocó la pregunta en primer lugar.
Edward
44
+1: Mezcle 2/3 con AOP y tendrá una forma maravillosa de usar INPC. [Notifica] public int Foo {get; conjunto; }
Steven Evers
1
@ Job Para cualquier clase que acceda a la clase, un setter privado es suficiente para la inmutabilidad. Sin embargo, dentro de la clase, el setter privado no impide configuraciones repetidas del valor, después de la construcción inicial. Una característica del lenguaje como un 'conjunto privado de solo lectura' podría solucionar este problema conceptualmente, pero no existe.
Sheldon Warkentin
1
Soy nuevo en C #, así que dime, ¿por qué usarlo en public int Foo {get; set;}lugar de public int Foo?
1
Si una clase o estructura se va a comportar como un POCO o PODS, ¿qué ventaja real hay para envolver los campos en las propiedades? Entiendo completamente que envolver campos en propiedades es útil cuando una clase o estructura necesita, o podría necesitar en el futuro, mantener invariantes con respecto a su contenido (tal vez asegurando que otros objetos se actualicen para que coincidan), pero si una clase o estructura especifica que los consumidores pueden escribir cualquier valor en cualquier orden sin restricción o efectos secundarios, ¿qué comportamientos útiles podrían agregarse a un miembro miembro?
supercat
18

En general, diría asignar al campo en el constructor y usar la propiedad en cualquier otro lugar. De esta manera, si alguien agrega funcionalidad a la propiedad, no la perderá en ningún lado.

Ciertamente no es un factor de rendimiento. El optimizador incluirá un simple get o set para usted y el código final de MSIL probablemente será idéntico.

pdr
fuente
¿Alguna razón particular para usar el campo en el constructor? ¿Menos posibilidades de efectos secundarios extraños?
Firma
44
@Sign: Supongo que si hay validación en la propiedad (ahora o en el futuro), no querrás correr el riesgo de que falle la validación durante la construcción. La validación no es lógica en esta etapa porque no se puede garantizar que el objeto sea estable hasta que finalice el constructor.
Steven Evers
@Sign: tanto lo que dijiste como lo que dijo Snorfus. O si deseo registrar los cambios en una propiedad, es probable que no quiera registrar la configuración inicial. Pero sí dije "en general".
pdr
3
@Sign: el problema es: si el método establecido de la propiedad puede anularse en una subclase, puede tener un efecto secundario durante la creación del objeto o un objeto inconsistente (es decir: la propiedad anulada está programada para no establecer ningún valor para Ese campo). El uso de propiedades en constructores solo es seguro si el método set es privado o si la clase está sellada.
Diego
4

Depende

Primero debe preferir las propiedades automáticas cuando sea posible:

public string MyValue {get;set;}

Segundo, el mejor enfoque probablemente sería usar las propiedades, si tiene alguna lógica allí, probablemente debería pasarla usted mismo, especialmente si esa lógica es la sincronización de subprocesos.

Pero también debe tener en cuenta que podría obstaculizar su rendimiento (un poco), si está sincronizando incorrectamente puede bloquearse, y en algún momento la ruta correcta es dar la vuelta a la lógica en la propiedad.

ALASKA_
fuente
3
También me gusta public string MyValue {get; private set;}.
Trabajo
3

Bueno, el enfoque directo al punto sería simplemente asignarlo a la variable en sí, ya que estás dentro del método de una clase y controlas el comportamiento de la clase, de todos modos.

Pero el punto central de las propiedades es que abstraen la variable. Mientras que una propiedad tan simple como en su ejemplo no tiene ningún uso sobre una simple variable de miembro público, las propiedades generalmente hacen (o deberían hacer) cosas adicionales dentro de sus captadores y establecedores. Y si desea que estas cosas se realicen automáticamente al cambiar la propiedad dentro de la clase, entonces es más claro trabajar en la propiedad en lugar de la variable para no tener que cambiar cada asignación de variable cuando cambia el comportamiento de configuración de la propiedad.

Solo tienes que razonar sobre esto conceptualmente. La propiedad es en realidad un identificador para acceder a algún estado interno del objeto, que puede estar compuesto por más de una variable miembro. Por lo tanto, debe preguntarse si desea cambiar solo el estado interno subyacente (o solo una parte de él) o la propiedad abstracta que representa este estado en su conjunto, y la mayoría de las veces es la última, ya que generalmente desea que su objeto siempre tenga Un estado consistente.

Chris dice reinstalar a Mónica
fuente
2

Si existe alguna posibilidad de que la implementación get / set de esa propiedad cambie a veces más tarde (por ejemplo, si desea generar un evento al llamar set, o agregará algún mecanismo de evaluación diferido más tarde a su getfunción), puede ser una buena idea que su código dentro de la clase usará la propiedad en casi todos los casos, excepto en los casos, muy probablemente raros, en los que explícitamente no desea que se usen esos eventos o mecanismos de evaluación diferidos.

De todos modos, haga lo que haga, hay una buena posibilidad de que cuando cambie la implementación de la propiedad más adelante de esa manera, tenga que mirar todos los lugares dentro de su clase que acceden a esa propiedad para verificar si realmente se accede a la propiedad o el Se utilizará la variable privada.

Doc Brown
fuente
2

Siempre uso la propiedad pública.

A menudo, alguna lógica que siempre debe ejecutarse cuando se establece la propiedad se agrega al setmétodo de una propiedad, y al establecer el campo privado en su lugar, el establecedor público omitirá cualquier lógica allí.

Tiene un comentario sobre MVVM que lleva a esta pregunta, y creo que esto es aún más importante cuando se trabaja con MVVM. Muchos objetos generan una PropertyChangenotificación al instalador, y otros objetos pueden suscribirse a este evento para ejecutar alguna acción cuando cambian propiedades específicas. Si establece la variable privada, estas acciones nunca se ejecutarán a menos que también genere manualmente el PropertyChangedevento.

Rachel
fuente
+1 Sí, en la mayoría de los casos (MVVM), el evento PropertyChanged es imprescindible. Y eso solo puede ser despedido dentro de una propiedad. Buena explicación.
Edward
1

En general, depende de usted lo que debe hacer con una propiedad y su campo de respaldo al obtener / configurar.

En la mayoría de los casos, solo para ser coherente en todo el código, debe utilizar los accesos públicos donde estén disponibles y sean apropiados. Eso le permite refactorizar con un mínimo cambio de código; si el método que realiza esta configuración debe eliminarse de la clase y colocarse en otro lugar donde el campo de respaldo ya no esté disponible (como una clase base), ¿a quién le importa? Estás usando algo que está disponible donde sea que la clase misma haga el trabajo. El campo de respaldo, en la mayoría de los casos, es un detalle de implementación; nadie fuera de tu clase debería saber que existe.

La situación principal que se me ocurre cuando debe usar el campo de respaldo y NO el descriptor de acceso a la propiedad es cuando el descriptor de acceso tiene lógica adicional (validación o actualización de otra información de estado en la clase) que no desea ejecutar. La población inicial de un objeto es un ejemplo; puede tener una clase que use dos valores de propiedad para calcular un tercero, que también se almacena en un campo de respaldo (por razones de persistencia). Al inicializar una nueva copia de los datos de ese objeto desde la base de datos, los accesores de propiedad que recalculan el tercer valor pueden quejarse si no se establece el otro valor necesario. Al usar los campos de respaldo para establecer los valores iniciales de estas dos (o tres) propiedades, omite la lógica de validación / cálculo hasta que la instancia esté en un estado lo suficientemente consistente para que la lógica funcione normalmente.

KeithS
fuente
0

Siempre use el que tenga sentido. Sí, sé que suena bastante falso hasta el punto de no ser una respuesta.

El punto de las propiedades es proporcionar una interfaz a través de la cual puede acceder de forma segura a un modelo de datos. Para la mayoría de las situaciones, siempre desea acceder de forma segura al modelo de datos a través de esa interfaz, como:

public Foo Bar
{
  get { return _bar; }
  set { _bar = doSomethingTo(value); }
}

Pero en otras situaciones, simplemente puede estar usando una propiedad como una vista de un modelo de datos:

public Double SomeAngleDegrees
{
  get { return SomeAngleRadians * 180 / PI; }
  set { SomeAngleRadians = value * PI / 180; }
}

Si tiene sentido usar la forma de radianes SomeAngle, entonces utilícela por todos los medios.

Al final, asegúrese de beber su propio kool-aid. Su API de cara al público debe ser lo suficientemente resistente como para trabajar internamente.

zzzzBov
fuente