[你必须知道的.NET] 第八回:品味类型---值类型与引用类型(上)-内存有理

发布日期:2007.5.23 作者:Anytao

©2007 Anytao.com ,原创作品,转贴请注明作者和出处。

 

本文将介绍以下内容:

  • 类型的基本概念 
  • 值类型深入
  • 引用类型深入
  • 值类型与引用类型的比较及应用

 

1. 引言

买了新本本,忙了好几天系统,终于开始了对值类型和引用类型做个全面的讲述了,本系列开篇之时就是因为想写这个主题,才有了写个系列的想法。所以对值类型和引用类型的分析,是我最想成文的一篇,其原因是过去的学习过程中我就是从这个主题开始,喜欢以IL语言来分析执行,也喜好从底层的过程来深入了解。这对我来说,似乎是一件找到了有效提高的方法,所以想写的冲动就没有停过,旨在以有效的方式来分享所得。同时,我也认为,对值类型和引用类型的把握,是理解语言基础环节的关键主题,有必要花力气来了解和深入。  

2. 一切从内存开始

2.1 基本概念

从上回《第七回:品味类型---从通用类型系统开始》我们知道,CLR支持两种基本类型:值类型引用类型。因此,还是把MSDN这张经典视图拿出来做个铺垫。

 

值类型(Value Type),值类型实例通常分配在线程的堆栈(stack)上,并且不包含任何指向实例数据的指针,因为变量本身就包含了其实例数据。其在MSDN的定义为值类型直接包含它们的数据,值类型的实例要么在堆栈上,要么内联在结构中。我们由上图可知,值类型主要包括简单类型、结构体类型和枚举类型等。通常声明为以下类型:int、char、float、long、bool、double、struct、enum、short、byte、decimal、sbyte、uint、ulong、ushort等时,该变量即为值类型。  

引用类型(Reference Type),引用类型实例分配在托管堆(managed heap)上,变量保存了实例数据的内存引用。其在MSDN中的定义为引用类型存储对值的内存地址的引用,位于堆上。我们由上图可知,引用类型可以是自描述类型、指针类型或接口类型。而自描述类型进一步细分成数组和类类型。类类型是则可以是用户定义的类、装箱的值类型和委托。通常声明为以下类型:class、interface、delegate、object、string以及其他的自定义引用类型时,该变量即为引用类型。

下面简单的列出我们类型的进一步细分,数据来自MSDN,为的是给我们的概念中有清晰的类型概念,这是最基础也是最必须的内容。

  

2.2 内存深入

2.2.1. 内存机制

那么.NET的内存分配机制如何呢?

数据在内存中的分配位置,取决于该变量的数据类型。由上可知,值类型通常分配在线程的堆栈上,而引用类型通常分配在托管堆上,由GC来控制其回收。例如,现在有MyStruct和MyClass分别代表一个结构体和一个类,如下:

using System;

public class Test
{
    
static void Main()
    {
        
//定义值类型和引用类型,并完成初始化
        MyStruct myStruct = new MyStruct();
        MyClass myClass 
= new MyClass();
        
        
//定义另一个值类型和引用类型,
        
//以便了解其内存区别
        MyStruct myStruct2 = new MyStruct();
        myStruct2 
= myStruct;
        
        MyClass myClass2 
= new MyClass();
        myClass2 
= myClass;        
    }
}

在上述的过程中,我们分别定义了值类型变量myStruct和引用类型变量myClass,并使用new操作符完成内存分配和初始化操作,此处new的区别可以详见《第五回:深入浅出关键字---把new说透》  的论述,在此不做进一步描述。而我们在此强调的是myStruct和myClass两个变量在内存分配方面的区别,还是以一个简明的图来展示一下:

 

