Skip to content

C# 深拷贝方法效率比较(2)

🏷️ C#

之前的博客 中比较了两种深拷贝的性能,但是最近发现通过反射深拷贝的 DeepCopyByReflect 方法虽然效率高,但是在遇到数组/列表类型的字段时会报错。

添加了其对数组/列表类型的支持后,测试代码更新如下。

另外新增了一个通过序列化为 JSON 字符串,然后再反序列化的方法。使用了 Newtonsoft.Json 包。

csharp
using Newtonsoft.Json;
using System;
using System.Diagnostics;
using System.Linq;
using System.IO;
using System.Reflection;
using System.Runtime.Serialization.Formatters.Binary;
using System.Collections.Generic;

namespace CloneCompare
{

    class Program
    {
        static void Main(string[] args)
        {
            int loop = 10 * 10000;
            var staff = new Staff()
            {
                FirstName = "佳佳",
                LastName = "刘",
                Site = "liujiajia.me",
                History = new string[] { "A University", "A Company" },
                Scores = new List<Score> {
                    new Score() { Course = "语文", Point = 80 },
                    new Score() { Course = "数学", Point = 85 },
                }
            };

            Stopwatch sw1 = new Stopwatch();
            sw1.Start();
            for (int i = 0; i < loop; i++)
            {
                _ = DeepCopyByReflect(staff);
            }
            sw1.Stop();
            Console.WriteLine($"通过 DeepCopyByReflect 方法创建深拷贝耗费 {sw1.ElapsedMilliseconds} ms");

            sw1.Reset();
            sw1.Start();
            for (int i = 0; i < loop; i++)
            {
                _ = DeepCopyByBin(staff);
            }
            sw1.Stop();
            Console.WriteLine($"通过 DeepCopyByBin 方法创建深拷贝耗费 {sw1.ElapsedMilliseconds} ms");

            sw1.Reset();
            sw1.Start();
            for (int i = 0; i < loop; i++)
            {
                _ = DeepCopyByJSON(staff);
            }
            sw1.Stop();
            Console.WriteLine($"通过 DeepCopyByJSON 方法创建深拷贝耗费 {sw1.ElapsedMilliseconds} ms");

            Console.ReadLine();
        }

        /// <summary>
        /// 深拷贝,通过序列化与反序列化实现
        /// </summary>
        /// <typeparam name="T">深拷贝的类类型</typeparam>
        /// <param name="obj">深拷贝的类对象</param>
        /// <returns></returns>
        public static T DeepCopyByBin<T>(T obj)
        {
            object retval;
            using (MemoryStream ms = new MemoryStream())
            {
                BinaryFormatter bf = new BinaryFormatter();
                //序列化成流
                bf.Serialize(ms, obj);
                ms.Seek(0, SeekOrigin.Begin);
                //反序列化成对象
                retval = bf.Deserialize(ms);
                ms.Close();
            }
            return (T)retval;
        }

        /// <summary>
        /// 深拷贝,通过 JOSN 实现
        /// </summary>
        /// <typeparam name="T">深拷贝的类类型</typeparam>
        /// <param name="obj">深拷贝的类对象</param>
        /// <returns></returns>
        public static T DeepCopyByJSON<T>(T obj)
        {
            return JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(obj));
        }

        /// <summary>
        /// 深拷贝,通过反射实现
        /// </summary>
        /// <typeparam name="T">深拷贝的类类型</typeparam>
        /// <param name="obj">深拷贝的类对象</param>
        /// <returns></returns>
        public static T DeepCopyByReflect<T>(T obj)
        {
            // 如果是字符串或值类型则直接返回
            if (obj is string || obj.GetType().IsValueType) return obj;
            // 如果是数组类型
            if (obj.GetType().IsArray)
            {
                var array = obj as Array;
                var newArray = Array.CreateInstance(array.GetType().GetElementType(), array.Length);
                for (int i = 0; i < array.Length; i++)
                {
                    newArray.SetValue(DeepCopyByReflect(array.GetValue(i)), i);
                }
                return (T)(object)newArray;
            }
            if (obj.GetType().FullName.IndexOf("System.Collections.Generic.List") == 0)
            {
                var list = Activator.CreateInstance(obj.GetType());
                int count = Convert.ToInt32(obj.GetType().GetProperty("Count").GetValue(obj));
                for (int i = 0; i < count; i++)
                {
                    obj.GetType().GetMethod("Add").Invoke(list, new object[] { DeepCopyByReflect(obj.GetType().GetProperty("Item").GetValue(obj, new object[] { i })) });
                }
                return (T)list;
            }
            object retval = Activator.CreateInstance(obj.GetType());
            FieldInfo[] fields = obj.GetType().GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static);
            foreach (FieldInfo field in fields)
            {
                try {
                    field.SetValue(retval, DeepCopyByReflect(field.GetValue(obj)));
                }
                catch (Exception ex) {
                    Console.WriteLine(ex);
                }
            }
            return (T)retval;
        }

        /// <summary>
        /// 深拷贝,通过反射实现
        /// </summary>
        /// <typeparam name="T">深拷贝的类类型</typeparam>
        /// <param name="obj">深拷贝的类对象</param>
        /// <returns></returns>
        public static List<T> DeepCopyByReflect<T>(List<T> obj)
        {
            return obj.Select(m => DeepCopyByReflect(m)).ToList();
        }
    }

    [Serializable]
    class Staff
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string Site { get; set; }

        public string[] History { get; set; }

        public List<Score> Scores { get; set; }
    }

    [Serializable]
    public class Score
    {
        public string Course { get; set; }
        public int Point { get; set; }
    }
}

执行结果如下:

通过 DeepCopyByReflect 方法创建深拷贝耗费 755 ms
通过 DeepCopyByBin 方法创建深拷贝耗费 3645 ms
通过 DeepCopyByJSON 方法创建深拷贝耗费 1195 ms

可以看到通过反射的实现(DeepCopyByReflect)耗时增加了好多,但仍然是效率最高的。
通过 JSON 序列化/反序列化的实现效率跟反射相比差距不大,实现起来比较简单,而且 Newtonsoft.Json 包使用的人也很多,健壮性比较高。性能要求不是很苛刻的地方,推荐使用这种方法。