Una inmersión profunda: tipos de valor y referencia en .Net

Los tipos en Microsoft .Net pueden ser de valor o de referencia. Mientras que los tipos de valor se almacenan generalmente en la pila, los tipos de referencia se almacenan en el montón administrado.

Un tipo de valor se deriva de System.ValueType y contiene los datos dentro de su propia asignación de memoria. En otras palabras, las variables, los objetos o los tipos de valores tienen su propia copia de los datos.

Mientras tanto, un tipo de referencia extiende System.Object y apunta a una ubicación en la memoria que contiene los datos reales. Puede imaginar un tipo de referencia similar a un puntero que se desreferencia implícitamente cuando accede a ellos. Los tipos de referencia integrados admitidos por C # incluyen: objeto, cadena y dinámica. Todos los tipos de datos fundamentales, booleanos, fecha, estructuras y enumeraciones son ejemplos de tipos de valor. Los ejemplos de tipos de referencia incluyen: cadenas, matrices, objetos de clases, etc. Para crear tipos de referencia en C #, puede aprovechar estas palabras clave: clase, interfaz y delegado.

Tenga en cuenta que, a diferencia de un tipo de referencia, no puede derivar de un tipo de valor, ni puede asignar un valor nulo directamente a un tipo de valor. Puede asignar un valor nulo a un tipo de valor solo aprovechando los tipos que aceptan valores NULL, una característica agregada a las versiones más recientes de .Net Framework. Cuando se copia un tipo de valor en otro, se copia el valor. Por lo tanto, puede manipular los valores en ellos independientemente del otro: un cambio en uno no afecta al otro. Por el contrario, cuando copia un tipo de referencia a otro, la referencia se copia. Si cambia uno de ellos, el otro también se verá afectado. Por ejemplo, si una de las referencias se establece en nula, la otra también se convierte en nula.

Ubicaciones de almacenamiento

El CLR almacena objetos en tres tipos de ubicaciones de almacenamiento: los registros, la pila o el montón administrado. Mientras que los objetos de corta duración se almacenan dentro de registros o pilas, los objetos de larga duración se almacenan en el montón. Como mencioné anteriormente, los tipos de valor generalmente se almacenan en la pila.

Es un error común pensar que los tipos de valor siempre se almacenan en la pila. Prefiero decir que los tipos de valor se pueden almacenar en la pila cuando la variable es una variable temporal o es una variable local y el compilador JIT decide no registrar el valor. En esencia, la ubicación real de un tipo de valor depende de la implementación del compilador JIT. Tenga en cuenta que un tipo de valor se puede almacenar en un marco de pila, en el registro de la CPU o incluso en la memoria de pila si el tipo de valor está contenido dentro de un objeto, es decir, si es parte de un tipo de referencia. Por el contrario, los tipos de referencia se almacenan en el montón de GC. La referencia se almacena en una pila mientras que el objeto se asigna en el montón.

Las instancias o referencias de un tipo de valor se almacenan en la pila, el registro o en el montón, dependiendo de si el tiempo de vida de la instancia o la referencia es de corta o larga duración. Un tipo de valor puede residir en la pila si son variables locales y en el montón administrado si son campos de una clase, es decir, pertenecen o son parte de un tipo de referencia.

Pasando por valor y pasando por referencia

La siguiente lista de códigos ilustra cómo puede pasar una variable a un método por valor.

 static void Increment(int i)

        {

            i = i + 1;

        }

        static void Main()

        {

            int x = 1;

            Increment(x);

            Console.WriteLine("The value of x is: " +x);

            Console.Read();

        }

Tenga en cuenta que puede pasar un tipo de valor como referencia a un método utilizando la palabra clave ref. La siguiente lista de códigos ilustra esto.

static void Increment(ref int i)

        {

            i = i + 1;

        }

        static void Main()

        {

            int x = 1;

            Increment(ref x);

            Console.WriteLine("The value of x is: " +x);

            Console.Read();

        }

Cuando se ejecuta el código anterior, el mensaje "El valor de x es: 2" se mostrará en la consola.

Boxeo y unboxing

La conversión de un tipo de valor a un tipo de referencia se conoce como boxing. El desempaquetado es todo lo contrario: se define como el proceso de conversión de un tipo de referencia en un tipo de valor. El siguiente fragmento de código ilustra el boxeo y unboxing en C #.

int i = 100;

Object obj = i; //Boxing

i = (int) obj; //Unboxing