Reputation:
I have a C# class called "SmallClass".
I have an existing list myList containing objects of type "SmallClass"
I want a deep clone of the list "myList". That is, deep Clone the containing list and deep clone the objects contained in the list.
How should I do this.
public class SmallClass: ICloneable {
public string str1;
public string str2;
public string str3;
public SmallClass Clone() //This just deep clones 1 object of type "SmallClass"
{
MemoryStream m = new MemoryStream();
BinaryFormatter b = new BinaryFormatter();
b.Serialize(m, this);
m.Position = 0;
return (SRO)b.Deserialize(m);
}
public override equals(Object a)
{
return Object.Equals(this.str1 && a.str1);
}
}
public class AnotherClass
{
SomeCode();
List<SmallClass> myList = new List<SmallList>(); //myList is initialized.
// NOW I want to deep clone myList. deep Clone the containing list and deep clone the objects contained in the list.
List<SmallClass> newList = new List<SmallClass>();
foreach(var item in myList)
{
newList.Add((SmallClass)item.Clone());
}
}
Upvotes: 1
Views: 6741
Reputation: 54877
Warning: The
BinaryFormatter
type is dangerous when used with untrusted input. Whilst the usage below should be safe, Microsoft recommend avoidingBinaryFormatter
altogether due to its potential for misuse, and will remove it from .NET 7–8. Consider using another serializer or approach for your deep clones.
First off, you can define a utility method for deep-cloning any object (root):
public static T DeepClone<T>(T obj)
{
using (var stream = new MemoryStream())
{
var formatter = new BinaryFormatter();
formatter.Serialize(stream, obj);
stream.Position = 0;
return (T)formatter.Deserialize(stream);
}
}
If you want to deep-clone myList
, all you need to do is pass it as parameter to the method above:
List<SmallClass> myListClone = DeepClone(myList);
The most important consideration you need to pay attention to is that all your classes must be marked as serializable, typically through the [SerializableAttribute]
.
[SerializableAttribute]
public class SmallClass
{
// …
}
Upvotes: 6
Reputation: 271
There are a few ways of creating a deep copy which include serialization and using Object.MemberwiseClone Method (). Since an example of using serialization is already available here, I have an approach with using "MemberwiseClone".
NOTES: Recursive, Platform: .NETStandard2.0
/// <summary>
/// Returns a deep copy of an object.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="source"></param>
/// <returns></returns>
public static T DeepClone<T>(this T source) where T : class
{
if(source == null) return null;
if(source is ICollection<object> col)
{
return (T)DeepCloneCollection(col);
}
else if(source is IDictionary dict)
{
return (T)DeepCloneDictionary(dict);
}
MethodInfo method = typeof(object).GetMethod("MemberwiseClone", BindingFlags.NonPublic | BindingFlags.Instance);
T clone = (T)method.Invoke(source, null);
foreach(FieldInfo field in source.GetType().GetRuntimeFields())
{
if(field.IsStatic) continue;
if(field.FieldType.GetTypeInfo().IsPrimitive) continue;
object sourceValue = field.GetValue(source);
field.SetValue(clone, DeepClone(sourceValue));
}
return clone;
}
private static ICollection<object> DeepCloneCollection(ICollection<object> col)
{
object[] arry = (object[])Activator.CreateInstance(col.GetType(), new object[] { col.Count });
for(int i = 0; i < col.Count; i++)
{
object orig = col.ElementAt(i);
object cln = DeepClone(orig);
arry[i] = cln;
}
return arry;
}
private static IDictionary DeepCloneDictionary(IDictionary dict)
{
IDictionary clone = (IDictionary)Activator.CreateInstance(dict.GetType());
foreach(object pair in dict)
{
object key = pair.GetValueOf("Key");
object original = pair.GetValueOf("Value");
clone.Add(key, original.DeepClone());
}
return clone;
}
public static dynamic GetValueOf<T>(this T value, string property)
{
PropertyInfo p = value.GetType().GetTypeInfo().GetProperty(property);
if(p != null && p.CanRead)
{
dynamic val = p.GetValue(value);
return val;
}
return Activator.CreateInstance(p.PropertyType); //Property does not have value, return default
}
Upvotes: 0
Reputation: 8095
Your SmallClass needs to implement the ICloneable interface. Then copy every element using the Clone() method.
List<SmallClass> newList = new List<SmallClass>();
foreach(var item in myList)
{
newList.Add((SmallClass)item.Clone());
}
Upvotes: 4