Lectura estática vs const

1387

He leído sobre consty static readonlycampos. Tenemos algunas clases que contienen solo valores constantes. Usado para varias cosas en nuestro sistema. Entonces me pregunto si mi observación es correcta:

¿Debería este tipo de valores constantes ser siempre static readonlypara todo lo público? ¿Y solo se usa constpara valores internos / protegidos / privados?

¿Que recomiendas? ¿Debería incluso no usar static readonlycampos, sino usar propiedades tal vez?

Svish
fuente
55
Aquí hay un caso único muy interesante que acabo de encontrar a favor static readonly: intente usar un const dentro de uno IEnumeratorque desencadene un irrecuperable yield y obtendrá un temido "Error de compilador interno" . No probé el código fuera de Unity3D, pero confío en que sea un error mono o .NET . Sin embargo, es un problema de C # .
cregox
8
Otra diferencia es que se puede utilizar una cadena const en un interruptor, pero no es una cadena de sólo lectura estática
flagg19
77
static readonlyno se puede usar en la switch-casedeclaración como casevariable, constse requiere para este propósito.
Mostafiz Rahman
3
static readonlyNo se puede utilizar como parámetro de atributo, también
Dread Boy

Respuestas:

940

public static readonlylos campos son un poco inusuales; public staticlas propiedades (con solo a get) serían más comunes (quizás respaldadas por un private static readonlycampo).

constlos valores se graban directamente en el sitio de la llamada; esto es de doble filo:

  • es inútil si el valor se obtiene en tiempo de ejecución, tal vez desde config
  • Si cambia el valor de un constante, necesita reconstruir todos los clientes
  • pero puede ser más rápido, ya que evita una llamada al método ...
  • ... que a veces podría haber sido alineado por el JIT de todos modos

Si el valor nunca cambia, entonces const está bien, Zeroetc., haga consts razonables; p Aparte de eso, las staticpropiedades son más comunes.

Marc Gravell
fuente
13
¿Por qué una propiedad sobre un campo? Si es una clase inmutable, no veo diferencia.
Michael Hedgpeth el
73
@Michael: las mismas razones de siempre; Oculta la implementación. Puede encontrar (más adelante) que necesita una carga lenta, una configuración, una fachada o lo que sea. En realidad, a menudo estaría bien ...
Marc Gravell
42
@CoffeeAddict por definición, una constante no está extrayendo valores de un archivo de configuración; se graba como un literal en tiempo de compilación. La única forma en que puede usar una constante en tiempo de ejecución es mediante la reflexión sobre los campos. En cualquier otro momento que intente usarlo, el compilador ya ha sustituido su uso constante por el uso literal ; es decir, si un método en su código usa 6 constantes, y lo inspecciona como IL, no se mencionará ninguna búsqueda constante; los valores literales simplemente se cargarán in situ
Marc Gravell
37
@MarcGravell - PRECAUCIÓN: los readonlycampos no se pueden usar en las declaraciones de cambio / caso, en cambio, necesita que lo sean const.
Luciano
77
@didibus Cambiar un campo a una propiedad, de hecho, rompe la API. Un campo en C # actúa efectivamente como una variable, mientras que una propiedad en C # es un auxiliar de sintaxis para escribir un método getter y / o un método setter. Esta diferencia es importante cuando hay otros conjuntos involucrados. Si cambia un campo a una propiedad, y otros ensamblajes dependían de este campo, entonces esos otros ensamblajes deben volver a compilarse.
Stephen Booher
237

Lo usaría static readonlysi el consumidor está en un ensamblaje diferente. Tener el consty el Consumidor en dos conjuntos diferentes es una buena manera de dispararse en el pie .

