C# - 获取枚举描述 - 使用增量源生成器

news/2025/3/26 9:33:27/文章来源:https://www.cnblogs.com/broadm/p/18786843

前言

C# 获取枚举描述的方法有很多, 常用的有通过 DescriptionAttribute 反射获取, 进阶的可以加上缓存机制, 减少反射的开销。今天我们还提供一种更加高效的方法,通过增量源生成器生成获取枚举描述的代码。这是在编译层面实现的, 无需反射, 性能更高。

本文的演示代码基于 VS2022 + .NET 8.0 + .NET Standard 2.0

1. 基本反射

这种方法是最常用的方法, 但是反射开销比较大。

public enum Color
{[Description("红色")]Red,[Description("绿色")]Green,[Description("蓝色")]Blue
}public static string GetDescription(Color color)
{var fieldInfo = typeof(Color).GetField(color.ToString());var descriptionAttribute = fieldInfo.GetCustomAttribute<DescriptionAttribute>();return descriptionAttribute?.Description;
}

2. 反射 + 缓存

缓存机制可以减少反射的开销, 避免反射过于频繁。

private static readonly Dictionary<Color, string> _descriptionCache = new Dictionary<Color, string>();public static string GetDescription(Color color)
{if (_descriptionCache.TryGetValue(color, out var description)){return description;}var fieldInfo = typeof(Color).GetField(color.ToString());var descriptionAttribute = fieldInfo.GetCustomAttribute<DescriptionAttribute>();description = descriptionAttribute?.Description;_descriptionCache.Add(color, description);return description;
}

3. 反射 + 缓存 + 泛型类 (推荐)

泛型可以减少代码重复。下面的代码为基本实现, 没有考虑线程安全问题。线程安全问题可以通过锁机制解决。可以使用静态构造函数初始化缓存。或者使用 ConcurrentDictionary 代替 Dictionary。或者使用 Lazy 代替缓存。

public class EnumDescription<T> where T : Enum
{private static readonly Dictionary<T, string> _descriptionCache = new Dictionary<T, string>();public static string GetDescription(T value){if (_descriptionCache.TryGetValue(value, out var description)){return description;}var fieldInfo = typeof(T).GetField(value.ToString());var descriptionAttribute = fieldInfo.GetCustomAttribute<DescriptionAttribute>();description = descriptionAttribute?.Description;_descriptionCache.Add(value, description);return description;}
}

4. 增量源生成器 (消除反射)

创建增量源生成器类库项目 (.NET Standard 2.0)

  1. 创建一个基于 .NET Standard 2.0 的类库项目名为: SourceGenerator

  2. 添加 NuGet 包 Microsoft.CodeAnalysis.CSharp 版本 4.8.0

<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.8.0" /></ItemGroup>
</Project>
  1. 添加 EnumDescriptionGenerator 类, 实现 IIncrementalGenerator 接口
using System.Linq;
using System.Text;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text;[Generator]
public class EnumDescriptionGenerator : IIncrementalGenerator
{public void Initialize(IncrementalGeneratorInitializationContext context){var enumDeclarations = context.SyntaxProvider.CreateSyntaxProvider(predicate: (syntaxNode, _) => syntaxNode is EnumDeclarationSyntax,transform: (generatorSyntaxContext, _) =>{var enumDeclaration = (EnumDeclarationSyntax)generatorSyntaxContext.Node;var enumSymbol = generatorSyntaxContext.SemanticModel.GetDeclaredSymbol(enumDeclaration) as INamedTypeSymbol;return new { EnumDeclaration = enumDeclaration, EnumSymbol = enumSymbol };}).Where(t => t.EnumSymbol != null).Collect();var compilationAndEnums = context.CompilationProvider.Combine(enumDeclarations);context.RegisterSourceOutput(compilationAndEnums, (sourceProductionContext, tuple) =>{var compilation = tuple.Left;var enums = tuple.Right;foreach (var item in enums){var enumDeclaration = item.EnumDeclaration;var enumSymbol = item.EnumSymbol;if (!enumSymbol.GetMembers("GetDescription").Any()){var source = GenerateSourceCode(enumSymbol);sourceProductionContext.AddSource($"{enumSymbol.Name}Descriptions.g.cs", SourceText.From(source, Encoding.UTF8));}}});}// 生成枚举描述扩展方法的代码private static string GenerateSourceCode(INamedTypeSymbol enumSymbol){var enumName = enumSymbol.Name;var namespaceName = enumSymbol.ContainingNamespace?.ToString() ?? "Global";var sb = new StringBuilder();sb.AppendLine($"namespace {namespaceName};");sb.AppendLine($"public static partial class {enumName}Extensions");sb.AppendLine("{");sb.AppendLine($"    public static string GetDescription(this {enumName} value) =>");sb.AppendLine("        value switch");sb.AppendLine("        {");// 4. 遍历枚举成员foreach (var member in enumSymbol.GetMembers().Where(m => m.Kind == SymbolKind.Field)){var description = member.GetAttributes().FirstOrDefault(a => a.AttributeClass?.Name == "DescriptionAttribute")?.ConstructorArguments.FirstOrDefault().Value?.ToString()?? member.Name;sb.AppendLine($"            {enumName}.{member.Name} => \"{description}\",");}sb.AppendLine("            _ => string.Empty");sb.AppendLine("        };");sb.AppendLine("}");return sb.ToString();}
}

