【Emit基础】调用Tostring()方法的IL表示

首先看一个例子:

<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--> publicvoidTest(int a,StudentTypestudentType)
{
stringss2=
a.ToString();
stringss=
studentType.ToString();
}

StudentType是一个枚举类型,那么Test方法对应的IL是什么样子了?int和枚举的ToString方法的IL表示是一样的吗?我们来揭开谜底:

<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--> .maxstack1
.localsinit(
[
0]string ss2,
[
1]string
ss)
L_0000:nop
L_0001:ldarga.sa
L_0003:callinstance
string
[mscorlib]System.Int32::ToString()
L_0008:stloc.
0

L_0009:ldarg.2

L_000a:boxTheTest.StudentType
L_000f:callvirtinstance
string
[mscorlib]System.Object::ToString()
L_0014:stloc.
1

L_0015:ret

虽然,StudentType和Int都是值类型,但是Tostring()方法的调用却是如此的不同,我们来逐一分析。

1.方法调用(call、callvir)只能基于引用来调用方法。

我们如果想调用int的Tostring()方法,就必须先获取其“引用”,但是,值类型不存在引用,所以我们使用ldarga.s来获取其地址(指针就相当于引用了)。

而对于枚举类型,先将其Box获取装箱对象的引用,然后再调用Tostring()方法。

注意,IL虚拟机的针对内置类型的基本操作(如 add),直接基于对象本身操作即可,而不是针对引用(对象的地址)进行。

2.对于.NET的内置基础值类型,使用call直接调用其自身的Tostring()方法。对于枚举这样的值类型,则需要使用callvirt来调用object的Tostring()虚方法。

我们再来看个带ref参数的例子:

<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--> publicvoidTest2(refinta,ref StudentTypestudentType)
{
stringss2=
a.ToString();
stringss=
studentType.ToString();
}

其对应的IL如下:

<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--> .maxstack1
.localsinit(
[
0]string ss2,
[
1]string
ss)
L_0000:nop
L_0001:ldarg.1

L_0002:callinstance
string
[mscorlib]System.Int32::ToString()
L_0007:stloc.
0

L_0008:ldarg.
2

L_0009:ldind.i4
L_000a:boxTheTest.StudentType
L_000f:callvirtinstance
string
[mscorlib]System.Object::ToString()
L_0014:stloc.
1

L_0015:ret

说明如下:

3.由于ref传递的本来就是地址,所以针对第一个参数,可以直接加载参数然后调用Tostring()方法。

4.由于StudentType不是IL内置的基础类型,IL不认识它,所以必须先通过ldind.i4间接加载对象,接着装箱后再调用Tostring()方法。注意:默认的枚举是用int来表示的,这也是使用ldind.i4而不是ldind.i2的原因。

最后,再看一个引用类型的例子:

<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--> publicvoidTest3(ref Studenta)
{
stringss2=
a.ToString();
}

对应的IL如下:

<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--> .maxstack1
.localsinit(
[
0]string ss2)
L_0000:nop
L_0001:ldarg.
1

L_0002:ldind.ref

L_0003:callvirtinstance
string [mscorlib]System.Object::ToString()
L_0008:stloc.
0

L_0009:ret

有了上面的基础,这段代码很容易理解,由于参数传入的是对象引用的地址(即,地址的地址),所以先要通过ldind.ref把对象的引用加载到计算堆栈上,然后才能调用方法。注意,自定义class的ToString()方法都是使用callvirt进行调用的。

你可能感兴趣的:(toString)