This implementation makes heavy use of the FormatterServices object used by serialization:

http://msdn.microsoft.com/en-us/library/system.runtime.serialization.formatterservices_members.aspx

It provides several helpful methods in this case that are aware of custom serialization options like [NonSerialized]. In the case of an object not being marked [Serializable] you have to create field list manually. Here is an example shallow object clone:

using System.Runtime.Serialization;
using System.Reflection;

    static class Clonable
    {
        public static T Clone<T>(this T instance)
        {
            object copy;
            Type type = instance.GetType();

            if (instance is ICloneable)
                return (T)((ICloneable)instance).Clone();

            List<MemberInfo> fields = new List<MemberInfo>();
            if (type.GetCustomAttributes(typeof(SerializableAttribute), false).Length == 0)
            {
                Type t = type;
                while (t != typeof(Object))
                {
                    fields.AddRange(t.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly));
                    t = t.BaseType;
                }
            }
            else
                fields.AddRange(FormatterServices.GetSerializableMembers(instance.GetType()));

            copy = FormatterServices.GetUninitializedObject(instance.GetType());
            object[] values = FormatterServices.GetObjectData(instance, fields.ToArray());
            FormatterServices.PopulateObjectMembers(copy, fields.ToArray(), values);

            return (T)copy;
        }
    }

As with anything, use this carefully. This method run against some objects will cause native handles to be copied and can then lead to application crashes in unmanaged code.

If you need a deep clone things get a lot more complicated. I don’t recommend it; rather you should insist that objects are decorated with [Serialiable] and simply use serialization to copy the object. That being said, I will post a working deep-clone as a follow up to this post.

Comments