¿Hay alguna razón para usar propiedades privadas en C #?


245

Me acabo de dar cuenta de que la construcción de la propiedad C # también se puede usar con un modificador de acceso privado :

private string Password { get; set; }

Aunque esto es técnicamente interesante, no puedo imaginar cuándo lo usaría, ya que un campo privado implica aún menos ceremonia :

private string _password;

y no puedo imaginar cuándo necesitaría poder obtener internamente pero no establecer o establecer pero no obtener un campo privado:

private string Password { get; }

o

private string Password { set; }

pero tal vez haya un caso de uso con clases anidadas / heredadas o tal vez donde un get / set pueda contener lógica en lugar de simplemente devolver el valor de la propiedad, aunque tendería a mantener las propiedades estrictamente simples y dejar que los métodos explícitos hagan cualquier lógica, por ej GetEncodedPassword().

¿Alguien usa propiedades privadas en C # por alguna razón o es solo una de esas construcciones de código técnicamente posibles pero raramente usadas en el código real?

Apéndice

Buenas respuestas, al leerlas, seleccioné estos usos para propiedades privadas:

  • cuando los campos privados necesitan ser cargados perezosamente
  • cuando los campos privados necesitan lógica adicional o son valores calculados
  • ya que los campos privados pueden ser difíciles de depurar
  • para "presentar un contrato a ti mismo"
  • convertir / simplificar internamente una propiedad expuesta como parte de la serialización
  • envolviendo variables globales para ser utilizadas dentro de su clase

La técnica alentada por las propiedades privadas es la autoencapsulación
LBushkin

Respuestas:


212

Los uso si necesito almacenar en caché un valor y quiero cargarlo de forma diferida.

private string _password;
private string Password
{
    get
    {
        if (_password == null)
        {
            _password = CallExpensiveOperation();
        }

        return _password;
    }
}

42
un patrón común bonito de esto esreturn _password ?? (_password = CallExpensiveOperation());
Marc

1
@EvAlex Lazy <T> podría ser preferible si puede usarlo. Pero solo puede usar cosas estáticas, por lo que no puede acceder a métodos (no estáticos) u otros miembros. Por cierto, prefiero la sintaxis de return _password ?? (_password = CallExpensiveOperation());una sola línea desde hace un tiempo. O en realidad Resharper lo prefiere :).
Bart

44
@Marc desde C # 6 ni siquiera tiene que escribir un get y return. private string Password => _password ?? (_password = CallExpensiveOperation());
Otto Abnormalverbraucher

1
Con C # 8 es aún más corto:private string Password => _password ??= CallExpensiveOperation();
Dave M

2
@Bas Eso no es carga lenta porque CallExpensiveOperation();se llama durante la construcción / inicialización del objeto contenedor y no cuando se accede por primera vez a la propiedad.
Stefan Podskubka

142

El uso principal de esto en mi código es la inicialización diferida, como otros han mencionado.

Otra razón para las propiedades privadas sobre los campos es que las propiedades privadas son mucho más fáciles de depurar que los campos privados. Con frecuencia quiero saber cosas como "este campo se está configurando inesperadamente; ¿quién es la primera persona que llama que configura este campo?" y es mucho más fácil si solo puedes poner un punto de interrupción en el setter y presionar ir. Puedes poner el registro allí. Puede poner métricas de rendimiento allí. Puede realizar comprobaciones de coherencia que se ejecutan en la compilación de depuración.

Básicamente, todo se reduce a: el código es mucho más poderoso que los datos . Cualquier técnica que me permita escribir el código que necesito es buena. Los campos no le permiten escribir código en ellos, las propiedades sí.


55
¿Es "el código es mucho más poderoso que los datos" tu dicho? Buscar en Google devuelve referencias que apuntan a usted. Solo quiero saber para poder citarlo correctamente cuando sea necesario.
Joan Venge

24
@Joan: No lo se. O lo inventé, o escuché a alguien decirlo y pensé "wow, debería robarlo por completo, y luego olvidarme de quién se lo robé".
Eric Lippert

1
+ CodeLens le dice dónde se hace referencia a la propiedad
UuDdLrLrSs

43

tal vez hay un caso de uso con clases anidadas / heredadas o tal vez donde un get / set puede contener lógica en lugar de simplemente devolver el valor de la propiedad

