C#, inmutabilidad y campos públicos de solo lectura
properties field (5)
C # 6.0 ahora es compatible con los inicializadores de propiedad automática.
El inicializador de propiedad automática permite la asignación de propiedades directamente dentro de su declaración. Para las propiedades de solo lectura, se ocupa de todas las ceremonias necesarias para garantizar que la propiedad sea inmutable.
Puede inicializar las propiedades de solo lectura en el constructor o usar el inicializador automático
public class Customer
{
public Customer3(string firstName, string lastName)
{
FirstName = firstName;
LastName = lastName;
}
public string FirstName { get; }
public string LastName { get; }
public string Company { get; } = "Microsoft";
}
var customer = new Customer("Bill", "Gates");
Puede leer más sobre los inicializadores de propiedad automática here
He leído en muchos lugares que exponer los campos públicamente no es una buena idea, porque si luego desea cambiar a las propiedades, tendrá que volver a compilar todo el código que utiliza su clase.
Sin embargo, en el caso de las clases inmutables, no veo por qué alguna vez necesitarías cambiar a las propiedades; después de todo, no vas a agregar lógica al ''conjunto''.
Alguna idea sobre esto, ¿me estoy perdiendo algo?
Ejemplo de la diferencia, para quienes leen el código más fácilmente que el texto :)
//Immutable Tuple using public readonly fields
public class Tuple<T1,T2>
{
public readonly T1 Item1;
public readonly T2 Item2;
public Tuple(T1 item1, T2 item2)
{
Item1 = item1;
Item2 = item2;
}
}
//Immutable Tuple using public properties and private readonly fields
public class Tuple<T1,T2>
{
private readonly T1 _Item1;
private readonly T2 _Item2;
public Tuple(T1 item1, T2 item2)
{
_Item1 = item1;
_Item2 = item2;
}
public T1 Item1 { get { return _Item1; } }
public T2 Item2 { get { return _Item2; } }
}
Por supuesto, puede usar auto-propiedades ( public T1 Item1 { get; private set; }
), pero esto solo le otorga "inmutabilidad acordada" en lugar de "inmutabilidad garantizada" ...
Como práctica estándar, sigo tu segundo ejemplo solo con ''readonly'' cuando el objeto es público o vulnerable a manipulaciones inadvertidas. Estoy usando el modelo de "inmutabilidad acordada" en un proyecto actual que crea un marco de plugins. Obviamente, con la inmutabilidad acordada, se elimina la protección de readonly
.
Solo en circunstancias excepcionales expongo un campo: público, interno o de otro tipo. Simplemente no se siente bien a menos que escribir una propiedad {get;} tome más tiempo de lo que estoy dispuesto a dar.
Es posible que no necesite agregar ninguna lógica a un colocador en el futuro, pero puede que necesite agregar lógica a un getter .
Esa es una razón lo suficientemente buena para que yo use propiedades en lugar de exponer campos.
Si me siento riguroso, optaría por la inmutabilidad completa (campos de respaldo de readonly
explícitos con getters expuestos y sin setters). Si me siento flojo, entonces probablemente optaría por la "inmutabilidad acordada" (auto-propiedades con getters expuestos y setters privados).
Es una omisión obvia de las propiedades que no se puede escribir algo así como:
public T2 Item2 { get; readonly set; }
Ni siquiera estoy seguro de que readonly
sea la mejor palabra para significar "solo se puede establecer en el constructor" , pero eso es a lo que nos tenemos que enfrentar.
Esta es en realidad una función que muchas personas han solicitado, así que esperemos que se la presente en una nueva versión hipotética de C # pronto.
La idea detrás de las propiedades es que, incluso si no tiene la intención de cambiarlas ahora o más tarde, mabye podría necesitarlo de alguna manera imprevista. Supongamos que necesita cambiar el captador para hacer algún tipo de cálculo o registro. Quizás necesite agregar manejo de excepciones. Muchos de los posibles motivos.
Considera también la semántica. si T1 es un tipo de valor en lugar de un tipo de referencia, el acceso a obj.Item1 devuelve una copia de _Item1 en el getter, mientras que el acceso al Item1 sin un getter no recuperaría una copia. Esto significa que mientras que el Item1 puede ser inmutable internamente, el objeto de tipo de valor devuelto no lo es. No puedo pensar en una razón por la que eso sería algo bueno , pero es una diferencia.