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 包使用的人也很多,健壮性比较高。性能要求不是很苛刻的地方,推荐使用这种方法。