.NET大牛之路 • 王亮@精致码农 • 2021.07.09

维基百科对编译器的解释是:编译器是一种程序,它将某种编程语言编写的源代码(原始语言)转换成另一种编程语言(目标语言)。编译是从源代码(通常为高阶语言)到能直接被计算机或虚拟机执行的目标代码(通常为低阶语言或机器语言)的翻译过程。

在 .NET 平台中,在执行模型的不同阶段有两个不同的编译器:一个叫 Roslyn 编译器,负责把 C# 和 VB 代码编译为程序集;另一个叫 RyuJIT 编译器,负责把程序集中的 IL(中间语言) 代码编译为机器码。

本文先介绍 Roslyn 编译器。我们不必深入研究它的工作原理,但要了解它的工作机制,要知道它可以用来做什么事情。

最初 C# 语言的编译器是用 C++ 编写的,后来微软推出了一个新的用 C# 自身编写的编译器:Roslyn,它属于自举编译器。

所谓自举编译器就是指,某种编程语言的编译器就是用该语言自身来编写的。自举编译器的每个版本都是用该版本之前的版本来编译的,但它的第一个版本必须由其它语言编写的编译器来编译,比如 Roslyn 的第一个版本是由 C++ 编写的编译器来编译的。很多编程语言发展成熟后都会用该语言本身来编写自己的编译器,比如 C# 和 Go 语言。

在 .NET 平台,Roslyn 编译器负责将 C# 和 VB 代码编译为程序集。

大多数现有的传统编译器都是“黑盒”模式,它们将源代码转换成可执行文件或库文件,中间发生了什么我们无法知道。与之不同的是,Roslyn 允许你通过 API 访问代码编译过程中的每个阶段。

它的工作机制是管道式的,整个工作管道包含四个阶段,每个阶段都是一个独立的模块,每个模块都提供了相应的 API。集成开发环境(IDE)可以利用这些 API 提供方便的工具以提高开发效率,如代码高亮、智能提示、重构工具、性能分析工具等。此外,通过 Roslyn,开发者可以在自己的程序中使用编译器,将编译器作为一种服务来使用。

下图描绘了 Roslyn 工作管道的各个阶段和各阶段对应的 API,以及各 API 可为 IDE 提供的对应功能:

  • Parser(解析)阶段,根据语言语法对源代码进行解析,将源代码转换为层次化的标记集合,形成语法树。语法树 API 用于在源代码编辑器中格式化、着色和代码大纲。

  • Declaration(声明)阶段,分析所有引用和导入的元数据,形成层次化的符号表。在编辑器和对象浏览器中的 Navigation To 特性使用这个 API。

  • Bind(绑定)阶段,对标记集合和符号表进行匹配。编辑器中的 Find All ReferencesRenameQuick InfoExtract Method 等特性使用这个 API。

  • Emit(生成)阶段,生成 IL 托管模块,将一个或多个 IL 托管模块和嵌入资源合并成程序集。编辑器中的 Edit and Continue 利用这个特性完成一次新的编译。

Roslyn 是少数几个让你有机会观察所有编译阶段和中间结果的编译器之一,它提供的这些 API 可以为语言服务实现丰富的功能。例如,代码高亮使用语法树,对象浏览器使用分层符号表。

下面我们来做一个简单的示例,利用 Roslyn 提供的 API 来动态生成代码。

创建一个控制台应用程序 ConsoleApp,编辑 Program.cs 的代码如下:

using System;

namespace ConsoleApp
{
partial class Program
{
static void Main(string[] args)
{
HelloFrom("Generated Code"); Console.ReadKey();
} static partial void HelloFrom(string name);
}
}

再创建一个 .NET Standard 类库,取名 MyGenerator,并添加两个 NuGet 包,项目文件内容如下:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup> <ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.2" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.10.0" />
</ItemGroup> </Project>

然后在 MyGenerator 项目中添加一个 Generator.cs 文件,代码如下:

using Microsoft.CodeAnalysis;

namespace MyGenerator
{
[Generator]
public class Generator : ISourceGenerator
{
public void Initialize(GeneratorInitializationContext context)
{
} public void Execute(GeneratorExecutionContext context)
{
// find the main method
var mainMethod = context.Compilation.GetEntryPoint(context.CancellationToken); // build up the source code
string source = $@"
using System; namespace {mainMethod.ContainingNamespace.Name}
{{
public static partial class {mainMethod.ContainingType.Name}
{{
static partial void HelloFrom(string name)
{{
Console.WriteLine($""Generator says: Hi from '{{name}}'"");
}}
}}
}}
";
// add the source code to the compilation
context.AddSource("generatedSource", source);
}
}
}

这里的 source 是我们的动态组装的代码,在实际应用中还可以从数据库或文本中读取代码片段。

最后在 ConsoleApp 项目中引用 MyGenerator 类库,并参照如下代码设置 OutputItemTypeReferenceOutputAssembly 属性:

  <ItemGroup>
<ProjectReference Include="..\MyGenerator\MyGenerator.csproj"
OutputItemType="Analyzer"
ReferenceOutputAssembly="false" />
</ItemGroup>

运行 ConsoleApp,可以看到控制台输出如下内容:

Roslyn 的功能非常强大,这个示例只是演示了 Roslyn 的一个非常简单的功能和用途。

Roslyn 不只是一个编译器,还是一个现成的框架,它使得在 .NET 平台上创建自己的语言服务变得更加容易。你可以使用 Roslyn 编译器的 API 在 .NET 平台上开发一个完整的应用程序,甚至创建你自己的 IDE、编写你自己的编译器、解释器或分析器来编译和运行你自己的编程语言。

