NET基础(4):引用类型和值类型

  CLR支持两种类型:引用类型和值类型。虽然FCL的大多数类型都是引用类型,但程序员用的最多的还是引用类型,引用类型总是从托管堆分配,c#的new操作符返回对象内存地址-即指向对象数据的内存地址。使用引用类型必须注意性能问题。首先要认清楚以下4个方面:

1、内存必须从托管堆分配。

2、堆上分配的每个对象都有一些额外的成员,这些成员必须初始化。

3、对象中的其它字节(为字段而设)总是设为零。

4、从托管堆分配对象时,可能强制执行一次垃圾回收。

  如果所有类型都是引用类型,应用程序的性能将会显著下降。设想每次使用Int32值时都进行一次内存分配,性能会受到多么大的影响,为了提升简单和常用的类型的性能,CLR提供了名为‘值类型’的轻量级类型,值类型的实例一般在线程栈上分配,在代表值类型实例的变量中不包含指向实例的指针。相反,变量中包含了实例本身的字段。由于变量已经包含了实例的字段。因此,值类型的使用缓解了托管堆的压力,并减少了应用程序生存期内的垃圾回收次数。

  文档清楚指出哪些是值类型,哪些是引用类型。在文档中查看类型时,任何成为‘类’的类型都是引用类型。例如:System.Exception类,System.IO.FileStream类以及System.Random类都是引用类型。相反所有的值类型都成为结构或枚举。例如:System.Int32结构、System.Boolean结构、System.Decimal结构、System.TimeSpan结构、System.DayOfWeek枚举等等。

  进一步研究文档,会发现所有的结构都是抽象类型System.ValueType的直接派生类。System.ValueType本身又直接从System.Object派生。根据定义,所有值类型都必须从System.ValueType派生。CLR和所有编程语言都给予枚举特殊待遇。

  虽然不能在定义值类型时为他选择基类型,但如果愿意,值类型可以实现一个或多个接口。除此之外,所有值类型都隐式密封,目的是防止将值类型用作其他引用类型或这类型的基类。例如:无法将Boolean、Char、Int32、Uint64、Single、Double、Decimal等类型来定义任何新类型。

  以下代码演示了引用类型和值类型的区别:

  

//引用类型(因为是class)
class SomeRef
{
     public Int32 x;
}

//值类型(因为是struct)
struct SomeVal
{
    public Int32 x;
}

static void ValueTypeDemo()
{
     SomeRef r1=new SomeRef();                 //往堆上分配
     SomeVal v1 = new SomeVal();               //往栈上分配

     r1.x = 5;                                                //提领指针
     v1.x = 5;                                               //在栈上修改
     Console.WriteLine(r1.x);                         //显示为5
     Console.WriteLine(v1.x);                        //同样显示为5

     SomeRef r2 = r1;                                 //只复用指针(引用)
     SomeVal v2 = v1;                                //在栈上分配并复制成员
     r1.x = 8;                                             //r1.x和r2.x都会修改成新值
     v1.x=9;                                              //v1.x会修改,v2.x不会修改

     Console.WriteLine(r1.x);                      //显示8
     Console.WriteLine(r2.x);                      //显示8
     Console.WriteLine(v1.x);                     //显示9
     Console.WriteLine(v2.x);                     //显示5
}

  上述代码中,SomeVal用struct声明,而不是用更常用的class。在C#中,常用struct声明的类型是值类型,用class声明的类型是引用类型。可以看出引用类型和值类型的区别相当大。再代码中使用类型时,必须注意是值类型还是引用类型,因为这会极大的影响在代码中表达自己意图的方式。

  上述代码中有这样一行:

SomeVal v1 = new SomeVal();               //往栈上分配

  因为这行代码的写法,似乎要在托管堆上分配一个SomeVal的实例。但c#编译器知道SomeVal是值类型,所以会生成正确的IL代码,在线程栈上分配一个SomeVal的实例。c#还会确保值类型中所有的字段都初始化为零。

SomeVal v1;   //在栈上分配空间

  这一行生成的IL代码也会在线程栈上分配实例,并将字段初始化为零。唯一的区别在于,如果使用new操作符,C#会认为实例已经初始化,以下代码更清楚的进行了说明:

//这两行代码都能够编译通过,因为c#认为v1的字段已经初始化为0
SomeVal v1 = new SomeVal();
Int32 a= v1.x;

//这两行代码不能够编译通过,因为c#不认为v1的字段已经初始化为0
SomeVal v1;
Int32 a= v1.x;  //error cs0170: 使用了可能未赋值的字段“x”;

  设计自己的类型时,要仔细考虑清楚是否应该定义成值类型还是引用类型。值类型有时候能提供更好的性能。具体的说,除非满足一下全部条件,否则不应该声明为值类型。

1、类型具有基元类型的行为。也就是说是十分简单的类型,没有成员会修改类型的任何实例字段。

2、类型不需要从其他任何类型继承。

3、类型也不派生出其它任何类型。

时间: 2024-08-05 14:59:55

NET基础(4):引用类型和值类型的相关文章

C#基础知识-引用类型和值类型的区别(六)

