TearSnow Fan


[C#] as 和 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,将会导致编译时的警告。

注意:

(1)is运算符只考虑引用转换、装箱转换和拆箱转换,不考虑其他转换(包括用户定义的转换)。例如虽然 int 和 double 是类型兼容的,但是使用is运算符的结果却是false。

(2)不能重载is运算符。

(3)在is运算符左侧不允许使用匿名方法(Lambda表达式例外)。

二、as 运算符

as 运算符用于在兼容的引用类型之间执行类似于强制类型转换的操作。与强制类型转换不同的是,如果无法进行转换,as运算符将返回null而不是引发异常。

语法:

expression as type

等效于:

expression is type ? (type) expression : (type) null

注意:

(1)as 运算符只能执行引用转换和装箱转换,不能执行拆箱转换(应使用is运算符判断配合强制类型装换来完成);

(2)as运算符也不能执行其他转换,如用户定义的转换(这类转换应该首先需要相应类型提供转换函数并使用强制转换表达式来完成,或者使用case语句等其他方法来执行)。

下面提供了上述情况不适用as运算符的替代方案:

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
    }
}

并使用如下强制类型转换(不能使用 is 运算符和 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());
    }
}

需要说明的是,上述代码中的 int? 或者 Nullable <int>是“可空类型”。Nullable <T> 结构支持只将一个值类型用作可空类型,因为引用类型本身就是可空的。

 

三、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方法可以取得任何对象实例的精确类型(注意区分Typeof运算符区分);

4. 基本数据类型是都是已知的值类型,可以使用 Convert 类实现类型转换;

5. 除了 string 以外的其他类型都有 Parse 方法,用于将字符串类型转换成对应的基本类型;

6. 值类型和引用类型的转换称为装箱(boxing)或拆箱(unboxing)。(根据MSDN编程指南:装箱是将值类型转换为object类型或由此值类型实现的任一接口类型的过程。当CLR对值类型进行装箱时,会将该值包装到System.Object内部,再将后者存储在托管堆上。取消装箱将从对象中提取值类型。)

 

参考资料:

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

本文固定链接: http://blog.xieyc.com/operator-as-and-is-and-safety-type-coercion/ | 小谢的小站

该日志由 xieyc 于2013年07月04日发表在 编程 分类下, 你可以发表评论,并在保留原文地址及作者的情况下引用到你的网站或博客。
原创文章转载请注明: [C#] as 和 is 运算符以及安全的类型强制转换讨论 | 小谢的小站
关键字: , ,

[C#] as 和 is 运算符以及安全的类型强制转换讨论:等您坐沙发呢!

发表评论

:wink: :neutral: :mad: :twisted: :smile: :shock: :sad: :roll: :oops: :eek: :mrgreen: :lol: :grin: :evil: :cry: :cool: :arrow: :???: :?: :!:

快捷键:Ctrl+Enter