zoukankan      html  css  js  c++  java
  • 跟小静读CLR via C#(02)基元类型、引用类型、值类型

    跟小静读CLR via C#(02)-基元类型、引用类型、值类型

    一、 基元类型

    编译器能够直接支持的数据类型叫做基元类型。例如int, string等。基元类型和.NET框架类库FCL存在着直接的映射关系。

    string和String

    面试的时候曾经被问到过这个问题,C#中的基元类型string实际上对应了System.String(FCL)类型,所以两者使用的时候没有什么不同。

    类型转换

    编译器能够在基元类型之间进行显式或隐式转换。如果转换是安全的,也就是转换过程不会造成数据丢失,则可以直接采用隐式转换。如果是不安全的,则必须采用显式转换。

    Int32 a=5;

    Int64 b=a;

    Int32 c=(Int32)b;

    二、 引用类型和值类型

    引用类型和值类型的区别:

    引用类型

    值类型

    从托管堆中分配

    从线程的堆栈中分配

    对象考虑垃圾回收机制

    不考虑垃圾回收机制

    所有类都是引用

    结构或枚举都是值类型

    继承自System.ValueType

    只有装箱形式

    有两种形式:装箱和未装箱

    可以继承和派生

    不能作为基类,不能有虚方法

    引用类型变量初始化时默认为null

    初始化时默认为0值

    复制时只拷贝内存地址

    复制时“字段对字段”的拷贝

       

    结构体直接继承自System.ValueType;而枚举直接继承自System.Enum, Enum类又直接继承自System.ValueType。

    下面通过例子看一下他们的区别:

    首先定义类和结构体:

    class SomeRef { public Int32 x; }

    struct SomeVal { public Int32 x; }

    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; //只把指针复制给了r2

    SomeVal v2 = v1; // 在栈上分配空间,并且将变量内容进行复制

    r1.x = 8; // r1指向(也是r2指向)的内容修改

    v1.x = 9; // 只修改v1内容,v2内容不会受影响

    Console.WriteLine(r1.x); // "8"

    Console.WriteLine(r2.x); // "8"

    Console.WriteLine(v1.x); // "9"

    Console.WriteLine(v2.x); // "5"

    看看下图的内存分配情况,就一目了然了。

    image

    三、 值类型的装箱与拆箱

    1. 装箱过程?

    装箱:将值类型转换为引用类型。当我们把值类型参数传递给需要引用类型参数的方法时,会自动进行装箱操作。过程如下:

    • 从托管堆为要生成的引用类型分配大小。大小为:值类型实例本身的大小+额外空间(方法表指针和SyncBlockIndex)。
    • 将值类型字段拷贝到刚刚分配的内存中。
    • 返回托管堆中新分配内存的地址。也就是指向对象的引用。

    2. 拆箱过程?

    拆箱:获取指向对象中包含的值类型部分的指针。一般拆箱之后会进行字段拷贝操作,两个操作加起来才是真正与装箱互反的操作。过程如下:

    • 如果引用为Null,则抛出NullReferenceException异常。
    • 如果引用对象不是一个期望值类型的已装箱对象,会抛出InvalidCastException异常。
    • 返回一个指向包含在已装箱对象中值类型部分的指针。

    3. 实例

    • 拆箱的转型结果必须是它原来未装箱时的类型。

    public static void Main() {

    Int32 x = 5;

    Object o = x; // 装箱

    Int16 y = (Int16) o; // 拆箱,抛出InvalidCastException异常

    }

           修正:Int16 z=(Int16)(Int32)o;//拆箱成功

    • 这段代码进行了几次装箱?

    public static void Main() {

    Int32 v = 5; // 创建值变量

    Object o = v; // 装箱

    v = 123; // Changes the unboxed value to 123

    Console.WriteLine(v + ", " + (Int32) o); // Displays "123, 5" ,装箱两次

    }

    上面的代码进行了3次装箱,最后一行中v被装箱为引用类型,o首先被拆箱然后再装箱为引用类型。

    我们看一下它的IL代码:

    QQ截图20111021101944

    字符串连接实际上调用的是Concat方法,该方法的参数要求是object,因此各参数都要转换成object类型。

    优化:

    Console.WriteLine(v.ToString()+”,”+o);//装箱0次

    我们要尽量少的进行值类型的装箱拆箱操作,以提高程序性能。

  • 相关阅读:
    二、Cocos2dx概念介绍(游戏开发中不同的坐标系,cocos2dx锚点)
    (2)入门指南——(7)添加jquery代码(Adding our jQuery code)
    Citrix 服务器虚拟化之三十一 XenApp 6.5负载均衡
    CSS——inline-block属性
    VMware Workstation(虚拟机软件) V10.0 简体中文版可以安装了
    [.NET MVC4 入门系列01]Helloworld MVC 4 第一个MVC4程序
    ElasticSearch NEST笔记
    什么是REST API?
    ArrayList与List<T>笔记
    C# Socket SSL通讯笔记
  • 原文地址:https://www.cnblogs.com/janes/p/2097540.html
Copyright © 2011-2022 走看看