我们知道,每个变量或者程序都有其堆栈,不同的变量不能共有同一个堆栈地址,因此myStruct和myStruct2在堆栈中一定占用了不同的堆栈地址,尽管经过了变量的传递,实际的内存还是分配在不同的地址上,如果我们再对myStruct2变量改变时,显然不会影响到myStruct的数据。从图中我们还可以显而易见的看出,myStruct在堆栈中包含其实例数据,而myClass在堆栈中只是保存了其实例数据的引用地址,实际的数据保存在托管堆中。因此,就有可能不同的变量保存了同一地址的数据引用,当数据从一个引用类型变量传递到另一个相同类型的引用类型变量时,传递的是其引用地址而不是实际的数据,因此一个变量的改变会影响另一个变量的值。从上面的分析就可以明白的知道这样一个简单的道理:值类型和引用类型在内存中的分配区别是决定其应用不同的根本原因,由此我们就可以很容易的解释为什么参数传递时,按值传递不会改变形参值,而按址传递会改变行参的值,道理正在于此。 

对于内存分配的更详细位置,可以描述如下:

  • 值类型变量做为局部变量时,该实例将被创建在堆栈上;而如果值类型变量作为类型的成员变量时,它将作为类型实例数据的一部分,同该类型的其他字段都保存在托管堆上,这点我们将在接下来的嵌套结构部分来详细说明。
  • 引用类型变量数据保存在托管堆上,但是根据实例的大小有所区别,如下:如果实例的大小小于85000Byte时,则该实例将创建在GC堆上;而当实例大小大于等于85000byte时,则该实例创建在LOH(Large Object Heap)堆上。

更详细的分析,我推荐《类型实例的创建位置、托管对象在托管堆上的结构》。

2.2.2. 嵌套结构 

嵌套结构就是在值类型中嵌套定义了引用类型,或者在引用类型变量中嵌套定义了值类型,相信园子中关于这一话题的论述和关注都不是很多。因此我们很有必要发挥一下,在此就顺藤摸瓜,从上文对.NET的内存机制着手来理解会水到渠成。

  • 引用类型嵌套值类型

值类型如果嵌套在引用类型时,也就是值类型在内联的结构中时,其内存分配是什么样子呢? 其实很简单,例如类的私有字段如果为值类型,那它作为引用类型实例的一部分,也分配在托管堆上。例如:

public class NestedValueinRef

  
//aInt做为引用类型的一部分将分配在托管堆上 
  private int aInt;  
  
public NestedValueinRef 
  { 
    
//aChar则分配在该段代码的线程栈上 
     char achar = 'a'
  } 

其内存分配图可以表示为:

  

  •  值类型嵌套引用类型

引用类型嵌套在值类型时,内存的分配情况为:该引用类型将作为值类型的成员变量,堆栈上将保存该成员的引用,而成员的实际数据还是保存在托管堆中。例如:

public struct NestedRefinValue
{
    
public MyClass myClass;
    
public NestedRefinValue
    {
        myClass.X 
= 1;
        myClass.Y 
= 2;
    }
}

其内存分配图可以表示为:

 

2.2.3. 一个简单的讨论

通过上面的分析,如果我们现在有如下的执行时:

AType[] myType = new AType[10];

试问:如果AType是值类型,则分配了多少内存;而如果AType是引用类型时,又分配了多少内存?

我们的分析如下:根据CRL的内存机制,我们知道如果ATpye为Int32类型,则表示其元素是值类型,而数组本身为引用类型,myType将保存指向托管堆中的一块大小为4×10byte的内存地址,并且将所有的元素赋值为0;而如果AType为自定义的引用类型,则会只做一次内存分配,在线程的堆栈创建了一个指向托管堆的引用,而所有的元素被设置为null值,表示为空。 

未完,下回即将发布。。。


 

参考文献

(USA)Jeffrey Richter, Applied Microsoft .NET Framework Programming

(USA)David Chappell, Understanding .NET


广而告之

本文有些长,因此分两回来展开。我们已经分析了类型的内存机制,接下来就该着重于类型的实际应用领域了,因此在下回中我们会从[通用规则与区别]、[实例分析]、[应用场合]、[类型比较]等几个方面来着重展开,希望给大家以帮助,对于表达有谬或者理解有误的地方还望不吝赐教,本人将不胜感激。

To be continue soon ...   

温故知新

[开篇有益]

[第一回:恩怨情仇:is和as]

[第二回:对抽象编程:接口和抽象类]

[第三回:历史纠葛:特性和属性]

