【Emit基础】调用Tostring()方法的IL表示
2008-09-20 11:52
253 查看
首先看一个例子:
public void Test(int a, StudentType studentType)
{
string ss2 = a.ToString();
string ss = studentType.ToString();
}
StudentType 是一个枚举类型,那么Test方法对应的IL是什么样子了?int和枚举的ToString方法的IL表示是一样的吗?我们来揭开谜底:
.maxstack 1
.locals init (
[0] string ss2,
[1] string ss)
L_0000: nop
L_0001: ldarga.s a
L_0003: [b]call [/b]instance string [mscorlib]System.Int32::ToString()
L_0008: stloc.0
L_0009: ldarg.2
L_000a: box TheTest.StudentType
L_000f: callvirt instance 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参数的例子:
public void Test2(ref int a, ref StudentType studentType)
{
string ss2 = a.ToString();
string ss = studentType.ToString();
}
其对应的IL如下:
.maxstack 1
.locals init (
[0] string ss2,
[1] string ss)
L_0000: nop
L_0001: ldarg.1
L_0002: call instance string [mscorlib]System.Int32::ToString()
L_0007: stloc.0
L_0008: ldarg.2
L_0009: ldind.i4
L_000a: box TheTest.StudentType
L_000f: callvirt instance 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的原因。
最后,再看一个引用类型的例子:
public void Test3(ref Student a)
{
string ss2 = a.ToString();
}
对应的IL如下:
.maxstack 1
.locals init (
[0] string ss2)
L_0000: nop
L_0001: ldarg.1
L_0002: ldind.ref
L_0003: callvirt instance string [mscorlib]System.Object::ToString()
L_0008: stloc.0
L_0009: ret
有了上面的基础,这段代码很容易理解,由于参数传入的是对象引用的地址(即,地址的地址),所以先要通过ldind.ref把对象的引用加载到计算堆栈上,然后才能调用方法。注意,自定义class的ToString()方法都是使用callvirt进行调用的。
public void Test(int a, StudentType studentType)
{
string ss2 = a.ToString();
string ss = studentType.ToString();
}
StudentType 是一个枚举类型,那么Test方法对应的IL是什么样子了?int和枚举的ToString方法的IL表示是一样的吗?我们来揭开谜底:
.maxstack 1
.locals init (
[0] string ss2,
[1] string ss)
L_0000: nop
L_0001: ldarga.s a
L_0003: [b]call [/b]instance string [mscorlib]System.Int32::ToString()
L_0008: stloc.0
L_0009: ldarg.2
L_000a: box TheTest.StudentType
L_000f: callvirt instance 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参数的例子:
public void Test2(ref int a, ref StudentType studentType)
{
string ss2 = a.ToString();
string ss = studentType.ToString();
}
其对应的IL如下:
.maxstack 1
.locals init (
[0] string ss2,
[1] string ss)
L_0000: nop
L_0001: ldarg.1
L_0002: call instance string [mscorlib]System.Int32::ToString()
L_0007: stloc.0
L_0008: ldarg.2
L_0009: ldind.i4
L_000a: box TheTest.StudentType
L_000f: callvirt instance 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的原因。
最后,再看一个引用类型的例子:
public void Test3(ref Student a)
{
string ss2 = a.ToString();
}
对应的IL如下:
.maxstack 1
.locals init (
[0] string ss2)
L_0000: nop
L_0001: ldarg.1
L_0002: ldind.ref
L_0003: callvirt instance string [mscorlib]System.Object::ToString()
L_0008: stloc.0
L_0009: ret
有了上面的基础,这段代码很容易理解,由于参数传入的是对象引用的地址(即,地址的地址),所以先要通过ldind.ref把对象的引用加载到计算堆栈上,然后才能调用方法。注意,自定义class的ToString()方法都是使用callvirt进行调用的。
相关文章推荐
- 【Emit基础】调用Tostring()方法的IL表示
- 【Emit基础】IL定义方法的语法详解
- 【Emit基础】IL定义方法的语法详解
- uniry3d(4)基类中的基础方法的调用顺序
- 2015/9/21 Python基础(17):绑定和方法调用
- Java基础之父类与子类之间变量和方法的调用
- Scala基础入门(九)Scala 方法中缀表示法、后缀表示法
- 结合IL和Windbg来看.Net调用继承虚方法的执行顺序
- JavaSE7基础 调用静态成员变量的三种方法
- List调用toString()方法后,去除两头的中括号实例
- js基础(四) toString()方法和valueOf()方法
- 基础巩固--理解方法调用
- python 调用shell命令三种方法 分类: python基础学习 python Module 2014-01-13 14:13 239人阅读 评论(0) 收藏
- java基础-反射3(反射,反射创建对象,操作对象属性,调用对象方法)
- JAVA_SE基础——32.this关键字调用本类的构造方法
- 【C#基础】方法及其调用、构造方法、out与ref参数及其返回值、方法重载、静态方法等简单介绍
- C# 获取所有对象的字符串表示一ToString方法
- java基础入门-动态绑定-调用方法的过程
- Python3基础 类名.方法名 调用没有self参数的方法
- 【Emit基础】IL中发布、订阅、触发事件