提问人:David Klempfner 提问时间:9/14/2019 更新时间:9/14/2019 访问量:438
将 IL 转换为 C#,没有语法糖
Converting IL to C# with no syntactic sugar
问:
我正在寻找一个程序,该程序将向我显示给定 IL 代码的最低级别(即无语法糖)C# 代码。
我尝试使用 .NET Reflector 查看一个包含带有循环的简单控制台应用程序的 .exe 文件,希望看到 、 等,但它将其显示为循环。foreach
GetEnumerator()
MoveNext()
Current
foreach
这样的程序存在吗?或者是否可以在 .NET Reflector 中选择“无语法糖”?
答:
ILSpy 的当前版本具有一组相当大的选项,用于启用/禁用反编译器转换功能:
...
static void Main(string[] args) {
foreach (var arg in args) {
Console.WriteLine(arg);
}
}
...
如果需要,你可以通过剥离逻辑来走得更远 和 ;也许可以提出一个问题,询问您的更改的 PR 是否会受到赞赏,因为这些“原始”设置中的大多数都是最近添加的。ICSharpCode.Decompiler.IL.Transforms.*
ICSharpCode.Decompiler.CSharp.StatementBuilder
枚举器的更好示例
简洁的代码片段
var numbers = new List<int> { 0, 1, 2 };
foreach (var num in numbers) Console.WriteLine(num);
编译为
System.Collections.Generic.List<int> list = new System.Collections.Generic.List<int>();
list.Add(0);
list.Add(1);
list.Add(2);
System.Collections.Generic.List<int> numbers = list;
System.Collections.Generic.List<int>.Enumerator enumerator = numbers.GetEnumerator();
try
{
while (enumerator.MoveNext())
{
int num = enumerator.Current;
System.Console.WriteLine(num);
}
}
finally
{
((System.IDisposable)enumerator).Dispose();
}
(如禁用所有转换设置时所示)
在for循环中:
就编译而言,与(除了放置 continue-label )相同,因此反编译器将根据 init-statement、condition 和 post-statement 之间的上下文相似性自由决定它可能是什么类型的循环,因此您甚至可以手动编写代码for (a; b; c) d
a; while (b) { d; c; }
var a = 0;
while (a < args.Length) {
Console.WriteLine(args[a]);
a++;
}
这将被检测为 for 循环(因为在 IL 中没有告知)
for (int a = 0; a < args.Length; a++)
{
System.Console.WriteLine(args[a]);
}
评论
在对 YellowAfterlife 的评论中,OP 说:
在您的屏幕截图中,它显示了一个 for 循环。但是在幕后,for 循环不用于 foreach,对吧?它使用 while 循环。
循环访问数组时,它不使用枚举器对象。它改用整数索引计数器变量。你知道,就像一个for循环。IL 使用 和 ,我们可以说是“goto”。当然,如果你坚持,你可以把它写成一个循环。OpCodes.Br_S
OpCodes.Blt_S
while
为了测试,我写了以下代码:
static void Main(string[] args)
{
var index = 0;
while (index < args.Length)
{
var arg = args[index];
Console.WriteLine(arg);
index++;
}
}
这是 ILSpy 的输出:
private static void Main(string[] args)
{
for (int index = 0; index < args.Length; index++)
{
Console.WriteLine(args[index]);
}
}
事实上,在 IL 中,检查是在循环之后移动的,并在开始时跳转到它。请记住,循环(与循环不同)应该在之前检查。请参阅 IL:while
do ... while
// for (int i = 0; i < args.Length; i++)
IL_0000: ldc.i4.0
IL_0001: stloc.0
// (no C# code)
IL_0002: br.s IL_0010
// loop start (head: IL_0010)
// Console.WriteLine(args[i]);
IL_0004: ldarg.0
IL_0005: ldloc.0
IL_0006: ldelem.ref
IL_0007: call void [mscorlib]System.Console::WriteLine(string)
// for (int i = 0; i < args.Length; i++)
IL_000c: ldloc.0
IL_000d: ldc.i4.1
IL_000e: add
IL_000f: stloc.0
// for (int i = 0; i < args.Length; i++)
IL_0010: ldloc.0
IL_0011: ldarg.0
IL_0012: ldlen
IL_0013: conv.i4
IL_0014: blt.s IL_0004
// end loop
// (no C# code)
IL_0016: ret
注意哪个获取数组的长度。ldlen
您可以在 ShatpLab 上验证此代码。
编译器正在优化对数组的访问。因此,我们可以争辩说编译器将我的循环变成了一个循环。while
for
评论