4.2 类型转换
2011-12-08 10:33 iRead 阅读(298) 评论(0) 收藏 举报CLR最重要的特征之一就是类型安全性。在运行时,CLR总是知道一个对象是什么类型。调用GetType方法,总是知道一个对象确切的类型是什么。由于这个方法是非虚方法,所以一个类型不可能伪装成另一个类型。例如:Employee类型不能重写GetType方法,并返回一个SuperHero类型。
开发人员经常需要将一个对象从一种类型转换为其他各种类型。CLR允许将一个对象转换为它的(实际)类型或者它的任何基类型。每种编程语言都规定了开发人员具体如何进行这种转型操作。例如,C#不要求任何特殊语法即可将一个对象转换为它的任何基类型,因为向基类型的转换被认为是一种安全的隐式转换。然而,将对象转换为它的某个派生类型时,C#要求开发人员只能进行显式转换,因为这样的转换可能在运行时失败。以下代码演示了向基类型和派生类型的转换:
//该类型隐式派生自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异常。
internal class Employee{
…
}
internal class Manager : Employee{
…
}
public sealed class Program{
public static void Main(){
//构造一个Manager对象,并把它传给PromoteEmployee,Manager“属于”(IS-A)Object,
//所以PromoteEmployee能成功运行
Manager m = new Manager();
PromoteEmployee(e);
//构造一个DateTime对象,并把它传给PromoteEmployee。
//DateTime不是从Employee派生的,所以PromoteEmployee
//抛出一个System.InvalidCastException异常
DateTime newYears = new DateTime(2010,1,1);
PromoteEmployee(newYears);
}
public static void PromoteEmployee(Object o){
//编译器在编译时无准确地获知对象o引用的是什么类型。所以,编译器运行代码通过编译。
//但在运行时,CLR知道了o引用的是什么类型(在每次执行转型的时候),
//所以它会核实对象的类型是不是Employee或者从Employee派生的任何类型
Employee e = (Employee) o;
…
}
}
在Main方法中,会构造一个Manager对象,并将其传给PromoteEmployee。这些代码能成功编译并运行,因为Manager最终是从Object派生的,而PromoteEmployee期待的正是一个Object。在PromoteEmployee内部,CLR核实o引用的是一个Employee对象,或者是从Employee派生的一个类型的对象。由于Manager是从Employee派生的,所以CLR执行类型转换,运行PromoteEmployee继续执行。
PromoteEmployee返回之后,Main继续构造一个DateTime对象,并将其传给PromoteEmployee。同样地,DateTime是从Object派生的,所以编译器会顺利编译调用PromoteEmployee的代码。但在PromoteEmployee内部,CLR会检查类型转换,发现o引用的是一个DateTime对象,它既不是一个Employee,也不是从Employee派生的任何类型。所以,CLR会禁止转型,抛出一个System.InvalidCastException异常。
如果CLR允许这样的转型,就无类型安全性可言了,将出现难以预料的结果—其中包括应用程序崩溃,以及安全漏洞的出现(因为一种类型能轻松地伪装成另一种类型)。类型伪装是许多漏洞的根源,它还会破坏应用程序的稳定性和健壮性。因此,类型安全是CLR的一个极其重要的目标。
顺便说一句,声明PromoteEmployee方法的正确方式是将类型参数指定为Employee,而不要指定为Object。在前面的例子中,如果想这样进行修改,编译时就会开始报错,避免等到运行时才报错。我之所以使用Object,是为了演示C#编译器和CLR如何处理类型转换和类型安全性。
浙公网安备 33010602011771号