field指的是类内部的成员变量,供类内部的方法调用,那在CIL代码中field是如何实现存储与加载的呢?下面让我们一窥究竟。
目标C#代码
先看一段简单的C#代码:
public class Program
{
// Fields
private string _timeArea;
// Methods
public string get_TimeArea()
{
// This item is obfuscated and can not be translated.
return this._timeArea;
}
public static void Main()
{
// This item is obfuscated and can not be translated.
Console.WriteLine("Hello");
}
public void set_TimeArea(string value)
{
// This item is obfuscated and can not be translated.
this._timeArea = value;
}
}
CIL实现field存储
例一:
.class public Program extends [mscorlib]System.Object
{
.field private string _timeArea
.method public static void Main()
{
.entrypoint
.maxstack 1
ldstr "Hello"
call void [mscorlib]System.Console::WriteLine(string)
call string [mscorlib]System.Console::ReadLine()
ret
}
.method public void set_TimeArea(string 'value')//(string val)不用加单引号
{
.maxstack 1
ldarg.0 //本操作码对应this._timeArea=value中的this
ldarg.1 //本操作码对应this._timeArea=value中的value
stfld string GuoNameSpace.Program::_timeArea //本操作码对应this._timeArea=value中的._timeArea
ret
}
}
例一解释:
public class Program
{
// Fields
private string _timeArea;
public static void Main()
{
// This item is obfuscated and can not be translated.
Console.WriteLine("Hello");
}
public void set_TimeArea(string value)
{
this._timeArea = value;
}
}
例二:
.class public Program extends [mscorlib]System.Object
{
.field private string _timeArea
.method public static void Main()
{
.entrypoint
.maxstack 1
ldstr "Hello"
call void [mscorlib]System.Console::WriteLine(string)
call string [mscorlib]System.Console::ReadLine()
ret
}
.method public void set_TimeArea(string 'value')//(string val)不用加单引号
{
.maxstack 1
ldarg.0 //本操作码与value._timeArea = "aaa"无关
ldarg.1 //本操作码对应value._timeArea = "aaa"中的value
ldstr "aaa" //本操作码对应value._timeArea = "aaa"中的"aaa"
stfld string GuoNameSpace.Program::_timeArea //本操作码对应value._timeArea = "aaa"中的._timeArea
ret
}
}
例二解释:
public class Program
{
// Fields
private string _timeArea;
public static void Main()
{
// This item is obfuscated and can not be translated.
Console.WriteLine("Hello");
}
public void set_TimeArea(string value)
{
// This item is obfuscated and can not be translated.
value._timeArea = "aaa";
}
}
例三:
.class public Program extends [mscorlib]System.Object
{
.field private string _timeArea
.method public static void Main()
{
.entrypoint
.maxstack 1
ldstr "Hello"
call void [mscorlib]System.Console::WriteLine(string)
call string [mscorlib]System.Console::ReadLine()
ret
}
.method public void set_TimeArea(string 'value')//(string val)不用加单引号
{
.maxstack 1
ldarg.0 //本操作码与"aaa"._timeArea = "bbb"无关
ldarg.1 //本操作码与"aaa"._timeArea = "bbb"无关
ldstr "aaa" //本操作码对应"aaa"._timeArea = "bbb"中的"aaa"
ldstr "bbb" //本操作码对应"aaa"._timeArea = "bbb"中的"bbb"
stfld string GuoNameSpace.Program::_timeArea //本操作码对应"aaa"._timeArea = "bbb"中的._timeArea
ret
}
}
例三解释:
public class Program
{
// Fields
private string _timeArea;
public static void Main()
{
// This item is obfuscated and can not be translated.
Console.WriteLine("Hello");
}
public void set_TimeArea(string value)
{
// This item is obfuscated and can not be translated.
"aaa"._timeArea = "bbb";
}
}
经过三个例子的对比发现,存储field时,stfld操作与堆栈顶部的两个值是密切相关的,执行stfld操作时堆栈最顶部的值就是field的值,位于堆栈次顶部的值表示field的所属对象。
CIL实现field加载
例一:
.class public Program extends [mscorlib]System.Object
{
.field private string _timeArea
.method public static void Main()
{
.entrypoint
.maxstack 1
ldstr "Hello"
call void [mscorlib]System.Console::WriteLine(string)
call string [mscorlib]System.Console::ReadLine()
ret
}
.method public string get_TimeArea()
{
.maxstack 1
.locals init(
string timeArea
)
ldarg.0 //本操作码对应this._timeArea中的this
ldfld string GuoNameSpace.Program::_timeArea //本操作码对应this._timeArea中的._timeArea
stloc.0
ldloc.0
ret
}
}
例一解释:
public class Program
{
// Fields
private string _timeArea;
// Methods
public string get_TimeArea()
{
return this._timeArea;
}
public static void Main()
{
// This item is obfuscated and can not be translated.
Console.WriteLine("Hello");
}
}
例二:
.class public Program extends [mscorlib]System.Object
{
.field private string _timeArea
.method public static void Main()
{
.entrypoint
.maxstack 1
ldstr "Hello"
call void [mscorlib]System.Console::WriteLine(string)
call string [mscorlib]System.Console::ReadLine()
ret
}
.method public string get_TimeArea()
{
.maxstack 1
.locals init(
string timeArea
)
ldarg.0 //本操作码与"aaa"._timeArea无关
ldstr "aaa" //本操作码对应"aaa"._timeArea中的"aaa"
ldfld string GuoNameSpace.Program::_timeArea //本操作码对应"aaa"._timeArea中的._timeArea
stloc.0
ldloc.0
ret
}
}
例二解释:
public class Program
{
// Fields
private string _timeArea;
// Methods
public string get_TimeArea()
{
return "aaa"._timeArea;
}
public static void Main()
{
// This item is obfuscated and can not be translated.
Console.WriteLine("Hello");
}
}
例三:
.class public Program extends [mscorlib]System.Object
{
.field private string _timeArea
.method public static void Main()
{
.entrypoint
.maxstack 1
ldstr "Hello"
call void [mscorlib]System.Console::WriteLine(string)
call string [mscorlib]System.Console::ReadLine()
ret
}
.method public string get_TimeArea()
{
.maxstack 1
.locals init(
string timeArea
)
ldarg.0 //本操作码与"bbb"._timeArea无关
ldstr "aaa" //本操作码与"bbb"._timeArea无关
ldstr "bbb" //本操作码对应"bbb"._timeArea中的"aaa"
ldfld string GuoNameSpace.Program::_timeArea //本操作码对应"bbb"._timeArea中的._timeArea
stloc.0
ldloc.0
ret
}
}
例三解释:
public class Program
{
// Fields
private string _timeArea;
// Methods
public string get_TimeArea()
{
return "bbb"._timeArea;
}
public static void Main()
{
// This item is obfuscated and can not be translated.
Console.WriteLine("Hello");
}
}
经过三个例子的对比发现,加载field时,ldfld操作与堆栈最顶部的值是密切相关的,执行ldfld操作时堆栈最顶部的值表示field的所属对象。
翻旧账
不知道大家还记不记得静态方法中只能使用静态字段,为什么不能使用非静态字段呢?是时候解释一下了。
静态方法中ldarg.0表示方法的第一个参数,所以没办法使用ldarg.0来调用this.field;
非静态方法中ldarg.0表示this,所以可以使用ldarg.0来调用this.field。
就这么简单,不知道各位看明白了吗?欢迎各位留言提出宝贵意见。