(四)类型与通用语言运行时

一、类型基础

1. 所有类型的基类型

    所有类型的基类型都是System.Object

   公有方法:Equal(),GetHashCode(),ToString(),GetType()

   受保护方法: MemberWise Clone(非虚方法,创建新的类型实力,并将其字段设置为和this对象的字段相同,返回对象引用); Finalize(虚方法,垃圾回收)

   new 操作执行的工作:

         从托管堆上分配对象所需字节的内存空间;

          初始化对象的附加成员(overhead members),每个对象都有两个附加成员,一个是指向类型方法表的指针,一个是 SyncBlockIndex(线程同步控制,垃圾回收标志)

          调用类型构造函数(大多数编译器都要求调用基类的构成函数,但是CRL没有这样的要求) 

         注意:CRL中没有使用delete 来释放空间,不能进行显式释放对象占用的内存)

2. 类型转换

       CRL最重要的一个特性就是类型安全,CRL运行时总能知道对象的类型,通过GetType能够精确知道类型,该方法不是虚方法,不可以呗篡改。

        转换成基类型被认为是安全的隐式操作。

       转换成其他派生类则需要显式操作,因为这样操作可能失败。

         is: 检查对象是否兼容,返回true or false,不会抛出异常(检查两次类型安全);

        as :如果兼容 返回指向对象的飞空指针,如果不检查返回null(检查一次,高效);

3.命名空间与程序集

      命名空间允许我们队相关类型进行逻辑上的组织,方便定位类型。using

     使用别名 using A = AAA.A  二义性。

      命名空间应该要注意唯一性。

二、基元类型,引用类型,值类型

1.基元类型

编译器直接支持的类型是基元类型,如int;

        Checked :在需要显现提出溢出溢出的使用;Checked{  可能溢出的代码}

        unchecked:不需要上,则使用;

2.引用类型与值类型

      引用类型性能考虑:

             内存必须从托管堆分配

             每个从托管堆中分配的对象都有一些与之关联的额外附加成员必须被初始化

             从托管堆中分配对象可能会导致执行垃圾收集

       类是应用类型

       枚举、结构是值类型 (值类型继承于System.ValueType,这个类型也是继承于System.Object) System.Booleam, System.Decimal, System.TimeSpan都是结构

       值类型 分配到线程堆栈上,复制(先在堆栈中分配,然后拷贝);

       引用类型 分配在托管堆上,复制仅拷贝指针,地址不再分配。

       考虑使用值类型的情况:

              类型行为类似基元类型 

              不需要继承任务其他类型

              不被其他类型继承

              不被 当做参数频率传递,返回值,用于集合中,拷贝分配内存,影响性能

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

            值类型对象有两种表示:一种是装箱,一种是未装箱;而引用类型对象只有装箱形式;

            值类型都是基础System.ValueType(System.ValueType 是继承System.Object),当定义我们的值类型是,应该重写equal(),GetHashCode();

            值类型不能被继承和继承其他派生类;不允许抽象方法和新的虚方法,所有方法都是隐藏的密封的(Sealed);

            引用类型变量包含着对象在托管堆中的内存地址,默认情况,引用对象呗创建是,初始化为null;而值类型变量总是包含一个符合他的类型值,所有对象初始化为0;不会抛出 NullReferenceException异常;

            赋值时,值类型会进行字段对字段的拷贝,而引用类型只拷贝内存地址。引用变量都是指向了托管堆中同一个对象,对引用变量操作,势必会影响其他变量;而值类型变量都有一份自己的对象数据拷贝,对值类型变量操作,不会影响其他值类型变量。

            未装箱不在托管堆上分配,一旦该类型实例的方法不处在活动状态,为他分配的内存地址会立即释放,这意味值类型实例在释放内存时,不会受到任何通知。

3.值类型的拆箱装箱

             装箱:用来将值类型转换为引用类型的机制;

             装箱操作有一下步骤:

                     从托管堆上为新生产的应用对象分配内存空间(值类型对象大小+额外成员(方发表引用, SynBlockIndex);

                     将值类型对象拷贝到新分配的内存中;

                     返回托管堆中新分配的地址,即引用。值类型对象就变成了引用对象。值类型对象释放内存;

            拆箱:将  引用类型转换为值类型;

             拆箱步骤:

                     如果该引用为null,抛出NullReferenceException;

                     如果该引用不是一个被期望的值类型的已装箱对象,抛出 InvalidCastException;

                     一个指向包含已装箱的值类型部分的指针被返回,该指针对引用对象的额外成员一无所知,实际上是已装箱值类型对象中的未装箱部分。

            拆箱操作代价要比装箱小很多,拆箱与操作不是真正的互反操作,需要加上字段拷贝才是。通常情况拆箱之后,紧接着就是字段拷贝。

           注意:C++ 的拆箱操作是直接就返回了已装箱的值类型对象部分,不必进行字段拷贝了。

            值类型转换成接口,也需要装箱。

三、通用对象操作

1.对象等值线与唯一性

       System.Object 提供的虚方法  Equal方法比较简单:判断是否指向同一对象;

       重写Equal方法,四原则:

            自反(X.Equals(X)  返回true); 对称(X.Equals(Y) 与Y.Equals(X) 值一致); 可传递;前后一致。

2.对象的散列码

      一个类必须同时重写Equals 和GetHashCode ,是因为System. Collections.  HashTable类型的实现要求任何两个相等的对象必须具有相同的散列值。

     重写原则:

             算法应该具有良好的随机分布;              

3.对象克隆

          克隆继承 接口ICloneable;

           浅拷贝:当对象的值类型对象被拷贝时,字段引用的对象不被拷贝;

           深拷贝:对对象实例中的引用对象也进行拷贝。  

你可能感兴趣的:((四)类型与通用语言运行时)