类型与通用语言运行时:类型转换

CLR 最重要的一个特性就是类型安全。CLR 在运行时总能知道一个对象的类型。我们也可以用过调用 GetType方法来得到对象的准确类型。因为该方法是一个虚方法。所以我们不可能利用它来篡改一个类型的信息。

例如:我们不可能重写 Employee 类的 GetType方法使之返回一个SpaceShuttle类型。

作为开发人员,我们经常会遇到需要将一个对象转换为其他类型。 CLR 允许我们将对象转换为其原来的类型或者它的任何一个基类型。各个编程语言自己决定如何提供这些转型操作。

例如,C# 不需要任何特殊的语法就可以将对象转换为其任何一个基类型,因为转换为基类型被认为是安全的隐式操作。然而在其将对象转换为它的任何派生类型时,C# 要求进行显式转换,因为这样的转型有可能会失败

下面的代码将演示如何将对象转化为它的基类和派生类型:

 1 //该类型隐式继承自 System.Object
 2 class Employee{
 3      ...   
 4 }
 5 
 6 class App{
 7     public static void Main(){
 8  
 9     //这里不需要转型,因为 new 返回的是一个 Employee 对象
10     //而 Object 又是   Employee 的基类型
11      Object  o = new Object ();
12 
13     //这里需要转型,因为 Employee  继承自 Object 
14     //其它一些语言可能不需要转型
15    Employee   e = ( Employee )o;
16     
17 }
18 }
View Code

 上面的例子显示了要使代码通过编译我们需要做的一些事情,下面解释运行时发生的行为,在运行时CLR会检查转型操作以确保总是将对象转型它的实际类型、或者它的任何基类型。

例如,下面的代码虽然能够通过编译,但是运行时,却会抛出 InvalidCastException 异常:

 1 class Manager:Employee {
 2 
 3   ...
 4 
 5 }
 6 
 7 
 8 class App{
 9 
10   public static void Main() {
11 
12     //构造一个 Manager 对象并将其传递给 PromoteEmployee
13 
14     //一个 Manager " Is-A" Object.PromoteEmployee 将正常运行
15 
16     Manager  m = new Manager();
17 
18     PromoteEmployee(m);
19 
20 
21     //构造一个DateTime 对象并将其传递给 PromoteEmployee
22 
23     //由于 DateTime 并非继承自 Employee,因此 PromoteEmployee 会抛出一个 System.InvalidCastException 异常
24 
25     DateTime newYears = new DateTime(2016,1,1);
26 
27     PromoteEmployee(newYears);
28 
29 }
30 
31   public static void PromoteEmployee(Object o) {
32 
33     //这里,编译器并不知道对象 o 引用的实际类型,所以编译器允许代码通过编译
34 
35     //当代码运行时,CLR会获知 o 的引用类型(每当进行转型操作时),
36 
37     //并且会检查对象的类型是否是Employee,或者是任何继承自Employee的类型
38 
39     Employee e = (Employee) o;
40 
41     ...
42 
43 }
44 
45 }
View Code

如果 CLR 允许上例中的转型操作,代码则会失去类型安全,并且结果也将变得不可预期——包括应用程序可能崩溃、以及由于类型欺骗引起的安全漏洞。

类型欺骗是很多安全漏洞的原因,它会极大地危机应用程序的 稳定性和健壮性。

 

* PromoteEmployee()的正确定义应该是接受一个 Employee对象,而不是 Object 对象作为参数,本文使用 Object 的目的仅仅只是为了演示编译器和 CLR 是如何处理转型操作的。

 

使用 is 和 as 操作符转型(C#)

检查对象是否和给定的类型兼容,并返回判断结果:true 或者 false 。另外,is 操作符永远不会抛出异常。

看下面的代码:

  System.Object o = new System.Object ();

  System.Boolean b1 = ( o is System.Object);  // b1 为true

  System.Boolean b1 = ( o is Employee);    // b1 为false

如果对象引用为null,那么 is 操作符总是返回 false,因为没有对象可以用来检查其类型。下面演示 is 操作符典型的使用方法:

  if( o is Employee){

    Employee e = (Employee) o;

    //在 "if" 语句中使用 e

  }

  在上面的代码中,CLR实际上对对象的类型检查了两次:is操作符首先检查 o 所引用的对象是否和 Employee 类型兼容。如果兼容,在 if 语句内,

CLR 在执行转型时又会检查 o 是否为一个 Employee 引用。由于这种编程范式十分常见,C#便为我们提供了一种新的转型方式,即 as 操作符,它可

以在简化代码的同时提高性能,例如:

  Employee e = o as Employee;

  if(e != null) {

    //在 "if" 语句中使用 e

  }

 

在上面的代码中,CLR 会检查 o 所引用的对象是否和 Employee 类型兼容,如果兼容,as 返回一个指向同一个对象的非空指针。如果不兼容,as 返回 null.

注意,在 as 操作符执行过程中,CLR只检查了一次对象的类型。紧接着的 if 语句只需要检查 e 是否为null 就可以

  

 

posted @ 2016-08-19 11:42  _小丫头片子  阅读(183)  评论(0)    收藏  举报