克隆方法是原型设计模式中必须使用的方式,它将返回一个与当前对象数据一致的对象。正如其名,犹如一个模子雕刻而出。克隆类型分为两种:浅克隆、深克隆。
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对象转换为我们需要的对象类型
以上就是详解 c# 克隆的详细内容,更多关于c# 克隆的资料请关注小牛知识库其它相关文章!
本文向大家介绍JavaScript浅层克隆与深度克隆示例详解,包括了JavaScript浅层克隆与深度克隆示例详解的使用技巧和注意事项,需要的朋友参考一下 1 相关知识点 浅克隆就是将栈内存中的引用复制一份,赋给一个新的变量,本质上两个指向堆内存中的同一地址,内容也相同,其中一个变化另一个内容也会变化。 深克隆就是创建一个新的空对象,开辟一块内存,然后将原对象中的数据全部复制过去,完全切断两个对象
本文向大家介绍详解c# SpinWait,包括了详解c# SpinWait的使用技巧和注意事项,需要的朋友参考一下 SpinWait封装常见旋转逻辑。在单处理器计算机上,始终使用 "生成" 而不是 "繁忙等待",在装有超线程技术的 Intel 处理器的计算机上,这有助于防止硬件线程不足。SpinWait 封装了一种很好的旋转和真正的生成。 SpinWait是一个值类型,这意味着低级别代码
本文向大家介绍详解C# FileStream类,包括了详解C# FileStream类的使用技巧和注意事项,需要的朋友参考一下 C# FileStream类 在 C# 语言中文件读写流使用 FileStream 类来表示,FileStream 类主要用于文件的读写,不仅能读写普通的文本文件,还可以读取图像文件、声音文件等不同格式的文件。区别于File类的是它对文件可进行分步读写,减小内存压力,缺点
本文向大家介绍c++常量详解,包括了c++常量详解的使用技巧和注意事项,需要的朋友参考一下 概念 常量是存放固定且不可变值的,一旦确定初始值则在程序其它地方不可改变, 所以const对象必须初始化。常量一般使用const关键字来修饰。 const 对象可以大致分为三类: 1. const int a const int a =10; int const b =10; 这两种格式是完全相同的。也就
本文向大家介绍C++ Template应用详解,包括了C++ Template应用详解的使用技巧和注意事项,需要的朋友参考一下 引言 模板(Template)指C++程序设计设计语言中采用类型作为参数的程序设计,支持通用程序设计。C++ 的标准库提供许多有用的函数大多结合了模板的观念,如STL以及IO Stream。 函数模板 在c++入门中,很多人会接触swap(int&, int&)这样的函数
本文向大家介绍详解C# 反射(Reflection),包括了详解C# 反射(Reflection)的使用技巧和注意事项,需要的朋友参考一下 C# 反射(Reflection) 反射指程序可以访问、检测和修改它本身状态或行为的一种能力。 程序集包含模块,而模块包含类型,类型又包含成员。反射则提供了封装程序集、模块和类型的对象。 您可以使用反射动态地创建类型的实例,将类型绑定到现有对象,或从现有对象中