mercredi 28 mai 2014

.net - deep objets clonage en c# - Stack Overflow


I want to do something like:


myObject myObj = GetmyObj(); //Create and fill a new object
myObject newObj = myObj.Clone();

And then make changes to the new object that are not reflected in the original object.


I don't often need this functionality, so when it's been necessary, I've resorted to creating a new object and then copying each property individually, but it always leaves me with the feeling that there is a better or more elegant way of handling the situation.


How can I clone or deep copy an object so that the cloned object can be modified without any changes being reflected in the original object?




Whilst the standard practice is to implement the ICloneable interface (described here, so I won't regurgitate), here's a nice deep clone object copier I found on The Code Project a while ago and incorporated it in our stuff.


As mentioned elsewhere, it does require your objects to be serializable.


using System;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;

/// <summary>
/// Reference Article http://www.codeproject.com/KB/tips/SerializedObjectCloner.aspx
/// Provides a method for performing a deep copy of an object.
/// Binary Serialization is used to perform the copy.
/// </summary>
public static class ObjectCopier
{
/// <summary>
/// Perform a deep Copy of the object.
/// </summary>
/// <typeparam name="T">The type of object being copied.</typeparam>
/// <param name="source">The object instance to copy.</param>
/// <returns>The copied object.</returns>
public static T Clone<T>(T source)
{
if (!typeof(T).IsSerializable)
{
throw new ArgumentException("The type must be serializable.", "source");
}

// Don't serialize a null object, simply return the default for that object
if (Object.ReferenceEquals(source, null))
{
return default(T);
}

IFormatter formatter = new BinaryFormatter();
Stream stream = new MemoryStream();
using (stream)
{
formatter.Serialize(stream, source);
stream.Seek(0, SeekOrigin.Begin);
return (T)formatter.Deserialize(stream);
}
}
}

The idea is that it serializes your object and then deserializes it into a fresh object. The benefit is that you don't have to concern yourself about cloning everything when an object gets too complex.


And with the use of extension methods (also from the originally referenced source):


In case you prefer to use the new extension methods of C# 3.0, change the method to have the following signature:


public static T Clone<T>(this T source)
{
//...
}

Now the method call simply becomes objectBeingCloned.Clone();.




Here's a good resource I've used in the past: C# Object Cloning




The reason not to use ICloneable is not because it doesn't have a generic interface. The reason not to use it is because it's vague. It doesn't make clear whether you're getting a shallow or a deep copy; that's up to the implementer.


Yes, MemberwiseClone makes a shallow copy, but the opposite of MemberwiseClone isn't Clone; it would be, perhaps, DeepClone, which doesn't exist. When you use an object through its ICloneable interface, you can't know which kind of cloning the underlying object performs. (And XML comments won't make it clear, because you'll get the interface comments rather than the ones on the object's Clone method.)


What I usually do is simply make a Copy method that does exactly what I want.




I prefer a copy constructor to a clone. The intent is clearer.




Simple extension method to copy all public properties. Works for any objects and does not require class to be [Serializable]. Can be extended for other access level.


    public static void CopyTo( this object S, object T )
{
foreach( var pS in S.GetType().GetProperties() )
{
foreach( var pT in T.GetType().GetProperties() )
{
if( pT.Name != pS.Name ) continue;
( pT.GetSetMethod() ).Invoke( T, new object[] { pS.GetGetMethod().Invoke( S, null ) } );
}
};
}



Well I was having problems using ICloneable in Silverlight, but I liked the idea of seralization, I can seralize XML, so I did this:


static public class SerializeHelper
{
//Michael White, Holly Springs Consulting, 2009
//michael@hollyspringsconsulting.com

public static T DeserializeXML<T>(string xmlData)
where T:new()
{
if (string.IsNullOrEmpty(xmlData))
return default(T);

TextReader tr = new StringReader(xmlData);
T DocItms = new T();
XmlSerializer xms = new XmlSerializer(DocItms.GetType());
DocItms = (T)xms.Deserialize(tr);

return DocItms == null ? default(T) : DocItms;
}

public static string SeralizeObjectToXML<T>(T xmlObject)
{
StringBuilder sbTR = new StringBuilder();
XmlSerializer xmsTR = new XmlSerializer(xmlObject.GetType());
XmlWriterSettings xwsTR = new XmlWriterSettings();

XmlWriter xmwTR = XmlWriter.Create(sbTR, xwsTR);
xmsTR.Serialize(xmwTR,xmlObject);

return sbTR.ToString();
}

public static T CloneObject<T>(T objClone)
where T:new()
{
string GetString = SerializeHelper.SeralizeObjectToXML<T>(objClone);

return SerializeHelper.DeserializeXML<T>(GetString);
}



}



I wanted a cloner for very simple objects of mostly primitives and lists. If your object is out of the box JSON serializable then this method will do the trick. This requires no modification or implementation of interfaces on the cloned class, just a JSON serializer like JSON.NET.


