entender a los setters privados

90

No entiendo la necesidad de tener establecedores privados que comenzaron con C # 2.

Tener un método de establecimiento para mí es permitir que el usuario establezca algunas variables en esa clase. Al hacerlo, no expondremos las variables directamente a los usuarios. En su lugar, les dejamos hacerlo a través de este método de establecimiento público.

Esto para mí está usando "encapsulación". Existen algunos argumentos que afirman que los establecedores privados le permitirán aplicar encapsulación.

¿No estoy usando la encapsulación usando métodos de establecimiento públicos? ¿Por qué necesitamos setters privados?

¿Cuál es la diferencia entre una clase inmutable y una clase con establecedores privados?

Cura principal
fuente
1
Me encantaron los setters privados a lo grande, me ayudó a replantear las clases feas. También hacen que sea imposible declarar y establecer una variable de instancia no constante a la vez, así: private File settingsFile = null;y luego en uno de los constructores: if (settingsFile == null) { settingsFile = GetSettingsFile() };. Refactorizar código como ese me hizo llorar a veces :). El hecho de que pueda establecer un miembro antes del constructor no significa que deba hacerlo, ya que, con varios constructores, esto hace que sea DIFÍCIL seguir la lógica. Los establecedores privados te obligan a establecer valores dentro del constructor o más tarde.
Hamish Grubijan

Respuestas:

261

Lógicamente.

La presencia de un establecedor privado se debe a que puede usar la propiedad automática:

public int MyProperty { get; set; }

¿Qué harías si quieres que sea de solo lectura?

public int MyProperty { get; }

¡¡Oh mierda!! No puedo acceder a él desde mi propia clase; Debería crearlo como una propiedad normal:

private int myProperty;
public int MyProperty { get { return myProperty; } }

Hmm ... pero perdí la función "Propiedad automática" ...

public int MyProperty { get; private set; }

AHHH .. eso es mejor !!

ktutnik
fuente
Gracias. Esto tiene sentido nuevamente
Dene
3
@ktutnik Gracias por presentar esto de la manera que lo hizo. ¡Para mí también tiene sentido ahora!
Vivek M. Chawla
3
Respuesta excelentemente ilustrada.
Imnk
3
Oh crap!! I can't access it from my own classA partir de C # 6.0, esto solo es cierto fuera de la fase de inicialización. Vea mi respuesta stackoverflow.com/a/34223746/198797
tsemer
1
Agregar a la respuesta de # tsemer, con c # 6, {get; }NO es equivalente a { get; private set; }. Por la primera vía property.GetSetMethod(true)regresa nully la segunda true. Esto me sorprendió.
emragins
37

Un establecedor privado es útil si tiene una propiedad de solo lectura y no desea declarar explícitamente la variable de respaldo.

Entonces:

public int MyProperty
{
    get; private set;
}

es lo mismo que:

private int myProperty;
public int MyProperty
{
    get { return myProperty; }
}

Para propiedades no implementadas automáticamente, le brinda una forma coherente de configurar la propiedad desde dentro su clase para que, si necesita validación, etc., solo la tenga en un lugar.

Para responder a su pregunta final, MSDN tiene esto que decir sobre los establecedores privados:

Sin embargo, para clases pequeñas o estructuras que solo encapsulan un conjunto de valores (datos) y tienen poco o ningún comportamiento, se recomienda hacer que los objetos sean inmutables declarando el descriptor de acceso del conjunto como privado.

Desde la página de MSDN sobre propiedades implementadas automáticamente

