欢迎来到科站长!

C#教程

当前位置: 主页 > 软件编程 > C#教程

详解C#中有趣的 SourceGenerator生成器

时间:2024-11-29 16:19:19|栏目:C#教程|点击:

一:背景

1. 讲故事

前些天在看 AOT的时候关注了下 源生成器,挺有意思的一个东西,今天写一篇文章简单的分享下。

二:源生成器探究之旅

1. 源生成器是什么

简单来说,源生成器是Roslyn编译器给程序员开的一道口子,在这个口子里可以塞入一些自定义的cs代码,让Roslyn编译器在编译代码的时候顺带给一起处理了,简单的说就是 夹带私货 ,但古话又说 师不顺路, 医不叩门,所以还是比较尴尬的,看一下官方给的图,图中的橙色区域就是夹带的私货。

有些朋友肯定好奇,这玩意有什么用?其实在AOT领域中,JsonSerializer 就使用了 SourceGeneration 来给序列化的类型(WeatherForecast)生成元数据,参考代码如下:

[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(WeatherForecast))]
internal partial class SourceGenerationContext : JsonSerializerContext
{
}

2. 一个简单的例子

上面的例子不过多深入,先看看怎么实现0到1的问题,这里使用官方例子,用钩子来实现 分布方法 的方法体。

新建 SourceGenerator 类库项目

这里面的source就是钩子代码,不过目前只能是.NET Standard 2.0项目,应该是要达到最大的兼容性,参考代码如下:

namespace SourceGenerator
{
    [Generator]
    public class HelloSourceGenerator : ISourceGenerator
    {
        public void Execute(GeneratorExecutionContext context)
        {
            // Find the main method
            var mainMethod = context.Compilation.GetEntryPoint(context.CancellationToken);
            // Build up the source code
            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;
            // Add the source code to the compilation
            context.AddSource($"{typeName}.g.cs", source);
        }
        public void Initialize(GeneratorInitializationContext context)
        {
            // No initialization required for this one
        }
    }
}

新建 Example_21_15 项目

这是一个控制台程序,引用刚才的项目,并声明部分方法 HelloFrom,参考代码如下:

namespace Example_21_15
{
    partial class Program
    {
        static void Main(string[] args)
        {
            HelloFrom("Generated Code");
            Console.ReadLine();
        }
        static partial void HelloFrom(string name);
    }
}

要记住在 Example_21_15.csproj 中 Include 时要额外增加两个参数,参考如下:

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

配置好之后就可以把程序跑起来了,可以看到方法体确实是钩子中的代码。

三:Roslyn如何夹带私货

1. windbg调试

究竟是如何夹带私货,本质上是 Roslyn 内部的逻辑,现在的问题是如何给他挖出来了呢?这就需要使用强大的 windbg,采用exe启动劫持的方式洞察,流程步骤如下:

windbg 的exe劫持

资深的 WinDbg 玩家我相信都知道这个招数,我写了一个简单的 bat 脚本,对 dotnet.exe 进行启动拦截,要提醒的是有安全软件的话可以先卸载掉,以免出现无权限的问题。

SET ApplicationName=dotnet.exe
SET WinDbgPath=C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\windbg.exe
REG ADD "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\%ApplicationName%" /v debugger  /t REG_SZ  /d "%WinDbgPath%" /f
ECHO 已成功设置
PAUSE


上一篇:c# 线性回归和多项式拟合示例详解

栏    目:C#教程

下一篇:C# 变量作用域常用说明小结

本文标题:详解C#中有趣的 SourceGenerator生成器

本文地址:https://www.fushidao.cc/ruanjianbiancheng/1306.html

广告投放 | 联系我们 | 版权申明

申明:本站所有的文章、图片、评论等,均由网友发表或上传并维护或收集自网络,属个人行为,与本站立场无关。

如果侵犯了您的权利,请与我们联系,我们将在24小时内进行处理、任何非本站因素导致的法律后果,本站均不负任何责任。

联系QQ:257218569 | 邮箱:257218569@qq.com

Copyright © 2018-2025 科站长 版权所有冀ICP备14023439号