Given the following class:
class A
{
public List<B> ListB;
// etc...
}
where B
is another class that may inherit/contain some other classes.
Given this scenario:
A
is a large class and contains many reference types- I cannot mark
B
as[Serializable]
as I don't have access to source code ofB
The following methods to perform deep copying do not work:
- I cannot use
ICloneable
or 开发者_如何学运维MemberwiseClone
as classA
contains many reference types - I cannot write a copy constructor for
A
, as the class is large and continuously being added to, and contains classes (likeB
) that cannot be deep copied - I cannot use serialization as I cannot mark a contained class (like
B
, where no source code available) as[Serializable]
How can I deep copy class A
?
I stopped using serialization for deep copying anyway, because there is not enough control (not every class needs to be copied the same way). Then I started to implement my own deep copy interfaces and copy every property in the way it should be copied.
Typical ways to copy a referenced type:
- use copy constructor
- use factory method (eg. immutable types)
- use your own "Clone"
- copy only reference (eg. other Root-Type)
- create new instance and copy properties (eg. types not written by yourself lacking a copy constructor)
Example:
class A
{
// copy constructor
public A(A copy) {}
}
// a referenced class implementing
class B : IDeepCopy
{
object Copy() { return new B(); }
}
class C : IDeepCopy
{
A A;
B B;
object Copy()
{
C copy = new C();
// copy property by property in a appropriate way
copy.A = new A(this.A);
copy.B = this.B.Copy();
}
}
You may think that this a huge amount of work. But at the end, it is easy and straight forward, can be tuned where needed and does exactly what you need.
You can try this. It works for me
public static object DeepCopy(object obj)
{
if (obj == null)
return null;
Type type = obj.GetType();
if (type.IsValueType || type == typeof(string))
{
return obj;
}
else if (type.IsArray)
{
Type elementType = Type.GetType(
type.FullName.Replace("[]", string.Empty));
var array = obj as Array;
Array copied = Array.CreateInstance(elementType, array.Length);
for (int i = 0; i < array.Length; i++)
{
copied.SetValue(DeepCopy(array.GetValue(i)), i);
}
return Convert.ChangeType(copied, obj.GetType());
}
else if (type.IsClass)
{
object toret = Activator.CreateInstance(obj.GetType());
FieldInfo[] fields = type.GetFields(BindingFlags.Public |
BindingFlags.NonPublic | BindingFlags.Instance);
foreach (FieldInfo field in fields)
{
object fieldValue = field.GetValue(obj);
if (fieldValue == null)
continue;
field.SetValue(toret, DeepCopy(fieldValue));
}
return toret;
}
else
throw new ArgumentException("Unknown type");
}
Thanks to DetoX83 article on code project.
Can't you do this?
[Serializable]
class A
{
...
[NonSerialized]
public List<B> ListB;
....
}
And then refer to How do you do a deep copy of an object in .NET (C# specifically)? for a cloning function
your interface IDeepCopy is exactly what ICloneable specifies.
class B : ICloneable
{
public object Clone() { return new B(); }
}
and with more friendly implementation :
class B : ICloneable
{
public B Clone() { return new B(); }
// explicit implementation of ICloneable
object ICloneable.Clone() { return this.Clone(); }
}
private interface IDeepCopy<T> where T : class
{
T DeepCopy();
}
private class MyClass : IDeepCopy<MyClass>
{
public MyClass DeepCopy()
{
return (MyClass)this.MemberwiseClone();
}
}
Pluss: Yoy can control copy process (if your class has identifier property you can set them, or you can write other business logic code)
Minus: class can be marked as sealed
Try using a memory stream to get a deep copy of your object:
public static T MyDeepCopy<T>(this T source)
{
try
{
//Throw if passed object has nothing
if (source == null) { throw new Exception("Null Object cannot be cloned"); }
// Don't serialize a null object, simply return the default for that object
if (Object.ReferenceEquals(source, null))
{
return default(T);
}
//variable declaration
T copy;
var obj = new DataContractSerializer(typeof(T));
using (var memStream = new MemoryStream())
{
obj.WriteObject(memStream, source);
memStream.Seek(0, SeekOrigin.Begin);
copy = (T)obj.ReadObject(memStream);
}
return copy;
}
catch (Exception)
{
throw;
}
}
Here is more.
An answer from a different thread that using json serialization is the best I've seen.
public static T CloneJson<T>(this T source)
{
if (Object.ReferenceEquals(source, null))
{
return default(T);
}
return JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(source));
}
精彩评论