最近拜读了大话设计模式:原型模式,该模式主要应用C# 深浅复制来实现的!关于深浅复制大家可参考MSDN:https://docs.microsoft.com/zh-cn/dotnet/api/system.object.memberwiseclone

所谓深浅复制可解读为:

  • 浅复制:在C#中调用Object类的 MemberwiseClone() 方法即为浅复制。如果字段是值类型的,则对字段执行逐位复制,如果字段是引用类型的,则复制对象的引用,而不复制对象,因此:原始对象和其副本引用同一个对象!
  • 深复制:如果字段是值类型的,则对字段执行逐位复制,如果字段是引用类型的,则把引用类型的对象指向一个全新的对象!

上述的解释可能看不太懂,我们作如下案例进行分析:

class Program
{
public static void Main()
{
//创建P1对象
Person p1 = new Person();
p1.Age = ;
p1.Name = "Sam";
p1.IdInfo = new IdInfo(""); //通过浅复制 得到P2对象
Person p2 = p1.ShallowCopy();
//分别输出
Console.WriteLine("对象P1相关属性如下");
DisplayValues(p1);
//p1.Name = "";
//p1.IdInfo.IdNumber = "XXXXX";
Console.WriteLine("对象P2相关属性如下");
DisplayValues(p2); //现在测试深复制
Person p3 = p1.DeepCopy(); p1.Name = "George";
p1.Age = ;
p1.IdInfo.IdNumber = "";
Console.WriteLine("对象P1相关属性如下");
DisplayValues(p1);
//p1.IdInfo.IdNumber = "CCCCCCC";
Console.WriteLine("对象P3相关属性如下");
DisplayValues(p3);
Console.Read();
} public static void DisplayValues(Person p)
{
Console.WriteLine(" Name: {0:s}, Age: {1:d}", p.Name, p.Age);
Console.WriteLine(" Value: {0:d}", p.IdInfo.IdNumber);
}
} public class IdInfo
{
public string IdNumber; public IdInfo(string IdNumber)
{
this.IdNumber = IdNumber;
}
} public class Person
{
public int Age;
public string Name;
public IdInfo IdInfo; public Person ShallowCopy()
{
return (Person)this.MemberwiseClone();
} public Person DeepCopy()
{
Person other = (Person)this.MemberwiseClone();
other.IdInfo = new IdInfo(IdInfo.IdNumber);
other.Name = String.Copy(Name);
return other;
}
}

上述代码分析如下:
原始对象P1,通过浅复制得到对象P2,通过深复制得到P3
原始对象P1中的值类型属性有:Age 和 Name ,引用类型对象有:IdInfo
根据上述浅复制的概念可知:P2中的Age 和 Name 相对于 P1是全新的,但P2中的 IdInfo 和 P1中的 IdInfo 是同一个对象,二者同在一个内存地址!
根据上述深复制的概念可知:P3中的Age 和 Name 相对于 P1是全新的,但P3中的 IdInfo 和 P1中的 IdInfo 不是同一个对象,也就是说 P3中的IdInfo是一个全新的对象,开辟了自己的内存地址!
上述代码测试如下:

我们现在讲代码修改如下:

public static void Main()
{
//创建P1对象
Person p1 = new Person();
p1.Age = ;
p1.Name = "Sam";
p1.IdInfo = new IdInfo(""); //通过浅复制 得到P2对象
Person p2 = p1.ShallowCopy();
//分别输出
Console.WriteLine("对象P1相关属性如下");
DisplayValues(p1);
p1.Name = "浅复制中,修改了P1的Name属性,但Name是值类型,所以不会影响P2";
p1.IdInfo.IdNumber = "浅复制中,修改了P1的IdInfo属性,但IdInfo是引用类型,所以会影响P2 (浅复制中引用类型原始对象和副本指向同一内存地址)";
Console.WriteLine("对象P2相关属性如下");
DisplayValues(p2); Console.Read();
}

在输出P2之前,我们修改了P1对象的值类型Name 和 引用类型 IdInfo 。
无论是浅复制还是深复制,副本中的值类型都是全新的!
浅复制中原始对象和副本的引用类型指向同一内存地址,所以,修改了P1的IdInfo会同时影响P2的IdInfo
输出如下:

继续修改代码,如下:

public static void Main()
{
//创建P1对象
Person p1 = new Person();
p1.Age = ;
p1.Name = "Sam";
p1.IdInfo = new IdInfo(""); //现在测试深复制
Person p3 = p1.DeepCopy(); p1.Name = "George";
p1.Age = ;
p1.IdInfo.IdNumber = "";
Console.WriteLine("对象P1相关属性如下");
DisplayValues(p1);
p1.IdInfo.IdNumber = "深复制中,修改了P1的IdInfo属性,即使IdInfo是引用类型,也不会影响P3 (深复制中引用类型原始对象和副本分别指向不同的内存地址)";
Console.WriteLine("对象P3相关属性如下");
DisplayValues(p3);
Console.Read();
}