ChrisF
fuente
1
Lo siento, todavía no veo el valor agregado por tener setters privados. Si no queremos que el setter quede expuesto, solo tenemos getter. Si queremos agregar un validador, podemos tener un establecedor público y agregarle validación. ¿Por qué necesitamos un setter que no es accesible? La forma en que lo entiendo es como "Toma este auto, pero no puedes conducirlo" ¿Por qué quieres darme el auto si no voy a conducirlo de todos modos?
Dene
@Dene: ciertamente puedes hacer eso, ya que no está mal. No es obligatorio tener propiedades implementadas automáticamente.
ChrisF
Sé que no hay nada de malo en hacer lo que expresé. Es solo para mí apreciar la mejora hecha por C # 2. Parece que hay mucho entusiasmo a su alrededor, pero no puedo sentirlo ni ver el valor.
Dene
@Dene, me perdí toda la publicidad al respecto. Pero, cuando finalmente vi que era posible hacer esto, me alegré porque sabía cómo limpiar algunas clases largas de la era .Net 1.1. Aunque probablemente pueda usar el valor de la propiedad que aún no se ha establecido, hacerlo es menos natural que usar el valor de una variable miembro de instancia que se ha establecido en nulo.
Hamish Grubijan
Simplifica el código. Al igual que las propiedades de los automóviles. Muchas de las actualizaciones posteriores de C # tratan de hacer que el código sea más conciso y, por lo tanto, legible. Sin embargo, aún puede hacer las cosas de otra manera si lo desea; la compatibilidad con versiones anteriores también parece ser un gran objetivo. Supongo que es cuestión de gustos.
niico
18

Es bastante simple. Los establecedores privados le permiten crear propiedades públicas o protegidas de solo lectura.

Eso es. Ésa es la única razón.

Sí, puede crear una propiedad de solo lectura especificando solo el captador, pero con las propiedades autoimplementadas, debe especificar tanto get como set, por lo que si desea que una propiedad autoimplementada sea de solo lectura, debe usar setters privados. No hay otra forma de hacerlo.

Es cierto que los establecedores privados no se crearon específicamente para propiedades de solo lectura implementadas automáticamente, pero su uso es un poco más esotérico por otras razones, y se centra principalmente en las propiedades de solo lectura y el uso de la reflexión y la serialización.

Erik Funkenbusch
fuente
2
Gracias "si desea que una propiedad implementada automáticamente sea de solo lectura, debe utilizar establecedores privados". Esto tiene sentido para mí
Dene
17

Con la introducción de C # 6.0 y la sintaxis para inicializadores de propiedades automáticas , los definidores privados ya no son necesarios para las propiedades que solo se establecen en la inicialización, ya sea en línea o dentro del constructor.

Estas nuevas sintaxis ahora compilan:

Propiedad inicializada en línea

public class MyClass1 {
  public string MyProperty { get; } = "Aloha!"
}

Propiedad inicializada del constructor

public class MyClass2 {
  public string MyProperty { get; }

  public MyClass2(string myProperty) {
    MyProperty = myProperty;
  }
}
tsemer
fuente
3
@Ziggler, de hecho no lo hace. OP tampoco lo pide. Simplemente no comprende la necesidad de tenerlos. Esto responde: "ya no es necesario tenerlos en este escenario".
tsemer
6

No entiendo la necesidad de tener establecedores privados que comenzaron con C # 2.

Por ejemplo, la clase de factura permite al usuario agregar o eliminar elementos de la propiedad Elementos, pero no permite que el usuario cambie la referencia Elementos (es decir, el usuario no puede asignar la propiedad Elementos a otra instancia de objeto de lista de elementos).


public class Item
{
  public string item_code;
  public int qty;

  public Item(string i, int q)
  {
    this.item_code = i;
    this.qty = q;
  }
}

public class Invoice
{
  public List Items { get; private set; }

  public Invoice()
  {
    this.Items = new List();
  }
}

public class TestInvoice
{
  public void Test()
  {
    Invoice inv = new Invoice();
    inv.Items.Add(new Item("apple", 10));

    List my_items = new List();
    my_items.Add(new Item("apple", 10));

    inv.Items = my_items;   // compilation error here.
  }
}
lauhw
fuente
+1 por resaltar que las propiedades se pueden manipular a través del getter público, aunque tengan un establecedor privado.
user1725145
4

Digamos, por ejemplo, que no almacena la variable real a través de la propiedad ni usa el valor para calcular algo.

En tal caso, puede crear un método para hacer su cálculo

private void Calculate(int value)
{
 //...
}

O puede hacerlo usando

public int MyProperty {get; private set;}

En esos casos, recomendaría usar el último, ya que las propiedades refactorizan cada elemento miembro intacto.

Aparte de eso, incluso si dice que asigna la propiedad con una variable. En tal caso, dentro de su código desea escribir así:

public int myprop;
public int MyProperty {get { return myprop;}}

... ...

this.myprop = 30;

... ...
if(this.MyProperty > 5)
   this.myprop = 40;

El código anterior se ve horrible ya que el programador siempre debe tener cuidado al usar MyProperty para Get y myprop para Set.