创建控制台主项目 MainProject

  1. 使用 .NET 8.0 , 引用 SourceGenerator 项目, 注意引用方式如下:
<Project Sdk="Microsoft.NET.Sdk"><PropertyGroup><OutputType>Exe</OutputType><TargetFramework>net8.0</TargetFramework><ImplicitUsings>enable</ImplicitUsings><Nullable>enable</Nullable></PropertyGroup><ItemGroup><ProjectReference Include="..\SourceGenerator\SourceGenerator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" /></ItemGroup></Project>
  1. MainProject 中使用生成的枚举描述扩展方法
namespace MainProject;class Program
{static void Main(){foreach (var color in Enum.GetValues<Color>()){Console.WriteLine(color.GetDescription());}Console.ReadKey();}
}
  1. 编译运行, 编译器会自动生成枚举描述扩展方法的代码。

演示程序截图:

image

image

总结

通过增量源生成器, 我们可以在编译期自动生成获取枚举描述的代码, 无需反射, 性能更高。

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

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

相关文章

实现页面动效的几种方式

本文列举五种页面加载动效的方式 1.使用transition组件实现页面动效 2.创建观察器IntersectionObserver结合animation实现动态效果 3.创建观察器IntersectionObserver结合指令实现懒加载动态效果 4.使用css伪元素结合css3动画实现动态效果 5.监听滚动事件结合animation实现动态…

记录:Cadence virtuoso IC617 个性化配置

本文主要记录如何对Cadence virtuoso IC617个性化配置,如原理图设置、仿真波形窗口的背景、线条等,并且每次打开时能自动加载,无需每次手动设置。网上相关内容比较杂,并且会踩一些坑,因此这里做一个记录方便下次查看。 环境:VMware + CentOS 7 软件:Cadence virtuoso IC…

revit 二次开发之收集器、过滤器和选择器

一、收集器 主要是在文档中,收集出所需要的元素和对象,在revit的定义中,收集器是一个可以迭代的对象。1.1 FilteredElementCollector名称 功能FilteredElementCollector(Document)从文档种收集所需要的元素FilteredElementCollector(Document, ElementId) 从文档和试图种收集…

超 400 人团队律所,如何用 NocoBase 高效管理律师提成?

400+人律所因业务扩张,传统薪酬工具无法应对案件多样、职级复杂、规则高频调整等问题,导致效率低、错漏多。借助 NocoBase 零代码平台动态配置规则,自动化处理数据、实时风控,提成核算效率提升 6倍,实现零错误率,预计规避超 50 万元损失。原文链接:https://www.nocobase…

9.9K star!大模型原生即时通信机器人平台,这个开源项目让AI对话更智能!

"😎高稳定、🧩支持插件、🦄多模态 - 大模型原生即时通信机器人平台"嗨,大家好,我是小华同学,关注我们获得“最新、最全、最优质”开源项目和高效工作学习方法"😎高稳定、🧩支持插件、🦄多模态 - 大模型原生即时通信机器人平台"项目亮点 🔥…

静态代码分析工具SAST与CodeQL区别有哪些?

静态代码分析工具SAST 静态软件安全测试工具在不需要执行程序的情况下,获得程序编译时信息,并根据这些信息对特定的漏洞模式进行检测,从而完成软件的安全分析。静态分析考虑了程序所有可能的运行情况,稳妥的分析策略使得分析结果具备可靠性。 静态分析的本质是建立程序的一…

ASE20N45-ASEMI智能照明专用ASE20N45

ASE20N45-ASEMI智能照明专用ASE20N45编辑:ll ASE20N45-ASEMI智能照明专用ASE20N45 型号:ASE20N45 品牌:ASEMI 封装:TO-220F 批号:最新 最大漏源电流:20A 漏源击穿电压:450V RDS(ON)Max:0.30Ω 引脚数量:3 沟道类型:N沟道MOS管、中低压MOS管 漏电流:ua 特性:N沟道…

Graylog日志系统部署

一、下载GrayLog5.1.2一键安装脚本及安装下载链接 链接:https://share.weiyun.com/mPeVDR7C 密码:p3srz7 二、上安装包上传至服务器上后解压 三、授权脚本777执行权限 chmod 777 GrayLogServer5.1.2_install.sh 四、执行 sh GrayLogServer5.1.2_install.sh脚本 五、检查mo…

WPF 和 Avalonia 开发者的 html css 前端指南 Canvas 篇

本文主要是向大家列出 WPF 和 Avalonia 的 Canvas 在 html 和 css 的实现方法。WPF 和 Avalonia 开发者的 html css 前端指南 Canvas 篇笔者前端框架使用的是 Vue3 + Deno。 笔者主要会以 Avalonia 作为 C# 技术部分的示例。 本文主要是向大家列出 WPF 和 Avalonia 的 Canvas 在…

C#/.NET/.NET Core技术前沿周刊 | 第 31 期(2025年3.17-3.23)

前言 C#/.NET/.NET Core技术前沿周刊,你的每周技术指南针!记录、追踪C#/.NET/.NET Core领域、生态的每周最新、最实用、最有价值的技术文章、社区动态、优质项目和学习资源等。让你时刻站在技术前沿,助力技术成长与视野拓宽。欢迎投稿、推荐或自荐优质文章、项目、学习资源等…

Fx5u写入参数后报警 erwa.cn 二娃备忘

Fx5u写入参数后报警 erwa.cn 二娃备忘