[第四回:后来居上:class和struct]

[第五回:深入浅出关键字---把new说透]

[第六回:深入浅出关键字---base和this]

[第七回:品味类型---从通用类型系统开始]

©2007 Anytao.com

原创作品,转贴请注明作者和出处,留此信息。

本贴子以现状提供且没有任何担保,同时也没有授予任何权利。
This posting is provided "AS IS" with no warranties, and confers no rights.

 

posted @ 2007-05-23 01:01 Anytao 阅读(21853) 评论(96) 编辑 收藏

 回复 引用 查看   
#1楼 2007-05-23 03:17 光之追随者      
在逐行逐句地看完此帖后,我的心久久不能压抑的喜悦,震动了整个生命!怎会有如此精妙绝伦的好贴?偶潜水网络bbs多年,自以为贴在人外,何肖评说,岂可妄动情谊呼!未曾想到今日竟有如此好贴现于眼前,激动啊! 楼猪,是你让偶幼小的心灵再次深深的领悟了何谓造旨之高深、文笔之挥洒。。谢谢you! 在看完这帖子以后,我立即动手回复,因为我生怕迟到的回复不能使更多的人领悟你的圣明,以至使这等网上少有的好贴就此轮沉,我担不起这样的罪名!更加重要的是,能在如此重要、精辟而又生动的贴子后,留上自己的网名,这对我的生命,以及我的家庭,乃至我所处的社会中是多么荣耀的一件事啊,请您高贵而又宽容的心,能够原谅我的这点小小私心!
 回复 引用   
#2楼 2007-05-23 08:52 cobra[未注册用户]
又长知识了。。。
 回复 引用 查看   
#3楼 2007-05-23 09:18 t-mac.NET      
@光之追随者
怎么看都像台词

 回复 引用   
#4楼 2007-05-23 09:23 拜托了![未注册用户]
楼主没有用Rss阅读器订阅过自己的博客吗?您博客的标题是“博客园-”,拜托,你这个标题和人家大博客园不一样吗?你就不能修改成你自己的标题啊,如“博客园-Anytao”吗?也让我看rss的时候知道是从谁那里来的啊 !拜托拜托了,实在是受不了了!
 回复 引用 查看   
#5楼[楼主] 2007-05-23 09:26 Anytao      
@光之追随者
非常感谢你如此热情洋溢的称赞,这对我真是莫大的鼓舞。系列开篇以来,得到很多朋友的支持和关注,在此一并谢过了。我将继续坚持写好系列的每一个主题,希望大家继续关注,非常感谢。

 回复 引用 查看   
#6楼[楼主] 2007-05-23 09:27 Anytao      
@cobra
感谢支持。

 回复 引用 查看   
#7楼[楼主] 2007-05-23 09:28 Anytao      
@拜托了!
呵呵,确实没有注意这一点,马上修改,谢谢提醒。

 回复 引用   
#8楼 2007-05-23 09:38 redw[未注册用户]
学习中
 回复 引用   
#9楼 2007-05-23 09:39 heweitykc[未注册用户]
将内存机制的时候,能不能使用一些DEBUG技巧,让我们能看到真实的内存中的分配情况,而不是只画一张图
 回复 引用 查看   
#10楼[楼主] 2007-05-23 09:42 Anytao      
@redw
感谢支持。

 回复 引用 查看   
#11楼[楼主] 2007-05-23 09:42 Anytao      
@heweitykc
下回的实例部分,我将尽力考虑这一点,谢谢。

 回复 引用   
#12楼 2007-05-23 11:26 blogtalker[未注册用户]
Great........
 回复 引用 查看   
#13楼 2007-05-23 11:54 装配脑袋      
AType[] myType = new AType[10];

这个分析的似乎有问题,数组是引用类型,因此无论AType是何种类型,数组都是在堆中分配的,myType只会是一个引用。而在堆中,数组的布局是不同的,值类型数组是紧密分配的一片内存,大小等于sizeof(AType) * 10,而引用类型的数组则是10个引用组成的数组。

 回复 引用 查看   
