Compartir a través de


Tipos de referencia y de valor (Visual C# Express)

Actualización: noviembre 2007

A diferencia de algunos lenguajes de programación con los que puede estar familiarizado, C# tiene dos variedades de tipos de datos: valor y referencia. Es importante saber la diferencia si el rendimiento es esencial para la aplicación o si se está interesado en cómo C# administra los datos y la memoria.

Cuando se declara una variable utilizando uno de los tipos de datos integrados básicos o una estructura definida por el usuario, éste es un tipo de valor. Una excepción es el tipo de datos string, que es un tipo de referencia.

Un tipo de valor almacena su contenido en la memoria asignada en la pila. Por ejemplo, en este caso el valor 42 se almacena en un área de memoria denominada pila.:

int x = 42;

Cuando la variable x queda fuera de ámbito, porque en el método en que se definió ha finalizado la ejecución, el valor se descarta de la pila.

Utilizar la pila es eficaz, pero el período limitado de duración de los tipos de valores los hace menos adecuados para compartir los datos entre las diferentes clases.

Por el contrario, un tipo de referencia, como una instancia de una clase o matriz, se asigna en un área diferente de memoria llamada "montón". En el ejemplo siguiente, el espacio requerido para los diez enteros que constituyen la matriz se asigna en el montón.

int[] numbers = new int[10];

Esta memoria no se devuelve al montón cuando finaliza un método; sólo se reclama cuando el sistema de recolección de elementos no utilizados de C# determina que ya no la necesita. Hay una sobrecarga mayor al declarar los tipos de referencia, pero éstos tienen la ventaja de ser accesibles desde otras clases.

Conversiones boxing y unboxing

Boxing es el nombre que se le da al proceso por medio del cual un tipo de valor se convierte en un tipo de referencia. Cuando se aplica la conversión boxing a una variable, se está creando una variable de referencia que señala una nueva copia en el montón. La variable de referencia es un objeto y, por lo tanto, puede utilizar todos los métodos que cada objeto hereda, por ejemplo, ToString(). Esto es lo que ocurre en el ejemplo de código siguiente:

int i = 67;                              // i is a value type
object o = i;                            // i is boxed
System.Console.WriteLine(i.ToString());  // i is boxed

Encontrará la conversión unboxing cuando utilice clases diseñadas para su uso con objetos: por ejemplo, al utilizar ArrayList para almacenar enteros. Cuando se almacena un entero en ArrayList, se aplica la conversión boxing. Cuando se recupera un entero, se debe aplicar la conversión unboxing.

System.Collections.ArrayList list = 
    new System.Collections.ArrayList();  // list is a reference type
int n = 67;                              // n is a value type
list.Add(n);                             // n is boxed
n = (int)list[0];                        // list[0] is unboxed

Problemas de rendimiento

Profundicemos un poco más. Cuando los datos se pasan a los métodos como parámetros de tipo de valor, se crea una copia de cada parámetro en la pila. Evidentemente, si el parámetro en cuestión es un tipo de datos grande, por ejemplo, una estructura definida por el usuario con muchos elementos, o el método se ejecuta muchas veces, puede afectar el rendimiento.

En estas situaciones, es preferible pasar una referencia al tipo, utilizando la palabra clave ref. Éste es el equivalente de C# de la técnica de C++ de pasar un puntero a una variable en una función. Al igual que con la versión de C++, el método tiene la capacidad de cambiar el contenido de la variable, lo cual no siempre es seguro. El programador debe decidirse por el equilibrio entre la seguridad y el rendimiento.

int AddTen(int number)  // parameter is passed by value
{
    return number + 10;
}
void AddTen(ref int number)  // parameter is passed by reference
{
    number += 10;
}

La palabra clave out es similar a la palabra clave ref, pero indica al compilador que el método debe asignar un valor al parámetro o se producirá un error de compilación.

void SetToTen(out int number)
{
    // If this line is not present, the code will not compile.
    number = 10;
}

Vea también

Conceptos

Manual del lenguaje C#

Tipos de datos integrados (Visual C# Express)

Matrices y colecciones (Visual C# Express)

Referencia

Tipos de valores (Referencia de C#)

Tipos de referencia (Referencia de C#)

Conversión boxing y unboxing (Guía de programación de C#)