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; }
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
执行结果如下:
通过 DeepCopyByReflect 方法创建深拷贝耗费 755 ms
通过 DeepCopyByBin 方法创建深拷贝耗费 3645 ms
通过 DeepCopyByJSON 方法创建深拷贝耗费 1195 ms
可以看到通过反射的实现(DeepCopyByReflect)耗时增加了好多,但仍然是效率最高的。
通过 JSON 序列化/反序列化的实现效率跟反射相比差距不大,实现起来比较简单,而且 Newtonsoft.Json 包使用的人也很多,健壮性比较高。性能要求不是很苛刻的地方,推荐使用这种方法。