问题
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
orMemberwiseClone
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
?
回答1:
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.
回答2:
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.
回答3:
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
回答4:
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
回答5:
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(); }
}
回答6:
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));
}
回答7:
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.
来源:https://stackoverflow.com/questions/2545025/how-to-deep-copy-a-class-without-marking-it-as-serializable