zoukankan      html  css  js  c++  java
  • 第五章 基元类型引用类型和值类型

    1.  溢出 Checked UnChecked

    checked打开时,如果发生溢出会抛出异常,Unchecked则不会排除异常。

    编译器默认是关闭溢出检查的Unchecked。若要打开溢出检查,使用/Checked+.在VS的项目属性中也可设置开启与否。

    也可以给一段代码添加这样的标记。如果这段代码中调用了另外一个方法,这个方法是不受这个标记控制的。

    System.Decimal不是基元类型,Checked和Unchecked标记对其无效,如果发生溢出是肯定会抛出异常的。

    System。Numberics.BigInteger内部使用UInt32表示任意大的整数,没有上限和下限,永远不会溢出,可能会有内存溢出。

    2. 引用类型和值类型

    引用类型:

    指针.多个引用可指向指向同一个实例.复制时只复制引用.

    内存必须从托管堆上分配,包含的值类型字段也在堆上。

    堆上分配的每个对象都有一些额外成员,都需要初始化。

    对象中的其他字节(为字段而设)总是设为0.

    从托管堆上分配对象时,可能强制执行一次垃圾回收。

    继承自Object.

    值类型:

    直接存储的是值本身。复制时复制成员.相互独立.

    轻量级类型,不受垃圾回收器的控制。减少了托管堆的压力,减少了进行垃圾回收的次数。

    所有的值类型都是隐式密封的,不能被继承。

    继承自System。ValueType

    C#会确保值类型中的所有字段都初始化为0.但是如果只声明一个值类型而不赋值,编译器会提示使用了可能未赋值的字段.

    值类型在参数传递时,传入和传出方法,都是复制的,也就是说在方法内的修改不会影响方法外.所以,在使用值类型传递参数时,要考虑参数的大小,涉及性能问题.

    值类型的Equal和GetHashCode存在性能问题,如果自定义值类型,应该重写这两个方法.

    值类型有未装箱和已装箱两种形式,引用类型总是处于已装箱.

    CLR控制类型中的字段布局

    为了提高性能,CLR会按照一种方式排列类型中的字段.System.Runtime.InteropSercives.StruceLayoutAttribute可以设置排列布局.

     

    3. 装箱和拆箱 在实际的代码书写中,应考虑装箱和拆箱操作.

    装箱时发生的事情:

    1. 在托管堆中分配内存,包括值类型的字段和两个额外的成员.

    2.  值类型的字段复制到新分配的堆内存.

    3. 返回对象地址.

    装箱之后,新对象和原来的值类型之间是没有联系的了.新对象的是由GC回收的.这样,已装箱的值类型的生存期超过了未装箱的值类型的生存期.

    System.Collections.Generic.List<T>       System.Collections.ArrayList ArrayList.Add(object)前者的效率和性能要高于后者,中间少了许多的装箱和拆箱操作.

    拆箱的时候有两个步骤:生成两条IL指令.

    1. 拆箱(获取已装箱实例中的字段的地址)

    2. 字段复制.

    拆箱不是直接将装箱过程倒过来,代价要低许多.

    拆箱的时候要先将其转型为未装箱时的值类型.如:

    Int32 x = 9;
    object y = x;
    Int16 z = (Int16)y;

    这样是会抛出错误的.正确的做法是: Int16 z = (Int16)(Int32)y;

    大多数方法进行重载唯一的目的就是减少常用值类型的装箱次数.

    由于没装修的值类型没有同步块索引,所以不能使用Monitor类型的各种方法(或者C#的lock)让多个线程对这个实例同步访问.

    调用一个值类型虚方法时,值类型不会被装箱.如Equals,GetHashCode,ToString.    调用一个非虚的继承的方法时(GetType)需要进行装箱操作.因为这些方法是Object(GetType)定义的.

    4. 对象相等性和同一性

    同一性是指看两个引用是否指向同一个对象.Object.ReferenceEquals能够达到此效果.

    Object.Equals方法实际实现的是同一性,而不是相等性.

    System.ValueType重写了Object的Equals方法,进行的是相等性检查,不是同一性检查.

    HashCode

    自定义的类型在重写Equals的方法时候,应该同时重写GetHashCode方法.因为哈希码用在Hashtable和Dictionary中用于索引项,要求两个对象相等,必须要具有相同的哈希吗.Dictionary是先获得Key的哈希码值,根据这个值确定存储位置(哈希桶),在根据Key查找的时候,先获得Key 的哈希码,根据哈希码查找(先找到哈希桶,在桶内找到哈希码相同的对象).如果修改了Key对象,集合就再也找不到这个对象了,所以应当先删除,再重新添加.

    哈希代码是一个用于在相等测试过程中标识对象的数值。 它还可以作为一个集合中的对象的索引。

    .NET Framework 不保证 GetHashCode 方法的默认实现以及它所返回的值在不同版本的 .NET Framework 中是相同的。 因此,在进行哈希运算时,该方法的默认实现不得用作唯一对象标识符。

    值类型基类的GetHashCode方法则使用了反射,效率也比较低。Object的GetHashCode方法返回的哈希码能作为全局唯一的标识,只是在对象的生命周期中不会变,被垃圾回收之后,这个哈希吗可能会被分给别的对象.如果一个类型重写了GetHashCode方法,这个ID就不具有唯一性了,可以使用System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode方法获取一个对象唯一性ID,即使重写了GetHashCode方法.

    5. dynamic基元类型

    代码使用dynamic变量或表达式调用一个成员时,编译器会生成特殊的IL描述这样的操作,称为payload(有效载荷).根据实际类型决定具体执行的操作.

    dynamic类型可以隐式转换为其他类型,Object不可以,如果类型不兼容,会抛出InvalidCastException.

    dynamic和var是不一样的.var是让编译器根据表达式推断具体的数据类型.dynamic是运行的时候决定类型.

    COM,如Excel操作时,excel.Cell[1,1]就是dynamic类型excel.Cell[1,1].value和((Range)excel.Cell[1,1]).Value效果是一样的.

    dynamic有时候可以简化反射的代码,直接调用方法的名字,而不用通过反射找到指定名字的方法,然后调用.

    但是dynamic类型是需要加载额外的dll的,额外开销是不容忽视的.所以用的时候需要综合考虑.

  • 相关阅读:
    概率算法实现八皇后问题-cpp
    交互式多媒体图书平台的设计
    Map容器
    序列容器和容器适配器
    ubuntu18.04 基于VSCode的C++环境搭建
    工程化编程实战callback接口学习
    stl_string
    通过filebeat的modules搜集nginx日志
    gitlab-runner安装配置
    EFK搜集MySQL慢日志
  • 原文地址:https://www.cnblogs.com/zhangliming/p/3454806.html
Copyright © 2011-2022 走看看