[.NET大牛之路 006] 了解 Roslyn 编译器的更多相关文章

  1. [.NET大牛之路 007] 详解 .NET 程序集

    .NET大牛之路 • 王亮@精致码农 • 2021.07.13 上一篇我们介绍了 Roslyn 编译器,我们知道,我们编写的 C#/VB 代码经过 Roslyn 编译器编译后会生成程序集文件.按照之前 ...

  2. [.NET大牛之路 005] .NET 的执行模型

    .NET大牛之路 • 王亮@精致码农 • 2021.07.06 前面我们介绍 .NET 历史时讲过,微软基于 .NET Framework 重新设计并创造了跨平台的 .NET Core,目前已经发展到 ...

  3. [ASP.NET MVC 大牛之路]01 - 开篇

    匆匆2014,转眼就到末尾了.在这一年,你还有哪事情些想做而没有做? 2014年在我身上发生了两件意义重大的事,一是正月初一宝宝出生,我升级成为了爸爸:二是进入了一家创业公司,成为了技术负责人. 去年 ...

  4. [ASP.NET MVC 大牛之路]02 - C#高级知识点概要(1) - 委托和事件

    在ASP.NET MVC 小牛之路系列中,前面用了一篇文章提了一下C#的一些知识点.照此,ASP.NET MVC 大牛之路系列也先给大家普及一下C#.NET中的高级知识点.每个知识点不太会过于详细,但 ...

  5. [ASP.NET MVC 大牛之路]03 - C#高级知识点概要(2) - 线程和并发

    本人博客已转移至:http://www.exblr.com/liam  我也想过跳过C#高级知识点概要直接讲MVC,但经过前思后想,还是觉得有必要讲的.我希望通过自己的经验给大家一些指引,带着大家一起 ...

  6. [ASP.NET 大牛之路]02 - C#高级知识点概要(1) - 委托和事件

    在ASP.NET MVC 小牛之路系列中,前面用了一篇文章提了一下C#的一些知识点.照此,ASP.NET MVC 大牛之路系列也先给大家普及一下C#.NET中的高级知识点.每个知识点不太会过于详细,但 ...

  7. Roslyn 编译器和RyuJIT 编译器

    Roslyn 编译器 https://msdn.microsoft.com/zh-cn/library/mt162308.aspx https://blogs.msdn.microsoft.com/d ...

  8. [ASP.Net] 转 > ASP.NET MVC 大牛之路

    URL: http://www.cnblogs.com/willick/ [ASP.NET MVC 大牛之路]01 - 开篇 [ASP.NET MVC 大牛之路]02 - C#高级知识点概要(1) - ...

  9. [.NET大牛之路 001] .NET 其名

    本文来自『.NET大牛之路』星球的分享 大家好,这是 .NET 大牛这路的第 1 篇文章.大家期待已久的课程今天正式开始了.既然我们整个体系课程都将围绕 .NET 展开,那我们今天就先聊一聊 .NET ...

随机推荐

  1. 生成工作区设置文件settings.json

    先Ctrl+Shift+P,然后输入setting搜索 找到工作区设置(工作区,仅在当前打开的界面下生效,文档位置与根目录/.vscode/setting.json(可自己创建,也可不创建)) 选择之 ...

  2. 单元测试布道二:在全新的 DDD 架构上进行单元测试

    目录 回顾 dotnet 单元测试相关的工具和知识 可测试性 不确定性/未决行为 依赖于实现:不可 mock 复杂继承/高耦合代码:测试困难 实战:在全新的 DDD 架构上进行单元测试 需求-迭代1: ...

  3. SpringBoot数据访问(三) SpringBoot整合Redis

    前言 除了对关系型数据库的整合支持外,SpringBoot对非关系型数据库也提供了非常好的支持,比如,对Redis的支持. Redis(Remote Dictionary Server,即远程字典服务 ...

  4. 重新点亮linux 命令树————帮助命令[一]

    前言 重新整理一下linux的命令. 正文 这里首先介绍帮助命令. 帮助命令常用的有三个: man help info 那么就来看下这三个. man 第一个man,man不是男人的意思,而是manua ...

  5. Windows10 上Docker 安装运行Consul

    背景简介 Consul是一种服务网格解决方案,提供具有服务发现,配置和分段功能的全功能控制平面. 这些功能中的每一个都可以根据需要单独使用,也可以一起使用以构建全服务网格. Consul需要数据平面并 ...

  6. 8、ITSM基本概念(2)

    6.流程和职能: 7.故障管理: (1)故障管理的输入和输出: (2)故障管理的目标: 优先解决影响度大的事件: (3)故障管理的指标: (4)故障管理的流程: 8.问题管理: (1)问题管理的输入和 ...

  7. 12、Linux磁盘设备基础知识(2)

    12.4.计算磁盘容量: 磁盘的大小=盘面大小*磁头数 盘面的大小=磁道大小*磁道数 磁道大小=512字节*扇区数 磁盘的大小=512字节*扇区数*磁道数*磁头数 磁盘的大小=柱面大小*柱面数 柱面大 ...

  8. 4、git和gitlab的配置(1)

    4.0.服务器说明: 服务器名称 ip地址 controller-node1 172.16.1.90 4.1.git介绍: 1.git分布式图: 2.git区域: 3.四种状态: 上面的操作在工作目录 ...

  9. 49、django工程(cookie+session)

    49.1.介绍: 1.cookie不属于http协议范围,由于http协议无法保持状态,但实际情况,我们却又需要"保持状态",因此cookie就是在这样一个场景下诞生. cooki ...

  10. Java中有趣的移位操作!彻底弄懂各个移位操作符的使用方式

    << <<: 左移运算,左移几位就补几个0 >> >>: 右移运算,为算术右移 如果数字为正数时,移位后在前面补0 如果数字为负数时,移位后在前面补1 ...