源码生成器(Source Generator)是 C#编译器的一个新特性,开发者可以使用编译器生成的元数据检查用户代码,并生成附加的源文件,与程序的其他部分一起编译。

受 F#类型提供程序的启发,C#源码生成器的目标也是为了启用元编程,只是以一种完全不同的方式。实际上,F#类型提供程序在内存中触发类型、属性和方法,而源码生成器是将 C#代码重新加入编译过程。

源码生成器不能修改已有代码,只能向编译添加新代码。源码生成器的另一个限制是它不对其他源码生成器生成的代码起作用。这样可以确保每个代码生成器将看到相同的编译输入,而不管应用程序的顺序是怎样的。有趣的是,源码生成器并不局限于检查源代码及其相关的元数据,它们还可以访问其他文件。

具体来说,源码生成器并不是代码重写工具,比如优化器或代码注入器,也不是用来创建新的语言特性的,尽管这在技术上来说是可行的。源码生成器的使用场景包括自动接口实现、数据序列化,等等。在源码生成器指南中可以找到更多应用场景,其中还包含了讨论内容。

源码生成器与 Roslyn 代码分析器有很大的关系,这从它的接口定义可以很明显地看出来:

namespace Microsoft.CodeAnalysis
{public interface ISourceGenerator{void Initialize(InitializationContext context);void Execute(SourceGeneratorContext context);}
}

编译器调用 Initialize 方法,生成器注册一些稍后将会调用的回调函数。代码生成发生在 Execute 方法里,它的参数是一个 SourceGeneratorContext 对象,该对象提供对当前 Compilation 对象的访问。

namespace Microsoft.CodeAnalysis
{public readonly struct SourceGeneratorContext{public ImmutableArray<AdditionalText> AdditionalFiles { get; }public CancellationToken CancellationToken { get; }public Compilation Compilation { get; }public ISyntaxReceiver? SyntaxReceiver { get; }public void ReportDiagnostic(Diagnostic diagnostic) { throw new NotImplementedException(); }public void AddSource(string fileNameHint, SourceText sourceText) { throw new NotImplementedException(); }}
}

可以修改 SourceGeneratorContext 对象,使用 AddSource 来包含其他代码。正如上面提到的,源码生成器不仅限于 C#文件。这从 AdditionalFiles 就可以看出来,它支持传给编译器的任意文件。

综上所述,要为“hello world”程序定义一个普通的源码生成器可以这样:

using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Text;namespace SourceGeneratorSamples
{[Generator]public class HelloWorldGenerator : ISourceGenerator{public void Execute(SourceGeneratorContext context){// begin creating the source we'll inject into the users compilationvar sourceBuilder = new StringBuilder(@"
using System;
namespace HelloWorldGenerated
{public static class HelloWorld{public static void SayHello() {Console.WriteLine(""Hello from generated code!"");Console.WriteLine(""The following syntax trees existed in the compilation that created this program:"");
");// using the context, get a list of syntax trees in the users compilationvar syntaxTrees = context.Compilation.SyntaxTrees;// add the filepath of each tree to the class we're buildingforeach (SyntaxTree tree in syntaxTrees){sourceBuilder.AppendLine($@"Console.WriteLine(@"" - {tree.FilePath}"");");}// finish creating the source to injectsourceBuilder.Append(@"}}
}");// inject the created source into the users compilationcontext.AddSource("helloWorldGenerator", SourceText.From(sourceBuilder.ToString(), Encoding.UTF8));}public void Initialize(InitializationContext context){// No initialization required for this one}}
}

微软已经发布了更多的介绍性示例,向开发人员展示如何使用这个新特性。

源代码生成器可在.NET 5 预览版和最新的 Visual Studio 预览版中使用。这个特性仍然处于早期阶段,它的 API 和特性可能会在将来的版本中发生变化。

Source Generator:C# 9 将迎来编译时元编程相关推荐

  1. 【Groovy】编译时元编程 ( 编译时方法注入 | 使用 buildFromSpec、buildFromString、buildFromCode 进行方法注入 )

    文章目录 一.在 MyASTTransformation#visit 方法中进行方法注入 1.使用 new AstBuilder().buildFromSpec 进行方法注入 2.使用 new Ast ...

  2. 【Groovy】编译时元编程 ( 编译时方法拦截 | 在 MyASTTransformation#visit 方法中进行方法拦截 )

    文章目录 一.在 MyASTTransformation#visit 方法中进行方法拦截 二.完整代码示例及进行编译时处理的编译过程 1.Groovy 脚本 Groovy.groovy 2.ASTTr ...

  3. 【Groovy】编译时元编程 ( ASTTransformation#visit 方法简介 | org.codehaus.groovy.ast.ModuleNode 脚本节点 )

    文章目录 一.ASTTransformation#visit 方法简介 二.org.codehaus.groovy.ast.ModuleNode 脚本节点 一.ASTTransformation#vi ...

  4. 【Groovy】编译时元编程 ( 方法拦截时用到的 AST 语法树节点 MethodNode 节点 | MethodNode 节点分析 | MethodNode 节点中的BlockStatement)

    文章目录 一.方法拦截时用到的 AST 语法树节点 MethodNode 节点 二.MethodNode 节点分析 三.MethodNode 节点中的 BlockStatement 集合 一.方法拦截 ...

  5. 【Groovy】编译时元编程 ( AST 语法树分析 | ClassNode 根节点 | 方法 Methods 节点 | 字段 Fields 节点 | 属性 Properties 节点 )

    文章目录 一.AST 语法树分析 一.AST 语法树分析 在上一篇博客 [Groovy]编译时元编程 ( 编译时元编程引入 | 声明需要编译时处理的类 | 分析 Groovy 类的 AST 语法树 ) ...

  6. 【Groovy】编译时元编程 ( 编译时元编程引入 | 声明需要编译时处理的类 | 分析 Groovy 类的 AST 语法树 )

    文章目录 一.编译时元编程引入 二.声明需要编译时处理的类 三.分析 Groovy 类的 AST 语法树 一.编译时元编程引入 在之前的 " [Groovy]MOP 元对象协议与元编程 &q ...

  7. 【Groovy】Groovy 语言特点简介 ( 支持 Java 语法 | 支持 Java 虚拟机 | Groovy 语言是动态语言 | Groovy 扩展 JDK | 编译时元编程 )

    文章目录 一.Groovy 支持 Java 语法 二.Groovy 支持 Java 虚拟机 三.Groovy 语言是 动态语言 四.Groovy 扩展 JDK 五.Groovy 编译时元编程 一.Gr ...

  8. 【Groovy】编译时元编程 ( 利用注解进行 AST 语法树转换 | 定义注解并使用 GroovyASTTransformationClass 注明 AST 转换接口 | AST 转换接口实现 )

    文章目录 一.利用注解进行 AST 语法树转换 1.定义注解并使用 GroovyASTTransformationClass 注明 AST 转换接口 2.AST 转换接口实现 3.定义 Groovy ...

  9. 【Groovy】编译时元编程 ( ASTTransformation#visit 方法中访问 Groovy 类、方法、字段、属性 | 完整代码示例及进行编译时处理的编译过程 )

    文章目录 一.ASTTransformation#visit 方法中访问 Groovy 类.方法.字段.属性 二.完整代码示例及进行编译时处理的编译过程 1.Groovy 脚本 Groovy.groo ...

最新文章

  1. Docker(六):Docker 仓库管理与镜像加速
  2. ASP编程常用的15个非常有用的代码及用法
  3. 【Git Bash】在window 下,设置打开gitbash快捷键、修改默认路径
  4. 摸清全国农村集体家底-农业大健康:产权改革谋定清产核资
  5. 力扣——字符串转换整数(atoi)
  6. 伯克利人工智能导论课开放:视频、PPT和练习都在这 | 资源
  7. python基于pillow库的简单图像处理
  8. NetBean快捷键
  9. CF 705A Hulk
  10. pdf怎么添加水印?
  11. 机器人导论 学习笔记2 - 运动学(D-H模型)
  12. $body.on(‘click‘,‘button‘,function() {中.on在这里是什么意思的
  13. LapSRN tensorflow版本环境配置
  14. 《Linux就该这么学》培训笔记_ch00_认识Linux系统和红帽认证
  15. Elasticsearch 索引模板
  16. Python自定义一个异常类【注释详细】
  17. 如何制定客户留存策略_网店如何提升买家留存率
  18. 武汉星起航:亚马逊产品listing中几个小技巧实现流量关联
  19. OID的获取及使用方法
  20. WPF开发之Prism详解【内附源码】

热门文章

  1. getElementByClassName()不兼容的解决办法
  2. Java的原始数据类型一共就8个
  3. c#3.0关于JSON简单操作的实用帮助类(泛型实现)
  4. 云计算基础知识:CPU虚拟化
  5. 这一新的可视化方法教你优雅地探索相关性
  6. jquery mobile页面切换效果(Flip toggle switch)(注:jQuery移动使用的数据属性的列表。 )...
  7. DNS(三)--子域授权和视图
  8. 了解android应用开发的更多方面有更好的认识
  9. 对症下药教你清除电脑中的木马
  10. 使用SQL语句获取SQL Server数据库登录用户权限