Deep cloning en C#: de la reflexión a la serialización

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

Copia profunda en C#: ¿por qué es un dolor de cabeza para cada desarrollador?

\n\n

¿Alguna vez has copiado un objeto en C# y luego te has sorprendido al descubrir que un cambio en la "copia" daña el original? ¿Te suena familiar? Este es el problema clásico de la copia superficial (shallow copy). Cuando un objeto tiene campos de referencia (otras clases, listas, arreglos), MemberwiseClone() simplemente copia las referencias. Como resultado, tanto el original como la copia apuntan al mismo objeto anidado.

\n\n

En este artículo, analizaremos el deep cloning en C#: el proceso de crear una copia completamente independiente de un objeto con todos los objetos anidados. Compararemos 5 enfoques populares, escribiremos código real, realizaremos un benchmark mental y determinaremos qué método elegir para tu proyecto.

\n\n

¿Qué es Deep Clone y en qué se diferencia de Shallow Clone?

\n\n

Imagina que tienes una clase Person que contiene Address:

\n\n
public class Address\n{\n    public string City { get; set; }\n    public string Street { get; set; }\n}\n\npublic class Person\n{\n    public string Name { get; set; }\n    public int Age { get; set; }\n    public Address HomeAddress { get; set; }\n}
\n\n

Con un shallow clone (el típico MemberwiseClone()) obtendrás una nueva persona, pero su HomeAddress apuntará al mismo objeto Address que el original. Cambiaste la calle en la copia: la calle también cambió en el original. Un desastre.

\n\n

Deep clone resuelve este problema: copia recursivamente todos los objetos anidados, creando una estructura de datos completamente independiente.

\n\n

Enfoque 1: Implementación manual mediante ICloneable y constructores de copia

\n\n

La forma más obvia, pero también la más laboriosa, es implementar la interfaz ICloneable o escribir tu propio método DeepClone().

\n\n

Ejemplo de implementación:

\n\n
public class Address : ICloneable\n{\n    public string City { get; set; }\n    public string Street { get; set; }\n\n    public object Clone()\n    {\n        return new Address { City = this.City, Street = this.Street };\n    }\n}\n\npublic class Person : ICloneable\n{\n    public string Name { get; set; }\n    public int Age { get; set; }\n    public Address HomeAddress { get; set; }\n\n    public object Clone()\n    {\n        return new Person\n        {\n            Name = this.Name,\n            Age = this.Age,\n            HomeAddress = (Address)this.HomeAddress?.Clone()\n        };\n    }\n}
\n\n

Ventajas:

\n
    \n
  • Control total sobre el proceso de copia
  • \n
  • Máximo rendimiento
  • \n
  • Código comprensible (sin "magia")
  • \n
\n\n

Desventajas:

\n
    \n
  • Gran cantidad de código repetitivo (boilerplate)
  • \n
  • Fácil olvidar actualizar el método Clone() al agregar un nuevo campo
  • \n
  • No funciona para clases de terceros (por ejemplo, de bibliotecas)
  • \n
\n\n

Cuándo usarlo: Solo para DTO simples con 2-3 campos, donde el rendimiento es crítico (por ejemplo, en motores de juegos).

\n\n

Enfoque 2: Reflexión (Reflection) — el soldado universal

\n\n

La reflexión permite escribir un método universal de deep cloning que funcionará con cualquier objeto.

\n\n

Ejemplo de código:

\n\n
public static T DeepCloneByReflection<T>(T source)\n{\n    if (source == null) return default;\n\n    var type = source.GetType();\n    \n    // Para tipos de valor y cadenas, simplemente devolvemos el valor\n    if (type.IsValueType || type == typeof(string))\n        return source;\n\n    // Para arreglos\n    if (type.IsArray)\n    {\n        var arr = source as Array;\n        var clonedArr = Array.CreateInstance(type.GetElementType(), arr.Length);\n        for (int i = 0; i < arr.Length; i++)\n            clonedArr.SetValue(DeepCloneByReflection(arr.GetValue(i)), i);\n                

Blogs

Book Recommendations