从IL看强制转换和is,as

来源:互联网 发布:ipad版淘宝怎么开店 编辑:程序博客网 时间:2024/05/21 18:50

1.强制类型转换。

 注意:下面的所有的IL代码都是release版,并且优化之后的。

Is和as的作用不多说,下面主要从IL角度来看一下他们的工作原理。

请看如下代码:

class CastTest    {        static void Main(string[]args)        {            objecto = "OK!";            stringstr = (string)o;            Console.WriteLine(str);        }}

 

然后使用ILDASM查看他的IL代码,如下:

 

 

首先,使用ldstr指令将对象“OK“的引用加载到栈上,然后使用stloc.0指令将引用赋值给变了o。因此,ldstr  “OK” 和stloc.0相当于object  o=”OK“。

注:stloc.0指令的全写为:“store the value on the top of stack  to  thefirst local. “意思将栈顶元素存储到第一个局部变量中。IL栈分为三个部分:参数区,局部变量区,计算栈。可以将他们分别看成一个数组,要访问他们都需要通过下标索引,譬如想要将第一个局部变量加载到计算栈上,就可以使用ldloc.0指令,其中0就是索引0.如果想要将计算栈顶元素存储到局部变量区中的第0个局部变量就可以使用stloc.0指令。

接下来,ldloc.0指令将变量o加载到计算栈中,然后使用castclass指令将o强制转换为System.String。

这里的核心指令是castclass,需要注意这个指令在转换不成功的时候,会抛出一个InvalidCastException异常。因此,在高级语言中一般建议不使用强制类型转换,而是使用可替代的is或者as关键字。


2.Is和as

由于使用强制转换可能会在运行的时候抛出一个异常,所以作为替代,C#提供了另一种类型转换方式,使用这种转换方式,即使在转换不成功的时候,也不会抛出一个异常,这就是使用关键字is和as来实现的。

修改上述代码如下:

class CastTest    {        static void Main(string[]args)        {            objecto = "OK!";            stringstr = o as string;            Console.WriteLine(str);        }    }


编译后,查看他的IL代码,如下:

 

这一次,C#没有生成castclass指令,而是生成了一个isinst指令。这个指令弹出栈顶元素,然后将其准换为System.String类型,如果转换成功那么就将转换后的类型压入栈顶,如果转换失败,那么就将一个null入栈,而不是抛出一个异常。

因此,我们只需要检查结果是否为null,就可以知道转换是否成功。

事实上,编译器为不仅仅为as关键字生成了isinst指令,也为is关键字生成了isinst。将上述代码修改如下:

class CastTest    {        static void Main(string[]args)        {            objecto = "OK!";            if(o is string)            {                //然后可以使用你想要的方式进行转换            }                  }    }

IL代码如下:


由于is关键字需要一个判断,所以编译器不仅仅会生成一个isinst指令,紧接着还会使用brfalse.s指令来判断转换的结果是否为flase(flase和null都是一个0),如果转换失败就跳转到IL_001b处,否则继续执行下面的指令。

正如你所看到的,编译器会为as关键字生成一个isinst指令,为is关键字生成两个指令:isinst和brfalse。但是,我们一般不会在源代码直接使用as关键字,而是在as之后,在来一个if语句来判断换转是否成功。如下:

 

  class CastTest    {        static void Main(string[]args)        {            objecto = "OK!";            stringstr = o as string;            if(str != null)            {                Console.WriteLine(str);            }                  }    }


查看一下IL代码,会发现什么呢?正如你所想的那样,这时候as和is的效果是一样。那么为什么还要推荐使用as而不是is呢?看一下二者的使用方式便知:

    class CastTest    {        static void Main(string[]args)        {            objecto = "OK!";            stringstr = o as string;            if(str != null)           {                Console.WriteLine(str);            }                  }    }


上面代码只转换一次(string str=o as string)。

 

再看下面代码:

 

    class CastTest    {        static void Main(string[]args)        {            objecto = "OK!";             if(o is string)            {                stringstr = (string)o;            }        }    }


这里却需转换两次,一处在o is string,这里主要使用isinst指令;一处在(string)o,这里使用castclass指令。