Michael Stum
fuente
55
Por lo tanto, creo que, como algunos han mencionado o aludido, puede ser aconsejable usar solo const para valores que en realidad son constantes bien conocidos si se hacen públicos; de lo contrario, deberían reservarse para el ámbito de acceso interno, protegido o privado.
jpierson
1
@Dio La razón por la que todavía existe es porque no es un problema en sí mismo; es algo a tener en cuenta, pero la capacidad de alinear const a través de los límites de ensamblaje es algo bueno para el rendimiento. Realmente es solo una cuestión de entender realmente que "constante" significa "nunca cambiará".
Michael Stum
1
@MichaelStum Ok, no debería llamarlo "un problema". En mi línea de trabajo, tengo constante y la comparto entre ensamblajes, pero recompilo para cada implementación o envío de código. Sin embargo, este hecho definitivamente vale la pena tomar nota de ello.
Dio Phung
1
Entonces, en general, internal consto public static readonlydependiendo de la visibilidad deseada.
Iiridayn
2
@Iiridayn Sí, esa no es una mala forma de verlo. Hay algunos casos extremos a considerar (por ejemplo, si usa Reflection, o si se necesita un valor en un atributo), y hay usos válidos para public const(por ejemplo, cualquier cosa que forme parte de un estándar. Cada vez que trabajo con XML, hay un espacio de nombres con un montón de public const string). Pero en general, public constsolo debe usarse después de considerar las implicaciones correctamente.
Michael Stum
200

Pocas cosas más relevantes a tener en cuenta:

const int a

  • debe ser inicializado
  • La inicialización debe realizarse en tiempo de compilación .

readonly int a

  • puede usar un valor predeterminado, sin inicializar.
  • La inicialización se puede hacer en tiempo de ejecución (Edición: solo dentro del constructor).
Peter
fuente
39
dentro del ctorunico.
Amit Kumar Ghosh
1
No solo dentro del constructor sino también en la declaración ( docs.microsoft.com/en-us/dotnet/csharp/language-reference/… ).
deChristo
176

Esto es solo un complemento de las otras respuestas. No los repetiré (ahora cuatro años después).

Hay situaciones en las que a consty a no constantes tienen una semántica diferente. Por ejemplo:

const int y = 42;

static void Main()
{
  short x = 42;
  Console.WriteLine(x.Equals(y));
}

imprime True, mientras que:

static readonly int y = 42;

static void Main()
{
  short x = 42;
  Console.WriteLine(x.Equals(y));
}

escribe False.

La razón es que el método x.Equalstiene dos sobrecargas, una que toma un short( System.Int16) y otra que toma un object( System.Object). Ahora la pregunta es si uno o ambos se aplican con mi yargumento.

Cuando yes una constante de tiempo de compilación (literal), el constcaso, se vuelve importante que exista una conversión implícita de int a short siempre que intsea ​​una constante, y siempre que el compilador de C # verifique que su valor esté dentro del rango de a short( cual 42es) Consulte Conversiones implícitas de expresiones constantes en la Especificación del lenguaje C #. Por lo tanto, se deben considerar ambas sobrecargas. Se Equals(short)prefiere la sobrecarga (cualquiera shortes una object, pero no todas lo objectson short). Entonces yse convierte a short, y se utiliza esa sobrecarga. Luego Equalscompara dos shortde valor idéntico, y eso da true.

Cuando yno es una constante, no existe conversión implícita de inta short. Eso es porque, en general, un intpuede ser demasiado grande para caber en un short. (Existe una conversión explícita , pero no lo dije Equals((short)y), así que eso no es relevante). Vemos que solo se aplica una sobrecarga, la Equals(object)única. Entonces yestá en caja para object. Luego Equalsse va a comparar a System.Int16con a System.Int32, y dado que los tipos de tiempo de ejecución ni siquiera están de acuerdo, eso dará lugar false.

Concluimos que en algunos casos (raros), cambiar un constmiembro de tipo a un static readonlycampo (o de otra manera, cuando eso sea posible) puede cambiar el comportamiento del programa.

Jeppe Stig Nielsen
fuente
17
Una buena adición a la respuesta aceptada. Me gustaría agregar que la conversión adecuada de los tipos de datos y otras pautas similares (como intentar capturas, etc.) deberían ser un elemento básico de los programadores experimentados y no dejarse al compilador. Sin embargo, aprendí algo nuevo de aquí. Gracias.
Uknight
Wow, he estado programando en C # durante mucho tiempo y nunca habría adivinado que una constante dentro del rango de un corto podría convertirse implícitamente en un corto. Debo decir que es bastante extraño. Me encanta C #, pero estas incoherencias extrañas que no parecen agregar mucho valor pero agregan una gran cantidad de potencia cerebral necesaria para considerar constantemente pueden ser molestas, especialmente para los principiantes.
Mike Marynowski
@MikeMarynowski Es cierto. Pero creo que hicieron esa regla (entre otras razones) para short x = 42;legalizar la declaración . Porque allí tienes un int, es decir, el literal 42, que se convierte implícitamente en short x. Pero entonces, podrían haber restringido esto a solo literales numéricos; sin embargo, eligieron permitir también cosas como short x = y;donde yse define como const int y = 42;, y luego terminaron con esto.
Jeppe Stig Nielsen
88