public static T Clone<T>(T source)
{
var serialized = JsonConvert.SerializeObject(source);
return JsonConvert.DeserializeObject<T>(serialized);
}



After much much reading about many of the options linked here, and possible solutions for this issue, I believe all the options are summarized pretty well at Ian P's link (all other options are variations of those) and the best solution is provided by Pedro77's link on the question comments.


So I'll just copy relevant parts of those 2 references here. That way we can have:


The best thing to do for cloning objects in c sharp!


First and foremost, those are all our options:



Why I choose IClonable (i.e. manually)


Mr Venkat Subramaniam (redundant link here) explains in much detail why.


All his article circles around an example that tries to be applicable for most cases, using 3 objects: Person, Brain and City. We want to clone a person, which will have its own brain but the same city. You can either picture all problems any of the other methods above can bring or read the article.


This is my slightly modified version of his conclusion:



Copying an object by specifying New followed by the class name often leads to code that is not extensible. Using clone, the application of prototype pattern, is a better way to achieve this. However, using clone as it is provided in C# (and Java) can be quite problematic as well. It is better to provide a protected (non-public) copy constructor and invoke that from the clone method. This gives us the ability to delegate the task of creating an object to an instance of a class itself, thus providing extensibility and also, safely creating the objects using the protected copy constructor.



Hopefully this implementation can make things clear:


public class Person implements Cloneable
{
private final Brain brain; // brain is final since I do not want
// any transplant on it once created!
private int age;
public Person(Brain aBrain, int theAge)
{
brain = aBrain;
age = theAge;
}
protected Person(Person another)
{
Brain refBrain = null;
try
{
refBrain = (Brain) another.brain.clone();
// You can set the brain in the constructor
}
catch(CloneNotSupportedException e) {}
brain = refBrain;
age = another.age;
}
public String toString()
{
return "This is person with " + brain;
// Not meant to sound rude as it reads!
}
public Object clone()
{
return new Person(this);
}

}

Now consider having a class derive from Person.


public class SkilledPerson extends Person
{
private String theSkills;
public SkilledPerson(Brain aBrain, int theAge, String skills)
{
super(aBrain, theAge);
theSkills = skills;
}
protected SkilledPerson(SkilledPerson another)
{
super(another);
theSkills = another.theSkills;
}

public Object clone()
{
return new SkilledPerson(this);
}
public String toString()
{
return "SkilledPerson: " + super.toString();
}
}

You may try running the following code:


public class User
{
public static void play(Person p)
{
Person another = (Person) p.clone();
System.out.println(p);
System.out.println(another);
}
public static void main(String[] args)
{
Person sam = new Person(new Brain(), 1);
play(sam);
SkilledPerson bob = new SkilledPerson(new SmarterBrain(), 1, "Writer");
play(bob);
}
}

The output produced will be:


This is person with Brain@1fcc69
This is person with Brain@253498
SkilledPerson: This is person with SmarterBrain@1fef6f
SkilledPerson: This is person with SmarterBrain@209f4e

Observe that, if we keep a count of the number of objects, the clone as implemented here will keep a correct count of the number of objects.




The short answer is you inherit from the ICloneable interface and then implement the .clone function. Clone should do a memberwise copy and perform a deep copy on any member that requires it, then return the resulting object. This is a recursive operation ( it requires that all members of the class you want to clone are either value types or implement ICloneable and that their members are either value types or implement ICloneable, and so on).


For a more detailed explanation on Cloning using ICloneable, check out this article:


http://www.ondotnet.com/pub/a/dotnet/2002/11/25/copying.html


The long answer is "it depends". As mentioned by others, ICloneable is not supported by generics, requires special considerations for circular class references, and is actually viewed by some as a "mistake" in the .NET Framework. The serialization method depends on your objects being serializable, which they may not be and you may have no control over. There is still much debate in the community over which is the "best" practice. In reality, none of the solutions are the one-size fits all best practice for all situations like ICloneable was originally interpreted to be.


See the this Developer's Corner article for a few more options (credit to Ian): http://developerscon.blogspot.com/2008/06/c-object-clone-wars.html




If you're already using a 3rd party application like ValueInjecter or Automapper, you can do something like this:


MyObject oldObj; // The existing object to clone

MyObject newObj = new MyObject();
newObj.InjectFrom(oldObj); // Using ValueInjecter syntax

Using this method you don't have to implement ISerializable or ICloneable on your objects. This is common with the MVC/MVVM pattern, so simple tools like this have been created.


see the valueinjecter deep cloning solution here: http://valueinjecter.codeplex.com/wikipage?title=Faster%20Deep%20Cloning%20using%20SmartConventionInjection%20and%20FastMember&referringTitle=Home





  1. Basically you need to implement IClonable interface and then realize object structure copying.

  2. If it's deep copy of all members, you need to insure (not relating on solution you choose) that all children are clonable as well.

  3. Sometimes you need to be aware of some restriction during this process, for example if you copying the ORM objects most of frameworks allow only one object attached to the session and you MUST NOT make clones of this object, or if it's possible you need to care about session attaching of these objects.


