A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

c#

© 568839480 中级黑马   /  2014-1-3 16:49  /  1435 人查看  /  3 人回复  /   0 人收藏 转载请遵从CC协议 禁止商业使用本文

引用类型与值类型的区别?

3 个回复

倒序浏览
1.值类型主要由两类组成:

结构

枚举

结构分为以下几类:

Numeric(数值)类型

整型

浮点型

decimal

bool

用户定义的结构。

值类型的主要功能
基于值类型的变量直接包含值。将一个值类型变量赋给另一个值类型变量时,将复制包含的值。这与引用类型变量的赋值不同,引用类型变量的赋值只复制对对象的引用,而不复制对象本身。

所有的值类型均隐式派生自 System..::.ValueType。

与引用类型不同,不可以从值类型派生出新的类型。但与引用类型相同的是,结构也可以实现接口。

与引用类型不同,值类型无法包含 null 值。然而,可译为 null 的类型功能确实允许将 null 赋给值类型。

每种值类型均有一个隐式的默认构造函数来初始化该类型的默认值。有关值类型的默认值的信息,请参见默认值表。
2.引用类型的变量又称为对象,可存储对实际数据的引用,不像值类型那样直接将数据存储在值类型变量中。值类型的局部变量的数据存储在线程栈中,而引用类型的数据全都存储在托管堆中

3.
回复 使用道具 举报
  1. //值类型对象的数据存储在栈中,而引用对象的数据存储在堆区
  2. //值类型和引用类型的不同的存储方案对程序逻辑的影响却是必须透彻了解的
  3. using System;
  4. class TestValueRef
  5. {
  6.         public int value;
  7. }
  8. class Test
  9. {
  10.         public static void Main()
  11.         {
  12.                 int valOne=0;
  13.                 int valTwo=valOne;
  14.                 int valThree=333;
  15.                 valTwo=333;//valOne==0 无影响
  16.                
  17.                 TestValueRef Ref1=new TestValueRef();
  18.                 TestValueRef Ref2=Ref1;//栈中的Ref1和Ref2指向堆中同一个地方
  19.                 Ref2.value=444;//Ref1.value==444 有影响
  20.                
  21.                 Console.WriteLine("{0},{1},{2}",valOne,valTwo,valThree);
  22.                 Console.WriteLine("{0},{1}",Ref1.value,Ref2.value);
  23.                 ///////////////////////////////////////////////////////
  24.                 //ReferenceEquals()判断引用是否相等,Equal()判断内容是否相等
  25.                 //测试值类型对象的“相等的含义”
  26.                 if(Object.ReferenceEquals(valOne,valTwo))//赋值是内容的赋值,引用是不可能相等的
  27.                 {
  28.                         Console.WriteLine("The references of valOne and valTwo are equal");
  29.                 }
  30.                 else
  31.                 {
  32.                         Console.WriteLine("The references of valOne and valTwo are not equal");
  33.                 }
  34.                
  35.                 if(Object.ReferenceEquals(valTwo,valThree))//仅内容一样
  36.                 {
  37.                         Console.WriteLine("The references of valTwo and valThree are equal");
  38.                 }
  39.                 else
  40.                 {
  41.                         Console.WriteLine("The references of valTwo and valThree are not equal");
  42.                 }
  43.                
  44.                 if(Object.Equals(valTwo,valThree))//Equals()内容一样就相等
  45.                 {
  46.                         Console.WriteLine("valTwo and valThree are equal");
  47.                 }
  48.                 else
  49.                 {
  50.                         Console.WriteLine("valTwo and valThree are not equal");
  51.                 }               
  52.                 /////////////////////////////////////////////////////////////////
  53.                 //测试引用类型对象的“相等的含义”
  54.                 if(Object.ReferenceEquals(Ref1,Ref2))//赋值是引用,两个引用相等,则两个引用对象的数据存放在堆中的同一个位置
  55.                 {
  56.                         Console.WriteLine("The references of Ref1 and Ref2 are equal");
  57.                 }
  58.                 else
  59.                 {
  60.                         Console.WriteLine("The references of Ref1 and Ref2 are not equal");
  61.                 }

  62.                 if(Object.Equals(Ref1,Ref2))//两个引用相等,数据成员必然也相等
  63.                 {
  64.                         Console.WriteLine("Ref1 and Ref2 are equal");
  65.                 }
  66.                 else
  67.                 {
  68.                         Console.WriteLine("Ref1 and Ref2 are not equal");
  69.                 }
  70.                
  71.                 TestValueRef Ref3=new TestValueRef();
  72.                 Ref3.value=444;
  73.                 if(Object.ReferenceEquals(Ref1,Ref3))//引用不同
  74.                 {
  75.                         Console.WriteLine("The references of Ref1 and Ref3 are equal");
  76.                 }
  77.                 else
  78.                 {
  79.                         Console.WriteLine("The references of Ref1 and Ref3 are not equal");
  80.                 }
  81.                
  82.                 if(Object.Equals(Ref1,Ref3))//重点,类型相同,数据相等,引用不同是不“相等的”(可以理解为栈中存放的地址不同所致)
  83.                 {
  84.                         Console.WriteLine("Ref1 and Ref3 are equal");
  85.                 }
  86.                 else
  87.                 {
  88.                         Console.WriteLine("Ref1 and Ref3 are not equal");
  89.                 }               
  90.         }
  91. }
  92. /*
  93. 0,333,333
  94. 444,444
  95. The references of valOne and valTwo are not equal
  96. The references of valTwo and valThree are not equal
  97. valTwo and valThree are equal
  98. The references of Ref1 and Ref2 are equal
  99. Ref1 and Ref2 are equal
  100. The references of Ref1 and Ref3 are not equal
  101. Ref1 and Ref3 are not equal
  102. */