Una cosa a tener en cuenta es que const está restringido a tipos primitivos / de valor (la excepción son las cadenas)

Chris S
fuente
30
En realidad, también constpodría usarse para otros tipos, excepto que debe inicializarse como nulo, lo que lo hace inútil :)
nawfal
66
excepción como en System.Exception? :)
Memet Olsen
44
@nawfal Más precisamente, los únicos tipos de valor para el que constse pueden utilizar, son sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal, bool, además de cualquier enumtipo. constno se puede usar para otros tipos de valores, como DateTimeo TimeSpano BigInteger. Tampoco se puede usar para la IntPtrestructura (algunos lo consideran un tipo "primitivo"; el término tipo primitivo es confuso en C #). ↵↵ Se constpuede usar para todos los tipos de referencia . Si el tipo es string, se puede especificar cualquier valor de cadena. De lo contrario, el valor debe ser null.
Jeppe Stig Nielsen
@JeppeStigNielsen - Recientemente tuve una discusión con Servy sobre esto - señaló que se puede hacer cualquier cosa (valor y tipos de referencia) constusando default. Para los structtipos, es una instancia con todos sus miembros configurados con los valores predeterminados.
Wai Ha Lee
28

Solo lectura estática : el valor se puede cambiar a través del staticconstructor en tiempo de ejecución. Pero no a través de la función miembro.

Constante : por defecto static. El valor no se puede cambiar desde ningún lugar (Ctor, Function, runtime, etc., donde no).

Solo lectura : el valor se puede cambiar a través del constructor en tiempo de ejecución. Pero no a través de la función miembro.

Puede echar un vistazo a mi repositorio: tipos de propiedad C # .

Yeasin Abedin Siam
fuente
1
Malas noticias ... enlace roto!
Fer R
2
@FerR allí tienes: github.com/yeasin90/advanced-csharp/blob/master/CSharpAdvanced/…
Yeasin Abedin Siam el
Buenos fragmentos Siam ভাই :)
Muhammad Ashikuzzaman
25

La readonlypalabra clave es diferente de la constpalabra clave. Un constcampo solo se puede inicializar en la declaración del campo. Un readonlycampo se puede inicializar en la declaración o en un constructor. Por lo tanto, los readonlycampos pueden tener valores diferentes según el constructor utilizado. Además, aunque un constcampo es una constante de tiempo de compilación, el readonlycampo se puede usar para constantes de tiempo de ejecución

Referencia breve y clara de MSDN aquí

yazanpro
fuente
16

consty readonlyson similares, pero no son exactamente lo mismo.

Un constcampo es una constante de tiempo de compilación, lo que significa que ese valor se puede calcular en tiempo de compilación. Un readonlycampo habilita escenarios adicionales en los que se debe ejecutar algún código durante la construcción del tipo. Después de la construcción, un readonlycampo no se puede cambiar.

Por ejemplo, los constmiembros se pueden usar para definir miembros como:

struct Test
{
    public const double Pi = 3.14;
    public const int Zero = 0;
}

Dado que valores como 3.14 y 0 son constantes de tiempo de compilación. Sin embargo, considere el caso en el que define un tipo y desea proporcionar algunas instancias prefabricadas del mismo. Por ejemplo, es posible que desee definir una clase de Color y proporcionar "constantes" para colores comunes como Negro, Blanco, etc. No es posible hacer esto con miembros constantes, ya que los lados derechos no son constantes de tiempo de compilación. Se podría hacer esto con miembros estáticos regulares:

public class Color
{
    public static Color Black = new Color(0, 0, 0);
    public static Color White = new Color(255, 255, 255);
    public static Color Red   = new Color(255, 0, 0);
    public static Color Green = new Color(0, 255, 0);
    public static Color Blue  = new Color(0, 0, 255);
    private byte red, green, blue;

    public Color(byte r, byte g, byte b) => (red, green, blue) = (r, g, b);
}

Pero entonces no hay nada para evitar que un cliente de Color se meta con él, tal vez intercambiando los valores de Blanco y Negro. No hace falta decir que esto causaría consternación a otros clientes de la clase Color. La función "solo lectura" aborda este escenario.

