[C#] as 和 is 运算符以及安全的类型强制转换

根据MSDN的说明:由于对象是多态的,因此基类类型的变量可以保存派生类型。若要访问派生类型的方法,需要将值强制转换回该派生类型。不过,在这些情况下,如果只尝试进行简单的强制转换,会导致引发 InvalidCastException 的风险。鉴于这个过程是不安全的,因此需要用 try - catch 语句块来进行保护,例如比较安全的代码方式应该如下所示:


    // 有一object类型的待转换对象 objTest
    GivenType value = null;
    try
    {
        value = (GivenType) objTest;
    }
    catchException 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运算符只考虑引用转换、装箱转换和拆箱转换,不考虑其他转换(包括用户定义的转换)。例如虽然intdouble是类型兼容的,但是使用is运算符的结果却是false。

 

其他需要注意的:

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

     is关键字用于检查对象是否与给定类型兼容。注意了,这里的is并不是的意思,而是兼容。也就是说,如果被检查对象能够强制转换成给定类型并不发生异常,那么表示它们类型兼容。如果所提供的表达式非空,并且所提供的对象可以强制转换为所提供的类型而不会导致引发异常,则is表达式的计算结果将是 true;否则,发生异常时is表达式将返回falseis的使用方式:
        
    if (obj is  MyObject)
        {
            //.........
        }
        
    如果已知表达式将始终是 true 或始终是 false,则 is 关键字将导致编译时警告,但是,通常在运行时才计算类型兼容性。注意,is 运算符只考虑引用转换装箱转换取消装箱转换不考虑其他转换,如用户定义的转换。特别注意:虽然intdouble是类型兼容的,但是使用is运算符的结果却是false,那是因为is运算符只考虑引用转换、装箱转换和拆箱转换。在 is 运算符的左侧不允许使用匿名方法。

    引用转换
        is
    作用于对象引用时,将判断被检查对象变量是否是给定类型的派生类型。如果是派生类型,返回true;否则返回false。例如:
        
    List<int> list = new List<int>();
        
    if(list is IEnumerable) //这里将返回true。因为List实现了IEnumerable接口。
        {
        }

    装箱转换
        
    int i = 5;
        
    if(i is Int32) {...} //这里返回true
        
    if(i is Int16) {...} //这里返回false,因为int类型的包装类型是Int32,而不是Int16

    拆箱转换
        Int32 int32 = 
    new Int32();
        
    if(int32 is int) {...} //这里返回true
        
    if(int32 is long) {...} //这里返回false,因为Int32类型的原始类型是int,而不是long

    注意

    1.     虽然PointPointFSizeSizeFRectangleRectangleF可以相互转换,但是使用is运算符的结果都是返回false,因为is运算符不支持用户自定义的类型转换检测。

    2.     intlongdouble这些原始类型、数值类型虽然可以进行相互间的强制转换,但是使用is运算符的结果都是返回false,也是因为is运算符不支持这种原始类型转换的检测。

            int i = 6; long l = 100;
            
    if(i is long ) {...} //返回false
            
    if(i is double) {...} //返回false
            
    if(l is double) {...} //返回false
            
    if(l is int ) {...} //返回false

     

    C#asis关键字

    as 运算符用于在兼容的引用类型之间执行某些类型的转换。例如:

    static void Main(string[] args)
    {
        
    object[] obj = new object[3];
        obj[
    0] = new class1();
        obj[
    1] = "hello";
        obj[
    2] = 10;

        
    for (int i = 0; i < obj.Length; i++)
        {
            
    string s = obj[i] as string;
            
    if (s != null)
            {
                Console.WriteLine(s);
            }
            
    else
            {
                Console.WriteLine(
    "not a string");
            }
        }

        Console.ReadLine();
    }

    结果:

    not a string

    hello

    not a string

     

    class Base
    {
        
    public override string ToString()
        {
            
    return "Base";
        }
    }
    class Derived : Base
    { }

    class Program
    {
        
    static void Main()
        {

            Derived d = 
    new Derived();

            Base b = d 
    as Base;
            
    if (b != null)
            {
                Console.WriteLine(b.ToString());
            }

        }
    }

    对于继承类,允许把子类转换成父类,但是不可以把父类转换成子类,不同类之间,值类型不可转换。

    .is检查对象是否与给定类型兼容。

    例如,下面的代码可以确定对象是否为 MyObject 类型的一个实例,或者对象是否为从 MyObject 派生的一个类型:

    复制代码

    if (obj is MyObject)

    {

    }

    如果所提供的表达式非空,并且所提供的对象可以强制转换为所提供的类型而不会导致引发异常,则 is 表达式的计算结果将是 true

    如果已知表达式将始终是 true 或始终是 false,则 is 关键字将导致编译时警告,但是,通常在运行时才计算类型兼容性。

    不能重载 is 运算符。

    请注意,is 运算符只考虑引用转换、装箱转换和取消装箱转换。不考虑其他转换,如用户定义的转换。

     is 运算符的左侧不允许使用匿名方法。lambda 表达式属于例外。

     

    class MyQuickSort
    {
        
    static void Main(string[] args)
        {
            class2 c2 = 
    new class2();

            
    if (c2 is class1)
            {
                Console.WriteLine(
    "Yes");
            }
            
    else
            {
                Console.WriteLine(
    "No");
            }

            Console.ReadLine();
        }
    }

    class class1
    {
        
    public override string ToString()
        {
            
    return "";
        }
    }

    class class2:class1
    {

    }

     

    结果:Yes

    二、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内部,再将后者存储在托管堆上。取消装箱将从对象中提取值类型。)

     

     

    参考:

    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


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值