深复制中原始对象和副本的引用类型指向各自的地址,两者完全是两个不同的对象!
因此:修改P1不会影响P3
so,是不是很简单,是不是很Easy.
深浅复制主要用于当创建一个对象需要消耗过多资源时,可以采取复制的方法提升效率!
大话设计模式的原话是这样滴:当你New一个对象时,每New一次,都需要执行一个构造函数,如果构造函数的执行时间很长,那么多次New对象时会大大拉低程序执行效率,因此:一般在初始化信息不发生变化的前提下克隆是最好的办法,这既隐藏了对象的创建细节,又大大提升了性能
当然,如果每个类都要写自己的深复制,这岂不是非常非常麻烦,因此,有一个通用的深复制方法,如下:

/// <summary>
/// 通用的深复制方法
/// </summary>
/// <typeparam name="T"></typeparam>
[Serializable]
public class BaseClone<T>
{
public virtual T Clone()
{
using (MemoryStream memoryStream = new MemoryStream())
{
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(memoryStream, this);
memoryStream.Position = ;
return (T)formatter.Deserialize(memoryStream);
}
}
}

相关案例如下(通用的深复制方法使用时必须为相关类及类的引用类型加上可序列化标识:[Serializable]):

class Program
{
public static void Main()
{
//创建P1对象
Person p1 = new Person();
p1.Age = ;
p1.Name = "Sam";
p1.IdInfo = new IdInfo(""); //现在测试深复制
Person p3 = p1.Clone(); p1.Name = "George";
p1.Age = ;
p1.IdInfo.IdNumber = "";
Console.WriteLine("对象P1相关属性如下");
DisplayValues(p1);
p1.IdInfo.IdNumber = "深复制中,修改了P1的IdInfo属性,即使IdInfo是引用类型,也不会影响P3 (深复制中引用类型原始对象和副本分别指向不同的内存地址)";
Console.WriteLine("对象P3相关属性如下");
DisplayValues(p3);
Console.Read();
} public static void DisplayValues(Person p)
{
Console.WriteLine(" Name: {0:s}, Age: {1:d}", p.Name, p.Age);
Console.WriteLine(" Value: {0:d}", p.IdInfo.IdNumber);
} } [Serializable]
public class IdInfo
{
public string IdNumber; public IdInfo(string IdNumber)
{
this.IdNumber = IdNumber;
}
} [Serializable]
public class Person : BaseClone<Person>
{
public int Age;
public string Name; public IdInfo IdInfo; public Person ShallowCopy()
{
return (Person)this.MemberwiseClone();
} public Person DeepCopy()
{
Person other = (Person)this.MemberwiseClone();
other.IdInfo = new IdInfo(IdInfo.IdNumber);
other.Name = String.Copy(Name);
return other;
} public override Person Clone()
{
using (MemoryStream memoryStream = new MemoryStream())
{
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(memoryStream, this);
memoryStream.Position = ;
return (Person)formatter.Deserialize(memoryStream);
}
}
} /// <summary>
/// 通用的深复制方法
/// </summary>
/// <typeparam name="T"></typeparam>
[Serializable]
public class BaseClone<T>
{
public virtual T Clone()
{
using (MemoryStream memoryStream = new MemoryStream())
{
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(memoryStream, this);
memoryStream.Position = ;
return (T)formatter.Deserialize(memoryStream);
}
}
}

原文链接

最新文章

  1. unzip 命令使用
  2. BFC 神奇背后的原理
  3. discuz论坛与其它网站登录注册整合
  4. Unity3D Shader入门指南(一)
  5. JavaWeb学习----JSP内置对象详解
  6. 《软件工程》individual project开发小记(一)
  7. HDU 4000 Fruit Ninja 树状数组 + 计数
  8. [转]JAVA中Action层, Service层 ,modle层 和 Dao层的功能区分
  9. Gradle sync failed: failed to find Build Tools revision 21.1.2
  10. openstack configure
  11. 通过管道进行线程间通信:字节流。字符流的用法及API类似
  12. List之根据某个字段在add的时候过滤掉重复的数据
  13. iOS LaunchScreen和LaunchImage的转换启动图
  14. C# 委托还能这样用
  15. 【原创】Open JDK更换过程及更换后的问题总结与分析
  16. 正则re模块
  17. 用mysql-connector操作MySQL数据库
  18. mfc标题栏 菜单 退出 关机 重启
  19. 算法笔记_204:第四届蓝桥杯软件类决赛真题(Java语言C组)
  20. Aspose------导出Excel

热门文章

  1. win7-VS2010-IIS网站的发布问题
  2. spring实战四之Bean的自动装配(注解方式)
  3. 整理了一下 jQuery 的原型关系图,理解起来更加方便一些。
  4. 255. Verify Preorder Sequence in Binary Search Tree
  5. android125 zhihuibeijing 缓存
  6. Window Ghosting(仍可拖动失去响应的窗口,因为我们真正的窗口已经让系统用Ghosting窗口替代了。使用IsHungAppWindow 探测)
  7. log4net使用具体解释
  8. 第四届蓝桥杯 c/c++真题
  9. EntityFramework Core迁移时出现数据库已存在对象问题解决方案
  10. mysql @value := 用法
  11. webpack2进阶之多文件,DLL,以及webpack-merge
  12. mysql explain rows理解
  13. Mybatis 事务管理
  14. interface{} 泛型编程
  15. iOS开发工具
  16. Redhat系统部署安装Splunk
  17. Zookeeper session超时
  18. nginx location正则写法
  19. 【LeetCode】Pascal&#39;s Triangle II (杨辉三角)
  20. CSS——div居中,window.open(0