查看C#编译器预编译的代码

时间:2015-12-09 03:58:05

标签: c#

我怎样才能看到C#"语法糖"真的在幕后做什么? 例如,我知道C#lock语句已预编译为:

var temp = obj;

Monitor.Enter(temp);

try
{
    // body
}
finally
{
    Monitor.Exit(temp);
}

我知道如果你直接在类中声明并初始化实例字段(而不是在构造函数中),那么它就是用于在类中声明这些字段并在构造函数中初始化它们的语法糖。

我的问题是,如果我使用这些语法糖编写代码,我怎样才能看到生成的C#代码是什么?

是否有"预编译"首先将这些语法糖转换为更复杂的C#代码,然后将其编译成CIL的过程?

2 个答案:

答案 0 :(得分:10)

  

我知道C#lock语句已经预编译为......

不,你不知道,因为那是假的。首先,因为它不再是为锁生成的代码,而且自2009年以来就没有,其次,因为C#规范并没有说编译器需要生成该代码并进行编译。它表示编译器需要生成与给定代码语义等效的代码。

  

我的问题是,如果我使用这些语法糖编写代码,我怎样才能看到生成的C#代码是什么?

我写了许多用于完成这些转换的语义分析器。没有这样的“生成”代码。在语义分析传递运行时,编译器不能在文本级别上工作。它适用于代表代码的内部数据结构,并对其进行转换。

  

是否存在“预编译”过程,首先将这些语法糖转换为更复杂的C#代码,然后将其编译为CIL?

不是你感兴趣的级别,不是。

  

我怎样才能看到C#“语法糖”在幕后真正做了什么?

从github获取Roslyn源代码并仔细检查其中包含“降低”一词的任何内容。这就是你感兴趣的魔法发生的地方。 (“降低”意味着从高级构造开始,如可以为空的整数加法,并将其重写为一系列低级操作,如调用HasValue,等等。)我可能会建议您特别关注可空算术降级,用户定义的转换降低和LINQ表达式降低,因为这些降低通过有许多有趣的问题需要解决。

您可能也对我在相关主题上撰写的文章感兴趣:http://ericlippert.com/2014/04/28/lowering-in-language-design-part-one/

答案 1 :(得分:4)

没有生成语法糖结构的C#代码的进程,代码最终编译为IL(可能在两者之间使用抽象语法树)。

您可以查看任何.Net反编译器的结果 - ILDasm作为框架的一部分,或其他工具,如LinqPadILSpy

即。编译LinqPad IL查看器显示的样本的结果:

IL_0000:  nop         
IL_0001:  newobj      System.Object..ctor
IL_0006:  stloc.0     // temp
IL_0007:  ldloc.0     // temp
IL_0008:  call        System.Threading.Monitor.Enter
IL_000D:  nop         
IL_000E:  nop         
IL_000F:  nop         
IL_0010:  leave.s     IL_001C
IL_0012:  nop         
IL_0013:  ldloc.0     // temp
IL_0014:  call        System.Threading.Monitor.Exit
IL_0019:  nop         
IL_001A:  nop         
IL_001B:  endfinally  
IL_001C:  nop         
IL_001D:  ret   

的输出类似
var temp = new object();
lock(temp)
{

}

IL:

IL_0000:  nop         
IL_0001:  newobj      System.Object..ctor
IL_0006:  stloc.0     // temp
IL_0007:  ldc.i4.0    
IL_0008:  stloc.1     // <>s__LockTaken0
IL_0009:  ldloc.0     // temp
IL_000A:  dup         
IL_000B:  stloc.2     // CS$2$0000
IL_000C:  ldloca.s    01 // <>s__LockTaken0
IL_000E:  call        System.Threading.Monitor.Enter
IL_0013:  nop         
IL_0014:  nop         
IL_0015:  nop         
IL_0016:  leave.s     IL_0028
IL_0018:  ldloc.1     // <>s__LockTaken0
IL_0019:  ldc.i4.0    
IL_001A:  ceq         
IL_001C:  stloc.3     // CS$4$0001
IL_001D:  ldloc.3     // CS$4$0001
IL_001E:  brtrue.s    IL_0027
IL_0020:  ldloc.2     // CS$2$0000
IL_0021:  call        System.Threading.Monitor.Exit
IL_0026:  nop         
IL_0027:  endfinally  
IL_0028:  nop         
IL_0029:  ret  

请注意,对于每个&#34;句法糖&#34;有...#34;内部...&#34;帖子/文章准确描述了正在发生的事情(因为阅读C#规范有时可能很难)。即What really happens in a try { return x; } finally { x = null; } statement?