以下是关于.NET的深复制(Deep copy)的说明和实例:
深复制的概念
在.NET中,对象类型包括值类型和引用类型。值类型在进行复制时,会重新创建一个新的副本;而引用类型在进行复制时,复制的只是引用地址,指向同一个原始对象。这样可能会引起一些问题,例如,当我们修改复制后的对象时,会影响原始对象。为了避免这种问题,我们需要进行深复制,即复制整个对象及其引用类型的所有子对象。所以深复制后的对象和原始对象是完全独立的,对复制对象的任何修改都不会影响原始对象。
深复制的两种常见实现方式
1.使用序列化和反序列化
使用这种方法,我们需要将需要复制的对象序列化并反序列化得到一个新的对象。其中,序列化将对象转化成字节流,而反序列化则将这些字节流还原成对象。示例如下:
using System;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
// 学生类
[Serializable]
public class Student
{
public int Id { get; set; }
public string Name { get; set; }
public Address Address { get; set; }
}
// 地址类
[Serializable]
public class Address
{
public string City { get; set; }
public string Street { get; set; }
}
class Program
{
static void Main(string[] args)
{
var student1 = new Student
{
Id = 1,
Name = "小明",
Address = new Address
{
City = "北京",
Street = "xx 街道"
}
};
// 深复制
var student2 = SerializeCopy<Student>(student1);
Console.WriteLine($"student1: {student1.Name}, {student1.Address.City}, {student1.Address.Street}");
Console.WriteLine($"student2: {student2.Name}, {student2.Address.City}, {student2.Address.Street}");
// 修改student2
student2.Name = "小红";
student2.Address.City = "上海";
Console.WriteLine($"修改后的student2: {student2.Name}, {student2.Address.City}, {student2.Address.Street}");
Console.WriteLine($"原始的student1: {student1.Name}, {student1.Address.City}, {student1.Address.Street}");
Console.ReadKey();
}
// 使用序列化和反序列化进行深复制
public static T SerializeCopy<T>(T obj)
{
using (var memory = new MemoryStream())
{
var formatter = new BinaryFormatter(null, new StreamingContext(StreamingContextStates.Clone));
formatter.Serialize(memory, obj);
memory.Position = 0;
return (T)formatter.Deserialize(memory);
}
}
}
上述代码中,我们首先定义了一个Student
和Address
类,然后对student1
对象进行了初始化。接着,我们调用SerializeCopy
方法进行深复制,得到了一个完整独立的student2
对象。然后,我们输出student1
和student2
的属性值,可以看到它们的属性值完全相同,但这并不代表它们是同一个对象。接着,我们演示了修改student2
后会影响原始的student1
对象的情况。
2.使用反射
不建议使用这种方式,因为它的效率和性能不如使用序列化方式。但是,这种方式确实比较灵活,可以自定义实现。这种方法需要使用反射获取并创建对象的所有字段和属性,递归遍历复制整个对象。示例如下:
using System;
using System.Reflection;
// 学生类
public class Student
{
public int Id { get; set; }
public string Name { get; set; }
public Address Address { get; set; }
}
// 地址类
public class Address
{
public string City { get; set; }
public string Street { get; set; }
}
class Program
{
static void Main(string[] args)
{
var student1 = new Student
{
Id = 1,
Name = "小明",
Address = new Address
{
City = "北京",
Street = "xx 街道"
}
};
// 深复制
var student2 = ReflectionCopy<Student>(student1);
Console.WriteLine($"student1: {student1.Name}, {student1.Address.City}, {student1.Address.Street}");
Console.WriteLine($"student2: {student2.Name}, {student2.Address.City}, {student2.Address.Street}");
// 修改student2
student2.Name = "小红";
student2.Address.City = "上海";
Console.WriteLine($"修改后的student2: {student2.Name}, {student2.Address.City}, {student2.Address.Street}");
Console.WriteLine($"原始的student1: {student1.Name}, {student1.Address.City}, {student1.Address.Street}");
Console.ReadKey();
}
// 使用反射进行深复制
public static T ReflectionCopy<T>(T obj)
{
// 获取类型和构造函数
var type = obj.GetType();
var obj2 = Activator.CreateInstance(type);
// 获取属性和字段
var fields = type.GetFields();
var properties = type.GetProperties();
// 遍历属性和字段,递归设置值
foreach (var field in fields)
{
var fieldName = field.Name;
var fieldValue = field.GetValue(obj);
if (fieldValue == null || field.FieldType.IsValueType || field.FieldType == typeof(string))
{
field.SetValue(obj2, fieldValue);
}
else
{
field.SetValue(obj2, ReflectionCopy(fieldValue));
}
}
foreach (var property in properties)
{
var propertyName = property.Name;
var propertyValue = property.GetValue(obj);
if (propertyValue == null || property.PropertyType.IsValueType || property.PropertyType == typeof(string))
{
property.SetValue(obj2, propertyValue);
}
else
{
property.SetValue(obj2, ReflectionCopy(propertyValue));
}
}
return (T)obj2;
}
}
上述代码中,我们使用反射获取并复制了整个对象的所有属性和字段。注意,我们使用了递归来处理对象的属性和字段。可以看到,这种方法比使用序列化方式要复杂和低效。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:.NET的深复制方法(以C#语言为例) - Python技术站