Effective C# Item3:操作符as或is优于强制转换
C#是一种强类型的变成语言,我们一般情况下,不推荐大家对变量的类型进行转换,但是针对代码底层来说,为了对业务代码提供尽可能多的支持,很多时候对方法参数的类型不会做强行限制,而只是将其置为System.Object,那么业务在调用这些方法时,如果希望在方法体内执行用户自定义的方法,就必须对参数的类型进行转换,将System.Object转换为用户自己定义的类型。
关于类型转换,C#有以下两种方式:1.as或者is;2.强制类型转换。我们推荐使用as或者is。
as进行类型转换时的机制:它会检查对象的运行时类型,是否是期待类型或者期待类型的派生类型,如果是,就进行转换;如果不是,就返回null。对于null来说,它通过as转换为任何类型后,依然是null,并不会产生异常。as并不会执行用户自定义的类型转换。
强制类型转换的机制:它使用转换操作符来进行类型转换的工作,在这其中,它会执行.NET框架自带的类型转换机制或者用户自定义的类型转换。这其中,就会有转换成功,但是转换后结果不正确的情况,例如将float强行转换成int,就可能会丢失一些信息。在使用时,需要格外注意这一点。
下面我们看一段非常有趣的代码,来自《Effective C#》一书。
1 public class SecondType
2 {
3 private MyType _value;
4
5 // other details elided
6
7 // Conversion operator.
8 // This converts a SecondType to
9 // a MyType, see item 29.
10 public static implicit operator
11 MyType( SecondType t )
12 {
13 return t._value;
14 }
15 }
16
17 object o = Factory.GetObject( );
18
19 // o is a SecondType:
20 MyType t = o as MyType; // Fails. o is not MyType
21
22 if ( t != null )
23 {
24 // work with t, it's a MyType.
25 } else
26 {
27 // report the failure.
28 }
29
30 // Version two:
31 try {
32 MyType t1;
33 t = ( MyType ) o; // Fails. o is not MyType
34 if ( t1 != null )
35 {
36 // work with t1, it's a MyType.
37 } else
38 {
39 // Report a null reference failure.
40 }
41 } catch
42 {
43 // report the conversion failure.
44 }
上述两种转换都会是失败的,原因:as和强制转换都是针对变量的运行时类型进行处理的,但是上述代码编译时,是去查看变量的编译时类型,编译器是无法得知变量的运行时类型的,因此,虽然上述代码中使用了强制类型转换,会去查看是否有用户自定义的类型转换,但是编译器只会去查看System.Object和MyObject这两种类型之间是否有用户自定义的类型转换,而不会去查看SecondType和MyType之间是否有用户自定义的类型转换。编译器在编译过程中,能够得到和处理的,始终是变量的编译时类型,同时,用户自定义的类型转换,只作用于对象的编译时类型,而不会作用于对象的运行时类型。
如果希望上述代码转换成功,可以写成以下的方式。
1 object o = Factory.GetObject( );
2
3 // Version three:
4 SecondType st = o as SecondType;
5 try {
6 MyType t;
7 t = ( MyType ) st;
8 if ( t != null )
9 {
10 // work with T, it's a MyType.
11 } else
12 {
13 // Report a null reference failure.
14 }
15 } catch
16 {
17 // report the failure.
18 }
以下情况中,是不适合使用as的:
- 针对值类型进行类型转换,原因:值类型中不包含null。对于值类型,我们可以先使用is进行类型检查,然后再使用强制类型转换。
- foreach语句中的类型转换是强制类型转换,原因:foreach语句的循环变量可能是值类型,也可能是引用类型,因此只能使用强制类型转换。这个也是foreach语句会跑出BadCastException异常的原因。
- as进行转换时,会对变量的类型进行检查,当发现变量类型是目标类型或者目标类型的派生类型时,就会进行转换,但是如果我们希望知道变量在运行时详细的类型信息,或者只针对变量是某一特定类型时,才会采取某种操作,这时使用as是不合适的,我们可以使用GetType方法来获取对象的运行时类型的详细信息。