19

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?

icedwater
  • 4,701
  • 3
  • 35
  • 50
Gaddigesh
  • 1,953
  • 8
  • 30
  • 41
  • @Will: My sympathie, I was trying to reformat, too, what a mess! – lexu Mar 30 '10 at 12:27
  • Thanks dude ,actually i am new to this site,I formatted while typing but when i posted it came like tht – Gaddigesh Mar 31 '10 at 03:46
  • this does it without serialization: http://valueinjecter.codeplex.com/wikipage?title=Deep%20Cloning&referringTitle=Home – Omu Feb 08 '11 at 08:18
  • Check this [Answer](https://stackoverflow.com/a/52097307/4707576): https://stackoverflow.com/a/52097307/4707576 about: Cloning objects without Serialization – BenSabry Aug 30 '18 at 12:40

7 Answers7

10

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.

Stefan Steinegger
  • 63,782
  • 15
  • 129
  • 193
  • What if you have a big object? Please don't tell me that having a big object is the problem because that just creates a bigger one. – Fabio Milheiro Dec 02 '15 at 14:27
  • @Bomboca;: Of course, you need the code for explicit deep copy somewhere. But there is no other clean solution. If there was another generic solution, it would either be very shaky or require a lot of configuration effort, which makes it less transparent and much harder to control. So what's the problem with just copying the values over, the exact way how you want to have them copied? – Stefan Steinegger Dec 03 '15 at 07:04
  • I appreciate your point of view but it's too much work to set up the copy of a big object. Then someone else adds a new field and forgets or doesn't even know about the copy thing. The QA then comes and says it doesn't work. Of course, we'll work it out but doing it generically ensures more future proof. Being less transparent is the price to pay for conventions and generic ways of doing things but this is necessary to abstract lower level operations so that we can focus on solving problems efficiently. I'd still accept your solution for very little and small classes though. – Fabio Milheiro Dec 03 '15 at 11:54
  • 1
    @Bomboca: It's not the solution for small classes, but for complex classes. As I tried to point out: some properties need deep copy, other reference copy, other probably something very specific. For instance, if you want to create a copy of an order item, you don't want the order or the article in the catalog being copied just because it is referenced. Big classes should probably be split into smaller ones which can be managed. The generic solution is for trivial classes which might have many properties, but all of them are kind of simple types. – Stefan Steinegger Dec 03 '15 at 14:47
  • I'd rather have conventions that make it work in the future without us doing nothing than things not getting mapped because of a human doesn't know or forgot about it. If serialization is not to be considered, reflection definitely should be. Serialisation was good enough for me in my case but I am pretty sure there's a nuget package that does just that preventing all of that manual work that is tedious and prone to error. I understand your point of view though. – Fabio Milheiro Dec 03 '15 at 16:36
3

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.

Suresh Kumar Veluswamy
  • 4,193
  • 2
  • 20
  • 35
  • The line Type elementType = Type.GetType( type.FullName.Replace("[]", string.Empty)); should be replaced with: Type elementType = type.GetElementType(); – Karrok Jun 27 '17 at 15:33
0
    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


Andrew
  • 25
  • 1
  • 7
    How does a MemberwiseClone() become a deep copy? Is it sample code, only? If so, why isn't it marked as? Why provide bad and unmarked sample code, if there's already an answer with good sample code? Who the hell upvoted this? – No answer Nov 07 '13 at 13:32
0

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

Community
  • 1
  • 1
Gad
  • 41,526
  • 13
  • 54
  • 78
  • 1
    @m_oLogin: The OP wants it without marking it `Serializable`. – KMån Mar 30 '10 at 12:35
  • From what I understand, the author states that he cannot mark the inner class as [Serializable] because he doesn't have access to the source code... He can still mark the inner "ListB" member as [NonSerialized] and then use serialization for deep copying. – Gad Mar 30 '10 at 12:38
0

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(); }
}
JRC
  • 17
  • 1
  • 5
    -1 ICloneable doesn't mean it's a deep copy. There are classes in .Net that are marked ICloneable but do not do a deep copy. – Amir Nov 17 '11 at 22:27
0

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.

Sold Out
  • 1,321
  • 14
  • 34
  • 3
    Title is "How to deep copy a class without marking it as Serializable". Your class needs to be Serializable for you to Serialize it with a MemoryStream. – Andy_Vulhop Jan 08 '18 at 19:50
  • @Andy_Vullhop I tried it with several (non serializable) classes and also my own simple example class. In none of the cases it required "Serializable" clausule. However, I admit, that you get exception unless your serialized class isn't marked as 'DataContractAttribute'. And all its members you want to serialize must be marked with 'DataMemberAttribute'. If the class is a collection, you should mark it 'CollectionDataContractAttribute'. You actually get exception message claiming this. I think that is fair enough.. – Sold Out Jan 11 '18 at 14:16
  • Yeah, and marking the original class anything - brings him to the begin again.. :o/ – Sold Out Jan 11 '18 at 14:31
-1

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));
}
Community
  • 1
  • 1
mellis481
  • 4,332
  • 12
  • 71
  • 118