在" .NET的堆和栈01,基本概念、值类型内存分配"中,了解了"堆"和"栈"的基本概念,以及值类型的内存分配。我们知道:当执行一个方法的时候,值类型实例会在"栈"上分配内存,而引用类型实例会在"堆"上分配内存,当方法执行完毕,"栈"上的实例由操作系统自动释放,"堆"上的实例由.NET Framework的GC进行回收。

在" .NET的堆和栈02,值类型和引用类型参数传递以及内存分配"中,我们了解了值类型参数和引用类型参数在传递时的内存分配情况。

而本篇的重点要放在:引用类型对象拷贝以及内存分配。

主要包括:
■  引用类型对象拷贝 成员都是值类型
■  引用类型对象拷贝 包含引用类型成员

引用类型对象拷贝 成员都是值类型

public struct Shoe{
public string Color;
} public class Dude
{
public string Name;
public Shoe RightShoe;
public Shoe LeftShoe; public Dude CopyDude()
{
Dude newPerson = new Dude();
newPerson.Name = Name;
newPerson.LeftShoe = LeftShoe;
newPerson.RightShoe = RightShoe; return newPerson;
} public override string ToString()
{
return (Name + " : Dude!, I have a " + RightShoe.Color +
" shoe on my right foot, and a " +
LeftShoe.Color + " on my left foot.");
} } public static void Main()
{
Dude Bill = new Dude();
Bill.Name = "Bill";
Bill.LeftShoe = new Shoe();
Bill.RightShoe = new Shoe();
Bill.LeftShoe.Color = Bill.RightShoe.Color = "Blue"; Dude Ted = Bill.CopyDude();
Ted.Name = "Ted";
Ted.LeftShoe.Color = Ted.RightShoe.Color = "Red"; Console.WriteLine(Bill.ToString());
Console.WriteLine(Ted.ToString());
}

输出结果:
Bill : Dude!, I have a Red shoe on my right foot, and a Red on my left foot
Ted : Dude!, I have a Red shoe on my right foot, and a Red on my left foot

以上,当引用类型的属性、成员都是值类型的时候,拷贝是完全拷贝。

引用类型对象拷贝 包含引用类型成员

把Shoe由struct值类型改成引用类型class。

public class Shoe{
public string Color;
}

再次运行,输出结果:
Bill : Dude!, I have a Red shoe on my right foot, and a Red on my left foot
Ted : Dude!, I have a Red shoe on my right foot, and a Red on my left foot

当Dude类包含引用类型属性Shoe的时候,在托管堆上的情况是这样的:

拷贝后,2个Dude的Shoe类型的属性指向了同一个托管堆内的Shoe实例,改变Shoe的值会同时影响到2个Dude。

很显然,这不是我们期望的完全拷贝,如何做到完全拷贝呢?
--实现ICloneable接口

ICloneable接口的Clone()方法,允许我们在拷贝的时候,进行一些自定义设置。

让引用类Shoe实现ICloneable接口。

public class Shoe : ICloneable
{
public string Color; public object Clone()
{
Shoe newShoe = new Shoe();
newShoe.Color = Color.Clone() as string;
return newShoe;
}
}

以上,Shoe的string类型属性Color之所以可以使用Color.Clone()方法,是因为string也实现了ICloneable接口;又由于Clone()返回类型是object,所以,在使用Color.Clone()方法之后,需要把object转换成string类型。

现在,在Dude类的CopyDude()方法中,当拷贝Shoe类型属性的时候,就可以使用Shoe独有的拷贝方法Clone()。

public Dude CopyDude()
{
Dude newPerson = new Dude();
newPerson.Name = Name;
newPerson.LeftShoe = LeftShoe.Clone() as Shoe;
newPerson.RightShoe = RightShoe.Clone() as Shoe; return newPerson;
}

客户端程序。

