¿Debo preferir propiedades con o sin campos privados?

16

La base de código en la que estoy trabajando ahora tiene la convención de usar campos privados y propiedades públicas. Por ejemplo, la mayoría de las clases tienen sus miembros definidos así:

// Fields
private double _foo;
private double _bar;
private double _baz;

// Properties
public double Foo
{
    get{ return _foo; }
    set{ _foo = value; }
}

public double Bar
{
    get{ return _bar; }
    set{ _bar = value; }
}

public double Baz
{
    get{ return _baz; }
}

Sé que estos pueden reescribirse sin sus propiedades privadas internas:

public double Foo{ get; set; }
public double Bar{ get; set; }
public double Baz{ get; private set; }

Quisiera alguna entrada sobre esto:

  • ¿Hay una buena razón para preferir el estilo más antiguo y explícito sobre el más nuevo y conciso?
  • ¿Debo escribir nuevas clases usando el estilo conciso, o debería tratar de hacer coincidir el código anterior para mantener la coherencia? ¿La coherencia vale lo suficiente en este caso para justificar el formato anterior?
KChaloux
fuente
Vea esta pregunta y respuesta .
Peter K.
@PeterK. Informativo. No responde si debería o no preocuparme por mantener el estilo del resto del programa, o si es un detalle lo suficientemente pequeño como para no importar.
KChaloux
@KChaloux: ¡Entendido! Por eso es un comentario, no una respuesta. :-)
Peter K.
@PeterK. Feria 'nuff = p
KChaloux
1
otra razón para no cambiar es si se pasa algo por el cable con WCF: las propiedades automáticas tienen problemas de contrato (debido a los caracteres no válidos en los nombres de campo de respaldo creados por .NET)
Leon

Respuestas:

16

Hay un par de instancias donde aún se requiere el llamado estilo "antiguo":

R: Tipos inmutables que usan la inmutabilidad proporcionada por el lenguaje. El readonlymodificador en C # congela ese valor después de la construcción. No hay forma de imitar esto con propiedades implementadas automáticamente (todavía).

public sealed class FooBarBazInator
{
    private readonly double foo;

    public FooBarBazInator(double foo)
    {
        this.foo = foo;
    }

    public double Foo
    {
        get
        {
            return this.foo;
        }
    }
}

B: Tus captadores / establecedores tienen alguna lógica. El código WPF y Silverlight (y similares) que están vinculados a datos a sus clases se implementarán de la siguiente INotifyPropertyChangedmanera:

public class FooBarBazInator : INotifyPropertyChanged
{
    private double foo;

    public event PropertyChangedEventHandler PropertyChanged;

    public double Foo
    {
        get
        {
            return this.foo;
        }

        set
        {
            this.foo = value;
            this.RaisePropertyChanged("Foo");
        }
    }