Cheers.




I came up with this to overcome a .NET shortcoming having to manually deep copy List<T>.


I use this:


static public IEnumerable<SpotPlacement> CloneList(List<SpotPlacement> spotPlacements)
{
foreach (SpotPlacement sp in spotPlacements)
{
yield return (SpotPlacement)sp.Clone();
}
}

And at another place:


public object Clone()
{
OrderItem newOrderItem = new OrderItem();
...
newOrderItem._exactPlacements.AddRange(SpotPlacement.CloneList(_exactPlacements));
...
return newOrderItem;
}

I tried to come up with oneliner that does this, but it's not possible, due to yield not working inside anonymous method blocks.


Better still, use generic List<T> cloner:


class Utility<T> where T : ICloneable
{
static public IEnumerable<T> CloneList(List<T> tl)
{
foreach (T t in tl)
{
yield return (T)t.Clone();
}
}
}



In general, you implement the ICloneable interface and implement Clone yourself. C# objects have a built-in MemberwiseClone method that performs a shallow copy that can help you out for all the primitives.


For a deep copy, there is no way it can know how to automatically do it.




I've seen it implemented through reflection as well. Basically there was a method that would iterate through the members of an object and appropriately copy them to the new object. When it reached reference types or collections I think it did a recursive call on itself. Reflection is expensive, but it worked pretty well.




Here is a deep copy implementation:


public static object CloneObject(object opSource)
{
//grab the type and create a new instance of that type
Type opSourceType = opSource.GetType();
object opTarget = CreateInstanceOfType(opSourceType);

//grab the properties
PropertyInfo[] opPropertyInfo = opSourceType.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);

//iterate over the properties and if it has a 'set' method assign it from the source TO the target
foreach (PropertyInfo item in opPropertyInfo)
{
if (item.CanWrite)
{
//value types can simply be 'set'
if (item.PropertyType.IsValueType || item.PropertyType.IsEnum || item.PropertyType.Equals(typeof(System.String)))
{
item.SetValue(opTarget, item.GetValue(opSource, null), null);
}
//object/complex types need to recursively call this method until the end of the tree is reached
else
{
object opPropertyValue = item.GetValue(opSource, null);
if (opPropertyValue == null)
{
item.SetValue(opTarget, null, null);
}
else
{
item.SetValue(opTarget, CloneObject(opPropertyValue), null);
}
}
}
}
//return the new item
return opTarget;
}



Follow these steps:



  • Define an ISelf<T> with a read-only Self property that returns T, and ICloneable<out T>, which derives from ISelf<T> and includes a method T Clone().

  • Then define a CloneBase type which implements a protected virtual generic VirtualClone casting MemberwiseClone to the passed-in type.

  • Each derived type should implement VirtualClone by calling the base clone method and then doing whatever needs to be done to properly clone those aspects of the derived type which the parent VirtualClone method hasn't yet handled.


For maximum inheritance versatility, classes exposing public cloning functionality should be sealed, but derive from a base class which is otherwise identical except for the lack of cloning. Rather than passing variables of the explicit clonable type, take a parameter of type ICloneable<theNonCloneableType>. This will allow a routine that expects a cloneable derivative of Foo to work with a cloneable derivative of DerivedFoo, but also allow the creation of non-cloneable derivatives of Foo.




I've just created CloneExtensions library project. It performs fast, deep clone using simple assignment operations generated by Expression Tree runtime code compilation.


How to use it?


Instead of writing your own Clone or Copy methods with a tone of assignments between fields and properties make the program do it for yourself, using Expression Tree. GetClone<T>() method marked as extension method allows you to simply call it on your instance:


var newInstance = source.GetClone();

You can choose what should be copied from source to newInstance using CloningFlags enum:


var newInstance 
= source.GetClone(CloningFlags.Properties | CloningFlags.CollectionItems);

What can be cloned?



  • Primitive (int, uint, byte, double, char, etc.), known immutable types (DateTime, TimeSpan, String) and delegates (including Action, Func, etc)

  • Nullable

  • T[] arrays

  • Custom classes and structs, including generic classes and structs.


Following class/struct members are cloned internally:



  • Values of public, not readonly fields

  • Values of public properties with both get and set accessors

  • Collection items for types implementing ICollection


How fast it is?


The solution is faster then reflection, because members information has to be gathered only once, before GetClone<T> is used for the first time for given type T.


It's also faster than serialization-based solution when you clone more then couple instances of the same type T.


and more...


Read more about generated expressions on documentation.


Sample expression debug listing for List<int>:


