Tipos · valor y referencia
struct frente a class en C#
El keyword importa menos que la semántica de asignación: una variable de class guarda una referencia a un objeto compartido; una de struct guarda su propia copia. Todo lo demás se deduce de ahí.
class: dos nombres, un objeto
Asignar una variable de clase copia la referencia, no el objeto. Ambos nombres apuntan a la misma caja:
class Perro
{
public string Nombre;
public Perro(string nombre) { Nombre = nombre; }
}
Perro a = new Perro("Rex");
Perro b = a; // b es un SEGUNDO NOMBRE del mismo objeto
b.Nombre = "Max"; // renombrando a través de b…
Console.WriteLine(a.Nombre); // …también se renombró para a
class Perro
{
public string Nombre;
public Perro(string nombre) { Nombre = nombre; }
}
Perro a = new Perro("Rex");
Perro b = new Perro("Rex"); // un objeto genuinamente nuevo y separado
b.Nombre = "Max";
Console.WriteLine(a.Nombre); // intacto
Console.WriteLine(a == b); // == compara REFERENCIAS: objetos distintos → False
struct: asignar es copiar
Los struct invierten la semántica. Asignar un struct copia el valor completo — las dos variables son independientes desde ese instante:
struct Punto
{
public int X;
public Punto(int x) { X = x; }
}
Punto a = new Punto(1);
Punto b = a; // COPIA COMPLETA — b tiene su propio almacenamiento
b.X = 99;
Console.WriteLine(a.X); // 1 — a nunca se tocó
Console.WriteLine(b.X); // 99
Nota honesta: el motor del navegador trata hoy los struct como clases (semántica de referencia) — ese programa en Visual Studio imprime 1 y 99 como se muestra. El concepto se mantiene; verifica el copiado en un compilador real hasta que el motor tenga semántica de valor.
La trampa que esto explica
¿Te ha pasado que cambiaste un campo a través de una variable y otra variable "lo vio"? Eso era un alias de referencia. Y su espejo: cambiaste un campo de un struct dentro de un método y la copia del llamador no cambió. Ambas son el comportamiento correcto de su tipo — el bug es esperar el otro:
| Escenario | class | struct |
|---|---|---|
b = a; copia… | la referencia | el valor completo |
¿Mutar por b afecta a a? | Sí — mismo objeto | No — copias separadas |
a == b significa… | misma identidad | mismos valores (si == está definido) |
| Vive… | en el heap, recolectado | en línea (pila o dentro de su dueño) |
¿Cuál elegir?
La regla práctica que siguen casi todas las bases de código:
- Por defecto,
class. Entidades con identidad (un Usuario, un Pedido, un Perro) — la identidad es justo lo que modela la semántica de referencia. - Revisa
structpara datos pequeños (< ~16–24 bytes), inmutables, de tipo valor, donde copiar es barato y natural: coordenadas, importes, fechas, colores. Los propiosint,double,bool,DateTimeyGuidde .NET son struct. - Los struct mutables son una trampa. Que una copia se mute a sí misma casi nunca es lo que querías — si el struct necesita cambiar, probablemente quiere ser class.
Autotest de un minuto
Ejecuta esto en el playground y predice la salida antes de pulsar Ejecutar — es la demo del alias con los nombres cambiados:
Perro x = new Perro("Uno");
Perro y = x;
y = new Perro("Dos");
Console.WriteLine(x.Nombre); // predice: ¿Uno o Dos? (referencia vs objeto)
Si dijiste "Uno" — asignar new Perro("Dos") a y reapunta la referencia de y; nunca toca el objeto al que apunta x. Lectura relacionada: var frente a tipos explícitos.