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 IU enrutados personalizados para WPF, pero no importa).
Veo dos formas de hacerlo:
class MyClass { public readonly object MyProperty = new object(); }
class MyClass { private readonly object my_property = new object(); public object MyProperty { get { return my_property; } } }
Con todos estos errores de FxCop que dicen que no debería tener variables miembro públicas, parece que la segunda es la forma correcta de hacerlo. ¿Correcto?
¿Hay alguna diferencia entre una propiedad de solo obtención y un miembro de solo lectura en este caso?
Agradecería cualquier comentario / consejo / etc.
c#
properties
readonly
Akonsu
fuente
fuente
get; readonly set;
opción.get; private set;
, al menos.{get;}
un tiempo, lo que resuelve este problema.Respuestas:
Control de versiones:
creo que no hay 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 establecedor sin romper el código compilado según su biblioteca.
Convención:
estás siguiendo la convención. En casos como este, donde las diferencias entre las dos posibilidades son relativamente menores, seguir la convención es mejor. Un caso en el que podría volver a morderlo es el código basado en la reflexión. Es posible que solo acepte propiedades y no campos, por ejemplo, un editor / visor de propiedades.
Serialización
Cambiar de un campo a una propiedad probablemente romperá muchos serializadores. Y AFAIK
XmlSerializer
solo serializa propiedades públicas y no campos públicos.Uso de una propiedad automática
Otra variación común es usar una propiedad automática con un establecedor privado. Si bien esto es breve y una propiedad, no impone la condición de solo lectura. Por eso prefiero los otros.
El campo de solo lectura se autodocumenta
Sin embargo, hay una ventaja del campo:
deja claro de un vistazo en la interfaz pública que es realmente inmutable (salvo la reflexión). Mientras que en el caso de una propiedad, solo puede ver que no puede cambiarla, por lo que tendría que consultar la documentación o implementación.
Pero para ser honesto, uso el primero con bastante frecuencia en el código de la aplicación, ya que soy un vago. En las bibliotecas, normalmente soy más minucioso y sigo la convención.
C # 6.0 agrega propiedades automáticas de solo lectura
public object MyProperty { get; }
Entonces, cuando no necesita admitir compiladores más antiguos, puede tener una propiedad de solo lectura con código que es tan conciso como un campo de solo lectura.
fuente
public type prop => get;
La segunda forma 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 configurar en un constructor).Como ha señalado, de esta manera no está exponiendo a un miembro interno, lo que le permite cambiar la implementación interna en el futuro.
fuente
public int MyProp { get; private set; }
? Sé que no es de solo lectura, pero está bastante cerca.Con la introducción de C # 6 (en VS 2015), ahora puede tener solo
get
propiedades automáticas, en las que el campo de respaldo implícito esreadonly
( 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 setter) en línea:
public string Name { get; } = "Boris";
Volviendo a la pregunta, esto le brinda las ventajas de la opción 2 (el 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 al 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 un configurador es indistinguible de tener un configurador privado.
fuente
readonly
campos se pueden alterar mediante la reflexión (no sé la respuesta, pero parece problemático)?Puedes hacerlo:
public int Property { get { ... } private set { ... } }
fuente
Estoy de acuerdo en que es preferible la segunda forma. La única razón real para esa preferencia es la preferencia general de que las clases .NET no tengan campos públicos. Sin embargo, si ese campo es de solo lectura, no veo cómo habría objeciones reales aparte de la falta de coherencia con otras propiedades. La diferencia real entre un campo de solo lectura y una propiedad de solo obtención es que el campo de solo lectura proporciona una garantía de que su valor no cambiará durante la vida del objeto y una propiedad de solo obtención no lo hace.
fuente
Se prefiere el segundo método debido a la encapsulación. Ciertamente, puede hacer que el campo de solo lectura sea público, pero eso va en contra de los modismos de C # en los que tiene acceso a datos a través de propiedades y no de 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 terminas rompiendo el resto del código porque la implementación está oculta detrás de una interfaz.
fuente
otra forma más (mi favorita), comenzando con C # 6
private readonly int MyVal = 5; public int MyProp => MyVal;
https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/properties#expression-body-definitions
fuente
public int MyProp { get; } = 5;
?En C # 9, Microsoft introducirá una nueva forma de establecer propiedades solo en la inicialización usando el
init;
método así:public class Person { public string firstName { get; init; } public string lastName { get; init; } }
De esta forma, puede asignar valores al inicializar un nuevo objeto:
var person = new Person { firstname = "John", lastName = "Doe" }
Pero más adelante, no puedes cambiarlo:
person.lastName = "Denver"; // throws a compiler error
fuente