#14楼 2007-05-23 11:58 装配脑袋      
我觉得探究对象的分配方法,应该观察一下SSCLI代码的说。那个最能说明名问题了。特别是引用类型的对象究竟如何在GC堆中分配,MethodTable怎么发挥作用等等。
 回复 引用 查看   
#15楼[楼主] 2007-05-23 12:42 Anytao      
@装配脑袋
理解有误,感谢提醒,已做修改,对大家很抱歉。

 回复 引用 查看   
#16楼 2007-05-23 13:40 天轰穿      
@光之追随者

如果是你真的受益以后别用这样阴损人的话回帖啦,呵呵!
这样很容易让人误解你欺负我们天天埋头编程的人不懂论坛灌水那套东西啦!

 回复 引用 查看   
#17楼 2007-05-23 14:39 光之追随者      
呵呵大家都是编程的,我的意思是来点别得东西大家高兴。
 回复 引用 查看   
#18楼[楼主] 2007-05-23 16:12 Anytao      
@天轰穿
@光之追随者
呵呵,并非意气之争,感谢二位。

 回复 引用   
#19楼 2007-05-24 18:06 Sunshine[未注册用户]
严重建设楼主出书啊。。。书名就叫[你必须知道的.NET]。。。
 回复 引用 查看   
#20楼[楼主] 2007-05-24 18:07 Anytao      
@Sunshine
呵呵,尽力做好这个系列,离那个高度还有距离,希望给大家以帮助:-)

 回复 引用 查看   
#21楼 2007-05-25 09:02 datasky      
mark
 回复 引用 查看   
#22楼[楼主] 2007-05-25 13:09 Anytao      
@datasky
It's my pleasure.

 回复 引用 查看   
#23楼 2007-05-25 15:42 try      
希望能结合这部分,顺便把 静态方法和实例方法区别也 讲下.现在网上对这个的观点误解实在是很多
 回复 引用 查看   
#24楼[楼主] 2007-05-25 17:08 Anytao      
@try
静态方法和实例方法, 和数据有些关系, 但是不属于类型范畴的考虑. 简单的说, 静态方法, 属于类访问级别, 不需要实例即可由类直接调用; 而实例方法, 属于对象访问级别, 需要创建实例对象才可调用. 这个主题也值得研究, 在系列的后面我也打算作为主题来阐述,感谢你的讨论.

 回复 引用 查看   
#25楼[楼主] 2007-05-26 12:26 Anytao      
@念时
感谢支持,继续努力中...

 回复 引用   
#26楼 2007-06-01 15:02 zz[未注册用户]
如何加速内存的释放也是个问题,象C++的Delete,好GC在内存的释放性能很差,不知是不是这样的?????????、
 回复 引用 查看   
#27楼[楼主] 2007-06-01 18:05 Anytao      
@zz
.NET提供了显式释放内存的机制,就是Dispose模式。GC本身也实现了相应的性能机制,称为代龄。简单的说就是,越新的对象,生存期越短,垃圾回收器可以更有效的释放内存。更详细的介绍以后希望有机会展开,在此仅供参考。

 回复 引用   
#28楼 2007-07-13 17:27 刘荣华
很不错。呵呵
提点小问题
堆栈?
堆和栈应该不是一样的概念吧。
分配在堆和分配在栈是有很大不同的。。


 回复 引用   
#29楼 2007-07-13 17:45 刘荣华
再给点建议。
LZ都写到这一步了,建议再把元数据与IL,PE文件的关系可以再写写。呵呵
那LZ应该就是介绍.NET底层架构的第一人啦。。
预先祝贺。。

 回复 引用 查看   
#30楼 2007-07-20 16:42 脚印      
我只是经常看,没有回帖,感觉不好意思,
还是……回帖感谢一下


继续……关注……

 回复 引用 查看   
#31楼 2007-07-23 23:26 appledou      
建议大家都去看.NET框架程序设计(修订版或者第二版),看完就全明白了~还有Dispose模式严格讲是释放资源的机制,而非释放内存,释放内存还是要靠GC
非常感谢LZ提供的交流平台

 回复 引用   
#32楼 2007-08-13 11:58 leeses[未注册用户]
@Anytao
能否将堆和栈区别来讲?谢谢!

 回复 引用 查看   
