¿Deberías hacer propiedades privadas?

19
private string mWhatever;

private string Whatever
{
    get
    {
        return this.mWhatever;
    }
    set
    {
        this.mWhatever = value;
    }
}

He visto a algunas personas que hacen propiedades para cada miembro, privado o no ... ¿Tiene sentido? Pude ver que tiene sentido en el 1% de los casos en los momentos en que desea controlar el acceso al miembro dentro de la clase que lo contiene porque si no usa las propiedades para cada miembro, generaría inconsistencias y verificar si el el miembro tiene acceso o no (ya que tiene acceso a ambos en el ámbito de la clase).

fordeka
fuente

Respuestas:

17

Respuesta corta: , cuando hay una necesidad. De lo contrario, use un getter y setter de propiedad implementado automáticamente comoprivate string Whatever { get; set;}

  • Es muy útil cuando está utilizando un enfoque de dominio cercano
  • También es útil cuando se debe verificar una lógica específica cuando se establece el valor

Aquí hay una descripción completa de cuándo usaría setters privados: uso de la propiedad C # .

Yusubov
fuente
También es realmente fácil hacer pública la propiedad más adelante si es necesario.
Tom Pickles
44
¿Qué es un enfoque de dominio cercano ?
Trisped
1
Me refería a un setter privado en una propiedad de la clase. Debe evitar el acceso directo al valor establecido desde el objeto y facilitar alguna verificación lógica antes de establecer el valor.
Yusubov
tener variables privadas con setter / getters públicos (incluso si son solo ingenuos) también tiene la ventaja de que si su código se compila en una biblioteca, la firma permanecerá sin cambios si reemplaza sus setter / getters ingenuos por otros que tienen efecto, etc. pero si utiliza una variable pública simple en su lugar, la firma de la biblioteca cambiaría si la cambiara a un setter / getters privado / no ingenuo. .. lo que significaría que si su biblioteca cambiara de esa manera, los consumidores de su biblioteca tendrían que volver a compilar.
orion elenzil
12

Algunas buenas respuestas aquí ya, pero creo que la mayoría de ellas pierden un punto de su pregunta:

He visto a algunas personas que hacen propiedades para cada miembro, privado o no ... ¿Tiene sentido?

Creo que esto rara vez es necesario de antemano, para cada miembro . Empezar con

 private string Whatever;

Cuando más tarde llegue a un punto en el que necesite encapsulación o un punto de interrupción condicional para este miembro específico, aún puede reemplazarlo por una propiedad con el mismo nombre, en la mayoría de los casos sin cambiar el código que utiliza Whatever. Pero cuidado, hay diferencias sutiles, vea la respuesta de Brian Rasmussen en esta publicación SO (enlace también proporcionado por Emmad Kareem, +1).

Doc Brown
fuente
+1, también, tienes razón, la mayoría de las respuestas omitieron los puntos de la pregunta original, somos los típicos chicos de TI :)
NoChance
Refactorizar un campo a una propiedad después del hecho es una pesadilla; requiriendo que recompile a todos los consumidores. Cambiar una propiedad automática para contener lógica personalizada es muy fácil. Si incluso hay una pequeña posibilidad de que el código vea el refactor, será mucho menos trabajo si solo usa las propiedades desde el principio.
Dan
@Dan: en la mayoría de los proyectos, he visto que el esfuerzo es el mismo, tienes que volver a compilar en ambos casos. Sin embargo, creo que tiene razón en que el uso de propiedades, especialmente las propiedades automáticas, puede ayudar a evitar algunos problemas sutiles con la reflexión o el uso de variables miembro en los parámetros "fuera".
Doc Brown
@DocBrown En cualquier caso, definitivamente no es una declaración general. Hay muchas razones para mantenerlo simple y usar un campo. Sin embargo, si puede predecir un refactor, vale la pena considerar incluso los problemas sutiles.
Dan
7

Una de las mayores ventajas de este enfoque es que puede controlar cuándo cambian sus variables .

Considera lo siguiente.
Estás depurando un gran proyecto. Un cierto método arroja una excepción. Establece un punto de interrupción y revela que una determinada variable miembro tiene un valor incorrecto. Digamos que su código se basa en esta variable de manera muy extensa, y encontrará cientos de usos de escritura ( escenario Spaghetti ). Por lo tanto, no puede determinar cuál de estos usos asignó un valor incorrecto.
Si el código es multiproceso, la depuración puede convertirse en una verdadera pesadilla.

Con la propiedad, puede establecer un punto de interrupción en un setter . Combinado con una condición, puede ser clavado en una sola carrera.

VC ++ tiene puntos de corte de datos , pero solo está disponible para código no administrado.

bytebuster
fuente
1

Si no usa una propiedad, su única otra opción es usar un campo para almacenar datos variables. Las propiedades y los campos tienen diferencias significativas. La mayoría de estas diferencias se encuentran en Campos vs. Propiedades . Le sugiero que estudie las diferencias y luego elija según las necesidades de su aplicación. Sin embargo, tenga en cuenta que el uso de campos en lugar de propiedades no es una práctica común de OO debido a su naturaleza y deficiencias.

