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

1. 概述

  本章讨论MS.NET Framework开发人员经常接触到的各种类型。

2. 名词解释

  ① 基元类型:编译器直接支持的数据类型。

  ② 装箱:将一个值类型转换成一个引用类型。

  ③ 拆箱:获取已装箱的对象中的各个字段的地址。

3. 主要内容

  3.1 编程语言的基元类型

    作者建议开发人员开发中使用FCL类型名称,不要使用简化的基元类型名称。原因如下:

    ① 简化名称的映射容易混淆。

    ② 不同的编程语言,规则不一。

    ③ 影响代码的阅读。

    ④ 时间长了容易忘掉多语言环境的支持。

    checked 和 unchecked 基元类型操作。

    两个特殊的类型:System.Decimal 和 System.Numerics.BigInteger .

  3.2 引用类型和值类型

    声明值类型的条件:

    ① 类型具有基元类型的行为。(建议全部字段都标记为readonly)

    ② 类型不需要从其他任何类型继承,也不会派生出其他任何类型。

    ③ 类型的实例较小(<=16b),或者不作为方法的实参传递,也不从方法返回。

    * 用StructLayoutAttribute来控制类型中的字段布局。

  3.3 值类型的装箱和拆箱

    装箱的内部逻辑:

      ① 在托管堆中分配内存。(包括各个字段和两个额外成员-类型对象指针和同步块索引)。

      ② 值类型的字段复制到新分配的堆内存。

      ③ 返回对象的地址。

    拆箱的内部逻辑:

      ① 如果变量为null,抛出 NullReferenceException异常。

      ② 如果类型不符,抛出 InvalidCastException 异常。

      ③ 返回当前对象各个字段的托管堆地址。

    3.3.1 使用接口可以更新已装箱值类型中的字段,但是不建议这么做。

    3.3.2 对象 相等性 和 同一性

      定义自己的类型时,如果要重写Equals,必须确定符合相等性的四个特征:

      ① 自反的。 x.Equals(x)肯定返回true。

      ② 对称的。x.Equals(y)肯定返回与y.Equals(x)相同的值。

      ③ 可传递的。

      ④ 一致的。

      可能还需要:

      ① 让类型实现 System.IEquatable<T>接口的Equals方法。

      ② 重载==和!=操作符方法。

  3.4 对象哈希码

    选择一种算法来计算类型实例的哈希码时,请遵守以下规则:

    ① 这个算法要提供良好的随机分布,使哈希表获得最佳性能。

    ② 可以调用基类的GetHashCode方法并包含它的返回值。不建议调用Object或者ValueType的GetHashCode方法。

    ③ 这个算法应该使用至少一个实例字段。

    ④ 这个算法使用的字段应该是不可变的。

    ⑤ 这个算法应该尽可能快的执行。

    ⑥ 包含相同值的不同对象应返回相同的哈希码。

  3.5 dynamic基元类型

    为了方便开发人员使用反射或者与基本组件通信,C#编译器允许将一个表达式的类型标记为dynamic。

    代码使用dynamic表达式/变量来调用一个成员时,编译器会生成特殊的IL代码来描述所需的操作。这种代码成为payload。

    在运行时,payload代码根据当前由dynamic表达式/变量引用的对象的实际类型来决定具体执行的操作。

4. 总结

  熟练掌握装箱拆箱的机制,有助于写出高效的代码。

时间: 2024-10-01 04:20:10

第五章 基元类型、引用类型 和 值类型的相关文章

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

一.引用类型与值类型的区别 CLR支持两种类型:引用类型和值类型.引用类型总是从托管堆上分配的,C#的new操作符会返回对象的内存地址.使用引用类型时,必须注意到一些性能问题. 1)内存必须从托管堆上分配. 2)堆上分配的每个对象都有一些额外的成员(类型对象指针和同步索引块),这些成员必须初始化. 3)对象中的其他字节(为字段而设)总是设为零. 4)从托管城市化上分配一个对象时,可能强制执行一次垃圾收集操作. 为了提升简单的.常用的类型的性能,CLR提供了名为"值类型"的轻量级类型.值

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

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

5.基元类型、引用类型和值类型

5.1 基远类型 编译器直接支持的数据类型称为基远类型(primitive type). 以下4行到吗生成完全相同的IL int a = 0; //最方便的语法 System.Int32 b = 0; //方便的语法 int c = new int(); //不方便的语法 System.Int32 d = new System.Int32(); //最不方便的语法 C#基元类型与对应的FCL类型 C#中的基元类型 FCL类型 是否与CLS兼容 描述 sbyte System.SByte N 有符

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直接映射

CLR VIA C#: 基元类型、 引用类型 和 值类型

一.基元类型 . 引用类型 和 值类型的区别: 1.基元类型(primitive type):编译器直接支持的数据类型: 基元类型 直接映射到 FCL 中存在的类型. C# 小写是基元类型,例如:string ,  大写是FCL类型,例如String,  基元类型直接映射到FCL类型,所以这两者之间没有区别,一模一样,不用纠结使用哪种方式了. 支持 直接使用FCL类型, 不使用基元类型, Float 对应 Single FCL类型: dynamic 对应  System.Object FCL类型

05 基元类型、引用类型和值类型

基元类型 书上一开头就说了一个概念 编译器直接支持的数据类型称为基元类型(primitive type). 以下是基元类型. C# Primitive Typ FCL Type CLS-Compliant sbyte System.SBte NO byte System.Byte YES short System.Int16 YES ushort System.UInt16 NO int System.Int32 YES uint System.UInt32 NO long System.Int

.NET 中,编译器直接支持的数据类型称为基元类型(primitive type).基元类型和.NET框架类型(FCL)中的类型有直接的映射关系.

.NET 中,编译器直接支持的数据类型称为基元类型(primitive type).基元类型和.NET框架类型(FCL)中的类型有直接的映射关系. The primitive types are Boolean, Byte, SByte, Int16, UInt16, Int32, UInt32, Int64, UInt64, IntPtr, UIntPtr, Char, Double, and Single. https://msdn.microsoft.com/zh-cn/library/s

12-12面向对象--引用类型与值类型、命名空间与类库

五.引用类型与值类型 .NET将变量的类型分为值类型与引用类型.例如int和float之类的变量属于值类型,而“类“类型的变量属于引用类型. 值类型与引用类型的变量在使用上是有区别的. 值类型的变量在一定义之后就可以马上使用. 引用类型的变量定义之后,还必须用new关键字创建对象后才可以使用. 如上图,创建两个新的对象时,直接比较两个对象是否相等,返回的bool值为false.因为两个对象都是新开辟的一个空间变量,彼此没有任何关系,所有是不相等的. 如图2:1)创建的第一个新的对象ss1,给ss

引用类型和值类型

CLR支持两种类型:引用类型和值类型. 虽然FCL中大多数都是引用类型,但开发人员用的最多的还是值类型.引用类型总是在托管堆上分配的,C#的new操作符会返回对象的内存地址——也就是指向对象数据的内存地址. 使用引用类型必须注意到一些性能问题,首先考虑一下事实: 1)内存必须从托管堆上分配. 2)对上分配的每个对象都有一些额外的成员(比如前面提到过得"类型对象指针"和"同步块索引"),这些成员必须初始化. 3)对象中的其他字节(为字段而设)总是设为零. 4)从托管堆