Reputation: 49423
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?
Upvotes: 2673
Views: 1053737
Reputation: 1037
Deep cloning objects can easily become quirky. Just cloning dictionaries/sets while maintaining HashCode
s is non-trivial. For .NET 8+ I've looked into various issues in a few popular cloning libraries and solved most of them in a new library FastCloner (MIT licensed). I don't claim to solve them all (for example, unmanaged resources can't be always solved) but it should be well above the prior art in terms of out-of-the-box experience.
Upvotes: 0
Reputation: 40223
Whereas one approach 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 into our code.
As mentioned elsewhere, it requires 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 via serialization.
/// </summary>
/// <typeparam name="T">The type of object being copied.</typeparam>
/// <param name="source">The object instance to copy.</param>
/// <returns>A deep copy of the object.</returns>
public static T Clone<T>(T source)
{
if (!typeof(T).IsSerializable)
{
throw new ArgumentException("The type must be serializable.", nameof(source));
}
// Don't serialize a null object, simply return the default for that object
if (ReferenceEquals(source, null)) return default;
using var stream = new MemoryStream();
IFormatter formatter = new BinaryFormatter();
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.
In case of 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();
.
EDIT (January 10 2015) Thought I'd revisit this, to mention I recently started using (Newtonsoft) Json to do this, it should be lighter, and avoids the overhead of [Serializable] tags. (NB @atconway has pointed out in the comments that private members are not cloned using the JSON method)
/// <summary>
/// Perform a deep Copy of the object, using Json as a serialization method. NOTE: Private members are not cloned using this method.
/// </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 CloneJson<T>(this T source)
{
// Don't serialize a null object, simply return the default for that object
if (ReferenceEquals(source, null)) return default;
// initialize inner objects individually
// for example in default constructor some list property initialized with some values,
// but in 'source' these items are cleaned -
// without ObjectCreationHandling.Replace default constructor values will be added to result
var deserializeSettings = new JsonSerializerSettings {ObjectCreationHandling = ObjectCreationHandling.Replace};
return JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(source), deserializeSettings);
}
Upvotes: 1932
Reputation: 285
This is a way to perfeclty clone also with anonymous or object instance
public static T CloneJson<T>(T source)
{
if (ReferenceEquals(source, null))
{
return default;
}
return JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(source));
}
private object Clone(object instance)
{
Type type = instance.GetType();
MethodInfo genericMethod = this.GetType().GetMethod(nameof(CloneJson));
MethodInfo cloneMethod = genericMethod.MakeGenericMethod(type);
object clone = cloneMethod.Invoke(null, new object[] { instance });
return clone;
}
Upvotes: 0
Reputation: 18687
Cheers.
Upvotes: 26
Reputation: 210350
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.
Upvotes: 204
Reputation: 3931
In the codebase I am working with, we had a copy of the file ObjectExtensions.cs
from the GitHub project Burtsev-Alexey/net-object-deep-copy. It is 9 years old. It worked, although we later realized it was very slow for larger object structures.
Instead, we found a fork of the file ObjectExtensions.cs
in the GitHub project jpmikkers/Baksteen.Extensions.DeepCopy. A deep copy operation of a large data structure that previously took us about 30 minutes, now feels almost instantaneous.
This improved version has the following documentation:
C# extension method for fast object cloning.
This is a speed-optimized fork of Alexey Burtsev's deep copier. Depending on your usecase, this will be 2x - 3x faster than the original. It also fixes some bugs which are present in the original code. Compared to the classic binary serialization/deserialization deep clone technique, this version is about seven times faster (the more arrays your objects contain, the bigger the speedup factor).
The speedup is achieved via the following techniques:
- object reflection results are cached
- don't deep copy primitives or immutable structs & classes (e.g. enum and string)
- to improve locality of reference, process the 'fast' dimensions or multidimensional arrays in the inner loops
- use a compiled lamba expression to call MemberwiseClone
How to use:
using Baksteen.Extensions.DeepCopy; ... var myobject = new SomeClass(); ... var myclone = myobject.DeepCopy()!; // creates a new deep copy of the original object
Note: the exclamation mark (null-forgiving operator) is only required if you enabled nullable referency types in your project
Upvotes: 6
Reputation: 2849
If you use net.core and the object is serializable, you can use
var jsonBin = BinaryData.FromObjectAsJson(yourObject);
then
var yourObjectCloned = jsonBin.ToObjectFromJson<YourType>();
BinaryData is in dotnet therefore you don't need a third party lib. It also can handle the situation that the property on your class is Object type (the actual data in your property still need to be serializable)
Upvotes: -1
Reputation: 2271
Building upon @craastad's answer, for derived classes.
In the original answer, if the caller is calling DeepCopy
on a base class object, the cloned object is of a base class. But the following code will return the derived class.
using Newtonsoft.Json;
public static T DeepCopy<T>(this T source)
{
return (T)JsonConvert.DeserializeObject(JsonConvert.SerializeObject(source), source.GetType());
}
Upvotes: 1
Reputation: 1374
Besides some of the brilliant answers here, what you can do in C# 9.0 & higher, is the following (assuming you can convert your class to a record):
record Record
{
public int Property1 { get; set; }
public string Property2 { get; set; }
}
And then, simply use with operator to copy values of one object to the new one.
var object1 = new Record()
{
Property1 = 1,
Property2 = "2"
};
var object2 = object1 with { };
// object2 now has Property1 = 1 & Property2 = "2"
I hope this helps :)
Upvotes: 3
Reputation: 387
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
//[email protected]
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);
}
}
Upvotes: 37
Reputation: 1
I’ll use the below simple way to implement this. Just create an abstract class and implement method to serialize and deserialize again and return.
public abstract class CloneablePrototype<T>
{
public T DeepCopy()
{
string result = JsonConvert.SerializeObject(this);
return JsonConvert.DeserializeObject<T>(result);
}
}
public class YourClass : CloneablePrototype< YourClass>
…
…
…
And the use it like this to create deep copy.
YourClass newObj = (YourClass)oldObj.DeepCopy();
This solution is easy to extend as well if you need to implement the shallow copy method as well.
Just implement a new method in the abstract class.
public T ShallowCopy()
{
return (T)this.MemberwiseClone();
}
Upvotes: 3
Reputation: 5594
Found this package, who seems quicker of DeepCloner
, and with no dependencies, compared to it.
https://github.com/AlenToma/FastDeepCloner
Upvotes: 0
Reputation: 69
I did some benchmark on current answers and found some interesting facts.
Using BinarySerializer => https://stackoverflow.com/a/78612/6338072
Using XmlSerializer => https://stackoverflow.com/a/50150204/6338072
Using Activator.CreateInstance => https://stackoverflow.com/a/56691124/6338072
These are the results
BenchmarkDotNet=v0.13.1, OS=Windows 10.0.18363.1734 (1909/November2019Update/19H2)
Intel Core i5-6200U CPU 2.30GHz (Skylake), 1 CPU, 4 logical and 2 physical cores [Host] : .NET Framework 4.8 (4.8.4400.0), X86 LegacyJIT DefaultJob : .NET Framework 4.8 (4.8.4400.0), X86 LegacyJIT
Method | Mean | Error | StdDev | Gen 0 | Allocated |
---|---|---|---|---|---|
BinarySerializer | 220.69 us | 4.374 us | 9.963 us | 49.8047 | 77 KB |
XmlSerializer | 182.72 us | 3.619 us | 9.405 us | 21.9727 | 34 KB |
Activator.CreateInstance | 49.99 us | 0.992 us | 2.861 us | 1.9531 | 3 KB |
Upvotes: 2
Reputation: 1816
C# 9.0 is introducing the with
keyword that requires a record
(Thanks Mark Nading). This should allow very simple object cloning (and mutation if required) with very little boilerplate, but only with a record
.
You cannot seem to be able to clone (by value) a class by putting it into a generic record
;
using System;
public class Program
{
public class Example
{
public string A { get; set; }
}
public record ClonerRecord<T>(T a)
{
}
public static void Main()
{
var foo = new Example {A = "Hello World"};
var bar = (new ClonerRecord<Example>(foo) with {}).a;
foo.A = "Goodbye World :(";
Console.WriteLine(bar.A);
}
}
This writes "Goodbye World :("- the string was copied by reference (undesired). https://dotnetfiddle.net/w3IJgG
(Incredibly, the above works correctly with a struct
! https://dotnetfiddle.net/469NJv)
But cloning a record
does seem to work as indented, cloning by value.
using System;
public class Program
{
public record Example
{
public string A { get; set; }
}
public static void Main()
{
var foo = new Example {A = "Hello World"};
var bar = foo with {};
foo.A = "Goodbye World :(";
Console.WriteLine(bar.A);
}
}
This returns "Hello World", the string was copied by value! https://dotnetfiddle.net/MCHGEL
More information can be found on the blog post:
https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/with-expression
Upvotes: 6
Reputation: 1786
using System.Text.Json;
public static class CloneExtensions
{
public static T Clone<T>(this T cloneable) where T : new()
{
var toJson = JsonSerializer.Serialize(cloneable);
return JsonSerializer.Deserialize<T>(toJson);
}
}
Upvotes: 1
Reputation: 70176
An addition to @Konrad and @craastad with using built in System.Text.Json
for .NET >5
Method:
public static T Clone<T>(T source)
{
var serialized = JsonSerializer.Serialize(source);
return JsonSerializer.Deserialize<T>(serialized);
}
Extension method:
public static class SystemExtension
{
public static T Clone<T>(this T source)
{
var serialized = JsonSerializer.Serialize(source);
return JsonSerializer.Deserialize<T>(serialized);
}
}
Upvotes: 1
Reputation: 191
Create an extension:
public static T Clone<T>(this T theObject)
{
string jsonData = JsonConvert.SerializeObject(theObject);
return JsonConvert.DeserializeObject<T>(jsonData);
}
And call it like this:
NewObject = OldObject.Clone();
Upvotes: 11
Reputation: 384
For the cloning process, the object can be converted to the byte array first and then converted back to the object.
public static class Extentions
{
public static T Clone<T>(this T obj)
{
byte[] buffer = BinarySerialize(obj);
return (T)BinaryDeserialize(buffer);
}
public static byte[] BinarySerialize(object obj)
{
using (var stream = new MemoryStream())
{
var formatter = new BinaryFormatter();
formatter.Serialize(stream, obj);
return stream.ToArray();
}
}
public static object BinaryDeserialize(byte[] buffer)
{
using (var stream = new MemoryStream(buffer))
{
var formatter = new BinaryFormatter();
return formatter.Deserialize(stream);
}
}
}
The object must be serialized for the serialization process.
[Serializable]
public class MyObject
{
public string Name { get; set; }
}
Usage:
MyObject myObj = GetMyObj();
MyObject newObj = myObj.Clone();
Upvotes: 0
Reputation: 3018
After reading all answers I was surprised no one mentioned this excellent package:
Elaborating a bit on its README, here are the reason why we chose it at work:
- It can deep or shallow copy
- In deep cloning all object graph is maintained.
- Uses code-generation in runtime, as result cloning is blazingly fast
- Objects copied by internal structure, no methods or ctors called
- You don't need to mark classes somehow (like Serializable-attribute, or implement interfaces)
- No requirement to specify object type for cloning. Object can be casted to interface or as an abstract object (e.g. you can clone array of ints as abstract Array or IEnumerable; even null can be cloned without any errors)
- Cloned object doesn't have any ability to determine that he is clone (except with very specific methods)
var deepClone = new { Id = 1, Name = "222" }.DeepClone();
var shallowClone = new { Id = 1, Name = "222" }.ShallowClone();
The README contains a performance comparison of various cloning libraries and methods: DeepCloner Performance.
Upvotes: 30
Reputation: 1301
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 sample on GitHub.
Upvotes: 38
Reputation: 7336
Keep things simple and use AutoMapper as others mentioned, it's a simple little library to map one object to another... To copy an object to another with the same type, all you need is three lines of code:
MyType source = new MyType();
Mapper.CreateMap<MyType, MyType>();
MyType target = Mapper.Map<MyType, MyType>(source);
The target object is now a copy of the source object. Not simple enough? Create an extension method to use everywhere in your solution:
public static T Copy<T>(this T source)
{
T copy = default(T);
Mapper.CreateMap<T, T>();
copy = Mapper.Map<T, T>(source);
return copy;
}
The extension method can be used as follow:
MyType copy = source.Copy();
Upvotes: 17
Reputation: 5383
Shortest way but need dependency:
using Newtonsoft.Json;
public static T Clone<T>(T source) =>
JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(source));
Upvotes: 6
Reputation: 2722
EDIT: project is discontinued
If you want true cloning to unknown types you can take a look at fastclone.
That's expression based cloning working about 10 times faster than binary serialization and maintaining complete object graph integrity.
That means: if you refer multiple times to the same object in your hierachy, the clone will also have a single instance beeing referenced.
There is no need for interfaces, attributes or any other modification to the objects being cloned.
Upvotes: 21
Reputation: 18408
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:
First and foremost, those are all our options:
The article Fast Deep Copy by Expression Trees has also performance comparison of cloning by Serialization, Reflection and Expression Trees.
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 : ICloneable
{
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.
Upvotes: 162
Reputation: 3688
Disclaimer: I'm the author of the mentioned package.
I was surprised how the top answers to this question in 2019 still use serialization or reflection.
BinaryFormatter
requires the Serializable
attribute, JsonConverter
requires a parameterless constructor or attributes, neither handle read only fields or interfaces very well and both are 10-30x slower than necessary.
You can instead use Expression Trees or Reflection.Emit to generate cloning code only once, then use that compiled code instead of slow reflection or serialization.
Having come across the problem myself and seeing no satisfactory solution, I decided to create a package that does just that and works with every type and is a almost as fast as custom written code.
You can find the project on GitHub: https://github.com/marcelltoth/ObjectCloner
You can install it from NuGet. Either get the ObjectCloner
package and use it as:
var clone = ObjectCloner.DeepClone(original);
or if you don't mind polluting your object type with extensions get ObjectCloner.Extensions
as well and write:
var clone = original.DeepClone();
A simple benchmark of cloning a class hierarchy showed performance ~3x faster than using Reflection, ~12x faster than Newtonsoft.Json serialization and ~36x faster than the highly suggested BinaryFormatter
.
Upvotes: 15
Reputation: 2814
A mapper performs a deep-copy. Foreach member of your object it creates a new object and assign all of its values. It works recursively on each non-primitive inner member.
I suggest you one of the fastest, currently actively developed ones. I suggest UltraMapper https://github.com/maurosampietro/UltraMapper
Nuget packages: https://www.nuget.org/packages/UltraMapper/
Upvotes: 3
Reputation: 7237
Using System.Text.Json
:
https://devblogs.microsoft.com/dotnet/try-the-new-system-text-json-apis/
public static T DeepCopy<T>(this T source)
{
return source == null ? default : JsonSerializer.Parse<T>(JsonSerializer.ToString(source));
}
The new API is using Span<T>
. This should be fast, would be nice to do some benchmarks.
Note: there's no need for ObjectCreationHandling.Replace
like in Json.NET as it will replace collection values by default. You should forget about Json.NET now as everything is going to be replaced with the new official API.
I'm not sure this will work with private fields.
Upvotes: 1
Reputation: 33
Deep cloning is about copying state. For .net
state means fields.
Let's say one have an hierarchy:
static class RandomHelper
{
private static readonly Random random = new Random();
public static int Next(int maxValue) => random.Next(maxValue);
}
class A
{
private readonly int random = RandomHelper.Next(100);
public override string ToString() => $"{typeof(A).Name}.{nameof(random)} = {random}";
}
class B : A
{
private readonly int random = RandomHelper.Next(100);
public override string ToString() => $"{typeof(B).Name}.{nameof(random)} = {random} {base.ToString()}";
}
class C : B
{
private readonly int random = RandomHelper.Next(100);
public override string ToString() => $"{typeof(C).Name}.{nameof(random)} = {random} {base.ToString()}";
}
Cloning can be done:
static class DeepCloneExtension
{
// consider instance fields, both public and non-public
private static readonly BindingFlags bindingFlags =
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
public static T DeepClone<T>(this T obj) where T : new()
{
var type = obj.GetType();
var result = (T)Activator.CreateInstance(type);
do
// copy all fields
foreach (var field in type.GetFields(bindingFlags))
field.SetValue(result, field.GetValue(obj));
// for every level of hierarchy
while ((type = type.BaseType) != typeof(object));
return result;
}
}
Demo1:
Console.WriteLine(new C());
Console.WriteLine(new C());
var c = new C();
Console.WriteLine($"{Environment.NewLine}Image: {c}{Environment.NewLine}");
Console.WriteLine(new C());
Console.WriteLine(new C());
Console.WriteLine($"{Environment.NewLine}Clone: {c.DeepClone()}{Environment.NewLine}");
Console.WriteLine(new C());
Console.WriteLine(new C());
Result:
C.random = 92 B.random = 66 A.random = 71
C.random = 36 B.random = 64 A.random = 17
Image: C.random = 96 B.random = 18 A.random = 46
C.random = 60 B.random = 7 A.random = 37
C.random = 78 B.random = 11 A.random = 18
Clone: C.random = 96 B.random = 18 A.random = 46
C.random = 33 B.random = 63 A.random = 38
C.random = 4 B.random = 5 A.random = 79
Notice, all new objects have random values for random
field, but clone
exactly matches the image
Demo2:
class D
{
public event EventHandler Event;
public void RaiseEvent() => Event?.Invoke(this, EventArgs.Empty);
}
// ...
var image = new D();
Console.WriteLine($"Created obj #{image.GetHashCode()}");
image.Event += (sender, e) => Console.WriteLine($"Event from obj #{sender.GetHashCode()}");
Console.WriteLine($"Subscribed to event of obj #{image.GetHashCode()}");
image.RaiseEvent();
image.RaiseEvent();
var clone = image.DeepClone();
Console.WriteLine($"obj #{image.GetHashCode()} cloned to obj #{clone.GetHashCode()}");
clone.RaiseEvent();
image.RaiseEvent();
Result:
Created obj #46104728
Subscribed to event of obj #46104728
Event from obj #46104728
Event from obj #46104728
obj #46104728 cloned to obj #12289376
Event from obj #12289376
Event from obj #46104728
Notice, event backing field is copied too and client is subscribed to clone's event too.
Upvotes: 2
Reputation: 1741
As nearly all of the answers to this question have been unsatisfactory or plainly don't work in my situation, I have authored AnyClone which is entirely implemented with reflection and solved all of the needs here. I was unable to get serialization to work in a complicated scenario with complex structure, and IClonable
is less than ideal - in fact it shouldn't even be necessary.
Standard ignore attributes are supported using [IgnoreDataMember]
, [NonSerialized]
. Supports complex collections, properties without setters, readonly fields etc.
I hope it helps someone else out there who ran into the same problems I did.
Upvotes: 5
Reputation: 1307
The generic approaches are all technically valid, but I just wanted to add a note from myself since we rarely actually need a real deep copy, and I would strongly oppose using generic deep copying in actual business applications since that makes it so you might have many places where the objects are copied and then modified explicitly, its easy to get lost.
In most real-life situations also you want to have as much granular control over the copying process as possible since you are not only coupled to the data access framework but also in practice the copied business objects should rarely be 100% the same. Think an example referenceId's used by the ORM to identify object references, a full deep copy will also copy this id's so while in-memory the objects will be different, as soon as you submit it to the datastore, it will complain, so you will have to modify this properties manually after copying anyway and if the object changes you need to adjust it in all of the places which use the generic deep copying.
Expanding on @cregox answer with ICloneable, what actually is a deep copy? Its just a newly allocated object on the heap that is identical to the original object but occupies a different memory space, as such rather than using a generic cloner functionality why not just create a new object?
I personally use the idea of static factory methods on my domain objects.
Example:
public class Client
{
public string Name { get; set; }
protected Client()
{
}
public static Client Clone(Client copiedClient)
{
return new Client
{
Name = copiedClient.Name
};
}
}
public class Shop
{
public string Name { get; set; }
public string Address { get; set; }
public ICollection<Client> Clients { get; set; }
public static Shop Clone(Shop copiedShop, string newAddress, ICollection<Client> clients)
{
var copiedClients = new List<Client>();
foreach (var client in copiedShop.Clients)
{
copiedClients.Add(Client.Clone(client));
}
return new Shop
{
Name = copiedShop.Name,
Address = newAddress,
Clients = copiedClients
};
}
}
If someone is looking how he can structure object instantiation while retaining full control over the copying process that's a solution that I have been personally very successful with. The protected constructors also make it so, other developers are forced to use the factory methods which gives a neat single point of object instantiation encapsulating the construction logic inside of the object. You can also overload the method and have several clone logic's for different places if necessary.
Upvotes: 2