复制代码


回复 使用道具 举报
值类型直接存储其值,变量本身就包含了其实例数据,而引用类型保存的只是实例数据的内存引用。因此,一个值类型变量就永远不会影响到其他的值类型变量,而两个引用类型变量则很有可能指向同一地址,从而发生相互影响。  
  从内存分配上来看,值类型通常分配在线程的堆栈上,作用域结束时,所占空间自行释放,效率高,无需进行地址转换,而引用类型通常分配在托管堆上,由GC来控制其回收,需要进行地址转换,效率降低,这也正是c#需要定义两种数据类型的原因之一。    值类型均隐式派生自System.ValueType,而System.ValueType又直接派生于System.Object,每种值类型均有一个隐式的默认构造函数来初始化该类型的默认值,注意所有的值类型都是密封(sealed)的,所以无法派生出新的值类型。而且System.ValueType本身是一个类类型,而不是值类型,因为它重写了object的Equals()方法,所以对值类型将按照实例的值来比较,而不是比较引用地址。  
  C# 的统一类型系统,使得值类型可以转化为对象来处理,这就是常说的装箱和拆箱。由于装拆箱需要装建全新对象或做强制类型转换,这些操作所需时间和运算要远远大于赋值操作,因此不提倡使用它,同时也要尽量避免隐式装拆箱的发生。  
注:栈是操作系统分配的一个连续的内存区域,用于快速访问数据。因为值类型的容量是已知的,因此它可存储在栈上。而托管堆是CLR在应用程序启动时为应用程序预留的一块连续内存区,是用于动态内存分配的内存区,引用类型的容量只有到运行时才能确定,所有用堆来存储引用类型。 
C#的两种数据类型延伸之一--嵌套类型的内存分配 
    对于引用类型嵌套值类型,以及值类型嵌套引用类型的情况下,内存分配可以根据以下两条规律来判断: 
•    引用类型始终部署在托管堆上; 
•    值类型总是分配在它声明的地方:作为字段时,跟随其所属的对象存储;作为局部变量时,存储在 栈上。 
C#的两种数据类型延伸之二--string类型 
    string是一个很有意思的引用类型,为什么说它很有意思呢?因为它表现了很多值类型的特点。请看一下代码示例: 示例1 
string str1 = "abc"; 
string str2 = str1; str1 = "123"; 
Console.WriteLine(str2); 示例2(msdn上的例子) string a = "hello"; string b = "h"; 
// Append to contents of 'b' b += "ello"; 
Console.WriteLine(a == b); 
示例1的输出结果是abc,改变str1的值对str2没有影响。 示例2的输出结果是True。 
    这样的结果会使我们误以为string就是值类型。其实不然,示例1中str1 = "123"语句编译器私底下创建了一个新的字符串对象来保存新的字符序列"123",也就是此str1已非彼str1了,“此”str1的值的改变也就不能影响“彼”str1的值了,当然str2的值也就不会改变了。实质上str1 = "123"是str1=new string("123")的简写,它的每一次赋值都会抛掉原来的对象而生成一个新的字符串对象,分配新的内存空间,因此string是不可改变的。如果要创建可修改的字符串,可使用stringbuilder以获得更好的性能。至于示例2是因为为了方便比较字符串的值重定义了string的运算符== 和 !=。
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马