Reputation: 41939
MSDN gives this example of a deep copy (http://msdn.microsoft.com/en-us/library/system.object.memberwiseclone.aspx)
public class Person
{
public int Age;
public string Name;
public IdInfo IdInfo;
public Person ShallowCopy()
{
return (Person)this.MemberwiseClone();
}
public Person DeepCopy()
{
Person other = (Person) this.MemberwiseClone();
other.IdInfo = new IdInfo(this.IdInfo.IdNumber);
return other;
}
}
But, doesn't a new Person object have to be instantiated, and then returned? For example, is this code below acceptable/equal/inferior to the code above for performing a deep copy?
As I understand the MemberwiseClone() method, it just performs a shallow copy, i.e. copies values/references from copied object to new object. This results in a shallow copy since the memory references are equal, i.e. the references point to the same objects.
public class Person
{
public int Age;
public string Name;
public IdInfo IdInfo;
public Person ShallowCopy()
{
return (Person)this.MemberwiseClone();
}
public Person DeepCopy()
{
Person other = new Person(); // difference
other.IdInfo = new IdInfo(this.IdInfo.IdNumber);
return other;
}
}
Upvotes: 13
Views: 26090
Reputation: 1488
I liked the answer of Matthias because it is most generic. But I could not apply it to my class because the type is not serializable in .NET. To work around it, I used Newtonsoft.Json, that has a more capable object serializer:
public static class ObjectExensions
{
public static T Copy<T>(this T source)
{
return JsonConvert.DeserializeObject<T>
(JsonConvert.SerializeObject(source));
}
}
Upvotes: 0
Reputation: 1205
Other alternative as object extension is the next:
public static class ObjectExtension
{
public static T Copy<T>(this T lObjSource)
{
T lObjCopy = (T)Activator.CreateInstance(typeof(T));
foreach (PropertyInfo lObjCopyProperty in lObjCopy.GetType().GetProperties())
{
lObjCopyProperty.SetValue
(
lObjCopy,
lObjSource.GetType().GetProperties().Where(x => x.Name == lObjCopyProperty.Name).FirstOrDefault().GetValue(lObjSource)
);
}
return lObjCopy;
}
}
For use:
User lObjUserCopy = lObjUser.Copy();
Upvotes: 0
Reputation: 16209
Alternatively, if you are able to set Serializable
attribute to all involved classes, you can use serialization. For the purpose of a generic deep copy I have this object
extension method:
public static class ObjectExtensions
{
#region Methods
public static T Copy<T>(this T source)
{
var isNotSerializable = !typeof(T).IsSerializable;
if (isNotSerializable)
throw new ArgumentException("The type must be serializable.", "source");
var sourceIsNull = ReferenceEquals(source, null);
if (sourceIsNull)
return default(T);
var formatter = new BinaryFormatter();
using (var stream = new MemoryStream())
{
formatter.Serialize(stream, source);
stream.Seek(0, SeekOrigin.Begin);
return (T)formatter.Deserialize(stream);
}
}
#endregion
}
This should also copy your IdInfo
field.
Usage is simple:
var copy = obj.Copy();
Upvotes: 7
Reputation: 19060
MemberwiseClone
does create a new object and copies all non static fields. In case of reference types this means copying the references. So yes, after a member wise clone the fields of the new object point to the same objects as the fields of the original object (for reference types). That is why the example in the MSDN create a new instance of the IdInfo
: To create a copy of that object as well.
Your DeepCopy
implementation is broken because it does not copy all fields, but if it would then the result would not be different from the MemberwiseClone
solution.
This question might also be an interesting read for you: Create a Deep Copy in C#
Upvotes: 4
Reputation: 446
Your DeepCopy will not copy the Age and Name fields from the object being copied. They will get their default(T) values instead (Age = 0, Name = null).
MemberwiseClone does create a new object just like you did, but it also copies the fields:
Person other = new Person();
other.Age = this.Age;
other.Name = this.Name;
As int is a value type it will be copied to the new object. The Name field will reference the same string that was referenced by Name - if that is not okay then you would need to Clone() that and set the reference in your DeepCopy() method just like IdInfo.
According to MSDN: The MemberwiseClone method creates a shallow copy by creating a new object, and then copying the nonstatic fields of the current object to the new object.
Upvotes: 0
Reputation: 12366
The implementation of MemberwiseClone
would do the following for your code.
Person p = new Person();
p.Age = this.Age; // value copy
p.Name = this.Name; // value copy
p.IdInfo = this.IdInfo; // reference copy. this object is the same in both coppies.
return p;
Upvotes: 3
Reputation: 23
Your new method does not copy the value of this.Age and this.Name, so i think it's even not called copy.
Upvotes: 0
Reputation: 726997
The MemberwiseClone()
creates a new instance of the class being copied, and also copies scalar fields into the corresponding members of the copy. It provides a better starting point for deep copying than just a plain new
, because you need to "fix" only the items requiring deep copying.
Upvotes: 8
Reputation: 166556
In the example that you specified, the values of Age and Name would be zero/blank.
This is due to the fact that you instantiate the Person object, but never set the values of these fields.
From Object.MemberwiseClone Method
The MemberwiseClone method creates a shallow copy by creating a new object, and then copying the nonstatic fields of the current object to the new object. If a field is a value type, a bit-by-bit copy of the field is performed. If a field is a reference type, the reference is copied but the referred object is not; therefore, the original object and its clone refer to the same object.
So as you can see, using the MemberwiseClone method, your Age/Name fields will also be copied/cloned.
Upvotes: 14