当前位置:网站首页>[C#] 彻底搞明白深拷贝
[C#] 彻底搞明白深拷贝
2022-04-23 17:02:00 【code bean】
每一种语言都有深拷贝这个概念,其实本质上都是一样的,这次从C#的角度解释。
C# 有值类型和引用类型。值类型,如int之类的不存在浅和深的问题,直接赋值就完了。
引用类型,比如我们自定义的类。通过new的方式创建,new返回的其实是个“指针”,
或者说是引用,他自己在栈上,但是new分配的空间在堆上。 如果直接对引用赋值的话,
相当于是改变“指针”的指向。之前被他管理的堆内存,就被悬空了,最后没人指向它,就会被垃圾回收机制给回收了。
比如,我自定义一个类。
public class Box
{
public double height; // 高度
public double width; // 宽度
}
然后new两个对象,并将b1直接赋值给b2
Box b1 = new Box();
Box b2 = new Box();
b2 = b1;
b2.width = 5; //改变b2 影响b1
Console.WriteLine(b1.width);
此时b2和b1指向了同一块内存,b2之前分配的内存被悬空了,最后被GC回收。
那如果不想这样,我想拷贝一个独立的b2(b2的改变不影响b1),那么唯一的办法就是逐个拷贝。
public static Box FixDeepCopy(double height, double width)
{
Box b = new Box();
b.height = height;
b.width = width;
return b;
}
这里我们注意到,我们没有改变b的指向,所以b指向的还是最开始自己new出的内存。由于逐一的赋值,所以此时算是深拷贝成功了。
但是这样过于麻烦,不同类,要写不同的深拷贝函数,而且随着类越复杂,方法越繁琐。
那么下面三种方法,帮你解决,仔细观察,其实这三种方法,其实也是将成员变量逐一拷贝的,
只是做了泛化处理。
不多说了,直接上代码了:
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
using System.Threading.Tasks;
namespace DeepCopyTest
{
internal class Program
{
[Serializable]
public class Box
{
public double height; // 高度
public double width; // 宽度
}
public static Box FixDeepCopy(double height, double width)
{
Box b = new Box();
b.height = height;
b.width = width;
return b;
}
public static T DeepCopyByBin<T>(T obj)
{
object retval;
using (MemoryStream ms = new MemoryStream())
{
BinaryFormatter bf = new BinaryFormatter();
//序列化成流
bf.Serialize(ms, obj);
//将当前流中的位置设置为指定值。也就是从SeekOrigin.Begin偏移0个位置。
ms.Seek(0, SeekOrigin.Begin);
//反序列化成对象
retval = bf.Deserialize(ms);
ms.Close();
}
return (T)retval;
}
/// <summary>
/// 注意需要给类加个[Serializable]
/// </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;
}
static void Main(string[] args)
{
Box b1 = new Box();
Box b2 = new Box();
Box b3 = new Box();
Box b4 = new Box();
Console.WriteLine(b1.width);
//直接赋值的方式
b2 = b1;
b2.width = 5; //改变b2 影响b1
Console.WriteLine(b1.width);
Console.WriteLine("--------------方式1----------------------");
//深拷贝
b2 = DeepCopyByBin(b1);
b2.width = 6; //改变b2 不影响b1
Console.WriteLine(b1.width);
Console.WriteLine("---------------方式2---------------------");
//深拷贝
b3 = DeepCopyByReflect(b1);
b3.width = 7; //改变b5 不影响b1
Console.WriteLine(b1.width);
Console.WriteLine("----------------方式3--------------------");
//深拷贝(需要安装包:Newtonsoft.Json)
b4 = JsonConvert.DeserializeObject<Box>(JsonConvert.SerializeObject(b1)); //深克隆
b4.width = 789; //改变b4 不影响b1
Console.WriteLine(b1.width);
Console.ReadKey();
}
}
}
小结:
1 感觉用 JsonConvert最方便,需要自己安装一个包Newtonsoft.Json(这个包太常用了)
JsonConvert.DeserializeObject<Box>(JsonConvert.SerializeObject(b1));
2 使用 BinaryFormatter的方式,注意需要给类加个可序列化的特性[Serializable],不然运行报错。
它比较直接,直接拷贝整个内存。
3 最后反射这种方式,要注意一下,因为可以看到,它用到了递归的方式,去检索每一个对象。
如果对象过于复杂,可能有点问题。
版权声明
本文为[code bean]所创,转载请带上原文链接,感谢
https://blog.csdn.net/songhuangong123/article/details/124365854
边栏推荐
- Multithreaded @ async thread pool
- 织梦DEDECMS安全设置指南
- Generation of barcode and QR code
- Generate random numbers with high quality and Gaussian distribution
- org. apache. parquet. schema. InvalidSchemaException: A group type can not be empty. Parquet does not su
- ACL 2022 | DialogVED:用于对话回复生成的预训练隐变量编码-解码模型
- Nodejs reads the local JSON file through require. Unexpected token / in JSON at position appears
- Handwritten event publish subscribe framework
- Deeply understand the relevant knowledge of 3D model (modeling, material mapping, UV, normal), and the difference between displacement mapping, bump mapping and normal mapping
- Lock lock
猜你喜欢
VLAN高级技术,VLAN聚合,超级Super VLAN ,Sub VLAN
Smart doc + Torna generate interface document
MySQL master-slave synchronization pit avoidance version tutorial
Shell脚本——Shell编程规范及变量
信息摘要、数字签名、数字证书、对称加密与非对称加密详解
Detailed explanation of the penetration of network security in the shooting range
Mock test
Project framework of robot framework
如何建立 TikTok用户信任并拉动粉丝增长
Knowledge points and examples of [seven input / output systems]
随机推荐
Detailed explanation of the penetration of network security in the shooting range
自定义my_strcpy与库strcpy【模拟实现字符串相关函数】
oracle 中快速获取表的列名列表
How vscode compares the similarities and differences between two files
VLAN advanced technology, VLAN aggregation, super VLAN, sub VLAN
SQL: How to parse Microsoft Transact-SQL Statements in C# and to match the column aliases of a view
VLAN高级技术,VLAN聚合,超级Super VLAN ,Sub VLAN
Modify the test case name generated by DDT
织梦DEDECMS安全设置指南
Mock test
Change the password after installing MySQL in Linux
杂文 谈谈古典的《拆掉思维里的墙》
Use case execution of robot framework
Camtasia2022软件新增功能介绍
Pycham connects to the remote server and realizes remote debugging
[problem solving] [show2012] random tree
groutine
Collect blog posts
Your brain expands and shrinks over time — these charts show how
Bytevcharts visual chart library, I have everything you want