Skip to content

C# 两种深拷贝方法效率比较

🏷️ C#

CSDN简书 上都看到一种通过 二进制的序列化和反序列化 来实现深拷贝的方式。

感觉这种方式应该比通过反射的方式更快,然而写了点代码测试了之后,结果大出所料。(下面测试中使用的两种实现均来自 这篇博客

csharp
using System;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.Serialization.Formatters.Binary;

namespace DeepCopyTest
{
    class Program
    {
        static void Main(string[] args)
        {
            int loop = 10 * 10000;
            var staff = new Staff()
            {
                FirstName = "佳佳",
                LastName = "刘",
                Site = "liujiajia.me",
            };

            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");

            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>
        /// 深拷贝,通过反射实现
        /// </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;

            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 { }
            }
            return (T)retval;
        }
    }

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

上述代码输出如下:

txt
通过 DeepCopyByReflect 方法创建深拷贝耗费 87 ms
通过 DeepCopyByBin 方法创建深拷贝耗费 955 ms

可以发现通过 反射 的实现比通过 二进制序列化反序列化 的实现效率高 10 倍以上。

2020-05-12 追记

最近发现在有数组/列表属性时,上文的 DeepCopyByReflect 方法会报异常。

另外新增了一种通过 JSON 的实现。
详情见 => C# 深拷贝方法效率比较(2)