.Lambda #Lambda1<System.Func`4[System.Collections.Generic.List`1[System.Int32],CloneExtensions.CloningFlags,System.Collections.Generic.IDictionary`2[System.Type,System.Func`2[System.Object,System.Object]],System.Collections.Generic.List`1[System.Int32]]>(
System.Collections.Generic.List`1[System.Int32] $source,
CloneExtensions.CloningFlags $flags,
System.Collections.Generic.IDictionary`2[System.Type,System.Func`2[System.Object,System.Object]] $initializers) {
.Block(System.Collections.Generic.List`1[System.Int32] $target) {
.If ($source == null) {
.Return #Label1 { null }
} .Else {
.Default(System.Void)
};
.If (
.Call $initializers.ContainsKey(.Constant<System.Type>(System.Collections.Generic.List`1[System.Int32]))
) {
$target = (System.Collections.Generic.List`1[System.Int32]).Call ($initializers.Item[.Constant<System.Type>(System.Collections.Generic.List`1[System.Int32])]
).Invoke((System.Object)$source)
} .Else {
$target = .New System.Collections.Generic.List`1[System.Int32]()
};
.If (
((System.Byte)$flags & (System.Byte).Constant<CloneExtensions.CloningFlags>(Fields)) == (System.Byte).Constant<CloneExtensions.CloningFlags>(Fields)
) {
.Default(System.Void)
} .Else {
.Default(System.Void)
};
.If (
((System.Byte)$flags & (System.Byte).Constant<CloneExtensions.CloningFlags>(Properties)) == (System.Byte).Constant<CloneExtensions.CloningFlags>(Properties)
) {
.Block() {
$target.Capacity = .Call CloneExtensions.CloneFactory.GetClone(
$source.Capacity,
$flags,
$initializers)
}
} .Else {
.Default(System.Void)
};
.If (
((System.Byte)$flags & (System.Byte).Constant<CloneExtensions.CloningFlags>(CollectionItems)) == (System.Byte).Constant<CloneExtensions.CloningFlags>(CollectionItems)
) {
.Block(
System.Collections.Generic.IEnumerator`1[System.Int32] $var1,
System.Collections.Generic.ICollection`1[System.Int32] $var2) {
$var1 = (System.Collections.Generic.IEnumerator`1[System.Int32]).Call $source.GetEnumerator();
$var2 = (System.Collections.Generic.ICollection`1[System.Int32])$target;
.Loop {
.If (.Call $var1.MoveNext() != False) {
.Call $var2.Add(.Call CloneExtensions.CloneFactory.GetClone(
$var1.Current,
$flags,


$initializers))
} .Else {
.Break #Label2 { }
}
}
.LabelTarget #Label2:
}
} .Else {
.Default(System.Void)
};
.Label
$target
.LabelTarget #Label1:
}

}


what has the same meaning like following c# code:


(source, flags, initializers) =>
{
if(source == null)
return null;

if(initializers.ContainsKey(typeof(List<int>))
target = (List<int>)initializers[typeof(List<int>)].Invoke((object)source);
else
target = new List<int>();

if((flags & CloningFlags.Properties) == CloningFlags.Properties)
{
target.Capacity = target.Capacity.GetClone(flags, initializers);
}

if((flags & CloningFlags.CollectionItems) == CloningFlags.CollectionItems)
{
var targetCollection = (ICollection<int>)target;
foreach(var item in (ICollection<int>)source)
{
targetCollection.Add(item.Clone(flags, initializers));
}
}

return target;
}

Isn't it quite like how you'd write your own Clone method for List<int>?




Serialization using BinaryFormatter is not bad solution, but very limited: objects must be marked with Serializable attributes, have default constructor, it is slow. Basically you can't copy any object you want.


If you want to deep copy any .NET object, this can be done using recursive shallow copying (Object.MemberwiseClone) until you reach "atomic" primitive type fields that are You can copy any .NET object, even including object that have no public or default constructor.


Here is the code for Object.Copy() extension method that uses this approach.


Code is production ready, I'm using it a lot for last 2 years.




This will copy all readable and writable properties of an object to another.


 public class PropertyCopy<TSource, TTarget> 
where TSource: class, new()
where TTarget: class, new()
{
public static TTarget Copy(TSource src, TTarget trg, params string[] properties)
{
if (src==null) return trg;
if (trg == null) trg = new TTarget();
var fulllist = src.GetType().GetProperties().Where(c => c.CanWrite && c.CanRead).ToList();
if (properties != null && properties.Count() > 0)
fulllist = fulllist.Where(c => properties.Contains(c.Name)).ToList();
if (fulllist == null || fulllist.Count() == 0) return trg;

fulllist.ForEach(c =>
{
c.SetValue(trg, c.GetValue(src));
});

return trg;
}
}

and this is how you use it:


 var cloned = Utils.PropertyCopy<TKTicket, TKTicket>.Copy(_tmp, dbsave,
"Creation",
"Description",
"IdTicketStatus",
"IdUserCreated",
"IdUserInCharge",
"IdUserRequested",
"IsUniqueTicketGenerated",
"LastEdit",
"Subject",
"UniqeTicketRequestId",
"Visibility");

or to copy everything:


var cloned = Utils.PropertyCopy<TKTicket, TKTicket>.Copy(_tmp, dbsave);


I want to do something like:


myObject myObj = GetmyObj(); //Create and fill a new object
myObject newObj = myObj.Clone();

And then make changes to the new object that are not reflected in the original object.


I don't often need this functionality, so when it's been necessary, I've resorted to creating a new object and then copying each property individually, but it always leaves me with the feeling that there is a better or more elegant way of handling the situation.


How can I clone or deep copy an object so that the cloned object can be modified without any changes being reflected in the original object?



Whilst the standard practice is to implement the ICloneable interface (described here, so I won't regurgitate), here's a nice deep clone object copier I found on The Code Project a while ago and incorporated it in our stuff.


As mentioned elsewhere, it does require your objects to be serializable.


using System;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;

/// <summary>
/// Reference Article http://www.codeproject.com/KB/tips/SerializedObjectCloner.aspx
/// Provides a method for performing a deep copy of an object.
/// Binary Serialization is used to perform the copy.
/// </summary>
public static class ObjectCopier
{
/// <summary>
/// Perform a deep Copy of the object.
/// </summary>
/// <typeparam name="T">The type of object being copied.</typeparam>
/// <param name="source">The object instance to copy.</param>
/// <returns>The copied object.</returns>
public static T Clone<T>(T source)
{
if (!typeof(T).IsSerializable)
{
throw new ArgumentException("The type must be serializable.", "source");
}

// Don't serialize a null object, simply return the default for that object
if (Object.ReferenceEquals(source, null))
{
return default(T);
}

IFormatter formatter = new BinaryFormatter();
Stream stream = new MemoryStream();
using (stream)
{
formatter.Serialize(stream, source);
stream.Seek(0, SeekOrigin.Begin);
return (T)formatter.Deserialize(stream);
}
}
}

The idea is that it serializes your object and then deserializes it into a fresh object. The benefit is that you don't have to concern yourself about cloning everything when an object gets too complex.


And with the use of extension methods (also from the originally referenced source):


In case you prefer to use the new extension methods of C# 3.0, change the method to have the following signature:


public static T Clone<T>(this T source)
{
//...
}

Now the method call simply becomes objectBeingCloned.Clone();.



Here's a good resource I've used in the past: C# Object Cloning



The reason not to use ICloneable is not because it doesn't have a generic interface. The reason not to use it is because it's vague. It doesn't make clear whether you're getting a shallow or a deep copy; that's up to the implementer.


Yes, MemberwiseClone makes a shallow copy, but the opposite of MemberwiseClone isn't Clone; it would be, perhaps, DeepClone, which doesn't exist. When you use an object through its ICloneable interface, you can't know which kind of cloning the underlying object performs. (And XML comments won't make it clear, because you'll get the interface comments rather than the ones on the object's Clone method.)


What I usually do is simply make a Copy method that does exactly what I want.



I prefer a copy constructor to a clone. The intent is clearer.



Simple extension method to copy all public properties. Works for any objects and does not require class to be [Serializable]. Can be extended for other access level.


    public static void CopyTo( this object S, object T )
{
foreach( var pS in S.GetType().GetProperties() )
{
foreach( var pT in T.GetType().GetProperties() )
{
if( pT.Name != pS.Name ) continue;
( pT.GetSetMethod() ).Invoke( T, new object[] { pS.GetGetMethod().Invoke( S, null ) } );
}
};
}


Well I was having problems using ICloneable in Silverlight, but I liked the idea of seralization, I can seralize XML, so I did this:


static public class SerializeHelper
{
//Michael White, Holly Springs Consulting, 2009
//michael@hollyspringsconsulting.com

public static T DeserializeXML<T>(string xmlData)
where T:new()
{
if (string.IsNullOrEmpty(xmlData))
return default(T);

TextReader tr = new StringReader(xmlData);
T DocItms = new T();
XmlSerializer xms = new XmlSerializer(DocItms.GetType());
DocItms = (T)xms.Deserialize(tr);

return DocItms == null ? default(T) : DocItms;
}

public static string SeralizeObjectToXML<T>(T xmlObject)
{
StringBuilder sbTR = new StringBuilder();
XmlSerializer xmsTR = new XmlSerializer(xmlObject.GetType());
XmlWriterSettings xwsTR = new XmlWriterSettings();

XmlWriter xmwTR = XmlWriter.Create(sbTR, xwsTR);
xmsTR.Serialize(xmwTR,xmlObject);

return sbTR.ToString();
}

public static T CloneObject<T>(T objClone)
where T:new()
{
string GetString = SerializeHelper.SeralizeObjectToXML<T>(objClone);

return SerializeHelper.DeserializeXML<T>(GetString);
}



}


I wanted a cloner for very simple objects of mostly primitives and lists. If your object is out of the box JSON serializable then this method will do the trick. This requires no modification or implementation of interfaces on the cloned class, just a JSON serializer like JSON.NET.


public static T Clone<T>(T source)
{
var serialized = JsonConvert.SerializeObject(source);
return JsonConvert.DeserializeObject<T>(serialized);
}


After much much reading about many of the options linked here, and possible solutions for this issue, I believe all the options are summarized pretty well at Ian P's link (all other options are variations of those) and the best solution is provided by Pedro77's link on the question comments.


So I'll just copy relevant parts of those 2 references here. That way we can have:


The best thing to do for cloning objects in c sharp!


First and foremost, those are all our options:



Why I choose IClonable (i.e. manually)


Mr Venkat Subramaniam (redundant link here) explains in much detail why.


All his article circles around an example that tries to be applicable for most cases, using 3 objects: Person, Brain and City. We want to clone a person, which will have its own brain but the same city. You can either picture all problems any of the other methods above can bring or read the article.


This is my slightly modified version of his conclusion:



Copying an object by specifying New followed by the class name often leads to code that is not extensible. Using clone, the application of prototype pattern, is a better way to achieve this. However, using clone as it is provided in C# (and Java) can be quite problematic as well. It is better to provide a protected (non-public) copy constructor and invoke that from the clone method. This gives us the ability to delegate the task of creating an object to an instance of a class itself, thus providing extensibility and also, safely creating the objects using the protected copy constructor.



Hopefully this implementation can make things clear:


public class Person implements Cloneable
{
private final Brain brain; // brain is final since I do not want
// any transplant on it once created!
private int age;
public Person(Brain aBrain, int theAge)
{
brain = aBrain;
age = theAge;
}
protected Person(Person another)
{
Brain refBrain = null;
try
{
refBrain = (Brain) another.brain.clone();
// You can set the brain in the constructor
}
catch(CloneNotSupportedException e) {}
brain = refBrain;
age = another.age;
}
public String toString()
{
return "This is person with " + brain;
// Not meant to sound rude as it reads!
}
public Object clone()
{
return new Person(this);
}

}

Now consider having a class derive from Person.


public class SkilledPerson extends Person
{
private String theSkills;
public SkilledPerson(Brain aBrain, int theAge, String skills)
{
super(aBrain, theAge);
theSkills = skills;
}
protected SkilledPerson(SkilledPerson another)
{
super(another);
theSkills = another.theSkills;
}

public Object clone()
{
return new SkilledPerson(this);
}
public String toString()
{
return "SkilledPerson: " + super.toString();
}
}

You may try running the following code:


public class User
{
public static void play(Person p)
{
Person another = (Person) p.clone();
System.out.println(p);
System.out.println(another);
}
public static void main(String[] args)
{
Person sam = new Person(new Brain(), 1);
play(sam);
SkilledPerson bob = new SkilledPerson(new SmarterBrain(), 1, "Writer");
play(bob);
}
}

The output produced will be:


This is person with Brain@1fcc69
This is person with Brain@253498
SkilledPerson: This is person with SmarterBrain@1fef6f
SkilledPerson: This is person with SmarterBrain@209f4e

Observe that, if we keep a count of the number of objects, the clone as implemented here will keep a correct count of the number of objects.



The short answer is you inherit from the ICloneable interface and then implement the .clone function. Clone should do a memberwise copy and perform a deep copy on any member that requires it, then return the resulting object. This is a recursive operation ( it requires that all members of the class you want to clone are either value types or implement ICloneable and that their members are either value types or implement ICloneable, and so on).


For a more detailed explanation on Cloning using ICloneable, check out this article:


http://www.ondotnet.com/pub/a/dotnet/2002/11/25/copying.html


The long answer is "it depends". As mentioned by others, ICloneable is not supported by generics, requires special considerations for circular class references, and is actually viewed by some as a "mistake" in the .NET Framework. The serialization method depends on your objects being serializable, which they may not be and you may have no control over. There is still much debate in the community over which is the "best" practice. In reality, none of the solutions are the one-size fits all best practice for all situations like ICloneable was originally interpreted to be.


See the this Developer's Corner article for a few more options (credit to Ian): http://developerscon.blogspot.com/2008/06/c-object-clone-wars.html



If you're already using a 3rd party application like ValueInjecter or Automapper, you can do something like this:


MyObject oldObj; // The existing object to clone

MyObject newObj = new MyObject();
newObj.InjectFrom(oldObj); // Using ValueInjecter syntax

Using this method you don't have to implement ISerializable or ICloneable on your objects. This is common with the MVC/MVVM pattern, so simple tools like this have been created.


see the valueinjecter deep cloning solution here: http://valueinjecter.codeplex.com/wikipage?title=Faster%20Deep%20Cloning%20using%20SmartConventionInjection%20and%20FastMember&referringTitle=Home




  1. Basically you need to implement IClonable interface and then realize object structure copying.

  2. If it's deep copy of all members, you need to insure (not relating on solution you choose) that all children are clonable as well.

  3. Sometimes you need to be aware of some restriction during this process, for example if you copying the ORM objects most of frameworks allow only one object attached to the session and you MUST NOT make clones of this object, or if it's possible you need to care about session attaching of these objects.


Cheers.



I came up with this to overcome a .NET shortcoming having to manually deep copy List<T>.


I use this:


static public IEnumerable<SpotPlacement> CloneList(List<SpotPlacement> spotPlacements)
{
foreach (SpotPlacement sp in spotPlacements)
{
yield return (SpotPlacement)sp.Clone();
}
}

And at another place:


public object Clone()
{
OrderItem newOrderItem = new OrderItem();
...
newOrderItem._exactPlacements.AddRange(SpotPlacement.CloneList(_exactPlacements));
...
return newOrderItem;
}

I tried to come up with oneliner that does this, but it's not possible, due to yield not working inside anonymous method blocks.


Better still, use generic List<T> cloner:


class Utility<T> where T : ICloneable
{
static public IEnumerable<T> CloneList(List<T> tl)
{
foreach (T t in tl)
{
yield return (T)t.Clone();
}
}
}


In general, you implement the ICloneable interface and implement Clone yourself. C# objects have a built-in MemberwiseClone method that performs a shallow copy that can help you out for all the primitives.


For a deep copy, there is no way it can know how to automatically do it.



I've seen it implemented through reflection as well. Basically there was a method that would iterate through the members of an object and appropriately copy them to the new object. When it reached reference types or collections I think it did a recursive call on itself. Reflection is expensive, but it worked pretty well.



Here is a deep copy implementation:


public static object CloneObject(object opSource)
{
//grab the type and create a new instance of that type
Type opSourceType = opSource.GetType();
object opTarget = CreateInstanceOfType(opSourceType);

//grab the properties
PropertyInfo[] opPropertyInfo = opSourceType.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);

//iterate over the properties and if it has a 'set' method assign it from the source TO the target
foreach (PropertyInfo item in opPropertyInfo)
{
if (item.CanWrite)
{
//value types can simply be 'set'
if (item.PropertyType.IsValueType || item.PropertyType.IsEnum || item.PropertyType.Equals(typeof(System.String)))
{
item.SetValue(opTarget, item.GetValue(opSource, null), null);
}
//object/complex types need to recursively call this method until the end of the tree is reached
else
{
object opPropertyValue = item.GetValue(opSource, null);
if (opPropertyValue == null)
{
item.SetValue(opTarget, null, null);
}
else
{
item.SetValue(opTarget, CloneObject(opPropertyValue), null);
}
}
}
}
//return the new item
return opTarget;
}


Follow these steps:



  • Define an ISelf<T> with a read-only Self property that returns T, and ICloneable<out T>, which derives from ISelf<T> and includes a method T Clone().

  • Then define a CloneBase type which implements a protected virtual generic VirtualClone casting MemberwiseClone to the passed-in type.

  • Each derived type should implement VirtualClone by calling the base clone method and then doing whatever needs to be done to properly clone those aspects of the derived type which the parent VirtualClone method hasn't yet handled.


For maximum inheritance versatility, classes exposing public cloning functionality should be sealed, but derive from a base class which is otherwise identical except for the lack of cloning. Rather than passing variables of the explicit clonable type, take a parameter of type ICloneable<theNonCloneableType>. This will allow a routine that expects a cloneable derivative of Foo to work with a cloneable derivative of DerivedFoo, but also allow the creation of non-cloneable derivatives of Foo.



I've just created CloneExtensions library project. It performs fast, deep clone using simple assignment operations generated by Expression Tree runtime code compilation.


How to use it?


Instead of writing your own Clone or Copy methods with a tone of assignments between fields and properties make the program do it for yourself, using Expression Tree. GetClone<T>() method marked as extension method allows you to simply call it on your instance:


var newInstance = source.GetClone();

You can choose what should be copied from source to newInstance using CloningFlags enum:


var newInstance 
= source.GetClone(CloningFlags.Properties | CloningFlags.CollectionItems);

What can be cloned?



  • Primitive (int, uint, byte, double, char, etc.), known immutable types (DateTime, TimeSpan, String) and delegates (including Action, Func, etc)

  • Nullable

  • T[] arrays

  • Custom classes and structs, including generic classes and structs.


Following class/struct members are cloned internally:



  • Values of public, not readonly fields

  • Values of public properties with both get and set accessors

  • Collection items for types implementing ICollection


How fast it is?


The solution is faster then reflection, because members information has to be gathered only once, before GetClone<T> is used for the first time for given type T.


It's also faster than serialization-based solution when you clone more then couple instances of the same type T.


and more...


Read more about generated expressions on documentation.


Sample expression debug listing for List<int>:


.Lambda #Lambda1<System.Func`4[System.Collections.Generic.List`1[System.Int32],CloneExtensions.CloningFlags,System.Collections.Generic.IDictionary`2[System.Type,System.Func`2[System.Object,System.Object]],System.Collections.Generic.List`1[System.Int32]]>(
System.Collections.Generic.List`1[System.Int32] $source,
CloneExtensions.CloningFlags $flags,
System.Collections.Generic.IDictionary`2[System.Type,System.Func`2[System.Object,System.Object]] $initializers) {
.Block(System.Collections.Generic.List`1[System.Int32] $target) {
.If ($source == null) {
.Return #Label1 { null }
} .Else {
.Default(System.Void)
};
.If (
.Call $initializers.ContainsKey(.Constant<System.Type>(System.Collections.Generic.List`1[System.Int32]))
) {
$target = (System.Collections.Generic.List`1[System.Int32]).Call ($initializers.Item[.Constant<System.Type>(System.Collections.Generic.List`1[System.Int32])]
).Invoke((System.Object)$source)
} .Else {
$target = .New System.Collections.Generic.List`1[System.Int32]()
};
.If (
((System.Byte)$flags & (System.Byte).Constant<CloneExtensions.CloningFlags>(Fields)) == (System.Byte).Constant<CloneExtensions.CloningFlags>(Fields)
) {
.Default(System.Void)
} .Else {
.Default(System.Void)
};
.If (
((System.Byte)$flags & (System.Byte).Constant<CloneExtensions.CloningFlags>(Properties)) == (System.Byte).Constant<CloneExtensions.CloningFlags>(Properties)
) {
.Block() {
$target.Capacity = .Call CloneExtensions.CloneFactory.GetClone(
$source.Capacity,
$flags,
$initializers)
}
} .Else {
.Default(System.Void)
};
.If (
((System.Byte)$flags & (System.Byte).Constant<CloneExtensions.CloningFlags>(CollectionItems)) == (System.Byte).Constant<CloneExtensions.CloningFlags>(CollectionItems)
) {
.Block(
System.Collections.Generic.IEnumerator`1[System.Int32] $var1,
System.Collections.Generic.ICollection`1[System.Int32] $var2) {
$var1 = (System.Collections.Generic.IEnumerator`1[System.Int32]).Call $source.GetEnumerator();
$var2 = (System.Collections.Generic.ICollection`1[System.Int32])$target;
.Loop {
.If (.Call $var1.MoveNext() != False) {
.Call $var2.Add(.Call CloneExtensions.CloneFactory.GetClone(
$var1.Current,
$flags,


$initializers))
} .Else {
.Break #Label2 { }
}
}
.LabelTarget #Label2:
}
} .Else {
.Default(System.Void)
};
.Label
$target
.LabelTarget #Label1:
}

}


what has the same meaning like following c# code:


(source, flags, initializers) =>
{
if(source == null)
return null;

if(initializers.ContainsKey(typeof(List<int>))
target = (List<int>)initializers[typeof(List<int>)].Invoke((object)source);
else
target = new List<int>();

if((flags & CloningFlags.Properties) == CloningFlags.Properties)
{
target.Capacity = target.Capacity.GetClone(flags, initializers);
}

if((flags & CloningFlags.CollectionItems) == CloningFlags.CollectionItems)
{
var targetCollection = (ICollection<int>)target;
foreach(var item in (ICollection<int>)source)
{
targetCollection.Add(item.Clone(flags, initializers));
}
}

return target;
}

Isn't it quite like how you'd write your own Clone method for List<int>?



Serialization using BinaryFormatter is not bad solution, but very limited: objects must be marked with Serializable attributes, have default constructor, it is slow. Basically you can't copy any object you want.


If you want to deep copy any .NET object, this can be done using recursive shallow copying (Object.MemberwiseClone) until you reach "atomic" primitive type fields that are You can copy any .NET object, even including object that have no public or default constructor.


Here is the code for Object.Copy() extension method that uses this approach.


Code is production ready, I'm using it a lot for last 2 years.



This will copy all readable and writable properties of an object to another.


 public class PropertyCopy<TSource, TTarget> 
where TSource: class, new()
where TTarget: class, new()
{
public static TTarget Copy(TSource src, TTarget trg, params string[] properties)
{
if (src==null) return trg;
if (trg == null) trg = new TTarget();
var fulllist = src.GetType().GetProperties().Where(c => c.CanWrite && c.CanRead).ToList();
if (properties != null && properties.Count() > 0)
fulllist = fulllist.Where(c => properties.Contains(c.Name)).ToList();
if (fulllist == null || fulllist.Count() == 0) return trg;

fulllist.ForEach(c =>
{
c.SetValue(trg, c.GetValue(src));
});

return trg;
}
}

and this is how you use it:


 var cloned = Utils.PropertyCopy<TKTicket, TKTicket>.Copy(_tmp, dbsave,
"Creation",
"Description",
"IdTicketStatus",
"IdUserCreated",
"IdUserInCharge",
"IdUserRequested",
"IsUniqueTicketGenerated",
"LastEdit",
"Subject",
"UniqeTicketRequestId",
"Visibility");

or to copy everything:


var cloned = Utils.PropertyCopy<TKTicket, TKTicket>.Copy(_tmp, dbsave);

0 commentaires:

Enregistrer un commentaire