How to deep copy a class without marking it as Ser

2020-03-01 04:57发布

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:

  1. A is a large class and contains many reference types
  2. I cannot mark B as [Serializable] as I don't have access to source code of B

The following methods to perform deep copying do not work:

  1. I cannot use ICloneable or MemberwiseClone as class A contains many reference types
  2. I cannot write a copy constructor for A, as the class is large and continuously being added to, and contains classes (like B) that cannot be deep copied
  3. 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?

7条回答
在下西门庆
2楼-- · 2020-03-01 05:26

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));
}
查看更多
男人必须洒脱
3楼-- · 2020-03-01 05:32

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.

查看更多
老娘就宠你
4楼-- · 2020-03-01 05:39

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.

查看更多
狗以群分
5楼-- · 2020-03-01 05:43
    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


查看更多
Explosion°爆炸
6楼-- · 2020-03-01 05:44

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

查看更多
ゆ 、 Hurt°
7楼-- · 2020-03-01 05:46

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.

查看更多
登录 后发表回答