#33楼[楼主] 2007-08-15 10:05 Anytao      
@刘荣华
@leeses
堆栈的问题在系列的后续中,一定做一交待,也是对自己的提高,感谢支持.

 回复 引用 查看   
#34楼[楼主] 2007-08-15 10:05 Anytao      
@脚印
:-)

 回复 引用 查看   
#35楼[楼主] 2007-08-15 10:07 Anytao      
@appledou
那本书我也很喜欢。
Dispose模式一般释放非内存资源,例如数据库连接、操纵系统句柄等。

 回复 引用 查看   
#36楼[楼主] 2007-09-26 11:17 Anytao      
@chinaifne
:-)

 回复 引用   
#37楼 2007-11-21 23:01 cc_net[未注册用户]
在学校用.net写过一些小系统,今年刚毕业,却没有搞。NET的工作。目前正在重新学习.NET框架知识,希望从地层来了解。

自己在看.NET框架程序设计,写结合书写了点读书笔记,这快确实比较难理解。学习了。

 回复 引用 查看   
#38楼[楼主] 2007-11-21 23:50 Anytao      
@cc_net
希望有所帮助,欢迎常来看看:-)

 回复 引用 查看   
#39楼 2007-12-07 10:57 mythzz      
滔滔江河 连绵不绝啊
 回复 引用 查看   
#40楼[楼主] 2007-12-07 11:09 Anytao      
@mythzz
可挖掘的东西很多,以后还想写点新特性。

买了新本本,忙了好几天系统。
现在的人啊,不知道为啥,买个本本也要说说。觉得很NB吗?忙了好几天系统?????
还有脸说,真是SB。

 回复 引用 查看   
#42楼[楼主] 2008-01-09 16:25 Anytao      
@呵呵用户名被注册?
?
所谓镜子里看到的永远是自己的嘴脸。一句简单的寒暄也能换来你这么多思考,真是难为你的想象了。
NB也好,SB也好,自有自己知道:-)

 回复 引用 查看   
#43楼 2008-01-17 09:19 乐子哥      
非常感谢楼主,我对以下一段话不是很理解

2.2.3. 一个简单的讨论

通过上面的分析,如果我们现在有如下的执行时:

AType[] myType = new AType[10];

试问:如果AType是值类型,则分配了多少内存;而如果AType是引用类型时,又分配了多少内存?

我们的分析如下:根据CRL的内存机制,我们知道如果ATpye为Int32类型,则表示其元素是值类型,而数组本身为引用类型,myType将保存指向托管堆中的一块大小为4×10byte的内存地址,并且将所有的元素赋值为0;而如果AType为自定义的引用类型,则会只做一次内存分配,在线程的堆栈创建了一个指向托管堆的引用,而所有的元素被设置为null值,表示为空。

 回复 引用 查看   
#44楼[楼主] 2008-01-17 10:29 Anytao      
@乐子哥
AType[] myType = new AType[10];

其实对于数组的分配不难理解,正如装配脑袋在回帖中所言,数组本身是引用类型,所以内存一定分配在托管堆中,myType则在线程栈上保存了指向分配于托管堆中的地址。对值类型和引用类型而言,不同的只是在托管堆中值类型分配了大小为sizeof(AType)*10的内存空间;而引用类型则是10个值为null的空引用而已。
对于值类型和引用类型的创建与分配,详见:
http://www.cnblogs.com/anytao/archive/2007/12/03/must_net_18.html
http://www.cnblogs.com/anytao/archive/2007/12/07/must_net_19.html
的分析。
:-)

 回复 引用   
#45楼 2008-02-29 17:01 alawn[未注册用户]
写的很好哦,支持哦

 回复 引用 查看   
#46楼[楼主] 2008-02-29 18:15 Anytao      
@alawn
谢谢,精彩还将继续:-)

 回复 引用 查看   
#47楼 2008-03-06 22:19 Sam Lin      
最近正在看楼主这一个系列,觉得写得很好,可能去面试有作用,楼主造诣很深,不知自己要努力几年才有楼主现在的水平,本人做了一年的.net,现在打算辞职,重新找工作,不知有何建议呢?
 回复 引用 查看   
