当前位置:网站首页>[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
边栏推荐
- Variable length parameter__ VA_ ARGS__ Macro definitions for and logging
- _ Mold_ Board_
- Easyexcel reads the geographical location data in the excel table and sorts them according to Chinese pinyin
- Pytorch: the pit between train mode and eval mode
- SQL: How to parse Microsoft Transact-SQL Statements in C# and to match the column aliases of a view
- Expression "func" tSource, object "to expression" func "tSource, object" []
- MySQL master-slave replication
- Dancenn: overview of byte self-developed 100 billion scale file metadata storage system
- Idea of batch manufacturing test data, with source code
- Shell script -- shell programming specification and variables
猜你喜欢

RTKLIB 2.4.3源码笔记

Derivation of Σ GL perspective projection matrix

自定义my_strcpy与库strcpy【模拟实现字符串相关函数】

Lock锁

∑GL-透视投影矩阵的推导

Easyexcel reads the geographical location data in the excel table and sorts them according to Chinese pinyin

Nodejs reads the local JSON file through require. Unexpected token / in JSON at position appears

PyMySQL

Change the password after installing MySQL in Linux

Quick install mongodb
随机推荐
PyTorch:train模式与eval模式的那些坑
Mock test using postman
Selenium IDE and XPath installation of chrome plug-in
Pseudo Distributed installation spark
Paging SQL
【PIMF】OpenHarmony啃论文俱乐部—在ACM Survey闲逛是什么体验
网络安全之渗透靶场实战详解
ACL 2022 | DialogVED:用于对话回复生成的预训练隐变量编码-解码模型
Kingdee Cloud Star API calling practice
An essay on the classical "tear down the wall in thinking"
Generate random numbers with high quality and Gaussian distribution
Read a blog, re understand closures and tidy up
PHP efficiently reads large files and processes data
How does flash cache data in memory?
About stream flow, write it down briefly------
MySQL master-slave synchronization pit avoidance version tutorial
【题解】[SHOI2012] 随机树
[PROJECT] small hat takeout (8)
面试百分百问到的进程,你究竟了解多少
Error in v-on handler: "typeerror: cannot read property 'resetfields' of undefined"