详解 c# 克隆
作者:一只独行的猿 发布时间:2021-12-16 16:23:12
克隆方法是原型设计模式中必须使用的方式,它将返回一个与当前对象数据一致的对象。正如其名,犹如一个模子雕刻而出。克隆类型分为两种:浅克隆、深克隆。
1、浅克隆
浅克隆方式是最简单、最直接的方式。只需要类实现接口ICloneable(在命名空间System.Runtime.InteropServices下)的Clone方法,在方法中使用加入对当前类的MemberwiseClone()方法即可。在浅克隆中,如果原型对象的成员变量是值类型,将复制一份给克隆对象;如果原型对象的成员变量是引用类型,则将引用对象的地址复制一份给克隆对象。
如:
public class Student:ICloneable
{
/// <summary>
/// 值类型
/// </summary>
public int ID { get; set; } /// <summary>
/// 引用类型
/// </summary>
public object obj { get; set; }
public object Clone()
{
return this.MemberwiseClone();
}
}
以上方法实现了对类对象的浅克隆方式。但是在该类中具有引用类型字段,浅克隆方法无法对引用字段进行克隆,引用字段仅仅是对其进行了地址引用。所以,当修改原本或者副本的引用字段的数据时,另一个对象的引用对象的数据同样会变化。深克隆将有效的解决此问题。
2、深克隆
深克隆相对于浅克隆方式比较复杂。深克隆是无论原型对象的成员变量是值类型还是引用类型,都将复制一份给克隆对象,深克隆将原型对象的所有引用对象也复制一份给克隆对象。
深克隆实现的机制是将对象进行序列化为数据后,再次将数据反序列化为新的对象。序列化就是将对象写到流的过程,写到流中的对象是原有对象的一个拷贝,而原对象仍然存在于内存中。通过序列化实现的拷贝不仅可以复制对象本身,而且可以复制其引用的成员对象,因此通过序列化将对象写到一个流中,再从流里将其读出来,可以实现深克隆。注意,在实现序列化前需要在类的上方标记为可序列化。本文采用的序列化方式为二进制序列化。
主要实现的代码如下:
[Serializable]//标记特性:可序列化
public class Student
{
/// <summary>
/// 值类型
/// </summary>
public int ID { get; set; }
/// <summary>
/// 引用类型
/// </summary>
public object obj { get; set; }
public Student Clone( )
{
Student clone = new Student();
using (Stream stream = new MemoryStream())
{
IFormatter formatter = new BinaryFormatter();
try
{
formatter.Serialize(stream, this);
stream.Seek(0, SeekOrigin.Begin);
clone = formatter.Deserialize(stream) as Student;
}
catch (SerializationException e)
{
Console.WriteLine("Failed to serialize. Reason: " + e.Message);
throw;
}
}
return clone;
}
}
深克隆实现机制相对复杂、效率稍慢,但它克服了浅克隆方式的不足,使得克隆对象时将类中的引用类型数据完全克隆为新的对象,而不是引用原本中的对象。如此,在修改双方的引用类型对象的数据时不会对另一方造成干扰。
但为每一个类都实现克隆方式,而重复书写相同代码未免麻烦。因此引入泛型方法。
3、泛型方法实现克隆
泛型的出现使得可以良好的解决在多个类或结构体中都需要进行克隆时重复编写代码的麻烦。在外部只需要使用相关方法即可。其代码如下:
public class Clone
{
/// <summary>
/// 深克隆
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="t"></param>
/// <returns></returns>
public static T DepthClone<T>(T t)
{
T clone = default(T);
using (Stream stream = new MemoryStream())
{
IFormatter formatter = new BinaryFormatter();
try
{
formatter.Serialize(stream, t);
stream.Seek(0, SeekOrigin.Begin);
clone = (T)formatter.Deserialize(stream);
}
catch (SerializationException e)
{
Console.WriteLine("Failed to serialize. Reason: " + e.Message);
throw;
}
}
return clone;
}
}
在外部使用的方法如下:
Student stu1 = new Student();//实例化一个对象
stu1.obj = new object();//实例化对象中的引用对象
Student stu2 = Clone.DepthClone(stu1);//深克隆对象
4、扩展方法
扩展方法的出现可以很好的解决类自身直接调用克隆方法,而不需要调用静态类的方法,返回对象值。但其本身与泛型方法类似,不过为了使所有类都能使用定义的深克隆方法,此处使用对顶级类Object进行方法的扩展,其返回的值也是object类型。具体方法如下:
/// <summary>
/// 注:扩展方法必须在静态类中
/// </summary>
public static class Clone
{
/// <summary>
/// 深克隆
/// </summary>
/// <param name="obj">原始版本对象</param>
/// <returns>深克隆后的对象</returns>
public static object DepthClone(this object obj)
{
object clone = new object();
using (Stream stream = new MemoryStream())
{
IFormatter formatter = new BinaryFormatter();
try
{
formatter.Serialize(stream, obj);
stream.Seek(0, SeekOrigin.Begin);
clone = formatter.Deserialize(stream);
}
catch (SerializationException e)
{
Console.WriteLine("Failed to serialize. Reason: " + e.Message);
throw;
}
}
return clone;
}
}
使用方法示例:
Student stu1 = new Student();//实例化一个对象
stu1.obj = new object();//实例化对象中的引用对象
Student stu2 = stu1.DepthClone() as Student;//深克隆对象;注意:在此需要将object对象转换为我们需要的对象类型
来源:https://www.cnblogs.com/pilgrim/p/9003447.html


