2010-05-22 12 views
8

Parece haber tres opciones para implementar constantes accesibles al público en C#. Tengo curiosidad si hay buenas razones para elegir una sobre la otra o si es solo una cuestión de preferencia personal.Cómo implementar mejor las constantes de acceso público en C#

Opción 1 - ámbito privado más propiedad de captador

private const string _someConstant = "string that will never change"; 

public string SomeConstant 
{ 
    get { return _someConstant; } 
} 

Opción 2 - propiedad getter única

public string SomeConstant 
{ 
    get { return "string that will never change"; } 
} 

Opción 3 - ámbito público sólo

public const string SomeConstant = "string that will never change"; 

¿Cuál recomienda y por qué?


actualización

Al parecer, esto se ha convertido en un debate sobre si se debe utilizar o conststatic readonly. No es exactamente lo que pretendía, pero sí me enseñó que la opción 3 es definitivamente una mala idea porque, si el valor de la const cambia en una versión futura, es necesario recompilar todos los ensamblados de referencia.

Sin embargo, no creo que nadie haya discutido realmente la Opción 2 todavía. Sigo teniendo curiosidad si hay alguna desventaja con solo tener un getter que devuelva un valor y nada más.

+0

Si está utilizando Visual Studio, tiene un mecanismo existente que autogenerará propiedades para usted: archivos de recursos. Básicamente, hace la opción 1 automáticamente. También tenga en cuenta que si alguna vez desea admitir varios idiomas, existen mecanismos especiales para eso también. El primero de ellos es que definirías tus constantes en un conjunto de satélites. –

Respuesta

10

Las opciones 1 y 2 son equivalentes, en realidad.

Me parece que hay realmente tres situaciones diferentes:

  • Se sabe con certeza que la cadena nunca, nunca cambiará. En este caso, es razonable hacerlo const. (Por ejemplo, Math.PI es const. Eso no va a cambiar pronto). Hay algunas sutiles implicaciones de memoria al hacer esto sobre el uso de static readonly, pero es muy poco probable que lo afecten. No debe hacer esto si el valor puede cambiar y; no desea volver a compilar a todos los que llaman en esa situación, por las razones que se mencionan en otra parte. Tenga en cuenta que para muchos proyectos (especialmente los corporativos internos) realmente no es un problema recompilar a todos los llamantes.

  • Cree que la cadena podría cambiar en el futuro, pero sabe que siempre será una constante en cualquier versión. En este caso, un campo public static readonly está bien. Tenga en cuenta que está bien hacer esto con cadenas ya que son inmutables, pero no debe hacer esto con ningún tipo mutable como matrices. (Exponga colecciones inmutables o use una propiedad y devuelva una copia nueva cada vez).

  • Cree que la cadena podría cambiar, e incluso podría cambiar dentro de la vida útil de un programa ... por ejemplo, "la corriente fecha, formateada ". En este caso, use una propiedad pública de solo lectura estática (una con solo un getter). Tenga en cuenta que el cambio de un campo de solo lectura a una propiedad de solo lectura es un fuente -cambio compatible, pero no un binario -cambio compatible, por lo tanto, si ha solicitado mi segunda viñeta pero luego necesita cambiarla a la tercera, necesitas recompilar todo.

+0

Hmm, acabo de probar su tercera opción, pero obtengo "El modificador' readonly' no es válido para este artículo. El código ofensivo era: 'public static readonly string SomeConstant {get {return" SomeValue "}}'. ¿Quería decir simplemente 'public static'? – devuxer

+0

En retrospectiva, creo que debe haber querido decir" readonly "en el sentido de" tiene un getter solamente, no setter ", no para literalmente intentar etiquetar la propiedad' readonly' :) – devuxer

5

Considere

public static readonly string myVar = "something"; 

Motivo: cuando se expone (y luego consume en otro lugar) un const, la const está incrustada en los metadatos del tipo de consumo.

A public static readonly no lo es, y como es static readonly, solo le cuesta crear una instancia una vez, y es inmutable como const.

+0

La const no está incrustada en los metadatos. El problema es que el valor literal de la const está incrustado en la IL. –

+2

¿por qué es eso un problema? – thecoop

+0

Es un problema para la compilación por separado. Si cambia la definición de la constante, necesitará volver a compilar (en lugar de solo volver a cargar/volver a ejecutar JIT) los ensamblados dependientes. –

2

la correcta es la opción # 4:

public static readonly string SomeConstant = "string that might change in a new version"; 

