学习Source Generators之HelloWorld
介绍
源生成器是 C# 开发人员可以编写的一种新组件,允许执行两个主要操作:
- 检索表示正在编译的所有用户代码的编译对象。 可以检查此对象,并且可以编写适用于正在编译的代码的语法和语义模型的代码,就像现在使用分析器一样。
- 生成可在编译过程中添加到编译对象的 C# 源文件。 也就是说,在编译代码时,可以提供其他源代码作为编译的输入。
结合使用这两项操作能充分发挥源生成器的强大功能。 可以使用编译器在编译时构建的丰富元数据检查用户代码。 然后,生成器将 C# 代码发送回基于已分析数据的同一编译。 如果你熟悉 Roslyn 分析器,可以将源生成器视为可发出 C# 源代码的分析器。
源生成器作为编译阶段运行,如下所示:

源生成器是由编译器与任何分析器一起加载的 .NET Standard 2.0 程序集。 它在可以加载和运行 .NET Standard 组件的环境中使用。
注意:目前只能用 .NET Standard 2.0 程序集作源生成器。
实现Hello Wolrd
接下来开始使用Source Genertor实现我们我HelloWorld程序。
创建项目
创建一个HelloWorld的控制台项目。
将Program改成部分类。并添加一个Hello的部分方法。
namespace HelloWorld
{
partial class Program
{
static void Main(string[] args)
{
Hello("Generated Code");
}
static partial void Hello(string name);
}
}
接下来创建一个netstandard2.0的类库。
命名成HelloWorld.Analysis。添加依赖Microsoft.CodeAnalysis.CSharp和Microsoft.CodeAnalysis.Analyzers。需要设置PrivateAssets=“all”。
完整配置如下:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>latest</LangVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.2.0" PrivateAssets="all" />
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" PrivateAssets="all" />
</ItemGroup>
</Project>
这里需要注意的是Microsoft.CodeAnalysis.CSharp不宜使用太高版本,太高版本可能会出现无法正常生成代码的情况。
在HelloWorld项目中添加HelloWorld.Analysis的项目依赖。并设置OutputItemType="Analyzer" ReferenceOutputAssembly="false"
如下所示:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\HelloWorld.Analysis\HelloWorld.Analysis.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
</ItemGroup>
</Project>
实现Generator
在HelloWorld.Analysis中添加HelloSourceGenerator类。继承并实现ISourceGenerator接口。并且需要在类上加上Generator特性标签。
然后再Exceute中实现我们的代码生成逻辑。
using Microsoft.CodeAnalysis;
namespace HelloWorld.Analysis
{
[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 Hello(string name) =>
Console.WriteLine($""Hello: '{{name}}'"");
}}
}}
";
var typeName = mainMethod.ContainingType.Name;
context.AddSource($"{typeName}.g.cs", source);
}
public void Initialize(GeneratorInitializationContext context)
{
}
}
}
在上面代码中,通过Compilation获取Program程序入口的信息。包括命名空间,类名等等等。最后AddSource($"{typeName}.g.cs", source);表示我们把代码生成到.g.cs后缀的文件中。
编译
接下来启动编译项目,在HelloWorld的依赖项的分析器中会出现一个Program.g.cs文件。

双击打开可以看到生成的代码。并且会提示该文件是自动生成的,无法编辑。
可以看到,文件中我们实现了部分类Program中的部分方法Hello。

运行项目
启动项目,可以看到我们成功输出由Source Genertor生成的Hello方法的实现。

注意事项
细心的同学可能会看到我们编译的时候会出现一个警告:
warning RS1036: “HelloWorld.Analysis.HelloSourceGenerator”: 包含分析器或源生成器的项目应指定属性“true”
建议我们在项目中添加EnforceExtendedAnalyzerRules的属性。
当我们添加这个属性后这个警告就会消失。
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>latest</LangVersion>
<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.2.0" PrivateAssets="all" />
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" PrivateAssets="all" />
</ItemGroup>
</Project>
设置 EnforceExtendedAnalyzerRules 为 true 的作用就是提供 API 禁用分析功能,防止写出分析器不支持的代码。设置 EnforceExtendedAnalyzerRules 为 true 时,有部分的 API 将会被提示不可用。具体API可以看: https://raw.githubusercontent.com/dotnet/roslyn-analyzers/2b6ab8d727ce73a78bcbf026ac75ea8a7c804daf/src/Microsoft.CodeAnalysis.Analyzers/Core/AnalyzerBannedSymbols.txt
Debug
前面我们直接编译就生成了代码,打断点也不会触发。那么我们如何调试SourceGenerator呢?
可以使用Debugger.Launch();来触发调试。
在我们的运行代码中加入这一行。在编译时会触发调试提示。
using Microsoft.CodeAnalysis;
using System.Diagnostics;
namespace HelloWorld.Analysis
{
[Generator]
public class HelloSourceGenerator : ISourceGenerator
{
public void Execute(GeneratorExecutionContext context)
{
Debugger.Launch(); //触发Debug
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 Hello(string name) =>
Console.WriteLine($""Hello: '{{name}}'"");
}}
}}
";
var typeName = mainMethod.ContainingType.Name;
context.AddSource($"{typeName}.g.cs", source);
}
public void Initialize(GeneratorInitializationContext context)
{
}
}
}
加入代码后,重新执行项目编译操作。会弹出Debugger提示。

点击OK即可进入调试模式。