    private void RaisePropertyChanged(string propertyName)
    {
        var propertyChanged = this.PropertyChanged;

        if (propertyChanged != null)
        {
            propertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

Aparte de eso, diría que usa el nuevo estilo. Mantiene su código conciso y ofrece menos área de superficie para que se introduzcan los errores. Además, si alguna vez necesita cambiar para incluir la lógica, no será incompatible con la firma.

Jesse C. Slicer
fuente
2
Todos parecen estar de acuerdo sobre ir con el nuevo estilo. Obtiene un +1 y el crédito de respuesta por contarme sobre la necesidad de un campo con el readonlymodificador, que no sabía. = D
KChaloux
3
Algunos (incluyéndome a mí) consideran las propiedades automotrices con acceso privado "suficientemente buenas" para propiedades inmutables. Es cierto que puede que no sean realmente inmutables, pero solo debes tener cuidado de no usar el setter fuera del constructor.
svick
2
Otra razón es si desea pasar el valor refque no funciona con las propiedades, por lo que necesita el campo
stijn
1
Una herramienta como Fody puede implementarse INotifyPropertyChangedsin la necesidad de campos de respaldo explícitos. github.com/Fody/PropertyChanged
Sebastian Redl
3
Tome nota: C # 6 permite "propiedades automáticas de solo getter" que proporciona en una sola línea lo que está haciendo mi ejemplo "A".
Jesse C. Slicer
6

Diría que tener un campo de respaldo explícito es simplemente inútil: hace que su código sea más largo y difícil de leer. También agrega potencial de error:

public double Bar
{
    get { return _baz; }
    set { _bar = value; }
}

Creo que un error como este podría ocurrir con bastante facilidad y sería bastante difícil de detectar.

Y si desea coherencia, hágalo de la otra manera: cambie el código que usa campos de respaldo en código que usa propiedades automáticas. Esto es especialmente fácil si usa una herramienta de refactorización como ReSharper (y si no usa algo así, creo que debería comenzar).

Por supuesto, todavía hay casos en los que es necesario tener un campo de respaldo, pero creo que eso no es relevante para esta pregunta.

svick
fuente
De hecho, he hecho eso en algunos de los viejos campos al refactorizar el código ... es un fastidio.
KChaloux
1

Aunque la pregunta es bastante antigua, pensé en agregar un par de puntos que leí de un libro que vale la pena mencionar aquí.

  1. Los motores de serialización en tiempo de ejecución conservan el nombre del archivado en una secuencia serializada. El compilador determina el nombre del campo de respaldo para una propiedad implementada automáticamente (AIP), y en realidad podría cambiar el nombre de este campo de respaldo cada vez que recompila su código, negando la capacidad de deserializar instancias de cualquier tipo que contenga un AIP Por lo tanto, no use AIP con ningún tipo que desee serializar o deserializar.

  2. Al depurar, no puede poner un punto de interrupción en un método get o set de AIP, por lo que no puede detectar fácilmente cuándo una aplicación obtiene o configura esta propiedad. Puede establecer puntos de interrupción en puntos de interrupción implementados manualmente

RAM
fuente
3
# 1 - La mayoría de los serializadores ignoran las propiedades / campos privados por defecto; Nunca me he encontrado con los problemas que mencionas. # 2 - Esto se solucionó en VS2015 y se puede solucionar en VS2013. Vea este artículo de Visual Studio Magazine .
Brian
-3

¿Hay una buena razón para preferir el estilo más antiguo y explícito sobre el más nuevo y conciso?

Realmente no. Aunque podría argumentar que cada vez que tiene un pase a través de una propiedad como esa, debería haber utilizado un campo público para empezar.

¿Debo escribir nuevas clases usando el estilo conciso, o debería tratar de hacer coincidir el código anterior para mantener la coherencia? ¿La coherencia vale lo suficiente en este caso para justificar el formato anterior?

Suponiendo que ignoraste los consejos anteriores y tienes propiedades pasantes, no trataría de hacer coincidir el estilo. Idealmente, volvería y refactorizaría el estilo antiguo en el nuevo estilo para que sea coherente, pero la posibilidad de que la gente lea mal ese código es escasa.

Telastyn
fuente
@svick - Bah. A menos que estés reflexionando sobre el tipo, no hay diferencia. Si está exponiendo públicamente el tipo como una interfaz de servicio o biblioteca, expondrá una interfaz que requerirá una propiedad de todos modos. El OP no está haciendo ninguna de esas cosas.
Telastyn
2
No hay ninguna razón técnica en estos casos limitados. No olvides que las propiedades se comportan de manera diferente en el depurador, los diagramas de clase, el intellisense y, justo cuando crees que no estás haciendo una reflexión, el framework .NET sí lo es. WinForms, WPF y varios otros componentes utilizan la reflexión internamente y tratan las propiedades por separado, por lo que muchos de los desarrolladores de este sitio recibirán mal el consejo de usar campos públicos.
Kevin McCormick
1
@KevinMcCormick y el marco se comporta perfectamente bien (por lo que entiendo) con los campos. Realmente no importa ya que ahora hay propiedades automáticas, pero el mayor problema es hacer públicas las cosas en primer lugar. Lejos, demasiadas personas piensan que simplemente hacer que las propiedades sean de alguna manera los absuelve de 'no hacer campos públicos'.
Telastyn
2
El Framework los maneja de manera diferente: intente usar un campo público en EF POCO, vincúlelo a un DataGridView, use un PropertyGrid, etc. Acabo de hacer una búsqueda rápida en mi descompilador a Type.GetProperties / GetProperty y el Framework hace llamadas a este método cientos de veces, pero no a GetField. En pocas palabras, son diferentes, y la recomendación de usar siempre propiedades para datos públicos se basa en parte en este hecho. Esto no impide el uso de campos, pero ese caso necesita justificación. Sin embargo, su otro punto es definitivamente cierto, exponer los datos públicos sin cuidado siempre es una mala idea.
Kevin McCormick