Deep Copy -klonowanie obiektow
Deti
1 Wstęp
2 Kod źródłowy
3 Przykład użycia
4 Ograniczenia
Wstęp
Wiele razy może zajść potrzeba sklonowania dowolnego obiektu w naszym programie. Klonowanie obiektów może być:
-
płytkie (Shallow Copy) - zostaje utworzony nowy obiekt ze skopiowanymi strukturami (wszystkie typy proste), natomiast pola referencyjne (klasy) nie są kopiowane - następuje jedynie przepisanie wskaźnika.
-
głębokie (Deep Copy) - wszystkie pola (proste i referencyjne) są kopiowane.
Pierwszy typ klonowania można uzyskać bardzo łatwo - dziedzicząc po ICloneable i używając metody MemberwiseClone().
Gotowiec ten pokazuje metodę na kopiowanie głębokie.
Kod źródłowy
Cała logika zawiera się w klasę HCloner:
using System;
using System.Reflection;
using System.Collections.Generic;
namespace HAKGERSoft {
public static class HCloner {
public static T DeepCopy<T>(T obj){
if(obj==null)
throw new ArgumentNullException("Object cannot be null");
return (T)Process(obj,new Dictionary<object,object>(){ });
}
static object Process(object obj,Dictionary<object,object>circular) {
if(obj==null)
return null;
Type type=obj.GetType();
if(type.IsValueType || type==typeof(string)) {
return obj;
}
if(circular.ContainsKey(obj))
return circular[obj];
if(type.IsArray) {
string typeNoArray=type.FullName.Replace("[]",string.Empty);
Type elementType=Type.GetType(string.Format("{0}, {1}",typeNoArray,type.Assembly.FullName));
var array=obj as Array;
Array copied=Array.CreateInstance(elementType,array.Length);
circular[obj]=copied;
for(int i=0; i<array.Length; i++) {
object element=array.GetValue(i);
object copy=null;
if(element!=null && circular.ContainsKey(element))
copy=circular[element];
else
copy=Process(element,circular);
copied.SetValue(copy,i);
}
return Convert.ChangeType(copied,obj.GetType());
}
if(type.IsClass) {
object toret=Activator.CreateInstance(obj.GetType());
circular[obj]=toret;
FieldInfo[] fields=type.GetFields(BindingFlags.Public|BindingFlags.NonPublic|BindingFlags.Instance);
foreach(FieldInfo field in fields) {
object fieldValue=field.GetValue(obj);
if(fieldValue==null)
continue;
object copy=circular.ContainsKey(fieldValue)?circular[fieldValue]: Process(fieldValue,circular);
field.SetValue(toret,copy);
}
return toret;
}
else
throw new ArgumentException("Unknown type");
}
}
}
Przykład użycia
Nic prostszego:
SimpleTestObject sto=new SimpleTestObject();
SimpleTestObject copy=HCloner.DeepCopy(sto);
.. gdzie SimpleTestObject to nasza klasa testowa.
Ograniczenia
- nie kopiuje już przypisanych delegatów do zdarzeń
- nie radzi sobie z polami readonly
- nie radzi sobie z klasami, które nie mają publicznego konstruktora bez parametrów