#48楼[楼主] 2008-03-07 09:23 Anytao      
@Sam Lin
呵呵,您太客气了,其实我也一直是个学习者,还谈不少造诣:-)
如果有建议的话,我觉得在学习.NET中必须将对.NET Framework和CLR的研究,与对应用(ASP.NET、Web Service)的热情放在同样重要的位置,而且必须专注,不可铺陈太宽。.NET技术有着博大精深的体系,盲目追求肯定陷入“不够精通”的泥潭,总感觉徘徊在技术之外。所以专注是必须的,至少在开始阶段,这一点非常重要。
另外的建议就是实践,多写代码,多看框架,多来博客园学习朋友们的分享,有可能的话将自己的所得写出来共享,都是必须的步骤。
其他的例如下功夫之类的,都已不言而喻。

祝愿你有更大的进步,常来讨论:-)

 回复 引用 查看   
#49楼 2008-04-09 15:39 二把刀      
@呵呵用户名被注册?
您是典型的SB!

 回复 引用 查看   
#50楼 2008-04-09 15:42 二把刀      
看涛哥的文章,每天进步一点点!
 回复 引用 查看   
#51楼[楼主] 2008-04-09 16:03 Anytao      
@二把刀
^_^
这个系列还有还多可以续写的东西。以后我还会逐步在此涉猎《你必须知道的.NET》一书中没有讲到的其他基础,分享更多所得。
共同进步:-)

 回复 引用 查看   
#52楼 2008-04-13 22:53 dushouke      
强人~~~看了好多书不明白的地方一下子就明白了~~~

 回复 引用 查看   
#53楼[楼主] 2008-04-18 22:20 Anytao      
@dushouke
呵呵,谢谢你的关注,这个系列的更多精彩可以参见:
http://www.cnblogs.com/anytao/archive/2007/09/14/must_net_catalog.html

http://insidedotnet.cnblogs.com/

 回复 引用 查看   
#54楼 2008-04-22 11:10 镜涛      
想学习编译器的内存分配方法,实现。不知搂住是否可以指导!谢谢!
 回复 引用 查看   
#55楼[楼主] 2008-04-22 23:16 Anytao      
@镜涛
呵呵,我也是泛泛的了解,很高兴能和你进行这方面的交流,常联系,给你发短消息了:-)

 回复 引用   
#56楼 2008-05-20 22:33 啊华[未注册用户]
恩 很不错!!!
 回复 引用 查看   
#57楼 2008-06-07 22:59 紫微星VGIS      
学习了,非常好!
一个小建议:图片最好用原图吧,不然有的图看不太清楚哦,呵呵。
谢谢你的系列文章!

 回复 引用 查看   
#58楼[楼主] 2008-06-08 22:06 Anytao      
@紫微星VGIS
谢谢你的提醒。
关于图片,我是用Visio画的,贴到这里就成这样了:-)
下次多注意了:-)

 回复 引用 查看   
#59楼[楼主] 2008-06-08 22:06 Anytao      
@啊华
谢谢,欢迎常来

 回复 引用   
#60楼 2008-06-29 19:08 楼猪太棒了[未注册用户]
写得真不错...在课堂上听不懂的在这看一下就能明白,谢谢LZ
 回复 引用 查看   
#61楼[楼主] 2008-06-29 23:03 Anytao      
@楼猪太棒了
哈哈,过奖了,欢迎常来讨论,技术需要更多的见解和争论:-)

 回复 引用   
#62楼 2008-08-15 08:26 mogen[未注册用户]
感谢楼主的无私奉献 同时希望楼主能够将一些无聊人的跟帖及时删除.
 回复 引用 查看   
#63楼[楼主] 2008-08-16 01:03 Anytao      
@mogen
实际算来,来园子已近两年,期间付出了很多的心血,收获很多的思考,结交很多的朋友,这里就像一个网上家园,我很用心的经营和维护。
开博之初,我为自己定下一些不成文的规矩,其中不删除任何一条留言和争取回复任何一条留言,是一点小小的原则。除了一些无聊的广告,我基本从未删除任何回帖,所以还是希望自己坚持原来的想法,不过很感谢你的提醒和鼓励。

