AOT 的使用以及 .NET 与 Go 互相调用 SDK.NET 7 SDK、Desktop development with C workload。IDEVisual Studio 2022Desktop development with C workload是一个工具集里面包含 C 开发工具需要在Visual Studio Installer中安装如下图红框中所示。创建一个控制台项目首先创建一个 .NET 7 控制台项目名称为CsharpAot。打开项目之后基本代码如图所示我们使用下面的代码做测试public class Program { static void Main() { Console.WriteLine(C# Aot!); Console.ReadKey(); } }体验 AOT 编译这一步可以参考官方网站的更多说明本地 AOT 部署概述 - .NET | Microsoft Learn为了能够让项目发布时使用 AOT 模式需要在项目文件中加上PublishAottrue/PublishAot选项。然后使用 Visual Studio 发布项目。发布项目的配置文件设置需要按照下图进行配置。AOT 跟生成单个文件两个选项不能同时使用因为 AOT 本身就是单个文件。配置完成后点击发布然后打开Release目录会看到如图所示的文件。.exe是独立的可执行文件不需要再依赖.NET Runtime环境这个程序可以放到其他没有安装 .NET 环境的机器中运行。然后删除以下三个文件CsharpAot.exp CsharpAot.lib CsharpAot.pdb光用.exe即可运行其他是调试符号等文件不是必需的。剩下CsharpAot.exe文件后启动这个程序C# 调用库函数这一部分的代码示例是从笔者的一个开源项目中抽取出来的这个项目封装了一些获取系统资源的接口以及快速接入 Prometheus 监控。不过很久没有更新了最近没啥动力更新读者可以点击这里了解一下这个项目CZGL.SystemInfo/src/CZGL.SystemInfo/Memory at net6.0 · whuanle/CZGL.SystemInfo · GitHub因为后续代码需要所以现在请开启 “允许不安全代码”。本小节的示例是通过使用kernel32.dll去调用 Windows 的内核 API(Win32 API)调用GlobalMemoryStatusEx函数检索有关系统当前使用物理内存和虚拟内存的信息。使用到的 Win32 函数可参考GlobalMemoryStatusEx 函数 (sysinfoapi.h) - Win32 apps | Microsoft Learn关于 .NET 调用动态链接库的方式在 .NET 7 之前通过这样调用[DllImport(Kernel32.dll, CharSet CharSet.Auto, SetLastError true)] [return: MarshalAs(UnmanagedType.Bool)] internal static extern Boolean GlobalMemoryStatusEx(ref MemoryStatusExE lpBuffer);在 .NET 7 中出现了新的操作方式[LibraryImport]。文档是这样介绍的Indicates that a source generator should create a function for marshalling arguments instead of relying on the runtime to generate an equivalent marshalling function at run time. 指示源生成器应创建用于编组参数的函数而不是依赖运行库在运行时生成等效的编组函数。简单来说就是我们要使用 AOT 写代码然后代码中引用到别的动态链接库时需要使用[LibraryImport]引入这些函数。笔者没有在 AOT 下测试过[DllImport]读者感兴趣可以试试。新建两个结构体MEMORYSTATUS.cs、MemoryStatusExE.cs。MEMORYSTATUS.cspublic struct MEMORYSTATUS { internal UInt32 dwLength; internal UInt32 dwMemoryLoad; internal UInt32 dwTotalPhys; internal UInt32 dwAvailPhys; internal UInt32 dwTotalPageFile; internal UInt32 dwAvailPageFile; internal UInt32 dwTotalVirtual; internal UInt32 dwAvailVirtual; }MemoryStatusExE.cspublic struct MemoryStatusExE { /// summary /// 结构的大小以字节为单位必须在调用 GlobalMemoryStatusEx 之前设置此成员可以用 Init 方法提前处理 /// /summary /// remarks应当使用本对象提供的 Init 而不是使用构造函数/remarks internal UInt32 dwLength; /// summary /// 一个介于 0 和 100 之间的数字用于指定正在使用的物理内存的大致百分比0 表示没有内存使用100 表示内存已满。 /// /summary internal UInt32 dwMemoryLoad; /// summary /// 实际物理内存量以字节为单位 /// /summary internal UInt64 ullTotalPhys; /// summary /// 当前可用的物理内存量以字节为单位。这是可以立即重用而无需先将其内容写入磁盘的物理内存量。它是备用列表、空闲列表和零列表的大小之和 /// /summary internal UInt64 ullAvailPhys; /// summary /// 系统或当前进程的当前已提交内存限制以字节为单位以较小者为准。要获得系统范围的承诺内存限制请调用GetPerformanceInfo /// /summary internal UInt64 ullTotalPageFile; /// summary /// 当前进程可以提交的最大内存量以字节为单位。该值等于或小于系统范围的可用提交值。要计算整个系统的可承诺值调用GetPerformanceInfo核减价值CommitTotal从价值CommitLimit /// /summary internal UInt64 ullAvailPageFile; /// summary /// 调用进程的虚拟地址空间的用户模式部分的大小以字节为单位。该值取决于进程类型、处理器类型和操作系统的配置。例如对于 x86 处理器上的大多数 32 位进程此值约为 2 GB对于在启用4 GB 调整的系统上运行的具有大地址感知能力的 32 位进程约为 3 GB 。 /// /summary internal UInt64 ullTotalVirtual; /// summary /// 当前在调用进程的虚拟地址空间的用户模式部分中未保留和未提交的内存量以字节为单位 /// /summary internal UInt64 ullAvailVirtual; /// summary /// 预订的。该值始终为 0 /// /summary internal UInt64 ullAvailExtendedVirtual; internal void Refresh() { dwLength checked((UInt32)Marshal.SizeOf(typeof(MemoryStatusExE))); } }定义引用库函数的入口public static partial class Native { /// summary /// 检索有关系统当前使用物理和虚拟内存的信息 /// /summary /// param namelpBuffer/param /// returns/returns [LibraryImport(Kernel32.dll, SetLastError true)] [return: MarshalAs(UnmanagedType.Bool)] internal static partial Boolean GlobalMemoryStatusEx(ref MemoryStatusExE lpBuffer); }然后调用Kernel32.dll中的函数public class Program { static void Main() { var result GetValue(); Console.WriteLine($当前实际可用内存量{result.ullAvailPhys / 1000 / 1000}MB); Console.ReadKey(); } /// exception crefWin32Exception/exception public static MemoryStatusExE GetValue() { var memoryStatusEx new MemoryStatusExE(); // 重新初始化结构的大小 memoryStatusEx.Refresh(); // 刷新值 if (!Native.GlobalMemoryStatusEx(ref memoryStatusEx)) throw new Win32Exception(无法获得内存信息); return memoryStatusEx; } }使用 AOT 发布项目执行CsharpAot.exe文件。减少体积在前面两个例子中可以看到CsharpAot.exe文件大约在 3MB 左右但是这个文件还是太大了那么我们如何进一步减少 AOT 文件的大小呢读者可以从这里了解如何裁剪程序https://learn.microsoft.com/zh-cn/dotnet/core/deploying/trimming/trim-self-contained需要注意的是裁剪是没有那么简单的里面配置繁多有一些选项不能同时使用每个选项又能带来什么样的效果这些选项可能会让开发者用得很迷茫。经过笔者的大量测试笔者选用了以下一些配置能够达到很好的裁剪效果供读者测试。首先引入一个库ItemGroup PackageReference IncludeMicrosoft.DotNet.ILCompiler Version7.0.0-* / /ItemGroup接着在项目文件中加入以下选项!--AOT 相关-- PublishAottrue/PublishAot TrimModefull/TrimMode RunAOTCompilationTrue/RunAOTCompilation PublishTrimmedtrue/PublishTrimmed TrimmerRemoveSymbolstrue/TrimmerRemoveSymbols PublishReadyToRunEmitSymbolsfalse/PublishReadyToRunEmitSymbols DebuggerSupportfalse/DebuggerSupport EnableUnsafeUTF7Encodingtrue/EnableUnsafeUTF7Encoding InvariantGlobalizationtrue/InvariantGlobalization HttpActivityPropagationSupportfalse/HttpActivityPropagationSupport MetadataUpdaterSupporttrue/MetadataUpdaterSupport UseSystemResourceKeystrue/UseSystemResourceKeys IlcDisableReflection true/IlcDisableReflection最后发布项目。吃惊生成的可执行文件只有 1MB 了而且还可以正常执行。笔者注虽然现在看起来 AOT 的文件很小了但是如果使用到HttpClient、System.Text.Json等库哪怕只用到了一两个函数最终包含这些库以及这些库使用到的依赖生成的 AOT 文件会大得惊人。所以如果项目中使用到其他 nuget 包的时候别想着生成的 AOT 能小多少C# 导出函数这一步可以从时总的博客中学习更多跨语言调用C#代码的新方式-DllExport - InCerry - 博客园PS时总真的太强了。在 C 语言中导出一个函数的格式可以这样// MyCFuncs.h #ifdef __cplusplus extern C { // only need to export C interface if // used by C source code #endif __declspec( dllimport ) void MyCFunc(); __declspec( dllimport ) void AnotherCFunc(); #ifdef __cplusplus } #endif当代码编译之后我们就可以通过引用生成的库文件调用MyCFunc、AnotherCFunc两个方法。如果不导出的话别的程序是无法调用库文件里面的函数。因为 .NET 7 的 AOT 做了很多改进因此.NET 程序也可以导出函数了。新建一个项目名字就叫CsharpExport吧我们接下来就在这里项目中编写我们的动态链接库。添加一个CsharpExport.cs文件内容如下using System.Runtime.InteropServices; namespace CsharpExport { public class Export { [UnmanagedCallersOnly(EntryPoint Add)] public static int Add(int a, int b) { return a b; } } }然后在.csproj文件中加上PublishAot选项。然后通过以下命令发布项目生成链接库dotnet publish -p:NativeLibShared -r win-x64 -c Release看起来还是比较大为了继续裁剪体积我们可以在CsharpExport.csproj中加入以下配置以便生成更小的可执行文件。!--AOT 相关-- PublishAottrue/PublishAot TrimModefull/TrimMode RunAOTCompilationTrue/RunAOTCompilation PublishTrimmedtrue/PublishTrimmed TrimmerRemoveSymbolstrue/TrimmerRemoveSymbols PublishReadyToRunEmitSymbolsfalse/PublishReadyToRunEmitSymbols DebuggerSupportfalse/DebuggerSupport EnableUnsafeUTF7Encodingtrue/EnableUnsafeUTF7Encoding InvariantGlobalizationtrue/InvariantGlobalization HttpActivityPropagationSupportfalse/HttpActivityPropagationSupport MetadataUpdaterSupporttrue/MetadataUpdaterSupport UseSystemResourceKeystrue/UseSystemResourceKeys IlcDisableReflection true/IlcDisableReflectionC# 调用 C# 生成的 AOT在本小节中将使用CsharpAot项目调用CsharpExport生成的动态链接库。把CsharpExport.dll复制到CsharpAot项目中并配置始终复制。在CsharpAot的Native中加上[LibraryImport(CsharpExport.dll, SetLastError true)] [return: MarshalAs(UnmanagedType.I4)] internal static partial Int32 Add(Int32 a, Int32 b);

相关新闻

最新新闻

AI短剧付费率持平真人剧:技术栈与最小制作管线拆解

AI短剧付费率持平真人剧:技术栈与最小制作管线拆解

如果一部短剧从头到尾没有一个真人演员,主角的脸是模型生成的,场景是文生视频跑出来的,观众的付费意愿却和真人剧差不多——这意味着什么? 从海外短剧行业最近释放的信号看,头部公司正在加大对AI剧集的投入&#xff0…

2026/8/27 7:37:52
Vercel + Next.js:从本地开发到自动化部署的完整指南

Vercel + Next.js:从本地开发到自动化部署的完整指南

1. 写在前面:为什么要关注 Vercel 和 Next.js这次我们来看一对经常一起出现的技术组合:Vercel 和 Next.js。如果你做过前端开发、写过 React 项目,或者想过“我的页面能不能做到秒开”“部署一个网站能不能不折腾服务器”,那你大概…

2026/8/27 7:37:52
Coding Agent Token 优化实战:36次工具调用如何省一半消耗

Coding Agent Token 优化实战:36次工具调用如何省一半消耗

这次我们看一个非常实操的问题:同样的 36 次工具调用,能不能把 Token 消耗砍掉一半? 跑过 AI Coding Agent 的人都有这种经验:任务本身不复杂,就是让 Agent 读取代码、定位问题、改文件、跑测试、再来一轮。可每次打开…

2026/8/27 7:37:52
RVC WebUI工程快照解析:Docker与.env环境部署指南

RVC WebUI工程快照解析:Docker与.env环境部署指南

简介:RVC WebUI并非传统软件安装包,而是一个包含完整构建上下文的工程快照,其核心在于Docker容器化部署与.env环境契约管理。理解Dockerfile分层构建原理、.env变量对CUDA版本和Python环境的硬性约束,是保障语音转换模型稳定推理的…

2026/8/27 7:37:52
C++模板类:从类型参数化到智能指针实战,掌握泛型编程核心

C++模板类:从类型参数化到智能指针实战,掌握泛型编程核心

1. 从“重复造轮子”到“一次编写,处处适配”如果你写过一段时间的C,尤其是在做一些数据结构(比如链表、栈、队列)或者算法工具(比如排序、查找)的封装时,大概率会遇到一个让人头疼的问题&#…

2026/8/27 7:37:52
智能体评测:为什么步骤比方法名更重要?

智能体评测:为什么步骤比方法名更重要?

如果你最近在关注智能体评测,大概率会碰到一种表述:ASI-Bench 认为,步骤比方法名更决定智能体表现。我第一次看到这个判断时,第一反应是把它当成一句常识——搞智能体开发的人都知道,写提示词别太迷信方法名。可再往下…

2026/8/27 7:32:52