Create a Deep Copy in C#

后端 未结 8 2013
孤独总比滥情好
孤独总比滥情好 2020-12-04 01:48

I want to make a deep copy of an object so I could change the the new copy and still have the option to cancel my changes and get back the original object.

My proble

相关标签:
8条回答
  • 2020-12-04 01:52

    Making a deep copy of an arbitrary object is quite difficult. What if the object contains access to a resource such as an open file with write capabilities, or a network connection? Without knowing what type of information the object holds, I would be hard to make a copy of an object, and have it function exactly the same way. You might be able to use reflection to do this, but it would get quite difficult. For starters you'd have to have some kind of list to keep all the objects you copied, otherwise, if A references B and B references A, then you might end up in an endless loop.

    0 讨论(0)
  • 2020-12-04 01:56

    It is completely impossible to deep-copy an arbitrary object.

    For example, how would you handle a Control or a FileStream or an HttpWebResponse?

    Your code cannot know how the object works and what its fields are supposed to contain.

    Do not do this.
    It's a recipe for disaster.

    0 讨论(0)
  • 2020-12-04 02:01

    Ok, I modified your routine a little bit. You'll need to clean it up but it should accomplish what you want. I did not test this against controls or filestreams, and care should be taken with those instances.

    I avoided Memberwise clone for Activator.CreateInstance. This will create new Instances of reference types and copy value types. If you use objects with multi-dimensional arrays you will need to use the Array Rank and iterate for each rank.

        static object DeepCopyMine(object obj)
        {
            if (obj == null) return null;
    
            object newCopy;
            if (obj.GetType().IsArray)
            {
                var t = obj.GetType();
                var e = t.GetElementType();
                var r = t.GetArrayRank();
                Array a = (Array)obj;
                newCopy = Array.CreateInstance(e, a.Length);
                Array n = (Array)newCopy;
                for (int i=0; i<a.Length; i++)
                {
                    n.SetValue(DeepCopyMine(a.GetValue(i)), i);
                }
                return newCopy;
            }
            else
            {
                newCopy = Activator.CreateInstance(obj.GetType(), true);
            }
    
            foreach (var field in newCopy.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
            {
                if (!field.FieldType.IsPrimitive && field.FieldType != typeof(string))
                {
                    var fieldCopy = DeepCopyMine(field.GetValue(obj));
                    field.SetValue(newCopy, fieldCopy);
                }
                else
                {
                    field.SetValue(newCopy, field.GetValue(obj));
                }
            }
            return newCopy;
        }
    
    0 讨论(0)
  • 2020-12-04 02:02

    To answer your question, you can deep-copy an array by calling Array.CreateInstance and copying the contents of the array by calling GetValue and SetValue.

    However, you should not do this for arbitrary objects.

    For example:

    • What about event handlers?
    • Some objects have unique IDs; you will end up creating duplicate IDs
    • What about objects that reference their parents?
    0 讨论(0)
  • 2020-12-04 02:05

    As other have said, deep-copying an arbitrary object can be disastrous. However, if you're pretty certain about the objects you are trying to clone, you can still attempt this.

    Two things about your original method:

    • In case of circular references you will fall into an infinite loop;
    • The only special case you need to worry about is copying members that are arrays. Everything else (Lists, Hashsets, Dictionaries, etc.) will boil down to that eventually (or in case of trees and linked lists will be deep-copy-able the standard way).

    Note also that there was a method which allowed to create an arbitrary class object without invoking any of its constructors. The BinaryFormatter used it and it was publicly available. Unfortunately I do not remember what it was called and where it lived. Something about runtimes or marshalling.

    Update: System.Runtime.Serialization.FormatterServices.GetUninitializedObject Note that

    You cannot use the GetUninitializedObject method to create instances of types that derive from the ContextBoundObject class.

    0 讨论(0)
  • 2020-12-04 02:12

    Agree with SLaks. You allways need custom copying semantics just to distingush between weather you want to have a deep copy or a flat copy. (What is a reference, what a contained reference in a sens of composite reference.)

    The pattern you are talking about is the memento pattern.

    Read the article on how to implement it. But basically it turns out to create a custom copy facitlity. Either internal in the class or external in a "copy factory".

    0 讨论(0)
提交回复
热议问题