谢谢:-)

 回复 引用 查看   
#64楼 2008-08-16 10:41 micenter      
涛哥您好!对于引用类型 string类型
string str1="cnblogs";
string str2=str1;
Console.WriteLine(str1);//输出 cnblogs
str2="cnblogs+anytao";
Console.WriteLine(str1);//输出 cnblogs
Console.WriteLine(str2);//输出 cnblogs+anytao

问:string 类型str1和str2 都是引用类型为什么输出的结果不一样呢。是不是str1和str2在manage heap 分别有各自开辟空间?

 回复 引用 查看   
#65楼[楼主] 2008-08-19 16:00 Anytao      
@micenter
这是个复杂的问题,可能并非三言两语就可交代清晰,涉及到值类型、引用类型,字符串驻留等多个方面的话题,所以如果有个较为清晰的了解,建议从这个系列的相关部分获取更多的概念:
http://www.cnblogs.com/anytao/archive/2007/09/14/must_net_catalog.html


不过以你的示例而言,第一个问题:
string 类型str1和str2 都是引用类型为什么输出的结果不一样呢。

其输出结果不管是值类型,还是引用类型,其实都是一样的,str2的改变并未改变原来str1的引用地址,所以输出的结果正如示例所示。将string换成另外其他的任何引用类型,结果都是一样的,你可以尝试一下。

第二个问题:
是不是str1和str2在manage heap 分别有各自开辟空间?
string的内存分配,受string intern机制的制约,一般说来两个不同字面量的string类型,在managed heap的存储位置是不同的,以本例而言,str1和str2实例在managed heap中肯定位于不同的存储空间。

关于string intern,在《你必须知道的.NET》中有详细的论述,我也计划于近期发表一篇这方面的补充文章,敬请关注;-)

 回复 引用   
#66楼 2008-09-09 23:16 flysun0311[未注册用户]
继续欣赏。

 回复 引用 查看   
#67楼[楼主] 2008-09-18 20:48 Anytao      
@flysun0311
~~~!~~~

 回复 引用 查看   
#68楼 2008-09-27 17:18 棠棠dotNet      
学习了。
支持楼主!好文一定要留言!

 回复 引用 查看   
#69楼[楼主] 2008-10-05 16:58 Anytao      
@棠棠dotNet
哈哈,谢谢:-)

 回复 引用 查看   
#70楼 2008-11-26 00:08 徐培华      
呵呵,谢谢了,我会经常来看的,
期待更好作品.

 回复 引用 查看   
#71楼[楼主] 2008-11-27 10:03 Anytao      
@徐培华
呵呵,继续努力,继续加油:-)

 回复 引用 查看   
#72楼 2009-03-02 11:47 我从草原来      
@呵呵用户名被注册?
真是一个垃圾 你丫把整锅汤都坏了

 回复 引用 查看   
#73楼 2009-03-02 11:49 我从草原来      
去年就知道有这个系列 可一直没看 现在看了觉得写的真好
虽然做.net有几年了 可感觉还是新手 一直不知道怎么入手学习.net。是你给我指明了前方的道路 非常感谢你的辛勤工作
thanks a lot!

 回复 引用 查看   
#74楼[楼主] 2009-03-02 13:10 Anytao      
@我从草原来
呵呵,我将继续努力,虽然工作太忙没有太多时间,不过空闲的时候,我还想继续这个系列,毕竟还有很多值得分享的东西:-)

 回复 引用 查看   
#75楼 2009-04-19 23:23 Kims      
感谢楼主的无私奉献,这个系统文章去年我也看到了,也觉得很好,但是自己一直处于不思进取的状态,最近一直努力调整自己的状态,希望自己学习完楼主的这个系列。再次感谢!
 回复 引用   
#76楼 2009-08-03 13:17 Jacen[未注册用户]
茫茫帖海之中,我找到了这篇文章。
谢谢……太谢谢了……
你的文章很及时,很生动,很形象,使我很受益……
谢谢……~~~~(>_<)~~~~

 回复 引用   
