¿Qué diferencia existe entre const y readonly en el lenguaje C#?

17/07/2019Artículo original

Una pregunta muy típica que se suele hacer la gente que empieza con .NET es la que da título a este artículo. Y es que, si una variable solamente se puede leer y por lo tanto no se puede cambiar… en realidad eso es la definición de una constante ¿no?

Sí y no. En realidad hay algunas sutiles diferencias que voy a explicar y que son básicas, pero también existe una diferencia enorme, mucho menos evidente, que mostraré al final y que ni siquiera muchos programadores experimentados conocen.

Vale, vamos a empezar por lo básico.

Constantes vs variables de solo lectura

Una constante se define en C# de la siguiente manera:

public const double PI = 3.14;

El modificador const le indica al compilador que esta variable va a ser constante, y solo la podemos inicializar al mismo tiempo que la declaramos. Si en cualquier otra parte del código la intentamos modificar, el compilador se quejará diciendo que el lado izquierdo de una asignación debe ser una variable, una propiedad o un indexador:

Una variable de solo lectura, se parece mucho a una constante y se declara de manera parecida:

public readonly double PI = 3.14;

y si luego la intentamos modificarla en casi cualquier otra parte del código, veremos el siguiente error:

Y es que, siendo de solo lectura, no se puede asignar tampoco. Sin embargo, hay algo en el error del compilador que nos da una pista de una de las diferencias con una constante, y la he destacado en el mensaje de error: excepto en un constructor.

Y es que los miembros de una clase que sean readonlypueden ser modificados mientras no termine de ser ejecutado el constructor de la clase. Es decir, dentro del constructor podemos modificarlos a voluntad, por ejemplo, obteniendo su valor a partir de los parámetros que se le pasen al constructor, algo que no es posible en el caso de una constante.

  Minimizar y ofuscar JavaScript desde el menú contextual del Explorador de Windows

Esto nos da una importante ventaja a la hora de crear estructuras de datos inmutables, que son aquellas que una vez establecidas podemos tener la seguridad de que no van a cambiar. Estas estructuras tienen muchas aplicaciones en programación y son especialmente útiles al ser inherentemente seguras para uso multi-hilo, ya que al no poder cambiar sus datos no hay peligros de interbloqueos.

En el caso de que nuestra variable de solo lectura apunte a un tipo por valor, sabemos que el valor no va cambiar, y si apunta a un tipo por referencia (otra clase), aunque la clase y sus datos cambien podemos tener la seguridad de que nuestra variable apuntará siempre a dicha clase.

Miembros estáticos

Otra importante diferencia entre una constante y una variable de solo lectura es que las constantes son siempre miembros estáticos de la clase en la que se declaran.

Es decir, si escribimos:

public class Clase01{public const double PI = 3.14;}

luego podemos acceder a ese valor PI sin necesidad de crear un objeto de la clase con new, así:

Console.Write("El valor de Pi en nuestro programa es " + Clase01.PI);

ya que es un miembro estático, común a todos los objetos de la clase y existente en la propia definición de ésta, por eso no necesitamos instanciar nada.

Para conseguir lo mismo con una variable de solo lectura tendríamos que añadir explícitamente el modificador static, así:

public class Clase01{public static readonly double PI = 3.14;}

y ya podríamos usarla de la misma manera.

La diferencia menos evidente es a la hora de compilar

Vale, hasta aquí lo básico todo claro: las constantes son estáticas y solo se pueden asignar al declararlas, y las de solo lectura no son estáticas por defecto y se pueden asignar también en el constructor de las clases. Perfecto, ya sabemos lo mínimo que hay que saber.

Ahora vamos a hilar un poco más fino.

Supón que tienes una clase súper-básica definida de esta manera en una biblioteca de código (que genera una DLL al final):

  Ocho cursos gratis para aprender Python desde cero

Tan solo define una clase con dos miembros estáticos: una variable de solo lectura y una constante para el número Pi.

Ahora generas un archivo .dll que distribuyes para que pueda ser utilizado para dotar de funcionalidad a otras aplicaciones.

A continuación creas un nuevo proyecto y le añades una referencia a esta DLL. Agregas el espacio de nombres y empiezas a utilizarla. Para seguir con código muy sencillo que nos permita ver entender a dónde quiero llegar, haces esto en un programa de consola:

O sea, simplemente usas la constante y la variable de solo lectura para mostrar por pantalla sus valores. En una aplicación real operarías con ellos, pero para lo que persigo me vale así.

Al ejecutarla se verá esto:

Bien, con tu nueva aplicación de consola lista, la compilas y la distribuyes junto con la DLL a tus “clientes” para que la usen. Al cabo de unos días decides que deberías darle más decimales a Pi, así que cambias sus definiciones y le asignas el valor 3.14156, con más decimales. Cambias el código de la DLL, la recompilas y la distribuyes.

En condiciones normales, con tan solo copiar la DLL por encima de la vieja, te funcionará todo perfectamente, ya que el código que nos interesa está en dicha DLL y el ejecutable tan solo hace uso de ella para esa funcionalidad. Sin embargo, si la ejecutas de nuevo, verás esto por pantalla:

WTF? ¿Cómo es posible?: sólo ha cambiado el segundo valor, el de la variable de solo lectura, pero no la constante.

El motivo lo encontramos si examinamos con un decompilador el código generado para el ejecutable es:

Lo que vemos es que, aunque son un miembro en una clase que pertenece a una DLL externa, ¡las constantes se compilan como literales!.

Es decir, el compilador, cuando analiza el código ve que se está utilizando una constante, y como esta no puede cambiar, directamente lo que hace es sustituirla en todas partes por su valor literal. Así que, las constantes, ¡incluso cuando residen en una DLL externa se compilan con su valor en todos los sitios en los que se utilicen!

  Paquetes en Java: qué son, para qué se utilizan, y cómo se usan (con vídeo)

Sin embargo, en el caso de la variable de solo lectura, como puede depender de cuándo se instancie la clase, no puede hacerlo, y se sigue utilizando desde la DLL.

Por eso, cuando copiamos una nueva versión de la DLL por encima de la anterior, aunque cambiemos alguna constante no nos valdrá solo con eso. Si queremos que “pillen” los nuevos valores de las constantes debemos recompilar de nuevo las aplicaciones que hagan uso de la biblioteca.

Y este es quizá el efecto más importante entre usar una constante y una variable de solo lectura. Y también el menos evidente y más complicado de ver, que nos puede traer por la calle de la amargura si no lo tenemos claro.

¡Espero que te resulte interesante! (y útil)!

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Contiene enlaces a sitios web de terceros con políticas de privacidad ajenas que podrás aceptar o no cuando accedas a ellos. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Más información
Privacidad