JASoft.org

El blog de José Manuel Alarcón Aguín. Programación web y mucho más...

MENÚ - JASoft: JM Alarcón

Clases y estructuras en .NET: cuándo usar cuál y otras cuestiones habituales

Algunas preguntas que recibo con frecuencia en el curso de preparación del examen 70-536 en campusMVP están relacionadas con las estructuras y las clases en .NET. Esta misma semana he tenido un par de ellas muy interesantes, lo que me lo ha recordado y me he decidido a resumir aquí algunas de estas preguntas y sus respuestas.

¿Cuál es la principal diferencia entre una estructura y una clase?

La principal diferencia entre Estructura y Clase es que las primeras son tipos por valor y las otras tipos por referencia. Es decir, aunque los primeros pueden trabajar como clases, realmente son valores ubicados en la pila directamente, y no punteros (referencias) a la estructura en memoria. Esto significa que los primeros se pueden gestionar más eficientemente al instanciarlos (se hace más rápido), sobre todo en grandes cantidades, como una matriz. Al crear una matriz de estructuras éstas se crean consecutivamente en memoria y no es necesario instanciar una a una y guardar sus referencias en la matriz, por lo que es mucho más rápido su uso. Por el contrario si pasas a un método una estructura, al hacerlo se crea una copia de la misma en lugar de pasar una referencia y por lo tanto los cambios aplicados sobre ella se pierden. El rendimiento es menor también.

Lo mismo pasa al aplicar ciertas funciones de objetos a estructuras en las que hay que hacer Boxing y Unboxing y eso merma el rendimiento.

¿Cuándo es interesante usar una estructura en lugar de una clase?

Lo habitual es no usar estructuras casi nunca, salvo para realizar optimizaciones.

Un caso concreto de uso de una estructura sería si, por ejemplo, quieres trabajar con números complejos, que como seguramente sabrás están representados por dos cifras: una parte real y una imaginaria. Si quieres tratarlos como un número más, es decir, que sean un tipo por valor, lo lógico es que los hagas estructuras para no tener que instanciarlos y para que cuando llames a métodos pasándole números complejos éstos se pasen por valor, y no se modifiquen con lo que hagas dentro del método al que los pasas. A todos los efectos se comportarían como si fueran otro tipo por valor, como un entero o un double.

Otro ejemplo, más común, es cuando debes trabajar con una cantidad muy elevada de objetos en memoria, uno tras otro. Por ejemplo imagínate que tienes que hacer una transformación sobre puntos tridimensionales en el espacio. Te puedes crear una clase que los represente, pero si debes ir creando y procesando miles de ellos será mucho más eficiente crearlos usando estucturas porque de este modo se crean en la pila y se libera su espacio en cuanto dejas de necesitarlos (los objetos quedarían en memoria hasta la próxima vez que los necesites). 

También hay escenarios de interoperabilidad, cuando se llama a código nativo usando PInvoke, o a objetos COM usando COM/Interop, donde es necesario pasar estructuras y no clases. Pero vamos, como resumen quedate con que, salvo casos raros, todo debe ser clases.

¿Qué diferencia hay entre usar o no New al declarar una variable cuyo tipo es una estructura?

Consideremos por ejemplo esta estructura en C#:

public struct Punto 
{
    public int x, y;
}

O en VB:

Public Structure Punto
    Public X, Y As Integer
End Structure

Puedes declarar una variable que las use de dos formas diferentes: usando New o no. Por ejemplo en VB:

Dim p As Punto
Dim p As New Punto

¿Cuál es la diferencia? A priori parece no haber ninguna. Sin embargo sí que la hay, y tiene que ver con extender las estructuras con propiedades y métodos. 

Las estructuras se pueden extender con métodos, propiedades, etc.. como si fueran clases. Por ejemplo, consideremos el siguiente código en C#:

public struct Punto
{
	int x, y;
	
	public int X
	{ 
		get { return x; }
		set { x = value; }
	}

	public int Y
	{ 
		get { return y; }
		set { y = value; }
	}
}

Si escribimos el siguiente código (el más habitual):

Punto p;
Console.WriteLine (p.x);

obtendremos un error que nos dice que la variable interna 'x' no está inicializada. Sin embargo con este otro código:

Punto p = new Punto();
Console.WriteLine (p.x);

No se produciría error alguno.

Lo importante aquí es que, al ser declaradas por defecto (sin constructor), las estructuras no inicializan sus miembros internos, por lo tanto antes de usarlos debemos asignarles un valor. Cada propiedad tiene un campo interno que no está inicializado cuando intentamos usarlo a través de la propiedad, y obtendrías un error al hacerlo (no se sabe si tu propiedad antes de asignarlo intenta hacer algo con él y por lo tanto te obliga a tenerlo inicializado). Si usas el constructor por defecto explícitamente (con la palabra clave new) lo que éste hace es inicializar todos los miembros internos a su valor por defecto, y a partir de ese momento ya te deja utilizarlos. Si no usas new no se llama al constructor por defecto y los campos están sin inicializar.

Otros posts

Los siguientes siguientes posts de este blog están relacionados también con, que explican precisamente algunas diferencias importantes entre clases y estructuras en determinadas situaciones:

- Diferencias al usar Clases y Estructuras en matrices
- Algunas sutilezas sobre paso de parámetros por referencia y por valor en C#
- Nada se crea ni se destruye, sólo se transforma... Excepto las cadenas en .NET

¡Espero que te resulte útil!

José Manuel Alarcón
Banner

Agregar comentario