Para mayor coherencia, puede usar un establecedor privado que hace que Propoerty sea solo de lectura en el exterior, mientras que puede usar su configurador dentro de su código.

abhishek
fuente
3

Creo que algunas personas han bailado sobre esto, pero para mí, el valor de los establecedores privados es que puedes encapsular el comportamiento de una propiedad, incluso dentro de una clase. Como señaló abhishek, si desea disparar un evento de cambio de propiedad cada vez que cambia una propiedad, pero no desea que una propiedad se lea / escriba para el público, entonces debe usar un establecedor privado o debe aumentar el evento en cualquier lugar donde modifique el campo de respaldo. Este último es propenso a errores porque es posible que lo olvide. De manera relacionada, si la actualización de un valor de propiedad da como resultado que se realice algún cálculo o que se modifique otro campo, o una inicialización perezosa de algo, entonces también querrá resumir eso en el setter privado en lugar de tener que recordar hacerlo en todas partes. uso del campo de respaldo.

siride
fuente
2

La encapsulación significa que el estado de un objeto solo ocurre a través de una interfaz definida, y debido a esto, la clase puede asegurarse de que este estado sea siempre válido y de acuerdo con el propósito de la clase.

En algunos casos, por lo tanto, está perfectamente de acuerdo con el principio de encapsulación simplemente exponer un campo públicamente: todos los valores posibles para el campo son válidos con todos los demás valores posibles de todos los demás campos y, por lo tanto, el programador puede decidir activamente permitir el campo para ser manipulado libremente por código externo.

Sin embargo, estos casos están restringidos principalmente a clases que son en su mayoría "datos antiguos sin formato". Tampoco son muy interesantes en este sentido, así que basta de ellos.

En otros casos, en otros lenguajes, se tendría un método getter y setter, algo así como int getId()obtener un valor y void setId(int val)actualizarlo.

Las propiedades nos permiten usar la misma sintaxis para leer y escribir a través de métodos que usaríamos para leer y escribir un campo. Este es un buen azúcar sintáctico, aunque no vital.

(En realidad, debido a la forma en que funciona la reflexión y casos como DataBinder.Evaléste, puede ser útil tener una propiedad incluso cuando un campo funcionaría bien, pero eso es otro asunto).

