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指令。