public static void Main()
{
Dude Bill = new Dude();
Bill.Name = "Bill";
Bill.LeftShoe = new Shoe();
Bill.RightShoe = new Shoe();
Bill.LeftShoe.Color = Bill.RightShoe.Color = "Blue"; Dude Ted = Bill.CopyDude();
Ted.Name = "Ted";
Ted.LeftShoe.Color = Ted.RightShoe.Color = "Red"; Console.WriteLine(Bill.ToString());
Console.WriteLine(Ted.ToString()); }

输出结果:  
Bill : Dude!, I have a Blue shoe on my right foot, and a Blue on my left foot
Ted : Dude!, I have a Red shoe on my right foot, and a Red on my left foot

这正是我们期望的完全拷贝!

完全拷贝,托管堆上的情况是这样的:

当然也可以让同时包含值类型和引用类型成员,同时需要拷贝的类实现ICloneable接口。

public class Dude: ICloneable
{
public string Name;
public Shoe RightShoe;
public Shoe LeftShoe; public override string ToString()
{
return (Name + " : Dude!, I have a " + RightShoe.Color +
" shoe on my right foot, and a " +
LeftShoe.Color + " on my left foot.");
}
#region ICloneable Members public object Clone()
{
Dude newPerson = new Dude();
newPerson.Name = Name.Clone() as string;
newPerson.LeftShoe = LeftShoe.Clone() as Shoe;
newPerson.RightShoe = RightShoe.Clone() as Shoe; return newPerson;
} #endregion
}

客户端调用。

public static void Main()
{
Class1 pgm = new Class1(); Dude Bill = new Dude();
Bill.Name = "Bill";
Bill.LeftShoe = new Shoe();
Bill.RightShoe = new Shoe();
Bill.LeftShoe.Color = Bill.RightShoe.Color = "Blue"; Dude Ted = Bill.Clone() as Dude;
Ted.Name = "Ted";
Ted.LeftShoe.Color = Ted.RightShoe.Color = "Red"; Console.WriteLine(Bill.ToString());
Console.WriteLine(Ted.ToString()); }

输出结果: 
Bill : Dude!, I have a Blue shoe on my right foot, and a Blue on my left foot.
Ted : Dude!, I have a Red shoe on my right foot, and a Red on my left foot.

也是我们期望的完全拷贝!

参考资料:
C# Heap(ing) Vs Stack(ing) in .NET: Part III

".NET的堆和栈"系列包括:

.NET的堆和栈01,基本概念、值类型内存分配

.NET的堆和栈02,值类型和引用类型参数传递以及内存分配

.NET的堆和栈03,引用类型对象拷贝以及内存分配

.NET的堆和栈04,对托管和非托管资源的垃圾回收以及内存分配

