C# 源代码生成器

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

什么是源生成器

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

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

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

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

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

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

Microsoft 文档模型图示

源生成器作为编译阶段运行

C# 源代码生成器_设计规范

源生成器是由编译器与任何分析器一起加载的 .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 = $@"// <auto-generated/>
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# 源代码生成器_源文件_02

  • 查看生成的代码
  • C# 源代码生成器_设计规范_03

结语

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


JavaScript中的几个tip,有用…… 1、JavaScript中的继承 在 JavaScript中实现继承的方法是:将子类的 prototype 属性设置为父类的对象。 例如,我有一个 Basket 类,继承 Hashtable 类: java代码: Basket.prototype = new Hashtable(); Basket.prot

spring boot post参数 springboot post单个参数

前提:客户端提交header,设置Content-Type类型为:application/json,这一项设置可有可无,但是为了避免出现其他不可预料的问题,事先说明,建议添加这一项请求头header设置。 一、使用@RequestParam@RequestMapping(value = "/login", method = RequestMethod.POST) public M