跟着AI学习C# Day28

📅 Day 28:C# 源生成器(Source Generators)与编译时元编程

✅ 学习目标:

  • 理解什么是 源生成器(Source Generator)
  • 掌握如何在 编译阶段生成 C# 代码,而不是运行时动态处理;
  • 使用源生成器替代运行时反射和表达式树,提升性能;
  • 实现一个基于源生成器的自动化 DTO 映射器;
  • 理解 Roslyn 编译器的工作流程;
  • 掌握源生成器开发的基本结构、语法分析技巧;
  • 构建高性能、零运行时开销的实用工具;
  • 了解源生成器的适用场景和局限性。

🧠 一、什么是源生成器?

源生成器(Source Generator) 是 C# 9 引入的一项新特性,它允许你在 编译阶段自动生成 C# 代码。这些代码会被加入到你的项目中,并参与正常的编译流程,就像你手动编写的一样。

✅ 优势:

特点说明
零运行时开销所有逻辑在编译期完成
更快的执行速度替代运行时反射、Expression 树等慢速操作
更好的可读性自动生成的代码是静态的,易于调试和优化
IDE 支持好可跳转、可重构、可提示

🔁 二、源生成器 vs 运行时反射

对比项源生成器运行时反射
执行时机编译期运行期
性能零开销相对较慢
可维护性生成代码可见动态逻辑难以追踪
调试支持完全支持不易调试
是否需要额外依赖可能需要缓存或委托生成

🧩 三、源生成器基本结构

要创建一个源生成器,你需要实现 IIncrementalGenerator 接口(.NET 6+ 推荐),或者继承 ISourceGenerator(旧版 .NET 5)。

我们以 .NET 6+IIncrementalGenerator 为例。

步骤概览:

  1. 创建类库项目并添加 SDK 支持;
  2. 添加对 Microsoft.CodeAnalysis.CSharpMicrosoft.CodeAnalysis.Analyzers 的引用;
  3. 实现 IIncrementalGenerator
  4. 注册分析器并生成代码;
  5. 在主项目中引用该源生成器 DLL;
  6. 查看生成的代码(在 obj/Debug/generators 文件夹下)。

🛠️ 四、实战示例:自动映射 DTO 与实体类

我们来实现一个简单的 DTO 映射器源生成器,它可以自动生成如下代码:

public static class PersonMapper
{public static PersonDto ToDto(this Person person){return new PersonDto{Name = person.Name,Age = person.Age};}
}

1️⃣ 定义 Attribute(用于标记需生成映射的类型)

[AttributeUsage(AttributeTargets.Class)]
public class GenerateMappingAttribute : Attribute
{public Type TargetType { get; }public GenerateMappingAttribute(Type targetType){TargetType = targetType;}
}

2️⃣ 创建源生成器类

[Generator]
public class MappingSourceGenerator : IIncrementalGenerator
{public void Initialize(IncrementalGeneratorInitializationContext context){var classes = context.SyntaxProvider.CreateSyntaxProvider(predicate: static (s, _) => IsCandidateForMapping(s),transform: static (ctx, _) => GetSemanticTargetForMapping(ctx)).Where(static m => m is not null)!;context.RegisterSourceOutput(classes, GenerateCode);}private static bool IsCandidateForMapping(SyntaxNode node){return node is ClassDeclarationSyntax { AttributeLists.Count: > 0 };}private static ClassDeclarationSyntax GetSemanticTargetForMapping(GeneratorSyntaxContext context){var classDeclaration = (ClassDeclarationSyntax)context.Node;foreach (var attributeList in classDeclaration.AttributeLists){foreach (var attribute in attributeList.Attributes){if (context.SemanticModel.GetSymbolInfo(attribute).Symbol is IMethodSymbol attributeSymbol &&attributeSymbol.ContainingType.ToDisplayString() == "GenerateMappingAttribute"){return classDeclaration;}}}return null;}private void GenerateCode(SourceProductionContext context, ClassDeclarationSyntax classDecl){var className = classDecl.Identifier.Text;var namespaceName = classDecl.Parent is NamespaceDeclarationSyntax ns? ns.Name.ToString(): "";var source = $@"
using System;namespace {namespaceName}
{{public static partial class {className}Mapper{{public static {className}Dto ToDto(this {className} model){{return new {className}Dto{{Name = model.Name,Age = model.Age}};}}}}
}}";context.AddSource($"{className}Mapper.g.cs", SourceText.From(source, Encoding.UTF8));}
}

3️⃣ 主项目使用方式

[GenerateMapping(typeof(PersonDto))]
public class Person
{public string Name { get; set; }public int Age { get; set; }
}public class PersonDto
{public string Name { get; set; }public int Age { get; set; }
}

编译后会自动生成 PersonMapper 类!


🧱 五、Roslyn 编译器基础概念

源生成器基于 Roslyn 编译器平台,它是 C# 和 VB.NET 的开源编译器框架。

关键组件:

名称作用
SyntaxTree表示解析后的语法树
SemanticModel提供语义信息(如变量类型、方法重载等)
Compilation表示整个项目的编译过程
ISymbol表示各种符号(类、方法、属性等)
GeneratorExecutionContext提供上下文信息用于生成代码

💡 六、源生成器适用场景

场景示例
DTO 映射自动生成 ToDto() 方法
ORM 属性绑定自动绑定数据库字段
JSON 序列化避免反射,直接生成序列化代码
本地化资源访问自动生成强类型资源访问器
日志记录自动生成日志包装器
AOP 拦截器生成代理类,替代运行时代理
枚举扩展自动生成枚举描述、转换方法

⚠️ 七、源生成器的限制

限制说明
不能访问运行时数据无法根据用户输入动态生成代码
不适合复杂逻辑复杂业务逻辑更适合运行时处理
调试困难生成的代码位于 obj 文件夹,不易修改
学习曲线陡峭需要熟悉 Roslyn API 和语法树结构
仅适用于编译时已知结构的类型无法处理运行时动态类型

🧪 八、查看生成的代码

生成的代码会放在你的项目目录下的:

[obj]\[Debug|Release]\net8.0\generators\run\Your.SourceGenerator\

你可以在这里看到所有由源生成器生成的 .g.cs 文件。


💪 九、构建高性能应用的建议

技术建议
尽量用源生成器替代反射减少运行时性能损耗
用源生成器预计算常量逻辑如路由匹配、配置加载
结合 Source Generator + Partial Method分离手写逻辑与生成逻辑
使用缓存机制如果必须运行时处理,缓存结果
使用 Source Generator 生成测试桩自动生成 Mock 数据、单元测试辅助类

📝 小结

今天你学会了:

  • 什么是 源生成器(Source Generator)
  • 掌握了如何在 编译阶段生成 C# 代码
  • 实现了一个基于源生成器的 自动 DTO 映射器
  • 理解了 Roslyn 编译器的基本工作原理;
  • 掌握了源生成器开发的基本结构和语法分析技巧;
  • 学会了如何构建高性能、零运行时开销的实用工具;
  • 了解了源生成器的适用场景与限制。

源生成器是一项强大的工具,尤其适用于构建高性能框架、减少运行时反射依赖、以及自动完成重复性代码编写任务。


🧩 下一步学习方向(Day 29)

明天我们将进入本次 挑战的 最终总结篇 —— C# 综合进阶知识回顾与职业发展建议,你将学到:

  • 所学知识点的系统回顾;
  • 如何构建完整的 C# 开发能力体系;
  • C# 高级开发者必备技能清单;
  • C# 在 Web、桌面、游戏、AI 等领域的应用场景;
  • 如何规划自己的 C# 职业成长路径;
  • 如何准备技术面试、参与开源项目、打造个人影响力。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/news/910273.shtml
繁体地址,请注明出处:http://hk.pswp.cn/news/910273.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

设计模式精讲 Day 4:建造者模式(Builder Pattern)

【设计模式精讲 Day 4】建造者模式(Builder Pattern) 文章简述: 在软件开发中,对象的构造过程往往复杂且容易出错,尤其是在对象包含多个可选参数或构建步骤时。建造者模式(Builder Pattern)正是…

如何轻松地将联系人从 iPhone 转移到 iPhone?

也许您升级到最新的 iPhone 型号,或者需要切换到另一部 iPhone 来工作。无论如何,您不能错过您的联系人,这对每个人来说都是最重要的数据。因此,今天我们将分享 5 种如何将联系人从 iPhone 转移到 iPhone 的方法,帮助您…

【51单片机简单的流水灯程序问题】2022-5-24

1.利用单片机的P2口接8个发光二极管。简单的流水灯程序问题-编程语言-CSDN问答 2.发光二极管自由闪烁(自己设计两种模式)。 3.可通过按键实现暂停、启动以及不用模式的切换。 4. 利用Proteus绘制电路原理图 5. 元件选型&#xff1…

第七节:Vben Admin 最新 v5.0 (vben5) 快速入门 - 用户管理(上)

Vben5 系列文章目录 💻 基础篇 ✅ 第一节:Vben Admin 最新 v5.0 (vben5) 快速入门 ✅ 第二节:Vben Admin 最新 v5.0 (vben5) 快速入门 - Python Flask 后端开发详解(附源码) ✅ 第三节:Vben Admin 最新 v5.0 (vben5) 快速入门 - 对接后端登录接口(上) ✅ 第四节:Vben Ad…

1572. 矩阵对角线元素的和

给你一个正方形矩阵 mat,请你返回矩阵对角线元素的和。 请你返回在矩阵主对角线上的元素和副对角线上且不在主对角线上元素的和。 示例 1: 输入:mat [[1,2,3],[4,5,6],[7,8,9]] 输出:25 解释:对角线的和为&#xf…

供应链场景使用ClickHouse最佳实践

一、概述 ClickHouse是一款由俄罗斯公司Yandex开发的开源列式数据库管理系统,以其高性能的分析查询能力和高压缩比著称。供应链场景中,数据量大且数据类型复杂,需要高效的数据存储和快速的查询性能,ClickHouse在这些方面具有显著…

RA4M2开发IOT(0)----安装e² studio

RA4M2开发IOT.0--安装e studio 概述视频教学样品申请安装 概述 瑞萨电子灵活配置软件包 (FSP) 是用于嵌入式系统设计的高质量增强型软件包,支持瑞萨电子 RA 产品家族 Arm 微控制器,提供用户友好的界面且可灵活扩展,确保从入门级到高性能的整…

【Ambari3.0.0 部署】Step2—免密登陆认证-适用于el8

如果有其他系统部署需求可以参考原文 戳我->所有组件编译教程 戳我->获取部署源代码 一、免密登录认证 🔐 在多台服务器协同工作的环境中,免密登录(SSH 免密认证)是一种常见的优化手段,能够极大地提升运维效率&…

网站自助广告投放系统源码 附安装教程(源码下载)

网站自助广告投放系统源码 全自动无人化出售网站广告位 站长必备 源码测试可用,部分加密。感兴趣自行下载 源码下载:https://download.csdn.net/download/m0_66047725/91093092 更多资源下载:关注我 图片:

日常运维问题汇总-15

42.SD开票计划产生的预收款在正式开票时未自动清账 统驭科目(应收、预收)对应的字段状态组中附加科目设置销售订单字段设置为了隐藏导致,更改为“可选输入项” 43.MIGO取消凭证时,用户反馈发现除一行外,其它都不能取消…

【设计模式】6.原型模式

every blog every motto: You can do more than you think. https://blog.csdn.net/weixin_39190382?typeblog 0. 前言 原型模式 1. 基础 import copyclass Resume:def __init__(self, name):self.name nameself.sex Noneself.age Noneself.time_area Noneself.compan…

【算法 day08】LeetCode 151.翻转字符串里的单词 |卡码网:55.右旋转字符串

151.翻转字符串里的单词 题目链接 | 文档讲解 |视频讲解 : 链接 1.思路: 1.去除字符串头尾的空格 ,使用库函数 trim() 2.对字符串进行分割,使用库函数split() 3.创建StringBuilder sb&#x…

【WordPress优化插件】WPOPT v2.4.7

WPOPT插件,是由本站开发的一款WordPress优化插件,能对WordPress底层功能进行优化,支持功能开关,系统加速等功能。 2.0版本全新发布,采用vite打包,界面采用Vue3+element-plus制作。无论是外观,还是框架功能,都是空前的强大。 功能更多,更强,是所有WordPress网站都值得…

如何使用 mkimage 工具生成 uImage 文件(RISC-V 环境)

一、mkimage 命令参数详解 在 RISC-V Linux 环境下,使用 U-Boot 的 mkimage 工具生成 uImage 的基本命令格式如下: mkimage -A riscv -O linux -T kernel -C compression -a load_addr -e entry_addr -n "描述信息" -d Image uImage核心参数…

React Native 搭建iOS与Android开发环境

目录 第一步 第二步 一、必须安装的工具 二、具体安装步骤 1. 安装 Homebrew 切换国内源和其他配置: 2. 安装 node 3.下载watchman 4. Ruby 5.CocoaPods 配置环境 6. jdk 7. 配置git 开发环境 第三步——启动项目(可以忽略) 1…

Vue 简写形式全解析:清晰记忆指南

Vue 简写形式全解析:清晰记忆指南 Vue 中的各种简写形式确实容易混淆,我将它们系统化整理,并提供了多种记忆方法,帮助你轻松掌握! 一、核心简写形式汇总表 完整形式简写形式适用场景记忆技巧v-bind:attribute:attribute动态绑定属性: 像链条,表示"绑定"v-on:…

车载电子电器架构 --- 电子电气架构设计方案

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 做到欲望极简,了解自己的真实欲望,不受外在潮流的影响,不盲从,不跟风。把自己的精力全部用在自己。一是去掉多余,凡事找规律,基础是诚信;二是…

MVCC中read_view的核心参数解析与读操作流程实战

在数据库并发控制领域,MVCC(多版本并发控制)是实现高性能读写并发的关键技术。其中,read_view作为MVCC判断数据可见性的核心组件,其内部参数的设计直接影响着并发访问的行为。本文将深入解析read_view的三个核心参数&a…

从代码学习深度强化学习 - REINFORCE 算法 PyTorch版

文章目录 前言**一、 理论基础:什么是策略梯度?****1.1 基于价值 vs. 基于策略****1.2 策略梯度(Policy Gradient)****1.3 REINFORCE 算法:蒙特卡洛策略梯度****1.4 REINFORCE 算法流程****二、 PyTorch 代码实践****2.1 环境与辅助函数****2.2 核心算法实现****2.3 训练与…

CRMEB 代码规范指南:ThinkPHP6+Uni-app 架构下的开发标准

二、代码规范 2.1 Vue .1.1 代码结构 <template><div id"my-component"><DemoComponent /></div> </template><script> import DemoComponent from ../components/DemoComponentexport default {name: MyComponent,component…