How do I clone a generic list in C#?

前端 未结 26 2506
失恋的感觉
失恋的感觉 2020-11-22 01:27

I have a generic list of objects in C#, and wish to clone the list. The items within the list are cloneable, but there doesn\'t seem to be an option to do list.Clone()

26条回答
  •  死守一世寂寞
    2020-11-22 01:56

    Another thing: you could use reflection. If you'll cache this properly, then it'll clone 1,000,000 objects in 5.6 seconds (sadly, 16.4 seconds with inner objects).

    [ProtoContract(ImplicitFields = ImplicitFields.AllPublic)]
    public class Person
    {
           ...
          Job JobDescription
           ...
    }
    
    [ProtoContract(ImplicitFields = ImplicitFields.AllPublic)]
    public class Job
    {...
    }
    
    private static readonly Type stringType = typeof (string);
    
    public static class CopyFactory
    {
        static readonly Dictionary ProperyList = new Dictionary();
    
        private static readonly MethodInfo CreateCopyReflectionMethod;
    
        static CopyFactory()
        {
            CreateCopyReflectionMethod = typeof(CopyFactory).GetMethod("CreateCopyReflection", BindingFlags.Static | BindingFlags.Public);
        }
    
        public static T CreateCopyReflection(T source) where T : new()
        {
            var copyInstance = new T();
            var sourceType = typeof(T);
    
            PropertyInfo[] propList;
            if (ProperyList.ContainsKey(sourceType))
                propList = ProperyList[sourceType];
            else
            {
                propList = sourceType.GetProperties(BindingFlags.Public | BindingFlags.Instance);
                ProperyList.Add(sourceType, propList);
            }
    
            foreach (var prop in propList)
            {
                var value = prop.GetValue(source, null);
                prop.SetValue(copyInstance,
                    value != null && prop.PropertyType.IsClass && prop.PropertyType != stringType ? CreateCopyReflectionMethod.MakeGenericMethod(prop.PropertyType).Invoke(null, new object[] { value }) : value, null);
            }
    
            return copyInstance;
        }
    

    I measured it in a simple way, by using the Watcher class.

     var person = new Person
     {
         ...
     };
    
     for (var i = 0; i < 1000000; i++)
     {
        personList.Add(person);
     }
     var watcher = new Stopwatch();
     watcher.Start();
     var copylist = personList.Select(CopyFactory.CreateCopyReflection).ToList();
     watcher.Stop();
     var elapsed = watcher.Elapsed;
    

    RESULT: With inner object PersonInstance - 16.4, PersonInstance = null - 5.6

    CopyFactory is just my test class where I have dozen of tests including usage of expression. You could implement this in another form in an extension or whatever. Don't forget about caching.

    I didn't test serializing yet, but I doubt in an improvement with a million classes. I'll try something fast protobuf/newton.

    P.S.: for the sake of reading simplicity, I only used auto-property here. I could update with FieldInfo, or you should easily implement this by your own.

    I recently tested the Protocol Buffers serializer with the DeepClone function out of the box. It wins with 4.2 seconds on a million simple objects, but when it comes to inner objects, it wins with the result 7.4 seconds.

    Serializer.DeepClone(personList);
    

    SUMMARY: If you don't have access to the classes, then this will help. Otherwise it depends on the count of the objects. I think you could use reflection up to 10,000 objects (maybe a bit less), but for more than this the Protocol Buffers serializer will perform better.

提交回复
热议问题