Fastest Way to do Shallow Copy in C#

Viewed 67510

I wonder what is the fastest way to do shallow copying in C#? I only know there are 2 ways to do shallow copy:

  1. MemberwiseClone
  2. Copy each field one by one (manual)

I found that (2) is faster than (1). I'm wondering if there's another way to do shallow copying?

8 Answers

This is a complex subject with lots of possible solutions and many pros and cons to each. There is a wonderful article here that outlines several different ways of making a copy in C#. To summarize:

  1. Clone Manually
    Tedious, but high level of control.

  2. Clone with MemberwiseClone
    Only creates a shallow copy, i.e. for reference-type fields the original object and its clone refer to the same object.

  3. Clone with Reflection
    Shallow copy by default, can be re-written to do deep copy. Advantage: automated. Disadvantage: reflection is slow.

  4. Clone with Serialization
    Easy, automated. Give up some control and serialization is slowest of all.

  5. Clone with IL, Clone with Extension Methods
    More advanced solutions, not as common.

This is a way to do it using dynamic IL generation. I found it somewhere online:

public static class Cloner
{
    static Dictionary<Type, Delegate> _cachedIL = new Dictionary<Type, Delegate>();

    public static T Clone<T>(T myObject)
    {
        Delegate myExec = null;

        if (!_cachedIL.TryGetValue(typeof(T), out myExec))
        {
            var dymMethod = new DynamicMethod("DoClone", typeof(T), new Type[] { typeof(T) }, true);
            var cInfo = myObject.GetType().GetConstructor(new Type[] { });

            var generator = dymMethod.GetILGenerator();

            var lbf = generator.DeclareLocal(typeof(T));

            generator.Emit(OpCodes.Newobj, cInfo);
            generator.Emit(OpCodes.Stloc_0);

            foreach (var field in myObject.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
            {
                // Load the new object on the eval stack... (currently 1 item on eval stack)
                generator.Emit(OpCodes.Ldloc_0);
                // Load initial object (parameter)          (currently 2 items on eval stack)
                generator.Emit(OpCodes.Ldarg_0);
                // Replace value by field value             (still currently 2 items on eval stack)
                generator.Emit(OpCodes.Ldfld, field);
                // Store the value of the top on the eval stack into the object underneath that value on the value stack.
                //  (0 items on eval stack)
                generator.Emit(OpCodes.Stfld, field);
            }

            // Load new constructed obj on eval stack -> 1 item on stack
            generator.Emit(OpCodes.Ldloc_0);
            // Return constructed object.   --> 0 items on stack
            generator.Emit(OpCodes.Ret);

            myExec = dymMethod.CreateDelegate(typeof(Func<T, T>));

            _cachedIL.Add(typeof(T), myExec);
        }

        return ((Func<T, T>)myExec)(myObject);
    }
}

MemberwiseClone requires less maintenance. I don't know if having default property values helps any, maybe if could ignore items with default values.

Here is a small helper class that uses reflection to access MemberwiseClone and then caches the delegate to avoid using reflection more than necessary.

public static class CloneUtil<T>
{
    private static readonly Func<T, object> clone;

    static CloneUtil()
    {
        var cloneMethod = typeof(T).GetMethod("MemberwiseClone", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
        clone = (Func<T, object>)cloneMethod.CreateDelegate(typeof(Func<T, object>));
    }

    public static T ShallowClone(T obj) => (T)clone(obj);
}

public static class CloneUtil
{
    public static T ShallowClone<T>(this T obj) => CloneUtil<T>.ShallowClone(obj);
}

You can call it like this:

Person b = a.ShallowClone();
Related