Hasta que se introduzcan los setters privados (en realidad, lo que cambió con C # 2 es la sintaxis para tener un setter privado y un getter público o protegido en el mismo bloque), podríamos tener un método privado para hacer el trabajo del setter privado, entonces los setters privados no son realmente necesarios. Sin embargo, son útiles, por lo que aunque solo son azúcar sintáctico, son bastante útiles.

La encapsulación no es una cuestión de si sus establecedores (o captadores) son públicos, privados, protegidos o internos, sino una cuestión de si son apropiados . Comience con un valor predeterminado de que cada campo sea privado (y para el caso readonly) y luego, según sea necesario, agregue miembros (ya sean propiedades o métodos) que modifiquen esos campos y asegúrese de que el objeto siga siendo válido a medida que cambien . Esto asegura que se mantenga el invariante de una clase , lo que significa que las reglas que describen el conjunto válido de estados en los que puede estar nunca se rompen (los constructores también ayudan al asegurarse de que comience en un estado válido).

En cuanto a su última pregunta, a ser medios inmutables que una clase no tiene público, protegido o fijadores internos y sin público, protegido o métodos internos que cambian todos los campos. Hay grados de esto, en C # hay tres grados posibles:

  1. Todos los campos de instancia de una clase son readonly, por lo tanto, incluso el código privado no puede alterarlo. Se garantiza que es inmutable (cualquier cosa que intente cambiarlo no se compilará) y posiblemente se pueden realizar optimizaciones en la parte posterior de esto.

  2. Una clase es inmutable desde el exterior porque ningún miembro público cambia nada, pero el uso de no garantiza readonlyque no se cambie desde el interior.

  3. Una clase es inmutable como se ve desde el exterior, aunque algún estado cambia como detalle de implementación. Por ejemplo, un campo podría memorizarse y, por lo tanto, mientras que desde el exterior un intento de obtenerlo simplemente recupera el mismo valor, el primer intento de este tipo realmente lo calcula y luego lo almacena para su recuperación en intentos posteriores.

Jon Hanna
fuente
1

Necesita un establecedor privado, si desea admitir el siguiente escenario (no solo por esto, sino que debe señalar una buena razón): Tiene una propiedad que es de solo lectura en su clase, es decir, solo la clase en sí puede cambiar , pero puede cambiarlo después de construir la instancia. Para los enlaces, necesitaría disparar un evento PropertyChanged, preferiblemente esto debería hacerse en el establecedor de propiedades (privado). En realidad, podría simplemente disparar el evento PropertyChanged desde otro lugar de la clase, pero usar el setter privado para esto es "buena ciudadanía", porque no distribuye sus activadores de cambio de propiedad en toda su clase, sino que lo mantiene en el propiedad, donde pertenece.

Simón D.
fuente
1

Sí, está utilizando la encapsulación mediante el uso de propiedades, pero la encapsulación tiene más matices que simplemente tomar el control sobre cómo se leen y escriben las propiedades. Negar que una propiedad se establezca desde fuera de la clase puede ser útil tanto para la solidez como para el rendimiento.

Una clase inmutable es una clase que no cambia una vez que se crea, por lo que se necesitan establecedores privados (o ningún establecedor) para proteger las propiedades.

Los establecedores privados se utilizaron con más frecuencia con la abreviatura de propiedad que se introdujo en C # 3. En C # 2, el establecedor a menudo se omitía y los datos privados se accedía directamente cuando se configuraban.

Esta propiedad:

public int Size { get; private set; }

es lo mismo que:

private int _size;
public int Size {
  get { return _size; }
  private set { _size = value; }
}

excepto que el nombre de la variable de respaldo es creado internamente por el compilador, por lo que no puede acceder directamente.

Con la propiedad abreviada, se necesita el definidor privado para crear una propiedad de solo lectura, ya que no puede acceder directamente a la variable de respaldo.

Guffa
fuente
Si no recuerdo mal, no se podía tener diferentes modificadores de acceso en gety setantes de C # 2.0. Además, creo que está mezclando 2.0 y 3.0, ya que la abreviatura implementada automáticamente a la que se refiere fue 3.0.
Anthony Pegram
1

No entiendo la necesidad de tener establecedores privados que comenzaron con C # 2.

Ejemplo de caso de uso:

Tengo una instancia de un objeto de aplicación 'UserInfo'que contiene una propiedad.SessionTokenIDV1 que no deseo exponer a los consumidores de mi clase.

También necesito la capacidad de establecer ese valor de mi clase.

Mi solución fue encapsular la propiedad como se muestra y hacer que el establecedor sea privado para poder establecer el valor del token de sesión sin permitir que el código de instanciación también lo establezca (o incluso verlo en mi caso)

public class UserInfo
{
   public String SessionTokenIDV1 { get; set; }

}


public class Example
{
  // Private vars
  private UserInfo _userInfo = new UserInfo();

  public string SessionValidV1
  {
    get { return ((_userInfo.SessionTokenIDV1 != null) && (_userInfo.SessionTokenIDV1.Length > 0)) ? "set" : "unset"; }
    private set { _userInfo.SessionTokenIDV1 = value; }
  }
}

Editar: Etiqueta de código fija Editar: El ejemplo tenía errores que se han corregido

Curt Eckhart
fuente
-1

Créditos a https://www.dotnetperls.com/property .

los establecedores privados son los mismos que los campos de solo lectura. Solo se pueden configurar en constructor. Si intenta establecer desde fuera, obtendrá un error de tiempo de compilación.

public class MyClass
{
    public MyClass()
    {
        // Set the private property.
        this.Name = "Sample Name from Inside";
    }
     public MyClass(string name)
    {
        // Set the private property.
        this.Name = name;
    }
    string _name;
    public string Name
    {
        get
        {
            return this._name;
        }
        private set
        {
            // Can only be called in this class.
            this._name = value;
        }
    }
}

class Program
{
    static void Main()
    {
        MyClass mc = new MyClass();
        Console.WriteLine(mc.name);

        MyClass mc2 = new MyClass("Sample Name from Outside");
        Console.WriteLine(mc2.name);
    }
}

Vea la captura de pantalla a continuación cuando intenté configurarla desde fuera de la clase.

ingrese la descripción de la imagen aquí

Ziggler
fuente