My dear, unfortunately .Net does not have the ability to clone an object in a controlled way. The only default form given by .Net is the MemberwiseClone
method itself, which does what is called non-deep cloning.
The way to clone also internal objects, requires intervention on your part, like this:
public object Clone()
{
var clone = (Pessoa)this.MemberwiseClone();
clone.Endereco = (Endereco)clone.Endereco.Clone();
return clone;
}
Alternative: Serialize / Deserialize
Another alternative would be to serialize and then deserialize the object, but this can have a number of drawbacks if objects exist in non-serializable objects.
Example:
IFormatter formatter = new BinaryFormatter();
using (Stream stream = new MemoryStream())
{
formatter.Serialize(stream, source);
return formatter.Deserialize(stream);
}
Alternative: Reflection
There is an alternative that gives more work, and in the case would be use reflection to mount the method of cloning. In this case, you would have to evaluate if this is worth it.
I use the following solution to do deep cloning of an object,
using reflection to be able to construct the methods of cloning so that
get good performance:
public static class CloneHelper
{
public static T Clone<T>(T objToClone) where T : class
{
return CloneHelper<T>.Clone(objToClone);
}
}
public static class CloneHelper<T> where T : class
{
private static readonly Lazy<PropertyHelper.Accessor<T>[]> _LazyCloneableAccessors =
new Lazy<PropertyHelper.Accessor<T>[]>(CloneableProperties, isThreadSafe: true);
private static readonly Func<object, object> MemberwiseCloneFunc;
static CloneHelper()
{
var flags = BindingFlags.Instance | BindingFlags.FlattenHierarchy | BindingFlags.NonPublic;
MemberwiseCloneFunc = (Func<object, object>)Delegate.CreateDelegate(
typeof(Func<object, object>),
typeof(T).GetMethod("MemberwiseClone", flags));
}
[ReflectionPermission(SecurityAction.Assert, Unrestricted = true)]
private static PropertyHelper.Accessor<T>[] CloneableProperties()
{
var bindingFlags = BindingFlags.Instance
| BindingFlags.FlattenHierarchy
| BindingFlags.Public
| BindingFlags.NonPublic;
var result = typeof(T)
.GetProperties(bindingFlags)
.Where(p => p.PropertyType != typeof(string) && !p.PropertyType.IsValueType)
.Where(p => p.PropertyType.GetMethods(bindingFlags).Any(x => x.Name == "Clone"))
.Select(PropertyHelper.CreateAccessor<T>)
.Where(a => a != null)
.ToArray();
return result;
}
public static T Clone(T objToClone)
{
var clone = MemberwiseCloneFunc(objToClone) as T;
// clonando todas as propriedades que possuem um método Clone
foreach (var accessor in _LazyCloneableAccessors.Value)
{
var propToClone = accessor.GetValueObj(objToClone);
var clonedProp = propToClone == null ? null : ((dynamic)propToClone).Clone() as object;
accessor.SetValueObj(objToClone, clonedProp);
}
return clone;
}
}
public static class PropertyHelper
{
// solução baseada em: http://stackoverflow.com/questions/4085798/creating-an-performant-open-delegate-for-an-property-setter-or-getter
public abstract class Accessor<T>
{
public abstract void SetValueObj(T obj, object value);
public abstract object GetValueObj(T obj);
}
public class Accessor<TTarget, TValue> : Accessor<TTarget>
{
private readonly PropertyInfo _property;
public Accessor(PropertyInfo property)
{
_property = property;
if (property.GetSetMethod(true) != null)
this.Setter = (Action<TTarget, TValue>)
Delegate.CreateDelegate(typeof(Action<TTarget, TValue>), property..GetSetMethod(true));
if (property.GetGetMethod(true) != null)
this.Getter = (Func<TTarget, TValue>)
Delegate.CreateDelegate(typeof(Func<TTarget, TValue>), property.GetGetMethod(true));
}
public Action<TTarget, TValue> Setter { get; private set; }
public Func<TTarget, TValue> Getter { get; private set; }
public override void SetValueObj(TTarget obj, object value) { Setter(obj, (TValue)value); }
public override object GetValueObj(TTarget obj) { return Getter(obj); }
public override string ToString() { return _property.ToString(); }
}
public static Accessor<T> CreateAccessor<T>(PropertyInfo property)
{
var getMethod = property.GetGetMethod();
if (getMethod == null || getMethod.GetParameters().Length != 0)
return null;
var accessor = (Accessor<T>)Activator.CreateInstance(
typeof(Accessor<,>).MakeGenericType(typeof(T),
property.PropertyType), property);
return accessor;
}
public static Accessor<TIn, TOut> CreateAccessor<TIn, TOut>(PropertyInfo property)
{
return (Accessor<TIn, TOut>)CreateAccessor<TIn>(property);
}
}
How to use:
public object Clone()
{
return CloneHelper.Clone(this);
}