My dear, unfortunately the . Net does not have the feature of cloning an object in a controlled way. The only standard form given by the . Net is the method itself MemberwiseClone
, that does what is called deep cloning.
The way to clone also internal objects, requires intervention on your part, thus:
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 several drawbacks, if in the structure of objects there are 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 this case would use reflection to assemble the cloning method. In this case, you would have to assess whether this is worth.
I use the following solution to make deep cloning of an object,
using reflection to build cloning methods so that
perform well:
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);
}
}
Way to use:
public object Clone()
{
return CloneHelper.Clone(this);
}
In the logic of
Clone
, Voce needs to clone at the addresses that exist within the Person object. This way the addresses also need to beIClonable
– Roger Barretto