猜你喜欢
- 紧接上文所述,在这篇文章中我将对Mapper映射文件进行详细的说明。Mapper映射文件是一个xml格式文件,必须遵循相应的dtd文件规范,
- 本文实例为大家分享了C语言实现两个矩阵相乘的具体代码,供大家参考,具体内容如下程序功能:实现两个矩阵相乘的C语言程序,并将其输出代码如下:#
- 这篇文章主要介绍了SpringBoot加载外部依赖过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要
- 目录微服务微服务架构SpringCould简介环境搭建1. 创建数据库2. 新建一个子模块api3. 新建一个子模块provider作为服务
- 我们知道,在 Python 里面,如果你要运行一段 JavaScript,你可以使用execJS这种第三方库。那么在 Golang 里面,你
- Eureka注册中心/服务发现框架Eureka是Netflix开发的服务发现框架,本身是一个基于REST的服务,主要用于定位运行在AWS域中
- 下面是android SDK中API中的主要java包的功能简介: android.app :提供高层的程序模型、提供基本的运行环
- 在Value中的Style.xml中,添加: <style name="NoTitle"
- 前言开发传统Java WEB工程时,我们可以使用JSP页面模板语言,但是在SpringBoot中已经不推荐使用了。SpringBoot支持如
- 使用正则抓捕网上邮箱这就是我们需要抓捕的网站。实现思路:1、使用java.net.URL对象,绑定网络上某一个网页的地址2、通过java.n
- 利用源码编译Android系统Java类库1、编写Java项目和Android.mk文件 ├── Android.mk &nbs
- 需要添加对 System.Management.dll 的引用 using System.Diagnostics; using System
- 本文实例讲述了Android中WebView图片实现自适应的方法。分享给大家供大家参考。具体实现方法如下:WebSettings ws =
- google benchmark已经为我们提供了类似的功能,而且使用相当简单。具体的解释在后面,我们先来看几个例子,我们人为制造几个时间复杂
- 一般振动时间的配置在如下文件:frameworks/base/core/res/res/values/config.xml &nb
- PS: 之前写过一篇关于 SpringBoo 中使用配置文件的一些姿势,不过嘛,有句话(我)说的好:曾见小桥流水,未睹观音坐莲!所以再写一篇
- 1.任何一门编程语言均有相关数据类型。C#也不例外,其基本数据类型有int,short,long,float,double,string等。
- Java的位操作符用来操作整数基本数据类型中的单个“比特”(bit),即代进制位。而我们知道比特就是0和1,那么,位操作就是对这些数据进行基
- 结构体有时候我们仅需要一个小的数据结构,类提供的功能多于我们需要的功能;考虑到性能原因,最好使用结构体。结构体是值类型,存储在栈中或存储为内
- 1.springboot 2.0 默认连接池就是Hikari了,所以引用parents后不用专门加依赖2.贴我自己的配置(时间单位都是毫秒)