CLR支持两种类型:引用类型和值类型。

  引用类型总是在托管堆上分配的,C#的new操作符会返回对象的内存地址——也就是指向对象数据的内存地址。
  使用引用类型必须注意到一些问题:
  1)内存必须从托管堆上分配。
  2)对上分配的每个对象都有一些额外的成员(比如前面提到过得"类型对象指针"和"同步块索引"),这些成员必须初始化。
  3)对象中的其他字节(为字段而设)总是设为零。
  4)从托管堆上分配一个对象时,可能强制执行一次垃圾回收操作。
  如果所有类型都是引用类型,应用程序的性能会显著下降。为了提升简单的、常用的类型的性能,CLR提供了名为"值类型"的轻量型类型。
  值类型的实例一般在线程栈上分配的(虽然也可作为字段嵌入一个引用类型的对象中)。在代表值类型的实例的一个变量中,并不包含一个指向实例的指针。相反,变量中包含了实例本身的字段。
  由于变量已经包含了实例的字段,所以为了操作实例中的字段,不再需要提供一个指针。值类型的实例不受垃圾回收器的控制。因此,值类型的使用缓解了托管堆中的压力,并减少了一个应用程序在其生存期内需要进行的垃圾回收次数。
  .NET Framework SDK文档明确指出,在查看一个类型时,任何称为"类"的类型都是引用类型。如System.Exception类、System.Random类等引用类型。文档将所有值类型都成为结构或枚举。如System.Int32结构、System.Boolean结构等值类型。
  所有值类型都必须从System.ValueType派生。所有枚举类型都从System.Enum抽象类派生,而System.Enum又是从System.ValueType派生的。CLR和所有编程语言都给予枚举特殊待遇,以后会提到。
  所有值类型都是隐式密封的(sealed),目的是防止将一个值类型用于其他任何引用类型或值类型的基类型。
  在托管代码中,要由定义类型的开发人员决定在什么地方分配类型的实例,使用该类型的人对此并无控制权。
  以下演示引用类型和值类型的区别:
//引用类型
class SomeRef
{
public Int32 x;
}
//值类型
struct SomeVal
{
public Int32 x;
} static void Main(string[] args)
{
SomeRef r1 = new SomeRef(); //在堆上分配
SomeVal v1 = new SomeVal(); //在栈上分配
r1.x = ;
v1.x = ;
Console.WriteLine(r1.x); //5
Console.WriteLine(v1.x); //5 SomeRef r2 = r1;
SomeVal v2 = v1;
r1.x = ;
v1.x = ;
Console.WriteLine(r1.x); //8
Console.WriteLine(r2.x); //8
Console.WriteLine(v1.x); //9
Console.WriteLine(v2.x); //5
}

  除非以下条件都能满足,否则不应该将一个类型声明成值类型:

  1)类型具有基元类型的行为。
  2)类型不需要从其他任何类型继承
  3)类型也不会派生出其他类型。    
  类型实例的大小应该在考虑之列,因为默认情况下,实参是以传值方式传递的,这会造成对值类型实例中的字段进行复制,从而影响性性能。同样的,被定义为返回一个值类型的一个方法在返回时,实例中的字段会赋值到调用者分配的内存中,从而影响性能。
  所以,选用值类型还应满足:
  1)类型的实例较小(约16字节或者更小)
  2)类型的实例较大(大于16字节),但不作为方法的实参传递,也不从方法返回。
  值类型的主要优势在于它们不作为对象在托管堆上分配。
  值类型和引用类型的区别:
  1)值类型对象有两种表示形式:未装箱(unboxed)和已装箱(boxed)。引用类型总是处于已装箱形式。
  2)值类型是从System.ValueType派生的。该类型提供了与System.Object定义的相同的方法。然而,System.ValueType重写了Equals方法和GetHashCode方法。由于这个默认实现存在性能问题,所以定义自己的值类型时,应该重写Equals和GetHashCode方法,并提供它们的显示实现。
  3)值类型的所有方法都不能是抽象的,而且所有方法都是隐式密封(sealed)方法。
  4)引用类型的变量包含的是堆上的一个对象的地址。默认情况,在创建一个引用类型的变量时,它被初始化为null,表明引用类型的变量当前不指向一个有效对象。相反,值类型初始化是,所有的成员都会初始化为0。由于值类型的变量不是指针,所以在访问一个值类型时,不会抛出NullReferenceException异常。CLR确实提供了一个特殊的特性,能为值类型 添加"可空"标识。如"int?"
  5)  将一个值类型的变量赋给另一个值类型变量,会执行一次逐字段复制。将引用类型赋给另一个引用类型时,只复制内存地址。
  6)由于为装箱的值类型不再堆上分配,所以一旦定义了该类型的一个实例的方法不再处于活动状态,为他们分配的内存就会被释放。这意味着值类型的实例在其内存被回收时,不会通过Finalize方法接收到一个通知。
 

