C#: Readonly vs Const

Hace unos días hablaba sobre los peligros de usar constantes en librerías y a raíz de eso Mafias me preguntaba cuál era la diferencia real entre una constante y una variable de solo lectura. Aunque conceptualmente sean lo mismo, existe una diferencia importante en la forma que tiene el CLR de tratar a ambas.

El CLR tratará una constante como un valor conocido en todo momento, universal e inmutable. Si usamos el ejemplo del post anterior, al crear la librería con la variable como constante, el compilador generará un campo para la constante:

Además, como el compilador conoce el valor, se lo habrá asignado:

Hasta aquí supongo que todo va según lo esperado. ¿Qué pasa entonces con una variable readonly?

El CLR trata a las variables readonly como variables normales, es decir, su valor no es conocido en todo momento, no es universal y es mutable. Únicamente añade una condición, su valor sólo podrá ser modificado por un constructor. Cualquier intento de modificar una variable readonly efectuado fuera de un constructor provocará una excepción.

1
2
3
4
5
6
7
using System;
 
public class IncredibleService
{
        public static readonly int MaxApiCalls = 150;
        // Here we'd have the rest of the implementation
}

Es cierto que estamos fijando el valor a 150 y que, conceptualmente, ese valor no va a cambiar, por lo que podríamos pensar que se incluirá en el código igual que en el caso anterior, ¿lo habrá hecho?


Sin embargo, una variable readonly solo se puede asignar en un constructor… y nosotros no tenemos, ¿o sí? Sabemos que cuando no declaramos uno de forma explícita, el compilador genera uno implícito por nosotros, el .ctor ¿estará ahí nuestra asignación?

¡Sorpresa! Tampoco está en el constructor. ¿Recuperados del susto? Bien, pensemos otra vez. Si la variable es estática, ¿cómo va a estar la asignación en un constructor que sólo será ejecutado si la clase se instancia? Eso nos obligaría a crear un objeto para obtener el valor de una variable estática… y eso simplemente no puede ser.

La asignación de nuestra variable se realiza en el constructor de clase (eh, yo nunca os dije que el constructor tuviera que ser de objeto!), el misterioso .cctor, en el que vemos lo siguiente:

Aquí sí se está realizando la asignación (recordad, 96 en hexadecimal, 150 en decimal). Al ser un constructor, el CLR lo permitirá aunque la variable sea readonly y al requerir construcción, cualquier código que use esta DLL se verá forzado a realizar la llamada y no a incrustar el valor en su código sin más, como ocurría al declararla constante.

¿Todo el monte es orégano? No. CLR sólo permite la asignación en constructores… excepto (siempre tiene que haber alguna excepción) si usamos reflectividad, que entonces podremos saltarnos las comprobaciones del CLR.

About the Author

Me llamo Pablo Carballude González, soy graduado en computación con master en HCI y Seguridad Informática. Actualmente trabajo para Amazon en Seattle como Software Developer Engineer. Soy de esas personas que no saben si los textos autobiográficos deben ser en primera o tercera persona. Lo intenté en segunda, pero no le entendí nada :P