Ninguna posibilidad
fuente
1

Las propiedades privadas pueden ser muy útiles para encapsular el comportamiento de cosas que son internas de su clase. El hecho de que sean privados no significa que no debas aprovechar el azúcar sintáctico que te dan las propiedades.


Sin embargo, el ejemplo dado es pobre. Los captadores y establecedores de propiedades de este tipo son casi siempre una mala idea. Si está utilizando C # 3.0 o posterior, las propiedades automáticas son una idea mucho mejor:

private string Whatever { get; set; };

Esto es más corto, más limpio y mucho más legible. De hecho, es apenas más largo que simplemente declarar la variable de respaldo.

Sin embargo, lo más importante es que las propiedades automáticas se pueden convertir en propiedades completas en cualquier momento sin cambiar la semántica del resto del programa. Por lo tanto, si necesita agregar validación o manejo de errores, puede hacerlo fácilmente.


Tal como están las cosas, siempre pensé que era una lástima que no pudieras tener una propiedad de solo lectura, para exigir solo poder escribir en el valor en el constructor ( prefiero los tipos inmutables ), pero esto se agregó en C # 6.0 como "Inicializadores de propiedad automática", que le permite hacer:

private string Whatever { get; } = ...;

o

private string Whatever { get; };

junto con

    Whatever = ...;

en el constructor

Mark Booth
fuente
0

No es absolutamente necesario hacer esto para propiedades privadas, pero permite modificar cómo la propiedad obtiene / establece el valor subyacente sin cambiar la forma en que se accede.

Por ejemplo, modificando cómo se establece el valor subyacente:

private string Whatever
{
    get
    {
        return this.mWhatever;
    }
    set
    {
        this.mWhatever = string.IsNullOrEmpty(value) ? string.Empty : value;
    }
}
Bernardo
fuente
0

Si

Yo personalmente uso esto como mecanismo de almacenamiento en caché, y podríamos llamarlo almacenamiento en caché a nivel de propiedad .

private List<User> users;
private List<User> Users
{
    get
    {
        if(users == null) users = new UserManager().GetUsers();
        return users;
    }
}

Ahora en otros lugares dentro de mi clase, uso la Userspropiedad en lugar del userscampo.

Otra situación factible podría ser cuando desea implementar algo de lógica en un campo, pero de manera centralizada en una clase. Así, ambos pueden crear un GetFoo()método o una Foopropiedad para el foocampo.

private string foo;
private string Foo
{
    get
    {
        return "Mr. " + foo;
    }
}
Saeed Neamati
fuente
0

Algo más a considerar:

Las propiedades reales son un detalle de implementación. Uno de los objetivos de OOP es tratar de minimizar la exposición de los detalles de implementación donde sea práctico.

La razón de esto es que si oculta las propiedades y solo las expone a través de getters y setters, tiene mucho más control. Por ejemplo, su captador puede validar su entrada y evitar que la propiedad se establezca en un estado no válido. Si la capacidad de cambiar el valor es de tiempo crítico, entonces el colocador puede proteger contra esto también. El getter, si el valor real es costoso por cualquier razón, puede realizar una inicialización y / o almacenamiento en caché.

Tener getters y setters expuestos y las propiedades ocultas significa que a veces no se necesita ninguna propiedad. Su captador puede calcular el valor en la invocación, o delegar la tarea en otro lugar, o cualquier cosa que le permita cumplir con sus especificaciones. Lo importante de su clase es la información a la que puede acceder desde ella, no cómo esa información se representa internamente.

Por supuesto, si no hay consecuencias negativas para que algo fuera de la clase pueda acceder a una propiedad directamente, entonces debe hacerlo público. Sin embargo, en mi experiencia, estos casos son relativamente pocos y distantes entre sí.

GordonM
fuente
0

Sé que esta es una vieja pregunta y todo lo que @Yusubov dijo es correcto, pero con respecto a su segundo punto sobre la lógica específica en el setter, y como no vi a nadie mencionar esto específicamente, un ejemplo perfecto sería cuando su clase implementa la INotifyPropertyChangedinterfaz

Esto se utiliza una tonelada en WCF y Silverlight, lo que le permite saber cuándo una propiedad específica ha cambiado a través de la lógica en su setter como en la clase a continuación:

public class Settings : INotifyPropertyChanged
{
    public Settings() 
    { 
        this.CountryField = String.Empty; 
    }

    private string CountryField;
    public string Country
    {
        get { return this.CountryField; }
        set
        {
            if (Object.ReferenceEquals(this.CountryField, value)) { return; }

            this.CountryField = value;
            this.RaisePropertyChanged("Country");
        } 
    }

    public event PropertyChangedEventHandler PropertyChanged;
    private void RaisePropertyChanged(string propertyName)
    {
        PropertyChangedEventHandler propertyChanged = this.PropertyChanged;

        if (propertyChanged != null)
            propertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
}
Code Maverick
fuente