CLR Via 读书笔记——第四章 类型基础

第四章 类型基础

4.1 所有类型都从System.Object中派生

CLR在运行时,要求每个类型都从System.Object类型派生,每个类型都保证了一组最基本的方法:

公共方法说明
Equals如果两个对象具有相同的值,就返回True
GetHashCode返回对象的值的哈希码
ToString默然返回类型的完整名称
GetType返回从Type派生的一个类型的实例,指出调用GetType的那个对象是什么类型

从System.Object派生的类型能访问MemberwiseClone和Finalize两个受保护方法

4.2 New操作符做的事情

CLR要求所有的对象都用New操作符来创建,以下是new操作符所做的事情:

  1. 计算类型及其所有基类型(一直到System.Object,虽然它没有定义自己的实例字段)中定义的所有实例字段需要的字节数。
  2. 从托管堆中分配类型要求的字节数,从而分配对象的内存,分配记的所有字节都设为零(0)。
  3. 初始化对象的"类型对象指针"和"同步块索引"成员。
  4. 调用类型的实力构造器,传递在new调用中制定的实参。

4.3 类型转换

CLR允许将对象转换为它的(实际)类型或者它的任何基类型,因为向基类型的转换被认为是一种安全的隐式转换。然而,将对象转换为它的某个派生类型时,只能进行显式转换,因为这种转换可能在运行时失败。

//该类型隐式派生自System.Object
internal class Employee {
...
}
public sealed class Program{
public static void Main(){
//不需要转型,因为new返回一个Employee对象,
//而Object是Employee的基类型
Object o = new Employee ();
//需要转型,因为Employee派生自Object。
//其他语言(比如Visual Basic)也许不要求像
//这样进行强制类型转换
Employee e = (Employee) o;
  }
}

在运行时,CLR检查转型操作,发现异常会禁止转型,并抛出InvalidCastException异常。

4.4 使用C#的is和as操作符来转型

is检查对象是否兼容于指定类型,返回Boolean值true或false。is操作符永远不抛出异常。

Object o = new Object();
Boolean bl = (o is Object);
Boolean b2 = (o is Employee);
//bl为true.
//b2为false.

如果对象引用null,is操作符总是返回false,因为没有可检查其类型的对象。
is操作符通常像下面这样使用:

if (o is Employee) {
Employee e = (Employee) o;
//在if语句剩余的部分中使用e
}

在上述代码中,CLR实际检查两次对象类型。is操作符首先核实是否兼容于Employee
类型。如果是,在if语句内部转型时,CLR再次核实o是否引用一个Employee。CLR的
类型检查增强了安全性,但无疑会对性能造成一定影响。这是因为CLR首先必须判断变量(o)引用的对象的实际类型。然后,CLR必须遍历继承层次结构,用每个基类型去核对指定的类型(Employee)。由于这是一个相当常用的编程模式,所以C#专门提供了as操作符,目的就是简化这种代码的写法,同时提升其性能。

Employee e = o as Employee;
if(e!=null){
//在if语句中使用e
}

在这段代码中,CLR核实o是否兼容于Employee类型:如果是,如果是,as返回对同一个对象的非null引用。如果o不兼容于Employee类型,as返回null。注意,as操作符造成CLR只校验一次对象类型。if语句只检查e是否为null;这个检查的速度比校验对象的类型快得多。
as操作符的工作方式与强制类型转换一样,只是它永远不抛出异常–相反,如果对象不
能转型,结果就是null。所以,正确做法是检查最终生成的引用月是否为null。企图直接使
用最终生成的引用会抛出System.NullReferenceException异常。

Object o = new Object();
Employee e = o as Employee;
//新建Object对象
//将o转型为Employee
//上述转型会失败,不抛出异常,但e被设为null

4.5 命名空间和程序集

命名空间对相关的类型进行逻辑分组,开发人员可通过命名名空间方便地定位类型。例如,System.Text命名空间定义了执行字符串处理的类型,而System.I命名空间定义了执行I/O操作的类型。以下代码构造一个System.IO.FileStream对象和一个System.Text.StringBuilder对象:

public sealed class Program {
public static void Main(){
System.IO.FileStream fs = new System.IO.FileSStream(...)System.Text.StringBuilder sb = new System. Text.StringBuilder()}

C#编译器通过using指令提供这个机制。以下代码和前面的例子完全一致:

using System. IO;
using System. Text;
//尝试附加"System.IO."前缀
//尝试附加"System.Text."前缀
public sealed class Program {
public static void Main()
FileStream fs = new FileStream(...);
StringBuilder sb = new StringBuilder();
  }
}

要在库中设计由第三方使用的类型,应该在专门的命名空间中定义这些类型:

namespace CompanyName
public sealed class A {   // TypeDef: CompanyName.A
}
namespace X{
public sealed class B {   //TypeDef:CompanyName.X.B
}  

类定义右侧的注释指出编译器在类型定义元数据表中添加的实际类型名称:这是CLR看到的实际类型名称。在C#中,namespace指令的作用只是告诉编译器为源代码中出现的每个类型名称附加命名空间名称前缀,让程序员少打一些字。

命名空间和程序集不一定相关,同一个命名空间中的类型可能在不同的程序集中实现,同一个程序集也可能包好不同命名空间中的类型。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值