La guía general para C # es usar siempre una propiedad sobre un campo público. Esto tiene sentido: al exponer un campo, está exponiendo muchos detalles de implementación. Con una propiedad, encapsula ese detalle para que quede oculto del código consumidor y los cambios de implementación se desacoplan de los cambios de interfaz.
Sin embargo, me pregunto si a veces hay una excepción válida a esta regla cuando se trata con la readonly
palabra clave. Al aplicar esta palabra clave a un campo público, ofrece una garantía adicional: inmutabilidad. Esto no es solo un detalle de implementación, la inmutabilidad es algo en lo que un consumidor podría estar interesado. El uso de un readonly
campo lo hace parte del contrato público y algo que no se puede romper con futuros cambios o herencias sin tener que modificar la interfaz pública. Eso es algo que una propiedad no puede ofrecer.
Entonces, ¿garantizar la inmutabilidad es una razón legítima para elegir un readonly
campo sobre una propiedad en algunos casos?
(Para aclarar, ciertamente no digo que siempre debas hacer esta elección solo porque el campo sea inmutable en este momento, solo cuando tenga sentido como parte del diseño de la clase y su uso previsto incluya la inmutabilidad en su contrato. Me interesan principalmente las respuestas que se centran en si esto puede estar justificado, en lugar de casos específicos en los que no lo está, como cuando necesita que el miembro esté en una interface
o desea realizar una carga diferida).
fuente
Respuestas:
Los campos públicos de solo lectura estática están bien. Se recomiendan para ciertas situaciones, como cuando desea un objeto constante con nombre pero no puede usar la
const
palabra clave.Los campos de miembros de solo lectura son un poco más complicados. No se obtiene mucha ventaja sobre una propiedad sin un setter público, y hay una gran desventaja: los campos no pueden ser miembros de interfaces. Por lo tanto, si decide refactorizar su código para operar contra una interfaz en lugar de un tipo concreto, tendrá que cambiar sus campos de solo lectura a propiedades con captadores públicos.
Una propiedad pública no virtual sin un setter protegido proporciona protección contra la herencia, a menos que las clases heredadas tengan acceso al campo de respaldo. Puede hacer cumplir ese contrato por completo en la clase base.
No ser capaz de cambiar la "inmutabilidad" del miembro sin cambiar la interfaz pública de la clase no es una gran barrera en este caso. Por lo general, si desea cambiar un campo público en una propiedad, se realiza sin problemas, excepto que hay dos casos con los que tiene que lidiar:
object.Location.X = 4
solo es posible siLocation
es un campo. Sin embargo, esto no es relevante para un campo de solo lectura, porque no puede modificar una estructura de solo lectura. (Esto supone queLocation
es un tipo de valor; de lo contrario, este problema no se aplicaría de todos modos porquereadonly
no protege contra ese tipo de cosas).out
oref
. Una vez más, esto no es realmente relevante para un campo de sólo lectura, porqueout
yref
parámetros tienden a ser modificado, y es un error de compilación de todos modos para pasar un valor de sólo lectura como fuera o ref.En otras palabras, aunque la conversión de un campo de solo lectura a una propiedad de solo lectura rompe la compatibilidad binaria, otro código fuente solo necesitaría ser recompilado sin ningún cambio para manejar este cambio. Eso hace que la garantía sea realmente fácil de romper.
No veo ninguna ventaja en el
readonly
campo de miembros, y la imposibilidad de tener ese tipo de cosas en una interfaz es una desventaja suficiente para mí que no lo usaría.fuente
Desde mi experiencia, la
readonly
palabra clave no es la magia que promete.Por ejemplo, tiene una clase donde el constructor solía ser simple. Dado el uso (y el hecho de que algunas de las propiedades / campos de clase son inmutables después de la construcción), puede pensar que no importa, por lo que usó la
readonly
palabra clave.Más tarde, la clase se vuelve más compleja, y también lo es su constructor. (Digamos que esto sucede en un proyecto que es experimental o tiene una velocidad lo suficientemente alta fuera del alcance / control pero necesita que funcione de alguna manera). Encontrará que los campos que
readonly
solo se pueden modificar dentro del constructor: usted no puede modificarlo en métodos incluso si ese método solo se llama desde un constructor.Esta limitación técnica ha sido reconocida por los diseñadores de C #, y podría corregirse en una versión futura. Pero hasta que se solucione, el uso de
readonly
es más restrictivo y puede fomentar un estilo de codificación incorrecto (poner todo en el método del constructor).A modo de recordatorio, ni la
readonly
propiedad de establecimiento público no ofrece ninguna garantía de un "objeto completamente inicializado". En otras palabras, es el diseñador de una clase quien decide qué significa "completamente inicializado" y cómo protegerlo contra el uso involuntario, y dicha protección generalmente no es infalible, lo que significa que alguien podría encontrar una forma de evitarlo.fuente
readonly
campo como unaout
oref
parámetro para otros métodos, que entonces será libre de modificar como les parezca.Los campos son SIEMPRE detalles de implementación. No desea exponer los detalles de su implementación.
Un principio fundamental de la ingeniería de software es que no sabemos cuáles serán los requisitos en el futuro. Si bien su
readonly
campo puede ser bueno hoy, no hay nada que sugiera que será parte de los requisitos de mañana. ¿Qué pasa si mañana es necesario implementar un contador de visitas para determinar cuántas veces se accede a ese campo? ¿Qué sucede si necesita permitir que las subclases anulen el campo?Las propiedades son tan fáciles de usar y son mucho más flexibles que los campos públicos que no puedo pensar en un solo caso de uso en el que elegiría c # como mi idioma y usaría campos públicos de solo lectura en una clase. Si el rendimiento fuera tan ajustado que no pudiera recibir el golpe de la llamada al método adicional, probablemente estaría usando un idioma diferente.
El hecho de que podamos hacer algo con el idioma no significa que debamos hacer algo con el idioma.
De hecho, me pregunto si los diseñadores de idiomas lamentan haber hecho públicos los campos. No recuerdo la última vez que incluso consideré usar campos públicos no constantes en mi código.
fuente
Rational
con público, inmutableNumerator
yDenominator
miembros, podría estar extremadamente seguro de que esos miembros no requerirán carga lenta o un contador de visitas o ¿similar?float
tipos paraNumerator
yDenominator
no tendrá que cambiarsedoubles
?float
tampocodouble
. Deberían serint
,long
oBigInteger
. Tal vez esos necesiten cambiar ... pero si es así también tendrían que cambiar en la interfaz pública, por lo que el uso de propiedades realmente no agrega encapsulación en torno a ese detalle.No. No es una justificación.
Usar el solo lectura como garantía de inmutabilidad, no como justificación, es más como un hack.
Solo lectura significa que el valor lo asigna un constructor o cuando se define la variable.
Creo que será una mala práctica
Si realmente desea garantizar la inmutabilidad, use una interfaz que tenga más ventajas que desventajas.
Ejemplo de interfaz simple:
fuente