在第一篇中我们介绍了C#中基本的15种数据类型,这15种数据类型中又分为两大类,一种是值类型,一种是引用类型.值类型有sbyte.short.long.int.byte.ushort.uint.ulong.float.double.bool,引用类型有object.string.class类型,这两大类最大的区别就是在内存中存储的位置.方式不一样.(当我们打开运行一个程序的时候,实际上就会把当前程序需要执行的资源从硬盘加载到内存中,再把内存的数据送去CPU处理),现在来看一下它们是如何来进行存储

CLR via C#深解笔记三 - 基元类型、引用类型和值类型 | 类型和成员基础 | 常量和字段

编程语言的基元类型 某些数据类型如此常用,以至于许多编译器允许代码以简化的语法来操纵它们. System.Int32 a = new System.Int32();  // a = 0 a = 1; 等价于: int a = 1; 这种语法不仅增强了代码的可读性,其生成的IL代码与使用System.Int32时生成的IL代码是完全一致的. 编译器直接支持的数据类型称为基元类型(primitive type).基元类型直接映射到Framework类库(FCL)中存在的类型.如C#中,int直接映射

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

堆.栈.引用类型.值类型 内存分为堆和栈(PS:还有一种是静态存储区域 [内存分为这三种]),值类型的数据存储在栈中,引用类型的数据存储在堆中. 堆.栈: 堆和栈的区别: 栈是编译期间就分配好的内存空间,因此你的代码中必须就栈的大小有明确的定义:局部值类型变量.值类型参数等都在栈内存中. 堆是程序运行期间动态分配的内存空间,你可以根据程序的运行情况确定要分配的堆内存的大小. 引用类型.值类型 1.将一个值类型变量赋给另一个值类型变量时,将复制包含的值.引用类型变量的赋值只复制对对象的引用,而不复

C#基础(六)——值类型与引用类型

CLR支持两种类型:值类型和引用类型. 值类型包括C#的基本类型(用关键字int.char.float等来声明),结构(用struct关键字声明的类型),枚举(用enum关键字声明的类型):而引用类型包括类(用class关键字声明的类型)和委托(用delegate关键字声明的特殊类)等. C#中的每一种类型要么是值类型,要么是引用类型.所以每个对象要么是值类型的实例,要么是引用类型的实例.值类型的实例通常是在线程栈上分配的(静态分配),但是在某些情形下可以存储在堆中.引用类型的对象总是在进程堆中

【基础巩固】js值类型和引用类型

示例 值类型 let a = 100; let b = a; a = 200; console.log(b); // 100 (互不影响) 引用类型 let a = { age: 20 }; let b = a; a.age= 21; console.log(b.age); // 21 (b.age随a.age的改变而改变) 存储方式 值类型 js变量存储在栈中,至于怎么存储咱先不管(毕竟我也不懂),这是js引擎的事情. 如图所示,key为变量名,value为存储内容,值类型变量的值直接存储在v

第5章 基元类型、引用类型和值类型

5.1编程语言的基元类型 编译器(Compiler)直接支持的数据类型称为基元类型(primitive type). 我希望编译器根本不要提供基元类型名称,强制开发人员使用FCL(Framework类库)类型名称: 许多开发人员都困惑于到底应该使用string还是String.由于C#的string直接映射到System.String,所以两者是没有区别的.int始终映射到System.Int32,所以不管在什么操作系统上运行,代表的都是32位整数. 5.2引用类型和值类型 虽然FCL中大多数类

c# 的引用类型和值类型和数据的拆箱和装箱

c#中引用类型和值类型的区分: 一般的以calss声明的变量的类型是引用类型的,引用类型是存放到内存的堆上存放的是数据的地址.值类型是像int float 还有struct等属于值类型的数据类型,值类型的数据是存放在堆栈上的存放的数据本身. 拆箱和装箱: 我们可以用一个例子来理解装箱和拆箱的关系和作用.我们都知道小时候在村里有哪种你给他一些铝制品他可以给你溶成一个你想要的其他的铝制器件.装箱和拆箱就是这个原理.例如:你想把一种数据类型转化为另一种数据类型,int16到int32的数据类型的转化,

【转】c#引用类型与值类型的区别大盘点

解析:CLR支持两种类型:值类型和引用类型.用Jeffrey Richter(<CLR via C#>作者)的话来说,“不理解引用类型和值类型区别的程序员将会把代码引入诡异的陷阱和诸多性能问题”.这就要求我们正确理解和使用值类型和引用类型. 值类型包括C#的基本类型(用关键字int.char.float等来声明),结构(用struct关键字声明的类型),枚举(用enum关键字声明的类型):而引用类型包括类(用class关键字声明的类型)和委托(用delegate关键字声明的特殊类).C#中的每

C#引用类型和值类型的区别

1***.C#中有两种类型: 值类型和引用类型: 值类型的变量直接包含他们的数据,而引用类型的变量存储 引用类型存储对他们的数据的引用,后者称为对象: 简单说:值类型直接存储其值,引用类型存储对值得引用.引用类型分为引用和引用的对象. 2***. 值类型:简单类型.枚举.结构 引用类型: 类类型.接口类型.数组类型和委托类型 3***.值类型与引用类型的内存存储 单纯的说值类型存储在栈上,引用类型存储在托管堆上是不对的. 4***.区别: 1. 托管堆: 同步块和方法表, x,y,托管堆上的需要