Yo personalmente uso esto incluso cuando no necesito lógica en el captador o definidor de una propiedad. El uso de una propiedad, incluso privada, ayuda a proteger su código a futuro para que pueda agregar la lógica a un getter más adelante, si es necesario.

Si siento que una propiedad eventualmente puede requerir lógica adicional, a veces la envolveré en una propiedad privada en lugar de usar un campo, para no tener que cambiar mi código más tarde.


En un caso semi-relacionado (aunque diferente a su pregunta), uso muy frecuentemente los setters privados en propiedades públicas:

public string Password 
{
    get; 
    private set;
}

Esto te da un captador público, pero mantiene al setter privado.


+1 tiene sentido: "Si siento que una propiedad eventualmente puede requerir lógica adicional, a veces la envolveré en una propiedad privada en lugar de usar un campo, para no tener que cambiar mi código más tarde".
Edward Tanguay

77
setters privados <3
Earlz

20

Un buen uso para las propiedades privadas de obtención solo son valores calculados. Varias veces he tenido propiedades que son privadas de solo lectura y solo hago un cálculo sobre otros campos en mi tipo. No es digno de un método y no es interesante para otras clases, por lo que es propiedad privada.


20

La inicialización diferida es un lugar donde pueden estar limpios, p. Ej.

private Lazy<MyType> mytype = new Lazy<MyType>(/* expensive factory function */);

private MyType MyType { get { return this.mytype.Value; } }

// In C#6, you replace the last line with: private MyType MyType => myType.Value;

Luego puede escribir: en this.MyTypetodas partes en lugar de this.mytype.Valuey encapsular el hecho de que se crea una instancia perezosa en un solo lugar.

Una cosa que es una pena es que C # no admite el alcance del campo de respaldo a la propiedad (es decir, declararlo dentro de la definición de la propiedad) para ocultarlo por completo y garantizar que solo se pueda acceder a él a través de la propiedad.


2
Acordó que sería tener el aspecto de alcance allí.
Chris Marisic

55
Utilizo esta misma técnica con frecuencia y también he deseado que un campo se pueda abarcar a un cuerpo de código. Es una buena característica pero de baja prioridad.
Eric Lippert

55
@Eric Lippert - field-declarations examinado dentro accessor-declarationsha clasificado el número 1 en mi lista de deseos de C # durante mucho tiempo. Si pudieras lograr que se diseñara e implementara en alguna versión futura (real), entonces te prepararé un pastel.
Jeffrey L Whitledge

13

El único uso que se me ocurre

private bool IsPasswordSet 
{ 
     get
     {
       return !String.IsNullOrEmpty(_password);
     }
}

+1 para la clase útil de propiedades que se calculan a partir de otras variables privadas
Daren Thomas

2
¿Por qué no usar un método privadoprivate bool IsPasswordSet() { return !String.IsNullOrEmpty(_password); }
Roman

10

Las propiedades y los campos no son uno a uno. Una propiedad se trata de la interfaz de una clase (ya sea hablando de su interfaz pública o interna), mientras que un campo se trata de la implementación de la clase. Las propiedades no deben verse como una forma de exponer los campos, sino que deben verse como una forma de exponer la intención y el propósito de la clase.

Al igual que utiliza propiedades para presentar un contrato a sus consumidores sobre lo que constituye su clase, también puede presentarse un contrato a usted mismo por razones muy similares. Entonces sí, uso propiedades privadas cuando tiene sentido. A veces, una propiedad privada puede ocultar detalles de implementación como la carga diferida, el hecho de que una propiedad es realmente un conglomerado de varios campos y aspectos, o que una propiedad necesita ser instanciada virtualmente con cada llamada (piense DateTime.Now). Definitivamente, hay momentos en los que tiene sentido imponer esto incluso a ti mismo en el back-end de la clase.


+1: "también puedes presentarte un contrato por razones muy similares" tiene sentido
Edward Tanguay

8

Los uso en la serialización, con cosas como DataContractSerializero protobuf-net que admiten este uso ( XmlSerializerno). Es útil si necesita simplificar un objeto como parte de la serialización:

