面向对象的编程语言(如C#、Java),都不可避免要在编写代码的过程中进行类型转换。类型转换虽然看似很简单,但在实际工作中仍是一件很让人头痛的事儿,特别是初学者,很多时候类型转换真正转起来的时候会很晕(我曾晕过,哈哈)。C#为我们做引用类型的转换提供了一个操作符as,as只能操作引用类型,还有一个用于类型判断的is,is操作符可以操作值类型和操作类型。关于值类型转换,值类型与引用类型的相互转换在本文中不涉及。
关于is和as可以参考:
http://www.cnblogs.com/anytao/archive/2007/04/07/must_net_01.html#commentform。is在MSDN中的解释是:检查对象是否与给定类型兼容。as的解释是:用于在兼容的引用类型之间执行某些类型的转换。看到很多书里说到做类型转换时用as比用is性能高,至于怎么个高法,其实真的很无聊,哈哈,我自这么认为。有的人干脆直接说as比is性能高,我认为as和is如果单独比较,而不是用于类型转换这个过程的话,性能应该一样(这个过程中只所以产生性能的差别,其实是类型判断和是否为null判断之间的差别。),甚至is有时可能比as还要高,这个只是猜想,有空验证一下。as和is用于引用类型时,它们的共同点是都做非空判断、类型判断,都做返回操作;不同的是当对象为null时,as直接返回一个空值,is返回false;当类型不兼容时,as会返回一个null值,is返回false,成功时返回as操作符前的对象引用,is返回一个true值。
类型转换的核心是类型兼容,那么什么是类型兼容的呢?其实可以用面向对象中的“里氏代换原则”来解释:所有父类能出现的地方,都可以用子类来替换。
还是看一个例子吧:
public class A
{
//……
}
public class B : A
{
//……
}
首先定义一个类A,B继承A;
public class C
{
public void LoadA(A a)
{
//……
}
public void LoadB(B b)
{
//……
}
}
类C中有两个方法,一个定义A类型的作为参数,一个定义B类型作为参数。
static void Main(string[] args)
{
C c = new C();
c.LoadA(new A());//正确
c.LoadA(new B());//正确
c.LoadB(new B());//正确
c.LoadB(new A());//错误无法从“A”转换为“B”
}
客户端调用LoadA时,传入A类型实例和B类型实例都可以,因为B是A的子类,所以根据“里氏代换原则”这是成立的;但是反过来就不成立,如调用LoadB时传入A类型实例时,编译都通不过,看来编译器帮助我们进行了“里氏代换原则”的检查。
C#规定我们在做类型转换时,子类对象可以隐式转换为父类对象,如:
A a = new B();
而反过来就要做显示转换,如:
A a = new B();
B b = (B)a;//强制转换
这个例子很好理解,但如果是这样呢?
A a = new A();
B b = (B)a;//可以通过编译,但会产生一个运行时错误。
看到一些人对上面的现象很不理解,为什么第一个没错,而第二个有错呢?
C#规定在进行类型转换时,被转换的实例类型必须是要转换类型的自身或它的子类,上例中变量a的类型为A,但它引用实际类型也是A;而A并不是B的子类,而是B的父类,当然不会成功。其实也不难理解,例如B类中声明了一个A中不存在的方法Abc,把A实例转为B类型就算成功,但是如果你调用b.Abc(),A实例中肯定是没有的。还有一点就是,类型变量是被分配在内存栈里,而我们用new B()实例化一个对象会被分配到堆里,类型变量我们可以把它看成一个保存对象地址的容器,也可理解为指向一个对象的指针,真正的操作是在堆中进行的。像A a = new A();a这个变量就指向一个类型为A的实例的;A a = new B();这时变量a指向的就是B对象,B为A的子类,B有A类所有的特性,而反过来,A可能不具有B的所有特性,所以我们就可以理解为什么A的实例不能转换为B类型了。在类型转换中一定要分清类型变量和类型实例的区别,这样才不至于迷茫。无论一个变量是什么类型,只要用GetType()方法来获取它引用的实例的真实类型,如:
A a = new B();
Console.WriteLine(a.GetType().Name);
显示为B。
在说一点is和as的事吧,在我们用is和as时。
如:
A a = new A();
B b = a as B;
这里不会抛出异常(任何地方都不会,哈哈,当然你写错例外),只是a as B会返回一个null值,所以要想转换成功,a引用的对象的类型必须是B类型或B类型的子类才能转换成功,成功与否和a变量的类型无关。
Is和as用法一样,只是返回值不同,
如:
A a = new A();
bool isCompatible = a is B;
is操作符也不会抛出异常,a is B这里a引用的对象的类型必须是B类型或B类型的子类才会返回true值,同样,是否返回true值也跟a变量的类型无关。