Simplemente introduciendo la readonlypalabra clave en las declaraciones, preservamos la inicialización flexible al tiempo que evitamos que el código del cliente se pierda.

public class Color
{
    public static readonly Color Black = new Color(0, 0, 0);
    public static readonly Color White = new Color(255, 255, 255);
    public static readonly Color Red   = new Color(255, 0, 0);
    public static readonly Color Green = new Color(0, 255, 0);
    public static readonly Color Blue  = new Color(0, 0, 255);
    private byte red, green, blue;

    public Color(byte r, byte g, byte b) => (red, green, blue) = (r, g, b);
}

Es interesante observar que los miembros const son siempre estáticos, mientras que un miembro de solo lectura puede ser estático o no, al igual que un campo normal.

Es posible utilizar una sola palabra clave para estos dos propósitos, pero esto conduce a problemas de versiones o problemas de rendimiento. Supongamos por un momento que usamos una sola palabra clave para esto (const) y un desarrollador escribió:

public class A
{
    public static const C = 0;
}

y un desarrollador diferente escribió un código que dependía de A:

public class B
{
    static void Main() => Console.WriteLine(A.C);
}

Ahora, ¿puede el código generado depender del hecho de que AC es una constante de tiempo de compilación? Es decir, ¿puede el uso de AC simplemente ser reemplazado por el valor 0? Si dice "sí" a esto, eso significa que el desarrollador de A no puede cambiar la forma en que se inicializa AC; esto ata las manos del desarrollador de A sin permiso.

Si responde "no" a esta pregunta, se pierde una optimización importante. Quizás el autor de A está seguro de que AC siempre será cero. El uso de const y readonly permite al desarrollador de A especificar la intención. Esto permite un mejor comportamiento de versiones y también un mejor rendimiento.

Ramesh Rajendran
fuente
12

Mi preferencia es usar const siempre que pueda, lo cual, como se mencionó anteriormente, está limitado a expresiones literales o algo que no requiere evaluación.

Si golpeo contra esa limitación, entonces recurro a la lectura estática solo , con una advertencia. En general, usaría una propiedad estática pública con un captador y un campo de solo lectura estático privado de respaldo, como Marc menciona aquí .

Peter Meyer
fuente
7

Const: Const no es más que "constante", una variable cuyo valor es constante pero en tiempo de compilación. Y es obligatorio asignarle un valor. Por defecto, una constante es estática y no podemos cambiar el valor de una variable constante en todo el programa.

Estática de sólo lectura: valor A estática Sólo lectura tipo de variable puede ser asignado en tiempo de ejecución o asignada en tiempo de compilación en tiempo de ejecución y cambió. Pero el valor de esta variable solo se puede cambiar en el constructor estático. Y no se puede cambiar más. Puede cambiar solo una vez en tiempo de ejecución

Referencia: c-sharpcorner

mayank
fuente
6

Un campo de solo lectura estático es ventajoso cuando se expone a otros ensamblajes un valor que podría cambiar en una versión posterior.

Por ejemplo, supongamos que el ensamblaje Xexpone una constante de la siguiente manera:

public const decimal ProgramVersion = 2.3;

Si el ensamblaje hace Yreferencia Xy usa esta constante, el valor 2.3 se integrará en el ensamblaje Ycuando se compile. Esto significa que si Xluego se vuelve a compilar con la constante establecida en 2.4, Yseguirá usando el valor anterior de 2.3 hastaY se vuelva a compilar. Un campo de solo lectura estático evita este problema.

Otra forma de ver esto es que cualquier valor que pueda cambiar en el futuro no es constante por definición, por lo que no debe representarse como uno.

Yagnesh Cangi
fuente
3

const:

  1. el valor debe darse al momento de la declaración
  2. constante de tiempo de compilación

solo lectura:

  1. El valor se puede dar en la declaración o durante el tiempo de ejecución utilizando constructores. El valor puede variar dependiendo del constructor utilizado.
  2. tiempo de ejecución constante
dasumohan89
fuente
3

Const : los valores de la variable const deben definirse junto con la declaración y después de eso no cambiará. Los const son implícitamente estáticos, por lo que sin crear una instancia de clase podemos acceder a ellos. esto tiene un valor en tiempo de compilación

ReadOnly : valores variables de solo lectura que podemos definir al declarar y usar el constructor en tiempo de ejecución. las variables de solo lectura no pueden acceder sin instancia de clase.

