zoukankan      html  css  js  c++  java
  • [CLR via C#]5.2 引用类型和值类型

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

      虽然FCL中大多数都是引用类型,但开发人员用的最多的还是值类型。引用类型总是在托管堆上分配的,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 = 5; 
        v1.x = 5; 
        Console.WriteLine(r1.x);                //5 
        Console.WriteLine(v1.x);                //5 
     
        SomeRef r2 = r1; 
        SomeVal v2 = v1; 
        r1.x = 8; 
        v1.x = 9; 
        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方法接收到一个通知。
     
     
     
     
  • 相关阅读:
    mybatis的mapper文件中的一个标签是否可以写多条SQL语句?是否存在事物?
    Filebeat快速入门
    JVM第一弹
    eclipse git 解决冲突 解决 mergetool 不能使用问题
    使用gitee作为图床 编写markdown自动上传文件
    JVM面试题 第一弹
    学习了JsonSchema,我自定义了一个校验代码
    JsonSchema用法
    Docker简介与安装
    Docker第二弹之常用命令
  • 原文地址:https://www.cnblogs.com/zxj159/p/3531260.html
Copyright © 2011-2022 走看看