一. 引言
类型安全是.NET设计之初重点考虑的内容之一,对于程序设计者来说,完全把握系统数据的类型安全,经常是力不从心的问题。现在,这一切已经在微软大牛们的设计框架中为你解决了。可以有以下方法在运行时获取对象的类型信息:
1.1typeof 运算符。如 System.Type type = typeof(int?);
1.2使用 System.Reflection 命名空间的类和方法。
1.3GetType()方法。在.NET中,一切类型都必须继承自System.Object类型,System.Object类里有一个GetType()方法。但是,此方法不能应用于可空值类型(可空值类型可以表示基础类型的所有值,另外还可以表示 null 值)。如果您试图使用 GetType 方法或 is 运算符在运行时获得可空类型变量的类型信息,得到的结果是表示可空值类型的基础类型而不是可空类型本身的 Type 对象。
1.4as 和 is 运算符。根据MSDN的说明:由于对象是多态的,因此基类类型的变量可以保存派生类型。若要访问派生类型的方法,需要将值强制转换回该派生类型。不过,在这些情况下,如果只尝试进行简单的强制转换,会导致引发 InvalidCastException 的风险。鉴于这个过程是不安全的,因此需要用 try - catch 语句块来进行保护,例如比较安全的代码方式应该如下所示:
// 有一object类型的待转换对象 objTest
GivenType value = null;
try
{
value = (GivenType) objTest;
}
catch( Exception e )
{
MessageBox.Show( e.Message );
}
但是如上的写法在C#中已经属于过时的写法,而且也属于比较低效的写法。但是类似的转换会经常发生,为了避免异常导致的低效和代码的不简洁,C# 提供 is 和 as 运算符来进行转换。可以使用这两个运算符来测试强制转换是否会成功,而没有引发异常的风险。
二、is 运算符
is 运算符检查对象是否与给定类型兼容。例如,if ( obj is MyObject ) 将检查对象 obj 是否为 MyObject 类型的一个实例,或者是从 MyObject 派生的一个类型的实例。
对于is表达式的结果应该这么看待:如果所提供的对象(表达式)非空,并且可以被强制转换为所提供的类型而不会引发异常,则is表达式的结果就为true,否则为false。
通常使用is表达式的情况是程序运行时才计算类型兼容性,如果已知表达式的值始终为true或者fale,将会导致编译时的警告。
注意:is运算符只考虑引用转换、装箱转换和拆箱转换,不考虑其他转换(包括用户定义的转换)。例如虽然int和double是类型兼容的,但是使用is运算符的结果却是false。
其他需要注意的:
不能重载is运算符;
在is运算符左侧不允许使用匿名方法(Lambda表达式例外)。
三、as 运算符
as 运算符用于在兼容的引用类型之间执行类似于强制类型转换的操作。与强制类型转换不同的是,如果无法进行转换,as运算符将返回null而不是引发异常。
语法:
expression as type
等效于:
expression is type ? (type) expression : (type) null
注意:as 运算符只能执行引用转换和装箱转换,不能执行拆箱转换(应使用is运算符判断配合强制类型装换来完成),也不能执行其他转换,如用户定义的转换(这类转换应该首先需要相应类型提供转换函数并使用强制转换表达式来完成,或者使用case语句等其他方法来执行)。下面举例说明:
2.1 不能使用 as 运算符进行拆箱转换
即 as 运算符不能应用在值类型数据,如下写会出现编译错误:
object objTest = 11;
int intValue = objTest as int;
正确的写法是:使用is操作符,再加上显式的类型转换操作,就可以安全完成转换,例如:
object objTest = 11;
if ( objTest is int )
{
int intValue = (int) objTest;
}
2.2 不能使用 as 运算符完成用户定义的转换
要想使用户定义的转换操作能正确完成,需要在原类型中增加类型转换操作符函数,例如:
public class NewTypeOne
{
public static explicit operator NewTypeTwo( NewTypeOne objTypeOne )
{
//Convert object into new type
}
}
并使用如下强制类型转换(不能使用 as 运算符):
NewTypeOne objTypeOne = new NewTypeOne();
NewTypeTwo newTestTwo = (NewTypeTwo)newTestOne;
2.3 对 as 运算符使用可空类型
允许使用 as 运算符完成基本数据类型(已知值类型)之间的转换,但是由于使用 as 运算符可能产生null值,应注意可能需要对 as 运算符使用 nullable 类型,否则同样可能抛出异常。
下面是摘自MSDN的代码:
void UseAsWithNullable(System.ValueType val)
{
int? j = val as int?;
if (j != null)
{
Console.WriteLine(j);
}
else
{
Console.WriteLine("Could not convert " + val.ToString());
}
}
四、as 运算符和 is 运算符的效率比较
通常,as 运算符更高效一些,因为如果可以成功进行强制转换,它会实际返回强制转换值。而 is 运算符只返回一个布尔值,可能在表达式为true时,还需要进行显示的转换,就需要执行两次类型兼容检查,例如:
object o ="abc";
if ( o is string) //执行第一次类型兼容性检查
{
string s = (string)o; //执行第二次类型兼容性检查,并转换
MessageBox.Show( "转换成功!" );
}
else
{
MessageBox.Show( "转换失败!" );
}
而使用as运算符,可以改写为:
object o ="abc";
string s = oasstring;//执行第一次类型兼容性检查,并返回结果
if(s != null)
MessageBox.Show("转换成功!");
else
MessageBox.Show("转换失败!");
对比两种方式,is 需要做两次对象的类型检查;而 as 需要做一次对象类型检查,再加一次null的检查,但是null检查的开销比对象类型检查少,因此相对来说,as的方法效率高些。当然,只是检测类型是否相符那么只用is就可以了,如果要进行类型转化可以直接用as。
五、类型转换总结
综上所述,那么在进行类型转换的时候,可以按照如下的方式进行选择。
类型转换
使用操作
Object --> 已知引用类型
使用 as 操作符来完成
Object --> 基本数据类型(拆箱)
先使用 is 操作符来进行判断,再用强制转换方式进行转换
用户定义类型之间转换
首先需要相应类型提供转换函数,再用强制转换方式进行转换
基本数据类型之间转换
最好使用.Net提供的Convert类所涉及的静态方法
其他类型转换相关:
1. 任何类型都可以转换为其基类类型,用隐式转换即可完成;
2. 任何类型转换为其派生类型时,必须进行显示转换。如:(类型名)对象名;
3. 使用 GetType 可以取得任何对象的精确类型;
4. 基本数据类型是都是已知的值类型,可以使用 Convert 类实现类型转换;
5. 除了 string 以外的其他类型都有 Parse 方法,用于将字符串类型转换成对应的基本类型;
6. 值类型和引用类型的转换称为装箱(boxing)或拆箱(unboxing)。(根据MSDN编程指南:装箱是将值类型转换为object类型或由此值类型实现的任一接口类型的过程。当CLR对值类型进行装箱时,会将该值包装到System.Object内部,再将后者存储在托管堆上。取消装箱将从对象中提取值类型。)
浅谈了类型转换的几个普遍关注的方面,该将主要精力放在is、as操作符的恩怨情仇上了。类型转换将是个较大的话题,留于适当的时机讨论。
is/as操作符,是C#中用于类型转换的,提供了对类型兼容性的判断,从而使得类型转换控制在安全的范畴,提供了灵活的类型转换控制。
is的规则如下:
检查对象类型的兼容性,并返回结果,true或者false;
如果对象为null,则返回值永远为false。
参考:
1. 如何:使用 as 和 is 运算符安全地进行强制转换(C# 编程指南). MSDN. http://msdn.microsoft.com/zh-cn/library/cc488006
2. as(C#参考). MSDN. http://msdn.microsoft.com/zh-cn/library/cscsdfbt
3. is(C#参考). MSDN. http://msdn.microsoft.com/zh-cn/library/scekt9xw
4. C# 中 as 和 is 的用法. http://www.cppblog.com/luyulaile/archive/2011/03/14/141773.html
5. C# as 与 is. http://blog.csdn.net/pengfeixiong/article/details/7409875