public SomeComplexType SomeProp { get;set;}
[DataMember(Order=1)]
private int SomePropProxy {
    get { return SomeProp.ToInt32(); }
    set { SomeProp = SomeComplexType.FromInt32(value); }
}

6

Una cosa que hago todo el tiempo es almacenar variables / caché "globales" en HttpContext.Current

private static string SomeValue{
  get{
    if(HttpContext.Current.Items["MyClass:SomeValue"]==null){
      HttpContext.Current.Items["MyClass:SomeValue"]="";
    }
    return HttpContext.Current.Items["MyClass:SomeValue"];
  }
  set{
    HttpContext.Current.Items["MyClass:SomeValue"]=value;
  }
}

5

Los uso de vez en cuando. Pueden facilitar la depuración de cosas cuando puede poner fácilmente un punto de interrupción en la propiedad o puede agregar una declaración de registro, etc.

También puede ser útil si luego necesita cambiar el tipo de datos de alguna manera o si necesita usar la reflexión.


Ídem; Si hay una lógica involucrada en un get / set, a veces puedo usar una propiedad privada o protegida. Generalmente depende de cuánta lógica: lógica simple que haré en la propiedad, mucha lógica usualmente usaré una función auxiliar. Lo que sea que haga que el código sea más fácil de mantener.
TechNeilogy

5

Utilizo propiedades privadas para reducir el código para acceder a subpropiedades que a menudo utilizo.

    private double MonitorResolution
    {
        get { return this.Computer.Accesories.Monitor.Settings.Resolution; }
    }

Es útil si hay muchas propiedades secundarias.


2

Es una práctica común modificar solo miembros con métodos get / set, incluso privados. Ahora, la lógica detrás de esto es para que sepa que su get / set siempre se comporta de una manera particular (por ejemplo, desencadenando eventos) que no parece tener sentido ya que no se incluirán en el esquema de propiedad ... pero los viejos hábitos tardan en morir.


2

Tiene mucho sentido cuando hay una lógica asociada con el conjunto de propiedades u obtener (piense en la inicialización diferida) y la propiedad se usa en algunos lugares de la clase.

Si es solo un campo de respaldo recto? Nada me viene a la mente como una buena razón.


2

Sé que esta pregunta es muy antigua, pero la información a continuación no figura en ninguna de las respuestas actuales.

No puedo imaginar cuándo necesitaría poder obtener internamente pero no configurar

Si está inyectando sus dependencias, es posible que desee tener un Getter en una Propiedad y no un setter, ya que esto denotaría una Propiedad de solo lectura. En otras palabras, la propiedad solo se puede establecer en el constructor y no se puede cambiar por ningún otro código dentro de la clase.

Además, Visual Studio Professional proporcionará información sobre una Propiedad y no un campo, lo que facilita ver qué campo se está utilizando.

PorpField


1

Bueno, como nadie mencionó, puede usarlo para validar datos o bloquear variables.

  • Validación

    string _password;
    string Password
    {
        get { return _password; }
        set
        {
            // Validation logic.
            if (value.Length < 8)
            {
                throw new Exception("Password too short!");
            }
    
            _password = value;
        }
    }
  • Cierre

    object _lock = new object();
    object _lockedReference;
    object LockedReference
    { 
        get
        {
            lock (_lock)
            {
                return _lockedReference;
            }
        }
        set
        {
            lock (_lock)
            {
                _lockedReference = value;
            }
        }
    }

    Nota: Al bloquear una referencia, no bloquea el acceso a los miembros del objeto referenciado.

Referencia diferida: cuando la carga diferida puede terminar necesitando hacerlo asíncrono para lo que hoy en día existe AsyncLazy . Si tiene versiones anteriores a las de Visual Studio SDK 2015 o no las usa, también puede usar AsyncLazy de AsyncEx .


0

Algunos usos más exóticos de campos explícitos incluyen:

  • necesita usar refo outcon el valor, tal vez porque es un Interlockedcontador
  • está destinado a representar un diseño fundamental, por ejemplo, en un structdiseño explícito (quizás para asignar a un volcado de C ++unsafe código de )
  • Históricamente, el tipo se ha utilizado con BinaryFormatterel manejo automático de campo (cambiar a accesorios automáticos cambia los nombres y, por lo tanto, rompe el serializador)
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.