Usando un campo de sólo lectura en lugar de un public const es importante. El valor literal de un const se compila en el IL. Si cambia el valor de const y recompila un ensamblado que lo usa, ahora tendrá una discrepancia con otros ensambles que también usan el const. Esas otras asambleas seguirán ejecutándose con el antiguo valor de const. Muy difícil de diagnosticar.

Esto no puede suceder con un campo de solo lectura, los demás ensamblajes siempre leerán el valor actualizado. Si usa const, asegúrese de hacerlo siempre privado.

+0

¿Hay alguna razón por la que no pude/no debería cambiar el valor de un 'const' en una nueva versión? Ciertamente lo he hecho antes sin notar ninguna consecuencia negativa. – devuxer

+1

Sí, hay. El valor literal de un const está incrustado en el IL. Te encontrarás con un gran problema cuando recompilas una sola biblioteca (por ejemplo, para corregir un error). Otros ensamblajes que usen const seguirán ejecutándose con el valor anterior. Una const siempre debe declararse privada para garantizar que nunca pueda suceder. –

+0

Hans, entonces ¿por qué no ir con la Opción # 1? La const se declara privada. ¿Y por qué la estática es necesariamente deseable? Tal vez me gustaría acceder al valor 'const' a través de una instancia de la clase para permitir el polimorfismo. – devuxer

2

const miembros son miembros de clase, no miembros de instancia (en otras palabras, const implica static).

+2

Buen punto, Phong. – devuxer

1

Si pudiera votar, votaría la respuesta de Jon Skeet. Para agregar a ella el # 1 y # 2 son exactamente idénticos, como se muestra aquí en IL:

.method public hidebysig specialname instance string 
    get_SomeConstant() cil managed 
{ 
    // Code size  11 (0xb) 
    .maxstack 1 
    .locals init ([0] string CS$1$0000) 
    IL_0000: nop 
    IL_0001: ldstr  "string that will never change" 
    IL_0006: stloc.0 
    IL_0007: br.s  IL_0009 
    IL_0009: ldloc.0 
    IL_000a: ret 
} // end of method Class1::get_SomeConstant 

Opción # 2:

.method public hidebysig specialname instance string 
    get_SomeConstant() cil managed 
{ 
    // Code size  11 (0xb) 
    .maxstack 1 
    .locals init ([0] string CS$1$0000) 
    IL_0000: nop 
    IL_0001: ldstr  "string that will never change" 
    IL_0006: stloc.0 
    IL_0007: br.s  IL_0009 
    IL_0009: ldloc.0 
    IL_000a: ret 
} // end of method Class2::get_SomeConstant 

Mira ahora en la opción 3 #. El número 3 es muy diferente de # 1 y # 2. La razón para esto es que, como se dijo anteriormente, el # 3 es estático ya que const es estático. Ahora la verdadera pregunta sería comparar manzanas con manzanas, ¿en qué pasa si los accesos estáticos # 1 y # 2 son estáticos? Entonces serían más comparables al # 3. En este momento, deberá inicializar una clase para la opción 1 y 2, pero no para la # 3. Por lo tanto, hay una inicialización innecesaria de un objeto en este caso y siempre quiere usar estática siempre que sea posible para evitar eso.

Ahora veamos el número 3 en la IL:

.field public static literal string SomeConstant = "string that will never change" 

Por lo tanto, la eficiencia, yo usaría # 3. Esto también es lo que me han enseñado muchos compañeros talentosos a lo largo de los años.

Ahora, para abordar el elefante blanco en la habitación. Readonly y const difieren en que cont se produce en tiempo de compilación y solo se produce en tiempo de ejecución. El sonido estático de solo lectura se inicializa una vez, mientras que el modo de lectura no estático se inicializa una vez por instancia. Si, por ejemplo, está pidiendo a su pregunta que haga algo así como crear una clase de cadenas de comandos para mensajes de error que nunca cambiarán, utilice la opción n. ° 3 y no solo de forma estática o de otro modo. Piense en tratar de inicializar cientos de mensajes de error en tiempo de ejecución en lugar de en tiempo de compilación, verá una notable diferencia en el rendimiento.Además, dado que usted declara claramente que se trata de una "cadena que nunca cambiará", readonly no debería considerarse en este caso porque "... nunca cambiará". Const y ReadOnly tienen sus lugares, pero readonly no es para elementos que nunca cambiarán y que se conocen en tiempo de compilación.

0

Una propiedad realmente parece ser la mejor opción porque el string devuelto no está incrustado en los metadatos. const son realmente para uso interno (como un ejemplo, un número de versión se puede hacer en un const, y podría cambiar) o para valores que absolutamente nunca cambiarán en lo que respecta al código que lo hace referencia.

Cuestiones relacionadas