🎮 UnityDocs

Structs vs clases: valor y referencia

La diferencia que confunde a medio mundo: los struct son tipos por valor (se copian) y las clases por referencia (se comparten). Cuándo usar cada uno, por qué Vector3 es un struct y su impacto en rendimiento.

⚖️ ComparativaIntermedio✅ RevisadoUnity 6.0 LTSActualizado 2026-07-15
DificultadConceptual Código Matemáticas
Tiempo📖 Lectura 8 min

La diferencia que lo explica todo#

En C# hay dos familias de tipos:

  • Tipos por valor (struct, y los básicos como int, float, bool): cuando los asignas o los pasas a un método, se copian.
  • Tipos por referencia (class): cuando los asignas o los pasas, compartes la misma instancia (una referencia a ella).

Esta única diferencia explica un montón de comportamientos "raros".

El ejemplo que lo deja claro#

C#
// STRUCT (por valor): se copia
struct PuntoStruct { public int x; }

PuntoStruct a = new PuntoStruct { x = 1 };
PuntoStruct b = a;      // COPIA
b.x = 99;
Debug.Log(a.x);         // 1  (a no cambió)

// CLASE (por referencia): se comparte
class PuntoClase { public int x; }

PuntoClase c = new PuntoClase { x = 1 };
PuntoClase d = c;       // MISMA instancia
d.x = 99;
Debug.Log(c.x);         // 99  (¡c también cambió!)

Con el struct, a y b son cajas independientes. Con la clase, c y d apuntan a la misma caja.

Comparativa#

⚖️ Comparativa · struct vs class
struct (valor)class (referencia)
Al copiar/pasarSe copia enteroSe comparte la referencia
Dónde vive (normalmente)En la pila (stack)En el montón (heap)
Puede ser nullNo (salvo nullable)
HerenciaNo hereda de otras structs
Coste de crear muchosBarato, sin basura GCGenera trabajo para el GC
Igualdad por defectoPor valor (contenido)Por referencia (misma instancia)

Por qué Vector3 es un struct#

Vector3, Vector2, Quaternion, Color son structs. Por eso pasa esto, que sorprende a todos:

C#
transform.position.x = 5f;   // ❌ ERROR de compilación

transform.position devuelve una copia del Vector3 (es por valor). Modificar la copia no tendría efecto, así que C# ni te deja. La forma correcta:

C#
Vector3 p = transform.position;   // copia
p.x = 5f;                          // modifica la copia
transform.position = p;            // asigna de vuelta
El error clásico de Vector3

"No puedo cambiar transform.position.x directamente" es una de las dudas más repetidas. La razón es exactamente esta: Vector3 es un struct (por valor), y position devuelve una copia. Guarda en una variable, modifica y reasigna.

Cuándo usar struct#

Los struct brillan para datos pequeños, inmutables y numerosos:

🧭 ¿Qué debería usar?
  • ¿Es un dato pequeño (unos pocos campos) que representa un valor (un punto, un color, un rango)? → struct
  • ¿Vas a crear muchísimos y quieres evitar basura para el GC? → struct (bien usado)
  • ¿Tiene identidad, se comparte, cambia de estado, o hereda? → class
  • ¿Dudas? → class (es lo más común y flexible; los MonoBehaviour y ScriptableObjects son clases)

Rendimiento y la trampa del boxing#

Boxing: el coste oculto

Meter un struct donde se espera un object (o una interfaz sin cuidado) provoca boxing: se crea una copia en el heap, generando basura para el recolector. Ocurre, por ejemplo, al guardar structs en colecciones no genéricas antiguas o al usar ciertas APIs. Con las colecciones genéricas modernas (List<T>) no hay boxing. En bucles calientes, el boxing repetido es una fuente silenciosa de tirones.

Structs mutables: cuidado

Un struct que se puede modificar campo a campo es fuente de bugs (por las copias). La recomendación de la industria es hacer los structs inmutables (asignar todo en el constructor y no cambiarlos). Si necesitas mutar estado con frecuencia, probablemente quieras una clase.

Errores frecuentes#

  • Esperar que modificar una copia de un struct afecte al original (transform.position.x = 5).
  • Usar struct para algo con identidad y estado que cambia (mejor clase).
  • Provocar boxing sin darte cuenta en bucles calientes.
  • Comparar clases con == esperando igualdad por contenido (por defecto es por referencia).

Ponte a prueba#

📝 Pon a prueba lo aprendido

Resumen#

struct = por valor (se copia); class = por referencia (se comparte). Por eso modificar la copia de un struct (como transform.position.x) no funciona, y por eso dos variables de una clase comparten cambios. Usa struct para datos pequeños e inmutables que creas en masa (como Vector3), y class para todo lo que tenga identidad, estado y herencia. Cuidado con el boxing en bucles calientes.

Fuentes y para profundizar