.NET的堆和栈03,引用类型对象拷贝以及内存分配的更多相关文章

  1. .NET 基础 一步步 一幕幕[面向对象之堆、栈、引用类型、值类型]

    堆.栈.引用类型.值类型 内存分为堆和栈(PS:还有一种是静态存储区域 [内存分为这三种]),值类型的数据存储在栈中,引用类型的数据存储在堆中. 堆.栈: 堆和栈的区别: 栈是编译期间就分配好的内存空 ...

  2. JVM内存结构之堆、栈、方法区以及直接内存、堆和栈区别

    JVM内存结构之堆.栈.方法区以及直接内存.堆和栈区别 一.  理解JVM中堆与栈以及方法区 堆(heap):FIFO(队列优先,先进先出):二级缓存:*JVM中只有一个堆区被所有线程所共享:对象和数 ...

  3. JVM的艺术-对象创建与内存分配机制深度剖析

    JVM的艺术-对象创建与内存分配机制深度剖析 引言 本章将介绍jvm的对象创建与内存分配.彻底带你了解jvm的创建过程以及内存分配的原理和区域,以及包含的内容. 对象的创建 类加载的过程 固定的类加载 ...

  4. 阿里面试官:小伙子,你给我说一下JVM对象创建与内存分配机制吧

    内存分配机制   逐步分析 类加载检查: 虚拟机遇到一条new指令(new关键字.对象的克隆.对象的序列化等)时,会先去检查这个指令的参数在常量池中定位到一个类的符号引用,并且这个符号引用代表的类是否 ...

  5. Java 关于创建String对象过程的内存分配

    一.String s = "abc"  和 String s = new String("abc") 的区别 1.String s = "abc&qu ...

  6. 创建String对象过程的内存分配

      转载自  https://blog.csdn.net/xiabing082/article/details/49759071       常量池(Constant Pool):指的是在编译期被确定 ...

  7. 关于创建String对象过程的内存分配

    String是引用数据类型 但是String实际上java给我们提供的是一个类 注意:String 全类被fianl所修饰 所以 String 又叫 字符串常量 String 的值 一旦定义 不可以改 ...

  8. .NET的堆和栈02,值类型和引用类型参数传递以及内存分配

    在" .NET的堆和栈01,基本概念.值类型内存分配"中,了解了"堆"和"栈"的基本概念,以及值类型的内存分配.我们知道:当执行一个方法的时 ...

  9. .NET的堆和栈01,基本概念、值类型内存分配

    当我们对.NET Framework的一些基本面了解之后,实际上,还是很有必要了解一些更底层的知识.比如.NET Framework是如何进行内存管理的,是如何垃圾回收的......这样,我们才能写出 ...

随机推荐

  1. 在SQL中有时候我们需要查看现在正在SQL Server执行的命令

    在SQL中有时候我们需要查看现在正在SQL Server执行的命令.在分析管理器或者Microsoft SQL Server Management Studio中,我们可以在"管理-SQL  ...

  2. MySQL学习笔记:delete

    使用 SQL 的 DELETE FROM 命令来删除 MySQL 数据表中的记录. 语法: DELETE FROM table_name [WHERE Clause] 如果没有指定 WHERE 子句, ...

  3. 安装SHARP MX-3618NC PCL6打印机驱动程序

    第一步,  打开MX-CR3_PCL_PS_1302a_ChineseS_Win8Server2012.exe 驱动程序 此驱动支持Win8.Server2012及以下版本的操作系统,同时兼容Win1 ...

  4. Nt函数原型头文件

    //转自看雪,可以作为一个头文件使用,方便快捷 1 NTSTATUS NTAPI NtAcceptConnectPort( OUT PHANDLE PortHandle, IN PVOID PortI ...

  5. MEF实现设计上的“松耦合”(一)

    1.什么是MEF 先来看msdn上面的解释:MEF(Managed Extensibility Framework)是一个用于创建可扩展的轻型应用程序的库. 应用程序开发人员可利用该库发现并使用扩展, ...

  6. C#控制台应用程序之选课系统

    本程序以文本文件作为存储媒介,实现了一个简化版的选课系统,主要实现了以下功能 对学生信息的增删改查 对课程信息的增删改查 对学生选课的增删改查 using System; using System.C ...

  7. IDEA创建Spring Boot项目

    首先安装Spring Boot CLI 先确定自己安装的JDK是1.8版本或者以上,然后下载Srping Boot CLI,Spring Boot CLI下载地址,下载下来是一个压缩包,解压,得到一个 ...

  8. PHP 中文字符串相关

    1.字符串长度 中文字符串使用strlen() 获取字符串长度时一个UTF8的中文字符是3个字节长度:使用mb_strlen(),选定内码为UTF8,则会将一个中文字符当作长度1来计算 在对含中文字符 ...

  9. 2017-2018-1 20179202《Linux内核原理与分析》第八周作业

    一 .可执行程序的装载 1. 预处理.编译.链接 gcc –e –o hello.cpp hello.c //预处理 gcc -x cpp-output -S -o hello.s hello.cpp ...

  10. web到service简单原理例子

    这是目前的理解 附上服务端源码 package com.lsw.server; import java.io.*; import java.net.*; import java.util.HashMa ...