Cómo implementar una propiedad de solo lectura


Necesito implementar una propiedad de solo lectura en mi tipo. Además, el valor de esta propiedad se establecerá en el constructor y no se cambiará (estoy escribiendo una clase que expone comandos de interfaz de usuario enrutados personalizados para WPF, pero no importa).

, veo dos maneras de hacerlo:

  1. class MyClass
    {
        public readonly object MyProperty = new object();
    }
    
  2. class MyClass
    {
        private readonly object my_property = new object();
        public object MyProperty { get { return my_property; } }
    }
    

Con todos estos errores de FxCop diciendo que no debería tener variables de miembro público, parece que la segunda es la manera correcta haciéndolo. ¿Correcto?

¿Hay alguna diferencia entre una propiedad get only y un miembro read only en este caso?

Agradecería cualquier comentario / consejo / etc.

Author: shA.t, 2010-10-12

6 answers

Versionado:
Creo que no hace mucha diferencia si solo está interesado en la compatibilidad de fuentes.
El uso de una propiedad es mejor para la compatibilidad binaria, ya que puede reemplazarla por una propiedad que tenga un setter sin romper el código compilado dependiendo de su biblioteca.

Convención:
Estás siguiendo la convención. En casos como este, donde las diferencias entre las dos posibilidades son relativamente menores después de la convención es mejor. Un caso en el que podría volver a morderte es el código basado en la reflexión. Solo puede aceptar propiedades y no campos, por ejemplo, un editor/visor de propiedades.

Serialización
Cambiar de campo a propiedad probablemente romperá muchos serializadores. Y AFAIK XmlSerializer solo serializa propiedades públicas y no campos públicos.

Usando una Autoproperiencia
Otra variación común es el uso de una autopropiedad con un setter privado. Mientras que esto es corto y un propiedad no impone la readonlynness. Así que prefiero los otros.

El campo Readonly es autodocumentante
Sin embargo, hay una ventaja del campo:
Deja claro de un vistazo a la interfaz pública que en realidad es inmutable (salvo reflexión). Mientras que en el caso de una propiedad solo puede ver que no puede cambiarla, por lo que tendría que referirse a la documentación o implementación.

Pero para ser honesto, uso el primero con bastante frecuencia en el código de aplicación ya que soy perezoso. En las bibliotecas normalmente soy más minucioso y sigo la convención.

C # 6.0 añade propiedades automáticas de solo lectura

public object MyProperty { get; }

Así que cuando no necesita soportar compiladores más antiguos, puede tener una propiedad verdaderamente readonly con código que es tan conciso como un campo readonly.

 37
Author: CodesInChaos,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-12-08 13:08:04

La segunda vía es la opción preferida.

private readonly int MyVal = 5;

public int MyProp { get { return MyVal;}  }

Esto asegurará que MyVal solo se pueda asignar en la inicialización (también se puede establecer en un constructor).

Como había señalado, de esta manera no está exponiendo a un miembro interno, lo que le permite cambiar la implementación interna en el futuro.

 52
Author: Oded,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-10-12 18:29:51

Con la introducción de C # 6 (en VS 2015), ahora puede tener get - solo propiedades automáticas, en las que el campo de respaldo implícito es readonly (es decir, los valores se pueden asignar en el constructor pero no en otro lugar):

public string Name { get; }

public Customer(string name)  // Constructor
{
    Name = name;
}

private void SomeFunction()
{
    Name = "Something Else";  // Compile-time error
}

Y ahora también puede inicializar propiedades (con o sin un setter) en línea:

public string Name { get; } = "Boris";

Volviendo a la pregunta, esto le da las ventajas de la opción 2 (miembro público es una propiedad, no un campo) con la brevedad de la opción 1.

Desafortunadamente, no proporciona una garantía de inmutabilidad a nivel de la interfaz pública (como en el punto de @CodesInChaos sobre la auto-documentación), porque para un consumidor de la clase, no tener ningún setter es indistinguible de tener un setter privado.

 37
Author: Bob Sammers,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-07-01 09:00:34

Puedes hacer esto:

public int Property { get { ... } private set { ... } }
 10
Author: Nobody,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-10-12 18:35:11

Estoy de acuerdo en que la segunda vía es preferible. La única razón real de esa preferencia es la preferencia general de que las clases.NET no tienen campos públicos. Sin embargo, si ese campo es readonly, no puedo ver cómo habría objeciones reales aparte de una falta de consistencia con otras propiedades. La diferencia real entre un campo readonly y la propiedad get-only es que el campo readonly proporciona una garantía de que su valor no cambiará durante la vida útil del objeto y una propiedad get-only la propiedad no.

 5
Author: Eric Mickelsen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-10-12 18:33:52

Se prefiere el segundo método debido a la encapsulación. Ciertamente puede hacer que el campo readonly sea público, pero eso va en contra de los modismos de C# en los que tiene acceso a los datos a través de propiedades y no campos.

El razonamiento detrás de esto es que la propiedad define una interfaz pública y si la implementación de respaldo a esa propiedad cambia, no termina rompiendo el resto del código porque la implementación está oculta detrás de una interfaz.

 4
Author: Joshua Rodgers,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-10-12 18:33:52