快乐地在AOT项目中用反射

news/2025/2/21 20:37:00/文章来源:https://www.cnblogs.com/axzxs2001/p/18730132

  反射是.NET开发的利器,但对于AOT来说,因为Native编译,所以反射的功能基本在AOT编译的项目中失效。办法总比困难多,这么好的东西不能扔掉,下面是“尽量”可以使用反射的例子,为什么“尽量”,看完下面的案例我们再做说明。

  在AOT项目中使用反射基本原理:利用源生成器,在build项目时,提前调用一下每个想要反射类型的GetMember。

  1、首先创建项目AOTReflectionHelper,这个项目中只有一个类AOTRefectionAttribute,并且这个类是分部类。

using System;namespace AOTReflectionHelper
{[AttributeUsage(AttributeTargets.Class)]public partial class AOTReflectionAttribute : Attribute{}
}

  2、然后创建AOTReflectionGenerator项目,这是一个源生成器的项目,项目文件如下:

<Project Sdk="Microsoft.NET.Sdk"><PropertyGroup><TargetFramework>netstandard2.0</TargetFramework><LangVersion>12.0</LangVersion><ImplicitUsings>enable</ImplicitUsings><Nullable>enable</Nullable></PropertyGroup><ItemGroup><PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.11.0-beta1.23525.2"><PrivateAssets>all</PrivateAssets><IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets></PackageReference><PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.9.0-2.final" /></ItemGroup>
</Project>

  源生器的实现代码如下,基本思路就是要生成上面AOTRefectionAttribute类的分部类,并在构造函数中调用项目中所有上了这个特性的择射GetMembers方法,因为在构建时处用反射获以成员,这部分没有问题。