如果不需要点击Cancel则可以跳过。
结语
本文初步的了解了SourceGenerator的功能以及使用和调试的方式,后面的文章我们再来逐步深入学习。
学习Source Generators之HelloWorld的更多相关文章
- Hadoop学习笔记(5) ——编写HelloWorld(2)
Hadoop学习笔记(5) ——编写HelloWorld(2) 前面我们写了一个Hadoop程序,并让它跑起来了.但想想不对啊,Hadoop不是有两块功能么,DFS和MapReduce.没错,上一节我 ...
- 跟着刚哥学习Spring框架--创建HelloWorld项目(一)
1.Spring框架简介 Spring是一个开源框架,Spring是在2003年兴起的一个轻量级的开源框架,由Rod johnson创建.主要对JavaBean的生命周期进行管理的轻量级框架,Spri ...
- Spring MVC 学习笔记1 - First Helloworld by Eclipse【& - java web 开发Tips集锦】
Spring MVC 学习笔记1 - First Helloworld by Eclipse reference:http://www.gontu.org 1. 下载 Spring freamwork ...
- 基于 Source Generators 做个 AOP 静态编织小实验
0. 前言 上接:用 Roslyn 做个 JIT 的 AOP 作为第二篇,我们基于Source Generators做个AOP静态编织小实验. 内容安排如下: source generators 是什 ...
- .NET初探源代码生成(Source Generators)
前言 Source Generators顾名思义代码生成器,可进行创建编译时代码,也就是所谓的编译时元编程,这可让一些运行时映射的代码改为编译时,同样也加快了速度,我们可避免那种昂贵的开销,这是有价值 ...
- 使用 MVVM Toolkit Source Generators
关于 MVVM Toolkit 最近 .NET Community Toolkit 发布了 8.0.0 preview1,它包含了从 Windows Community Toolkit 迁移过来的以下 ...
- springmvc学习(一)helloworld实例
今天介绍的是springmvc的学习,越来越多的企业开始选择springmvc+mybatis来构建系统架构,在电商热门的今天,springmvc+mybatis已成为电商项目架构的很好搭配.Spri ...
- springboot学习(一)——helloworld
以下内容,如有问题,烦请指出,谢谢 springboot出来也很久了,以前零散地学习了不少,不过很长时间了都没有在实际中使用过了,忘了不少,因此要最近准备抽时间系统的学习积累下springboot,给 ...
- 〖C语言学习笔记 〗(一) HelloWorld
前言 本文为c基础入门学习笔记 正文 HelloWorld #include <stdio.h> //标准输出流 int main() //每种语言都有一个执行入口,main方法就是其一 ...
- Android HIDL学习(2) ---- HelloWorld【转】
本文转载自: 写在前面 程序员有个癖好,无论是学习什么新知识,都喜欢以HelloWorld作为一个简单的例子来开头,咱们也不例外. OK,咱这里都是干货,废话就不多说啦,学习HIDL呢咱们还是需要一些 ...
随机推荐
- 【Unity3D】分离路面导航
1 前言 导航系统中介绍了导航的基本用法,但是不能解决分离路面导航问题.如下图,胶囊体需要导航到立方体位置,但是路面不连续,烘焙(Bake)导航网格后,断开处也没有导航网格,因此,胶囊体导航到分离 ...
- springboot+vue+elementui实现文件上传下载删除DEMO
说明 前面搜索了几个关于springboot+vue+elementui上传下载的文章,感觉写的都不尽如人意.要么是功能不完善,不好用.再者就是源码提供的实在差劲,都不完整.一气之下,自己搞了一个实用 ...
- Springboot+LayUI实现一个简易评论系统
说明 这是个简单的评论系统,目的在于介绍简单的评论和回复功能.同时基于此可以扩展更全面的.自定义的评论系统,本工程仅供学习交流使用.喜欢的朋友给个赞:) 源码 https://gitee.com/in ...
- C++ { } 的使用场景
{} 可以用于初始化 C++11 中的变量,就像它们用于初始化 C 中的数组和结构一样. {} 主要是为了提供语法的一致性(使用 {} 初始化将在所有上下文中都有效,而使用赋值运算符或()初始化将在特 ...
- Qt+MPlayer音乐播放器开发笔记(二):交叉编译MPlayer以及部署到开发板播放演示
前言 在ubuntu上arm交叉编译MPlayer播放器,并部署到开发板播放音乐. Demo Mplayer MPlayer是一款开源多媒体播放器,以GNU通 ...
- 【LeetCode排序专题02】最小k个数,关于快速排序的讨论
最小k个数 https://leetcode.cn/problems/smallest-k-lcci/ 输入整数数组 arr ,找出其中最小的 k 个数.例如,输入4.5.1.6.2.7.3.8这8个 ...
- 老生常谈的iOS- weak原理,你真的懂得还是为了应付面试
前言 weak对于iOS开发来说只要解决一些对象相互引用的时候,避免出现强强引用,对象不能被释放,出现内存泄露的问题. weak 关键字的作用域弱引用,所引用对象的计数器不会加一,并在引用对象被释放的 ...
- C C++内功心法-基础篇
大家好,今天给大家讲讲C C++的一些基础语法,小编整理了一些简单入门基础知识,对于我们编程也有很多的帮助. C++ cin C++中的cin是一个 istream对象,从标准输入中读取数据,在ios ...
- 【Azure 环境】使用Azure中的App Service部署Web应用,以Windows为主机系统是否可以启动防病毒,防恶意软件服务呢(Microsoft Antimalware)?
问题描述 使用Azure中的App Service部署Web应用,以Windows为主机系统是否可以启动防病毒,防恶意软件服务呢? Microsoft Antimalware for Azure is ...
- Go语言的100个错误使用场景(55-60)|并发基础
目录 前言 8. 并发基础 8.1 混淆并发与并行的概念(#55) 8.2 认为并发总是更快(#56) 8.3 分不清何时使用互斥锁或 channel(#57) 8.4 不理解竞态问题(#58) 8. ...