克隆方法是原型设计模式中必须使用的方式,它将返回一个与当前对象数据一致的对象。正如其名,犹如一个模子雕刻而出。克隆类型分为两种:浅克隆、深克隆。
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 : ICloneable { /// <summary> /// 值类型 /// </summary> public int ID { get; set; } /// <summary> /// 引用类型 /// </summary> public object obj { get; set; }
public Student Clone() { Student clone = new Student(); //序列化写入文件的位置 using (FileStream fs = new FileStream("temp.dat", FileMode.OpenOrCreate)) { BinaryFormatter formatter = new BinaryFormatter(); try { formatter.Serialize(fs, this);//序列化 } catch (SerializationException e) { Console.WriteLine("Failed to serialize. Reason: " + e.Message); throw; } finally { fs.Close(); } } //反序列化读取数据的位置(注意:此处的数据引用应该与序列化的文件名称一致) using (FileStream fs1 = new FileStream("temp.dat", FileMode.Open)) { BinaryFormatter formatter1 = new BinaryFormatter(); try { clone = formatter1.Deserialize(fs1) as Student;//反序列化数据,并解析为对应的类型 } catch (SerializationException e) { Console.WriteLine("Failed to deserialize. Reason: " + e.Message); throw; } finally { fs1.Close(); } } 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 (FileStream fs = new FileStream("temp.dat", FileMode.OpenOrCreate)) { BinaryFormatter formatter = new BinaryFormatter(); try { formatter.Serialize(fs, t);//序列化 } catch (SerializationException e) { Console.WriteLine("Failed to serialize. Reason: " + e.Message); throw; } finally { fs.Close(); } } //反序列化读取数据的位置(注意:此处的数据引用应该与序列化的文件名称一致) using (FileStream fs1 = new FileStream("temp.dat", FileMode.Open)) { BinaryFormatter formatter1 = new BinaryFormatter(); try { clone = (T)formatter1.Deserialize(fs1);//反序列化数据,并解析为对应的类型 } catch (SerializationException e) { Console.WriteLine("Failed to deserialize. Reason: " + e.Message); throw; } finally { fs1.Close(); } } return clone; } }
在外部使用的方法如下:
Student stu1 = new Student();//实例化一个对象 stu1.obj = new object();//实例化对象中的引用对象 Student stu2 = Clone.DepthClone(stu1);//深克隆对象