不使用语法糖将 IL 转换为 C#
Converting IL to C# with no syntactic sugar
我正在寻找一个程序,它会向我显示最低级别(即没有语法糖)的 C# 代码给定 IL 代码。
我尝试使用 .NET Reflector 查看一个 .exe 文件,该文件包含一个带有 foreach
循环的简单控制台应用程序,希望看到 GetEnumerator()
、MoveNext()
、Current
等,但是它显示为 foreach
循环。
有这样的程序吗?或者是否可以在 .NET Reflector 中 select "no syntactic sugar"?
ILSpy 的当前版本有一组相当大的 enabling/disabling 反编译器转换功能选项:
...
static void Main(string[] args) {
foreach (var arg in args) {
Console.WriteLine(arg);
}
}
...
如果需要,您可以通过剥离 ICSharpCode.Decompiler.IL.Transforms.*
和 ICSharpCode.Decompiler.CSharp.StatementBuilder
中的逻辑更进一步;也许打开一个问题询问,是否会感谢您的更改的 PR,因为这些 "rawness" 设置中的大多数都是最近才添加的。
枚举器的更好示例
一段简洁的代码
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 循环:
就编译而言,for (a; b; c) d
与 a; while (b) { d; c; }
相同(除了放置 continue-label),因此反编译器会自行决定它可能是哪种循环基于 init-statement、condition 和 post-statement 之间的上下文相似性,因此您甚至可以手动编写代码
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 说:
In your screenshot, it's showing a for loop. But behind the scenes a for loop is not used for a foreach, right? It uses a while loop.
迭代数组时不使用枚举器对象。它使用整数索引计数器变量代替。 你知道,就像一个 for 循环。 IL 使用 OpCodes.Br_S
和 OpCodes.Blt_S
,我们可以说它们是 "goto"。当然,如果你坚持的话,你可以把它写成一个 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 中,检查被移到循环之后,并在开始时跳转到它。请记住 while
循环(不同于 do ... while
循环)应该在之前进行检查。查看IL:
// 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
获取数组的长度。
你可以验证this code on ShatpLab.
编译器正在优化对数组的访问。因此,我们可以争辩说编译器将我的 while
循环变成了 for
循环。
我正在寻找一个程序,它会向我显示最低级别(即没有语法糖)的 C# 代码给定 IL 代码。
我尝试使用 .NET Reflector 查看一个 .exe 文件,该文件包含一个带有 foreach
循环的简单控制台应用程序,希望看到 GetEnumerator()
、MoveNext()
、Current
等,但是它显示为 foreach
循环。
有这样的程序吗?或者是否可以在 .NET Reflector 中 select "no syntactic sugar"?
ILSpy 的当前版本有一组相当大的 enabling/disabling 反编译器转换功能选项:
...
static void Main(string[] args) {
foreach (var arg in args) {
Console.WriteLine(arg);
}
}
...
ICSharpCode.Decompiler.IL.Transforms.*
和 ICSharpCode.Decompiler.CSharp.StatementBuilder
中的逻辑更进一步;也许打开一个问题询问,是否会感谢您的更改的 PR,因为这些 "rawness" 设置中的大多数都是最近才添加的。
枚举器的更好示例
一段简洁的代码
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 循环:
就编译而言,for (a; b; c) d
与 a; while (b) { d; c; }
相同(除了放置 continue-label),因此反编译器会自行决定它可能是哪种循环基于 init-statement、condition 和 post-statement 之间的上下文相似性,因此您甚至可以手动编写代码
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]);
}
在
In your screenshot, it's showing a for loop. But behind the scenes a for loop is not used for a foreach, right? It uses a while loop.
迭代数组时不使用枚举器对象。它使用整数索引计数器变量代替。 你知道,就像一个 for 循环。 IL 使用 OpCodes.Br_S
和 OpCodes.Blt_S
,我们可以说它们是 "goto"。当然,如果你坚持的话,你可以把它写成一个 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 中,检查被移到循环之后,并在开始时跳转到它。请记住 while
循环(不同于 do ... while
循环)应该在之前进行检查。查看IL:
// 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
获取数组的长度。
你可以验证this code on ShatpLab.
编译器正在优化对数组的访问。因此,我们可以争辩说编译器将我的 while
循环变成了 for
循环。