C# 源代码生成器

C# 源代码生成器

  • 什么是源生成器
  • 源生成器允许执行两个主要操作
  • Microsoft 文档模型图示
  • 常见方案
  • 源生成器的使用
    • 1、创建 .NET控制台应用程序
    • 2、创建源生成器项目
    • 3、在控制台程序中使用生成器项目
  • 结语

什么是源生成器

源生成器是由.NET Compiler Platform(“Roslyn”)SDK 附带。
通过源生成器,C# 开发人员可以在编译用户代码时检查用户代码。 生成器可以动态创建新的 C# 源文件,这些文件将添加到用户的编译中。 这样,代码可以在编译期间运行。 它会检查你的程序以生成与其余代码一起编译的其他源文件

  • 源生成器是 C# 开发人员可以编写的一种新组件

源生成器允许执行两个主要操作

1、检索表示正在编译的所有用户代码的编译对象。 可以检查此对象,并且可以编写适用于正在编译的代码的语法和语义模型的代码,就像现在使用分析器一样。

2、生成可在编译过程中添加到编译对象的 C# 源文件。 也就是说,在编译代码时,可以提供其他源代码作为编译的输入。

结合使用这两项操作能充分发挥源生成器的强大功能。 可以使用编译器在编译时构建的丰富元数据检查用户代码。 然后,生成器将 C# 代码发送回基于已分析数据的同一编译。 如果你熟悉 Roslyn 分析器,可以将源生成器视为可发出 C# 源代码的分析器。

Microsoft 文档模型图示

源生成器作为编译阶段运行
C# 源代码生成器_第1张图片

源生成器是由编译器与任何分析器一起加载的 .NET Standard 2.0 程序集。 它在可以加载和运行 .NET Standard 组件的环境中使用。

目前只有 .NET Standard 2.0 程序集可以用作源生成器。

常见方案

  • 运行时反射
  • 处理 MSBuild 任务
  • 交织中间语言 (IL)

可用于检查用户代码,并基于当今技术所使用的分析生成信息或代码。
详细内容参考Microsoft 文档

源生成器的使用

1、创建 .NET控制台应用程序

不使用顶级语句, 经典格式是必需的

强行使用顶级语句会报:
错误 CS0759 没有为分部方法“Program.HelloFrom(string)”的实现声明找到定义声明
方法未声明的错误

namespace SourceCodeConsole
{
    public partial class Program
    {
        static void Main(string[] args)
        {
            HelloFrom("Generated Code");
        }
        static partial void HelloFrom(string name);
    }
}

2、创建源生成器项目

引入NuGet包:
Microsoft.CodeAnalysis.CSharp
Microsoft.CodeAnalysis.Analyzers

继承 ISourceGenerator 并实现接口方法,也要为实现类添加[Generator]特性支持

using Microsoft.CodeAnalysis;
using System.Diagnostics;

namespace HelloSourceCode
{
    [Generator]
    public class HelloSourceGenerator : ISourceGenerator
    {
        public void Execute(GeneratorExecutionContext context)
        {
            // 找到主方法
            var mainMethod = context.Compilation.GetEntryPoint(context.CancellationToken);

            // 构建源代码
            string source = $@"// 
using System;

namespace {mainMethod.ContainingNamespace.ToDisplayString()}
{{
    public static partial class {mainMethod.ContainingType.Name}
    {{
        static partial void HelloFrom(string name) =>
            Console.WriteLine($""Generator says: Hi from '{{name}}'"");
    }}
}}
";
            var typeName = mainMethod.ContainingType.Name;
            // 将源代码添加到编译中
            context.AddSource($"{typeName}.g.cs", source);
        }

        public void Initialize(GeneratorInitializationContext context)
        {
            // 不需要初始化

            // 附加调试器进程
            //if (!Debugger.IsAttached)
            //{
            //    Debugger.Launch();
            //}
        }
    }
}

context 对象中,我们可以访问编译的入口点或 Main 方法。 mainMethod 实例是一个 IMethodSymbol,它表示一个方法或类似方法的符号(包括构造函数、析构函数、运算符或属性/事件访问器)。 Microsoft.CodeAnalysis.Compilation.GetEntryPoint 方法返回程序的入口点的 IMethodSymbol。 其他方法使你可以查找项目中的任何方法符号。 在此对象中,我们可以推理包含的命名空间(如果存在)和类型。 此示例中的 source 是一个内插字符串,它对要生成的源代码进行模板化,其中内插的缺口填充了包含的命名空间和类型信息。 使用提示名称将 source 添加到 context。 对于此示例,生成器创建一个新的生成的源文件,其中包含控制台应用程序中 partial 方法的实现。 可以编写源生成器来添加任何喜欢的源。

GeneratorExecutionContext.AddSource 方法中的 hintName 参数可以是任何唯一名称。 通常为该名称提供显式 C# 文件扩展名,例如 ".g.cs"".generated.cs"。 该文件名有助于将文件标识为正在生成源。

关于 Initialize 方法一般是不用初始化的,上面可以看到我是写了一段代码的,作用是进入调试,这样做的主要原因是,不进行附加是无法进行调试的

3、在控制台程序中使用生成器项目

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

新引用不是传统的项目引用,必须手动编辑以包含 OutputItemType 和 ReferenceOutputAssembly 属性。

  • 运行结果
    C# 源代码生成器_第2张图片

  • 查看生成的代码
    C# 源代码生成器_第3张图片

结语

本文学术内容皆引用于 Microsoft 文档
部分详细内容,本篇不再说明,Microsoft 文档说得很明确:文档地址
https://learn.microsoft.com/zh-cn/dotnet/csharp/roslyn-sdk/

你可能感兴趣的:(C#)