CLR via C#读书日记一' 引用类型和值类型'的更多相关文章

  1. 《CLR via C#》读书笔记--基元类型、引用类型和值类型

    编程语言的基元类型 编译器直接支持的数据类型称为基元类型.基元类型直接映射到Framework类库中存在的类型.例如:C#中的int直接映射到System.Int32类型.下表给出了C#基元类型与对应 ...

  2. 《CLR via C#》读书笔记(5)基元类型、引用类型和值类型

    5.1 基元类型 编译器直接支持的数据类型称为基元类型(primitive type). 以下4行到吗生成完全相同的IL int a = 0; //最方便的语法 System.Int32 b = 0; ...

  3. 【CLR Via C#】第5章 基元类型、引用类型、值类型

    第二遍看这本书,决定记录一下加深印象. 1,基元类型 什么事基元类型?基元类型是直接映射到FrameWork类库(FCL)中存在的类型,编译器直接支持的数据类型.比如int直接映射到System.In ...

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

    编程语言的基元类型   某些数据类型如此常用,以至于许多编译器允许代码以简化的语法来操纵它们. System.Int32 a = new System.Int32();  // a = 0 a = 1 ...

  5. [CLR via C#]5.2 引用类型和值类型

    原文:[CLR via C#]5.2 引用类型和值类型 CLR支持两种类型:引用类型和值类型. 虽然FCL中大多数都是引用类型,但开发人员用的最多的还是值类型.引用类型总是在托管堆上分配的,C#的ne ...

  6. <NET CLR via c# 第4版>笔记 第5章 基元类型、引用类型和值类型

    5.1 编程语言的基元类型 c#不管在什么操作系统上运行,int始终映射到System.Int32; long始终映射到System.Int64 可以通过checked/unchecked操作符/语句 ...

  7. [CLR via C#]引用类型和值类型

    一.引用类型与值类型的区别 CLR支持两种类型:引用类型和值类型.引用类型总是从托管堆上分配的,C#的new操作符会返回对象的内存地址.使用引用类型时,必须注意到一些性能问题. 1)内存必须从托管堆上 ...

  8. 重温CLR(四)基元类型、引用类型、值类型

    编程语言的基元类型 编译器直接支持的数据类型称为基元类型(primitive type).基元类型直接映射到framework类型(fcl)中存在的类型. 下表列出fcl类型 从另一个角度,可以认为C ...

  9. CLR via C#(02)-基元类型、引用类型、值类型

    http://www.cnblogs.com/qq0827/p/3281150.html 一. 基元类型 编译器能够直接支持的数据类型叫做基元类型.例如int, string等.基元类型和.NET框架 ...

随机推荐

  1. HashSet,LinkedHashSet,TreeSet的区别

    Set接口Set不允许包含相同的元素,如果试图把两个相同元素加入同一个集合中,add方法返回false.Set判断两个对象相同不是使用==运算符,而是根据equals方法.也就是说,只要两个对象用eq ...

  2. DEDECMS点击主栏目默认显示第一个子栏目列表的方法

    本文实例讲述了DEDECMS点击主栏目默认显示第一个子栏目列表的方法.分享给大家供大家参考.具体分析如下: 今天公司有个需求是,点击导航上的父栏目进去默认显示第一个子栏目的列表,以下是具体实现方法,可 ...

  3. PostgresSQL中的限制和级联删除

    摘录自:http://www.mamicode.com/info-detail-879792.html 删除和更新时对应的操作是一样的

  4. Hystrix-命令名称、分组以及线程池划分

    命令名称: 1.1 以继承的方式实现Hystrix命令使用类名作为默认的命令名称,我们也可以在构造函数中通过Setter静态类来设置 1.2 为了保存每个命令分配的Setter分配,你也可以像这样缓存 ...

  5. Express使用进阶:cookie-parser中间件实现深入剖析

    文章导读 cookie-parser是Express的中间件,用来实现cookie的解析,是官方脚手架内置的中间件之一. 它的使用非常简单,但在使用过程中偶尔也会遇到问题.一般都是因为对Express ...

  6. scrapy_随机user-agent

    什么是user-agent? 用户代理,服务器识别用户的操作系统,浏览器类型和渲染引擎,不同浏览器的user-agent是不同的 如何随机更改user-agent? 1. 在setting中添加use ...

  7. python_鸡兔同笼问题

    鸡兔同笼问题 -- 今有雉兔同笼,上有三十五头,下有九十四足,问雉兔各几何? --鸡和兔在一个笼子里,从上面数,有35个头:从下面数,有94只脚.问笼中各有几只鸡和兔 如何逻辑整理? -- 鸡头和兔子 ...

  8. vs Code 运行一个本地WEB服务器

    VS Code作为前端开发工具,值得推荐,用上之后,爱不释手 当我们建立一个全静态的前端项目时,如果需要将整个项目运行起来也非常简单,两步即可: 1.安装npm install -g live-ser ...

  9. Log4j源码解析--框架流程+核心解析

    OK,现在我们来研究Log4j的源码: 这篇博客有参照上善若水的博客,原文出处:http://www.blogjava.net/DLevin/archive/2012/06/28/381667.htm ...

  10. Servlet--继承HttpServlet写自己的Servlet

    前面2篇关注的都是Servlet接口,在实际编码中一般不直接实现这个接口,而是继承HttpServlet类.因为j2e的包里面写好了GenericServlet和HttpServlet类来让我们简化编 ...