首页 > 其他分享 >学习Source Generators之HelloWorld

学习Source Generators之HelloWorld

时间:2024-03-28 11:37:16浏览次数:23  
标签:编译 void 生成器 HelloWorld Source Generators context Hello

介绍

源生成器是 C# 开发人员可以编写的一种新组件,允许执行两个主要操作:

  1. 检索表示正在编译的所有用户代码的编译对象。 可以检查此对象,并且可以编写适用于正在编译的代码的语法和语义模型的代码,就像现在使用分析器一样。
  2. 生成可在编译过程中添加到编译对象的 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文件。
image.png
双击打开可以看到生成的代码。并且会提示该文件是自动生成的,无法编辑。
可以看到,文件中我们实现了部分类Program中的部分方法Hello。
image.png

运行项目

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

注意事项

细心的同学可能会看到我们编译的时候会出现一个警告:
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提示。
image.png
点击OK即可进入调试模式。
image.png
如果不需要点击Cancel则可以跳过。

结语

本文初步的了解了SourceGenerator的功能以及使用和调试的方式,后面的文章我们再来逐步深入学习。

标签:编译,void,生成器,HelloWorld,Source,Generators,context,Hello
From: https://www.cnblogs.com/fanshaoO/p/18101185

相关文章

  • Scala第十二章节(Source读取数据的功能、写入数据的功能以及学员成绩表案例)
    章节目标掌握Source读取数据的功能掌握写入数据的功能掌握学员成绩表案例1.读取数据在Scala语言的Source单例对象中中,提供了一些非常便捷的方法,从而使开发者可以快速的从指定数据源(文本文件,URL地址等)中获取数据,在使用Source单例对象之前,需要先导包,即......
  • Xilinx ZYNQ 7000+Vivado2015.2系列(三)之HelloWorld实验(最小系统)(纯PS)
    前言:使用的板子是zc702。用Vivado的IP核搭建最小系统,包括ARM核(CPUxc7z020),DDR3(4×256M),一个UART串口(MiniUSB转串口),纯PS,通过串口打印出HelloWorld,工程虽小,五脏俱全,算是一种朝圣。配置要和板子对应,大家注意修改。操作步骤:硬件部分1.新建Vivado工程选择芯片型号xc7z020clg484_1......
  • 2024-03-26 16:26:50.745 [main] INFO c.a.d.s.b.a.DruidDataSourceAutoConfigure -
    2024-03-2616:42:38.759[main]INFOc.a.d.s.b.a.DruidDataSourceAutoConfigure-InitDruidDataSource2024-03-2616:42:43.114[main]INFOcom.alibaba.druid.pool.DruidDataSource-{dataSource-1}inited2024-03-2616:42:47.348[main]INFOcom.alibaba.druid.po......
  • SpringBoot3项目使用Knife4j时访问doc.html出现Knife4j文档请求异常且开发者工具网络
    1.在各个pom.xml中替换Knife4j的依赖版本,升级为4.0以上,如果找不到依赖可以在Maven配置中多添加几个镜像,或者使用汉化插件重启IDEA;<dependency><groupId>com.github.xiaoymin</groupId><artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId......
  • drf : source,定制序列化字段以及反序列化新增。局部钩子(validate_字段名),全局钩子(va
    source,SerializerMethodField,局部钩子,全局钩子serialzer.py:source用处对应字段:起别名,用处2对应方法:在表模型中定义一个方法,source可以与其关联用处3对应方法:可以当做字段第三种方法的扩展用法:使用程度高。model.pyfromdjango.dbimportmodels#Createyourmo......
  • dotNet符号文件(pdb),符号包(snupkg)和SourceLink
    前言本文的主题是VisualStudio调试NuGet包,以及符号包的概念,如何去发布一个NuGet包,让我们的NuGet包支持SouceLink,这些都是我们开发中比较容易忽视的内容,但是熟悉了以后可以让我们在调试中得心应手,也是开源项目开发的基础篇。demo源代码在文末pdb符号文件程序数......
  • WPF Add ResourceDictionary file and declared in app.xaml
    //AddresourcedictionaryfilenamedBrushes.xaml<ResourceDictionaryxmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"><LinearG......
  • wpf add resource dynamically in cs file
    //xaml<Windowx:Class="WpfApp12.MainWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:d="http://schemas.mic......
  • @Autowired,@Resource,@Value,@Lazy注入的核心逻辑原理
    classDefaultListableBeanFactoryextendsAbstractAutowireCapableBeanFactory{@Override@NullablepublicObjectresolveDependency(DependencyDescriptordescriptor,StringrequestingBeanName,Set<String>autowiredBeanNames,TypeConverter......
  • @Autowired注解与@Resource注解
    1.包来源@Autowired注解是spring框架自带的。@Resource注解是JDK扩展包中,使用需要引入(jdk8-11不需要引入)。2.装配规则@Autowired注解默认是根据类型byType装配,如果想根据名称装配,需要配合@Qualifier注解一起使用。@Resource注解默认根据名称byName装配,未指定name时,使用属性名......