zoukankan      html  css  js  c++  java
  • [.NET大牛之路 006] 了解 Roslyn 编译器

    .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 提供的对应功能:

    来源:bit.ly/3AKnWyb

    • 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="..MyGeneratorMyGenerator.csproj"
            OutputItemType="Analyzer"
            ReferenceOutputAssembly="false" />
      </ItemGroup>
    

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

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

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

    作者:精致码农-王亮

    出处:http://cnblogs.com/willick

    联系:liam.wang@live.com

    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。如有问题或建议,请多多赐教,非常感谢。
  • 相关阅读:
    java线程间的协作
    java线程间的共享
    java多线程基础API
    java并发编程基础概念
    如何设计一套规则引擎系统
    Stream—一个早产的婴儿
    Java函数式编程的前生今世
    关于微服务划分的一些思考
    如何更优雅的给控制器 “减负”
    PHP简洁之道
  • 原文地址:https://www.cnblogs.com/willick/p/15102485.html
Copyright © 2011-2022 走看看