using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using System.Text;namespace AOTReflectionGenerator
{[Generator]public class AOTReflectionGenerator : ISourceGenerator{public void Execute(GeneratorExecutionContext context){var types = GetAOTReflectionAttributeTypeDeclarations(context);var source = BuildSourse(types);context.AddSource($"AOTReflectionGenerator.g.cs", source);}string BuildSourse(IEnumerable<(string NamespaceName, string ClassName)> types){var codes = new StringBuilder();foreach (var type in types){codes.AppendLine($"   typeof({(type.NamespaceName != "<global namespace>" ? type.NamespaceName + "." : "")}{type.ClassName}).GetMembers();");}var source = $$"""
                         using System;[AttributeUsage(AttributeTargets.Class)]public partial class AOTReflectionAttribute:Attribute{public AOTReflectionAttribute(){{{codes}}}}""";         return source;}IEnumerable<(string NamespaceName, string ClassName)> GetAOTReflectionAttributeTypeDeclarations(GeneratorExecutionContext context){var list = new List<(string, string)>();foreach (var tree in context.Compilation.SyntaxTrees){var semanticModel = context.Compilation.GetSemanticModel(tree);var root = tree.GetRoot(context.CancellationToken);var typeDecls = root.DescendantNodes().OfType<TypeDeclarationSyntax>();foreach (var decl in typeDecls){// 获取类型的语义模型var symbol = semanticModel.GetDeclaredSymbol(decl);// 检查类型是否带有 AOTReflectionAttribute 特性if (symbol?.GetAttributes().Any(attr => attr.AttributeClass?.Name == "AOTReflectionAttribute") == true){// 处理带有 AOTReflectionAttribute 特性的类型var className = decl.Identifier.ValueText;var namespaceName = symbol.ContainingNamespace?.ToDisplayString();list.Add((namespaceName, className));}}}return list;}public void Initialize(GeneratorInitializationContext context){}}
}

  3、APITest项目是一个测试项目,项目文件如下,注意12行的源生器,要加两个属性。

<Project Sdk="Microsoft.NET.Sdk.Web"><PropertyGroup><TargetFramework>net8.0</TargetFramework><Nullable>enable</Nullable><ImplicitUsings>enable</ImplicitUsings><InvariantGlobalization>true</InvariantGlobalization><PublishAot>true</PublishAot></PropertyGroup><ItemGroup><ProjectReference Include="..\AOTReflectionGenerator\AOTReflectionGenerator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" /><ProjectReference Include="..\AOTReflectionHelper\AOTReflectionHelper.csproj" /></ItemGroup>
</Project>

  下面是实现代码,只要给想要反射的类型加上[AOTReflection]即可,我们就能第25行的方法中使用GetProperties了,否则这个方法返回的是个空数组。

using System.Text.Json.Serialization;
using System.Text;
using APITest.Models;
using AOTReflectionHelper;var builder = WebApplication.CreateSlimBuilder(args);builder.Services.ConfigureHttpJsonOptions(options =>
{options.SerializerOptions.TypeInfoResolverChain.Insert(0, AppJsonSerializerContext.Default);
});var app = builder.Build();
app.MapGet("/test", () =>
{var order = new Order { Name = "桂素伟", Age = 10, Birthday = DateTime.Now, Hobbies = new string[] { "足球", "代码" } };InvockMethod(order);return GetString(order);});
app.MapPost("/test", (Person person) =>
{return GetString(person);
});
string GetString<T>(T t) where T : Parent
{var sb = new StringBuilder();var pros = typeof(T)?.GetProperties();foreach (var pro in pros){      if (pro != null){if (pro.PropertyType.IsArray){var arr = pro.GetValue(t) as string[];sb.Append($"{pro?.Name}:{string.Join(",", arr)};");}else{sb.Append($"{pro?.Name}:{pro?.GetValue(t)};");}}}t.Print(sb.ToString());return sb.ToString();
}void InvockMethod<T>(T t)
{var method = typeof(T)?.GetMethod("Print");method?.Invoke(t, new object[] { "用反射调用Print" });
}
app.Run();[JsonSerializable(typeof(Person))]
[JsonSerializable(typeof(string[]))]
public partial class AppJsonSerializerContext : JsonSerializerContext
{
}
public partial class Parent
{public void Print(string content){      Console.WriteLine($"反射类型名:{GetType().Name},Print参数:{content}");}
}[AOTReflection]
public partial class Order : Parent
{public string Name { get; set; }public int Age { get; set; }public DateTime Birthday { get; set; }public string[] Hobbies { get; set; }
}namespace APITest.Models
{[AOTReflection]public class Person : Parent{public string Name { get; set; }public int Age { get; set; }public DateTime Birthday { get; set; }public string[] Hobbies { get; set; }}
}

 下面是Get的结果:

 下面是Post的结果:

   如果你详细尝试了上面的代码,可能就会理解到“尽量”了,因为.NET的反射非常强大,而使用这个方法,只能解决自己定义的类型的反射,因为是通过硬编码的方式,给自定义类型添加[AOTReflection]来完成的。不过提供的这个思路,你可以找到你所反射类型的特征,来针对性的在源生器里调用GetMembers,这就你就不受限制了。

  文章来源微信公众号

  想要更快更方便的了解相关知识,可以关注微信公众号 

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

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

相关文章

10亿数据,如何做迁移?

前言 某次金融系统迁移项目中,原计划8小时完成的用户数据同步迟迟未能完成。 24小时后监控警报显示:由于全表扫描SELECT * FROM users导致源库CPU几乎熔毁,业务系统被迫停机8小时。 这让我深刻领悟到——10亿条数据不能用蛮力搬运,得用巧劲儿递接! 今天这篇文章,跟大家一…

2025.2.21的模拟赛题“糖果”题解

2025.2.21的模拟赛题“糖果”题解为避免混淆,题目中的 \(X\) 在下文中为大写。 称前 \(k-1\) 次操作成为1号操作,第 \(k\) 次(不含)以后的操作称作2号操作 设 \(f_{x,i,j}\) 表示在 \(x\) 的子树中,1号操作做了 \(i\) 次,2号操作做了 \(j\) 次 限制就是\(X\) 到跟的路径不…

【飞书】Zabbix 7.2 告警与飞书机器人深度联动的全流程

midnight, the sound of a phone buzzing wakes you up abruptly. "又是告警!"你猛地从床上坐起,眼睛酸涩地盯着手机屏幕。作为一名运维工程师,这样的场景几乎成了家常便饭。无论是深夜还是清晨,系统故障总能找到你的联系方式,仿佛在提醒你:科技24小时不眠不休…

Windows平台上Idea、Tomcat、浏览器页面显示乱码问题解决

核心:乱码问题是由于字符编码不一致导致的,所以解决乱码问题的核心概念是统一字符编码。首先我们查看各个平台的字符编码: Windows cmd中输入chcp 命令查看(Windows系统默认字符编码时GBK)936 代表 GBK 65001 代表 UTF-8 注意:通过chcp命令如 chcp 65001 来更改字符编码只…

关于 xrlong 是神的论证

xrlong orz搜索内容搜索结果__________________________________________________________________________________________本文来自博客园,作者:CuFeO4,转载请注明原文链接:https://www.cnblogs.com/hzoi-Cu/p/18729881

idea插件+cursor使用技巧

cursorignore配置# Compiled files *.class *.jar *.war# Build directories target/ build/# IDE files .idea/ *.iml .vscode/ .project .classpath .settings/# Logs *.log logs/# Temporary files *.tmp *.bak *.swp# Maven specific .mvn/ mvnw mvnw.cmd# Spring Boot spe…

设计测试用例方法 -经验方法(三种)

1、基于经验的测试技术之错误推测法 错误推测法也叫错误猜测法,就是根据经验猜想,已有的缺陷,测试经验和失败数据等可能有什么问题并依此设计测试用例2、基于经验的测试技术之异常分析法 系统异常分析法就是针对系统有可能存在的异常操作、软硬件缺陷引起的故障进行分析,依…

DeepSeek 即将发布 5 个开源项目;Cartesia Voice Changer:声音转换、克隆和实时语音翻译丨日报

开发者朋友们大家好:这里是 「RTE 开发者日报」 ,每天和大家一起看新闻、聊八卦。我们的社区编辑团队会整理分享 RTE(Real-Time Engagement) 领域内「有话题的 技术 」、「有亮点的 产品 」、「有思考的 文章 」、「有态度的 观点 」、「有看点的 活动 」,但内容仅代表编辑…

Spring复习-注解开发

@Component 基本Bean注解,主要是使用注解的方式替代原有xml的 标签及其标签属性的配置使用@Component 注解替代标签使用:再类上使用 @Component(value = "userDao") //若没有配置value,默认使用类名作为beanName public class UserDaoImpl implements UserDao { }…

测试用例编写技巧

一、用例模板 模板1:模板2:模板3:二、用例的来源 (1)公司就有模板 (2)自己设计模板 (3)用例管理工具种导出模板 三、写用例的核心要素 用例编号、用例标题、前置条件、用例步骤,预期结果、优先级(必填) 系统名称、模块名称、用例创建时间、用例类型、实际结果、执行…

利用deepseek,体验ai写代码

vscode利用cline接入火山引擎deepseek,免费体验ai写代码。注册火山引擎,开通deepseek服务进行火山引擎官网https://www.volcengine.com/,进行注册。进入控制台,访问火山方舟在左侧下拉菜单选择开通管理,开通模型,由于我已经开通过R1模型,以DeepSeek-R1-Distill-Qwen-32B…

测试用例编写

模板1:模板2:模板3:二、用例的来源(1)公司就有模板(2)自己设计模板(3)用例管理工具种导出模板三、写用例的核心要素用例编号、用例标题、前置条件、用例步骤,预期结果、优先级(必填)系统名称、模块名称、用例创建时间、用例类型、实际结果、执行时间(非必填项)四…