23.05.14 《CLR via C#》 笔记4

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

  1. 基元类型:int、float等编译器直接支持(不需要显式new)的类型
  2. c#中,不论32/64操作系统,int总是Int32,long总是Int64
  3. 基元类型之间可以显示或隐式的类型转换(比如int值直接赋值给long)
  4. check和uncheck操作:基元类型的算术运算,可能出现溢出
    1. 编译时,使用/checked命令来决定溢出时像c语言一样回滚还是像basic一样抛出异常
    2. 使用checked/unchecked操作符或语句指定
  5. 作者推荐使用无符号类型,来让编译器检测更多溢出错误,以及减少强制类型转换的次数
  6. 使用引用类型时需要注意性能问题:
    1. 内存必须从托管堆分配
    2. 堆上分配的每个对象都有额外成员,额外成员都要初始化
    3. 对象中的其它字节(字段)总是设为0
    4. 从托管堆中分配对象时,可能强行执行一次垃圾回收
  7. 值类型一般在线程栈上分配,包含实例本身的字段,操作值类型不需要提领指针,不受垃圾回收器控制
  8. 类都是引用类型;结构和枚举都是值类型(所有结构都派生自ValueType,ValueType派生自Object,所有枚举派生自Enum,Enum派生自ValueType)
  9. 但是对于非托管C/C++,静态内存分配在编译时分配到栈上,动态内存分配使用new运算符分配在堆上,与它是引用类型还是值类型无关,和声明这个类型的代码无关
  10. 值类型可以实现接口,但是不能作为基类
  11. 就算是使用new操作符来实例化值类型,它仍然会在栈上分配,但是会被认为已经被初始化为0(不会报错:可能使用了未被赋值的字段)
  12. 要把类型声明成值类型,应当满足以下条件(为什么建议用class而不是struct)
    1. 类型有基元类型的行为,没有成员会修改类型的实例字段,对值类型,作者建议把全部成员标记为readonly
    2. 类型不从其它类型继承,也不派生出其它类型
    3. 类型的实例较小(<16字节)
    4. 类型的实例较大,但是不作为方法实参传递,也不从方法返回
  13. 值类型和引用类型的区别
    1. 值类型有已装箱和未装箱两种形式,引用类型只有已装箱形式
    2. ValueType重写了Equals(所有字段相等即返回true)和GetHashCode(考虑所有字段的值)
    3. 值类型的所有方法都不能是抽象的(因为不能派生出其它类)
    4. 引用类型初建变量时初始化为null;值类型所有字段初始化为0,访问未初始化的值类型不能抛出null异常
    5. 值类型赋值会逐字段复制;引用类型只复制地址
    6. 一旦定义了值类型的方法不再活动,该值类型实例会被立刻释放,不会等待垃圾回收
  14. 值类型转换成引用类型(装箱机制)struct->object
    1. 在托管堆分配内存(所有字段的内存+额外成员)
    2. 值类型字段值复制到新分配的内存
    3. 返回对象地址(引用)
  15. 拆箱机制
    1. 如果引用为null,抛出异常;如果引用对象不是所需类型的已装箱实例,抛出异常
    2. 获取对象各字段地址(这个过程是拆箱)
    3. 将字段包含的值从堆复制到栈上的值类型实例中(这个过程是字段复制)
  16. 现在使用的泛型集合类,操作集合时不再需要装箱/拆箱(List等)
  17. 未装箱值类型可以调用继承或重写的虚方法;如果要调用需方法在基类的实现,需要装箱;如果要调用非虚、继承的方法时,需要装箱;转换成某个接口时,需要装箱
  18. C# 不允许更改已装箱值类型中的字段
    1. 一个值类型p已装箱成object o, 此时不允许使用o更改字段(首先o拆箱成值类型,储存在【临时变量】中,只会修改临时变量的值)
    2. 使用接口可以修改值类型的字段(值类型p装箱成object o,o转换成接口时,不需要再装箱,就可以更改字段的值)
  19. 对象的相等性和同一性
    1. Object的Equals实现的是同一性,即this和实参变量是不是引用同一个对象
    2. 由于类型可以重载Equals,所以不能用它测试同一性,而应该使用ReferenceEquals
    3. 值类型重载了Equals方法,执行的是相等性检查(每个实例字段的值相等)
    4. 重写Equals方法时要注意1、实现IEquatable接口的Equals方法2、重载==、!=运算符
  20. 对象哈希码GetHashCode
    1. 当重写Equals方法时,应同时重写GetHashCode方法(在哈希表、字典等集合的实现中,要求两个变量有相同的哈希码才可以相等,所以必须确保相等性算法与哈希码算法一致)
    2. 实现哈希算法时应注意
      1. 有良好的随机分布
      2. 可以调用基类的GetHashCode,但不要使用Object或ValueType的GetHashCode实现(两者性能差)
      3. 算法至少使用一个实例字段
      4. 算法使用的字段在对象生存期值不改变
      5. 执行速度快
      6. 包含相同值的不同对象返回相同的哈希码
  21. dynamic基元类型
    1. c#可以将表达式的类型标记为dynamic,可以将表达式的结果存在标记为dynamic的变量中;
    2. 编译器会自动将dynamic转换为Object
    3. 实参可以是dynamic;任何表达式可以隐式转换为dynamic;从dynamic转换为其它类型时CLR将验证对象类型是否兼容
    4. 使用dynamic会造成额外的性能开销(考虑使用反射方法或进行手动类型转换)

你可能感兴趣的:(c#,笔记)