C#深拷贝方法探究及性能比较(多种深拷贝)
什么是深拷贝
在 C# 的开发中,经常需要复制一份对象,以便于对该对象进行修改而不影响原来的对象。浅拷贝(shallow copy)只是简单地复制了一份对象的引用,而不是实际的对象,原始对象和副本对象共享引用类型的成员变量。而深拷贝(deep copy)则会创建一份新的对象,并复制原始对象所有的成员变量,包括引用类型成员变量所引用的对象。
深拷贝的实现方式
1.手动实现
手动实现深拷贝的方法是通过递归复制所有被引用的对象及其属性。我们可以按照如下格式实现一个对象的深拷贝:
public class MyClass
{
public int IntProperty { get; set; }
public MySecondClass MySecondClassProperty { get; set; }
public MyClass DeepCopy()
{
MyClass copy = new MyClass();
copy.IntProperty = this.IntProperty;
copy.MySecondClassProperty = new MySecondClass();
copy.MySecondClassProperty.Property1 = this.MySecondClassProperty.Property1;
copy.MySecondClassProperty.Property2 = this.MySecondClassProperty.Property2;
copy.MySecondClassProperty.Property3 = this.MySecondClassProperty.Property3;
return copy;
}
}
public class MySecondClass
{
public int Property1 { get; set; }
public string Property2 { get; set; }
public bool Property3 { get; set; }
}
虽然手动实现深拷贝比较繁琐,但是可以确保100%的拷贝结果正确,且运行速度比其他方法快一些。
2.使用序列化实现深拷贝
使用序列化实现深拷贝的方法是对被拷贝对象进行序列化和反序列化,实现对象的克隆。以下是使用BinaryFormatter实现对象的深拷贝的示例代码:
public static T DeepCopy<T>(T obj)
{
using var stream = new MemoryStream();
var formatter = new BinaryFormatter
{
Context = new StreamingContext(StreamingContextStates.Clone)
};
formatter.Serialize(stream, obj);
stream.Seek(0, SeekOrigin.Begin);
return (T)formatter.Deserialize(stream);
}
但是,使用序列化实现深拷贝的方法会对性能有一定的影响,并且不是所有对象都可以序列化。
3.使用反射实现深拷贝
使用反射实现深拷贝的方法是通过反射机制获取对象的类型信息,然后对其进行复制的操作。以下是使用反射实现对象的深拷贝的示例代码:
public static T DeepCopy<T>(T obj)
{
if (obj == null)
{
return default(T);
}
var type = obj.GetType();
if (!type.IsValueType && type != typeof(string))
{
object copy = Activator.CreateInstance(obj.GetType());
foreach (FieldInfo field in type.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public))
{
var fieldValue = field.GetValue(obj);
if (fieldValue != null)
{
field.SetValue(copy, DeepCopy(fieldValue));
}
}
foreach (PropertyInfo property in type.GetProperties())
{
if (property.CanRead && property.CanWrite && property.GetIndexParameters().Length == 0)
{
try
{
var propertyValue = property.GetValue(obj, null);
if (propertyValue != null)
{
property.SetValue(copy, DeepCopy(propertyValue), null);
}
}
catch
{
// Some indexers or properties may throw exception here, but I don’t care.
}
}
}
obj = (T)copy;
}
return obj;
}
深拷贝的性能比较
我们可以使用以下代码对三种深拷贝方法的性能进行比较:
var sourceObject = CreateLargeObjectGraph();
var stopwatch = Stopwatch.StartNew();
var copyByReflection = DeepCopyByReflection(sourceObject);
var reflectionTime = stopwatch.Elapsed;
stopwatch.Restart();
var copyBySerialization = DeepCopyBySerialization(sourceObject);
var serializationTime = stopwatch.Elapsed;
stopwatch.Restart();
var copyByManual = sourceObject.DeepCopy();
var manualTime = stopwatch.Elapsed;
其中,CreateLargeObjectGraph()方法用于创建一个包含1K个MyClass对象的对象图。
实验结果表明,手动实现深拷贝方法的速度最快,而基于序列化实现深拷贝的方法速度最慢。
示例说明
下面我们以一个Student类为例,演示如何使用各种深拷贝方法进行对象的拷贝。
public class Student
{
public string Name { get; set; }
public int Age { get; set; }
public List<string> Courses { get; set; }
public Student DeepCopy()
{
var copy = new Student();
copy.Name = this.Name;
copy.Age = this.Age;
copy.Courses = new List<string>();
foreach(var course in this.Courses)
{
copy.Courses.Add(course);
}
return copy;
}
}
使用手动实现深拷贝方法:
var student1 = new Student() { Name = "Tom", Age = 18, Courses = new List<string>() { "Math", "English" } };
var student2 = student1.DeepCopy();
student1.Name = "Jerry";
student1.Age = 19;
student1.Courses.Add("History");
Console.WriteLine(student1.Name + " " + student1.Age + " " + string.Join(",", student1.Courses));
Console.WriteLine(student2.Name + " " + student2.Age + " " + string.Join(",", student2.Courses));
运行以上代码,输出:
Jerry 19 Math,English,History
Tom 18 Math,English
使用BinaryFormatter实现深拷贝方法:
var student1 = new Student() { Name = "Tom", Age = 18, Courses = new List<string>() { "Math", "English" } };
var student2 = DeepCopyBySerialization(student1);
student1.Name = "Jerry";
student1.Age = 19;
student1.Courses.Add("History");
Console.WriteLine(student1.Name + " " + student1.Age + " " + string.Join(",", student1.Courses));
Console.WriteLine(student2.Name + " " + student2.Age + " " + string.Join(",", student2.Courses));
使用反射实现深拷贝方法:
var student1 = new Student() { Name = "Tom", Age = 18, Courses = new List<string>() { "Math", "English" } };
var student2 = DeepCopyByReflection(student1);
student1.Name = "Jerry";
student1.Age = 19;
student1.Courses.Add("History");
Console.WriteLine(student1.Name + " " + student1.Age + " " + string.Join(",", student1.Courses));
Console.WriteLine(student2.Name + " " + student2.Age + " " + string.Join(",", student2.Courses));
以上这些示例可以帮助我们理解三种深拷贝方法的实现过程,并在实际开发中提供参考。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:C#深拷贝方法探究及性能比较(多种深拷贝) - Python技术站