查看C#编译器预编译了哪些代码

本文关键字:代码 编译 编译器 查看 | 更新日期: 2023-09-27 18:26:09

我如何才能看到C#"语法糖"在幕后真正在做什么?例如,我知道C#锁语句被预编译为:

var temp = obj;
Monitor.Enter(temp);
try
{
    // body
}
finally
{
    Monitor.Exit(temp);
}

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

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

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

查看C#编译器预编译了哪些代码

我知道C#锁语句是预先编译为…

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

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

我编写了很多进行这些转换的语义分析器。没有这样的"生成"代码。在语义分析过程运行时,编译器无法在文本级别上工作。它处理表示代码的内部数据结构,并转换这些数据结构。

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

没有达到你感兴趣的水平,没有。

我如何才能看到C#"语法糖"在幕后真正在做什么?

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

你可能还对我写的一篇关于相关主题的文章感兴趣:http://ericlippert.com/2014/04/28/lowering-in-language-design-part-one/

没有生成语法糖结构的C#代码的过程,代码最终编译到IL(可能中间有抽象语法树)。

您可以使用任何.Net反编译器(作为框架的一部分的ILDasm)或其他工具(如LinqPad或ILSpy)查看结果。

即编译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  

请注意,对于每一个"语法糖",都有"内部…"的帖子/文章,它们准确地描述了正在发生的事情(因为阅读C#规范有时可能很难)。也就是说,try{return x;}finally{x=null;}语句中到底发生了什么?

这个问题在谷歌上排名第一;c#预编译代码查看器";提示,所以我将在这里留下一个对我帮助很大的解决方案:https://sharplab.io/.您可以查看预编译的C#代码,而不需要";句法糖";带有设置:代码-C#;platfrom-默认;结果-C#。