Solo lectura estática estática: valores variables de solo lectura estática que podemos definir al declarar, así como solo a través del constructor estático pero no con cualquier otro constructor. Estas variables también podemos acceder sin crear una instancia de clase (como variables estáticas).

la lectura estática solo será una mejor opción si tenemos que consumir las variables en diferentes ensamblajes. Verifique los detalles completos en el siguiente enlace

https://www.stum.de/2009/01/14/const-strings-a-very-convenient-way-to-shoot-yourself-in-the-foot/

usuario1756922
fuente
¿Podrías decirme por qué rechazaste la respuesta, para que pueda actualizarme tan bien como aquí?
user1756922
No es el DV, pero puede ser que esta respuesta realmente no agregue nada a las respuestas ya completas aquí.
Marc L.
de hecho, recuerde que en Java, a fines de los años 90, tuvimos en un proyecto a varias personas que producían diferentes frascos con archivos de clase que interoperaban (se hacían referencia entre ellos) y la cadena de const pública tenía problemas de versiones, ya que estaban siendo copiados
George Birbilis
2

Hay una pequeña diferencia entre const y los campos de solo lectura estática en C # .Net

const debe inicializarse con valor en tiempo de compilación.

const es estático por defecto y debe inicializarse con un valor constante, que no puede modificarse más adelante. No se puede usar con todos los tipos de datos. Por ejemplo, DateTime. No se puede usar con el tipo de datos DateTime.

public const DateTime dt = DateTime.Today;  //throws compilation error
public const string Name = string.Empty;    //throws compilation error
public static readonly string Name = string.Empty; //No error, legal

readonly puede declararse como estático, pero no es necesario. No es necesario inicializar en el momento de la declaración. Su valor se puede asignar o cambiar usando el constructor una vez. Por lo tanto, existe la posibilidad de cambiar el valor del campo de solo lectura una vez (no importa, si es estático o no), lo que no es posible con const.

Chirag
fuente
0

Las constantes son como su nombre lo indica, campos que no cambian y generalmente se definen estáticamente en el momento de la compilación en el código.

Las variables de solo lectura son campos que pueden cambiar en condiciones específicas.

Pueden inicializarse cuando los declara por primera vez como una constante, pero generalmente se inicializan durante la construcción de objetos dentro del constructor.

No se pueden cambiar después de que se realizó la inicialización, en las condiciones mencionadas anteriormente.

La lectura estática de solo lectura me parece una mala elección ya que, si es estática y nunca cambia, así que solo utilícela const pública, si puede cambiar, entonces no es una constante y luego, dependiendo de sus necesidades, puede usar leer -solo o solo una variable regular.

Además, otra distinción importante es que una constante pertenece a la clase, ¡mientras que la variable de solo lectura pertenece a la instancia!

Claudiu Cojocaru
fuente
0

Una constante (que se determina en tiempo de compilación) se puede usar en casos en los que una lectura estática no puede, como en declaraciones de cambio o constructores de atributos. Esto se debe a que los campos de solo lectura solo se resuelven en tiempo de ejecución, y algunas construcciones de código requieren garantía de tiempo de compilación. Se puede calcular una estática de solo lectura en un constructor, que a menudo es algo esencial y útil. La diferencia es funcional, como debería ser su uso en mi opinión.

En términos de asignación de memoria, al menos con cadenas (que son un tipo de referencia), parece que no hay diferencia en que ambas están internadas y harán referencia a la única instancia internada.

Personalmente, mi valor predeterminado es de solo lectura estática, ya que tiene más sentido semántico y lógico para mí, especialmente porque la mayoría de los valores no son necesarios en el momento de la compilación. Y, por cierto, las estadísticas públicas de solo lectura no son inusuales o poco comunes, como dice la respuesta marcada: por ejemplo, System.String.Emptyes una.

DvS
fuente
0

Otra diferencia entre declarar const y static readonly está en la asignación de memoria.

Un campo estático pertenece al tipo de un objeto en lugar de a una instancia de ese tipo. Como resultado, una vez que se hace referencia a la clase por primera vez, el campo estático "vivirá" en la memoria por el resto del tiempo, y todas las instancias del tipo harán referencia a la misma instancia del campo estático.

Por otro lado, un campo constante "pertenece a una instancia del tipo.

Si la memoria de desasignación es más importante para usted, prefiera usar const . Si es velocidad, entonces use solo lectura estática .

Boris Lipschitz
fuente