#77楼 2009-12-04 00:29 visionsky12[未注册用户]
非常感谢楼主的文章,楼主讲的东西正是我想一直希望得到,很少又像楼主讲的这么底层的·
 回复 引用 查看   
#78楼 2009-12-13 10:32 tangself      
涛哥您好!对于引用类型 string类型
string str1="cnblogs";
string str2=str1;
Console.WriteLine(str1);//输出 cnblogs
str2="cnblogs+anytao";
Console.WriteLine(str1);//输出 cnblogs
Console.WriteLine(str2);//输出 cnblogs+anytao

问:string 类型str1和str2 都是引用类型为什么输出的结果不一样呢。是不是str1和str2在manage heap 分别有各自开辟空间?
这个不是和那个StringBulider可以解释的吗??
str2 = str1;因为string 类型的原因,并不是把str2指向了str1中的内容,而是str2又在内存开了一块空间同时str2丢弃以前的值,
所以,str1,str2此时还是指向不同的

 回复 引用 查看   
#79楼 2010-01-08 16:52 刘修明      
写的太生动了,看来中国人还是得看我们自己写的东西。
 回复 引用 查看   
#80楼 2010-07-06 13:27 design-life      
请教一个问题,有子类A、父类B

当用 B b = new (B)A;时是否性能上的损失,损失在哪儿?

另: 如果是object o = new (B) A;

以上均指转化过程(B)A

 回复 引用 查看   
#81楼 2010-07-19 11:31 freeish      
@tangself
你好,对于str2 = str1,我认为是这样理解的:str1只是在堆栈中保存了托管堆中“cnblogs”地址,而str2 = str1 是将str1这个引用地址赋给str1,当str2重新赋值为“cnblogs+anytao”的时候,str2即重新指向了“cnbolgs+anytao”在托管堆中的位置,即在堆栈中存储了“cnbolgs+anytao”的引用地址。
---------------------不知道理解得对不对,说得怪异的话请不要见怪 from freeish

 回复 引用 查看   
#82楼 2010-07-26 18:06 bozichou      
引用freeish:
@tangself
你好,对于str2 = str1,我认为是这样理解的:str1只是在堆栈中保存了托管堆中“cnblogs”地址,而str2 = str1 是将str1这个引用地址赋给str1,当str2重新赋值为“cnblogs+anytao”的时候,str2即重新指向了“cnbolgs+anytao”在托管堆中的位置,即在堆栈中存储了“cnbolgs+anytao”的引用地址。
---------------------不知道理解得对不对,说得怪异的话请不要见怪 from freeish

我同意这种理解。"cnblogs+anytao"应该是一个匿名的新字符串,在堆中有自己的地址,str2是指向了这个新字符串,并没有改变到str1的堆,还请楼主指教

 回复 引用 查看   
#83楼 2010-10-16 21:00 studyendless      
写的很好 已收藏楼主博客地址
 回复 引用 查看   
#84楼 2011-02-24 11:03 hp100277      
楼主您好:
如果将 值类型嵌套引用类型 的这个例子修改为
public struct NestedRefinValue
{
public MyClass myClass;
public NestedRefinValue
{
myClass.X = 1;
myClass.Y = 2;
}
public int iValue;
}
是否整个成员的实际数据都归分配到托管堆中。按图的理解是这样的。但是,写了个测试程序发现,对于值类型中的值类型仍然是放在堆栈中。

 回复 引用 查看   
#85楼 2011-05-04 17:48 Mr.Brandy      

 回复 引用 查看   
#86楼 2011-07-16 11:40 俊采星驰      
最近一直在看楼主的这个系列,好像迟了点,呵呵,支持!!
 回复 引用 查看   
#87楼 2011-07-20 15:34 浪子の无悔      
@俊采星驰
我认为没有早迟之分,我也是刚看到这个系列,写的很生动,对于咱这些一直专注于界面功能实现的新手来说,真的是学习理论的好系列。支持楼主~

 回复 引用 查看   
#88楼 2011-12-02 11:20 笨鸟先飞早入林      
新手学习了!