.NET NativeAOT 指南
- 作者: 五速梦信息网
- 时间: 2026年03月05日 13:17
.NET NativeAOT 指南
随着 .NET 8 的发布,一种新的“时尚”应用模型 NativeAOT 开始在各种真实世界的应用中广泛使用。
除了对 NativeAOT 工具链的基本使用外,“NativeAOT”一词还带有原生世界的所有限制,因此您必须知道如何处理这些问题才能正确使用它。
在这篇博客中,我将讨论它们。
基本用法
PublishAot=true
通常,它可以是:
dotnet publish -c Release -r win-x64 /p:PublishAot=true
win-x64linux-x64osx-arm64
bin/Release///publish
关于编译
在讨论使用 NativeAOT 时可能遇到的各种问题的解决方案之前,我们需要稍微深入一点,看看 NativeAOT 是如何编译代码的。
我们经常听说 NativeAOT 会剪裁掉没有被使用的代码。而实际上,它并不像 IL 剪裁那样从程序集中剪裁掉不必要的代码,而是只编译代码中引用的东西。
NativeAOT 编译包括两个阶段:
- 扫描 IL 代码,构建整个程序视图(一个依赖图),其中包含所有需要编译的必要依赖节点。
- 对依赖图中的每个方法进行实际的编译,生成代码。
请注意,在编译过程中可能会出现一些“延迟”的依赖,因此上述两个阶段可能会交错出现。
这意味着,在分析过程中没有被计算为依赖的任何东西最终都不会被编译。
反射
依赖图是在编译期间静态构建的,这也意味着任何无法静态分析的东西都不会被编译。不幸的是,反射,即在不事先告诉编译器的情况下在运行时获取东西,正是编译器无法弄清楚的一件事。
NativeAOT 编译器有一些能力可以根据编译时的字面量来推断出反射调用需要什么东西。
例如:
var type = Type.GetType(“Foo”);
Activator.CreateInstance(type);
class Foo
{
public Foo() => Console.WriteLine("Foo instantiated");<br/>
}
FooFooFooFoo
Foo instantiated
但是如果我们将代码改为如下:
var type = Type.GetType(Console.ReadLine());
Activator.CreateInstance(type);
class Foo
{
public Foo() => Console.WriteLine("Foo instantiated");<br/>
}
FooFoo
Unhandled Exception: System.ArgumentNullException: Value cannot be null. (Parameter ‘type’)
at System.ArgumentNullException.Throw(String) + 0x2b
at System.ActivatorImplementation.CreateInstance(Type, Boolean) + 0xe7
…
FooFootypenull
此外,依赖分析是精确到单个方法的,这意味着即使一个类型被认为是一个依赖,如果该类型中的某个方法没有被使用,该方法也不会被包含在代码生成中。
TrimmerRootAssemblyTrimmerRootAssembly
但是涉及泛型的情况就不是这样了。
动态泛型实例化
在 .NET 中,我们有泛型,编译器会为每个非共享的泛型类型和方法生成不同的代码。
Point
struct Point<T>
{
public T X, Y;<br/>
}
PointPointPoint.XPoint.YintPointPoint.XPoint.Yfloat
通常情况下,这不会导致任何问题,因为编译器可以静态地找出你在代码中使用的所有实例化,直到你试图使用反射来构造一个泛型类型或一个泛型方法:
var type = Type.GetType(Console.ReadLine());
var pointType = typeof(Point<>).MakeGenericType(type);
PointPointPoint
intfloatPoint<>PointPoint
TrimmerRootAssemblyPointPoint
解决方案
既然我们已经找出了在 NativeAOT 下可能发生的潜在问题,让我们来谈谈解决方案。
在其他地方使用它
最简单的想法是,我们可以通过在代码中使用它来让编译器知道我们需要什么。
例如,对于代码
var type = Type.GetType(Console.ReadLine());
var pointType = typeof(Point<>).MakeGenericType(type);
PointPoint
// 我们使用一个永远为假的条件来确保代码不会被执行
// 因为我们只想让编译器知道依赖关系
// 注意,如果我们在这里简单地使用一个 if (false)
// 这个分支会被编译器完全移除,因为它是多余的
// 所以,让我们在这里使用一个不平凡但不可能的条件
if (DateTime.Now.Year < 0)
{
var list = new List<Type>();<br/>
list.Add(typeof(Point<int>));<br/>
list.Add(typeof(Point<float>));<br/>
}
DynamicDependency
DynamicDependencyAttribute
所以我们可以利用它来告诉编译器:“如果 A 被包含在依赖图中,那么也添加 B”。
下面是一个例子:
class Foo
{
readonly Type t = typeof(Bar);
[DynamicDependency(DynamicallyAccessedMemberTypes.PublicProperties, typeof(Bar))]
public void A()<br/>
{<br/>
foreach (var prop in t.GetProperties())<br/>
{<br/>
Console.WriteLine(prop);<br/>
}<br/>
}<br/>
}
class Bar
{
public int X { get; set; }<br/>
public int Y { get; set; }<br/>
}
Foo.ABarBar
这个属性还有许多重载,可以接受不同的参数来适应不同的用例,您可以在这里查看文档。
Foo.AUnconditionalSuppressMessage
class Foo
{
readonly Type t = typeof(Bar);
[DynamicDependency(DynamicallyAccessedMemberTypes.PublicProperties, typeof(Bar))]
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2080",<br/>
Justification = "The properties of Bar have been preserved by DynamicDependency.")]<br/>
public void A()<br/>
{<br/>
foreach (var prop in t.GetProperties())<br/>
{<br/>
Console.WriteLine(prop);<br/>
}<br/>
}<br/>
}
DynamicallyAccessedMembers
TTType
void Foo<T>()
{
foreach (var prop in typeof(T).GetProperties())<br/>
{<br/>
Console.WriteLine(prop);<br/>
}<br/>
}
class Bar
{
public int X { get; set; }<br/>
public int Y { get; set; }<br/>
}
FooBarFooFooTBarBar
DynamicallyAccessedMembersT
void Foo<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] T>()
{
// ...<br/>
}
FooTBar
Type
void Foo([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] Type t)
{
foreach (var prop in t.GetProperties())<br/>
{<br/>
Console.WriteLine(prop);<br/>
}<br/>
}
string
Foo(“Bar”);
void Foo([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] string s)
{
foreach (var prop in Type.GetType(s).GetProperties())<br/>
{<br/>
Console.WriteLine(prop);<br/>
}<br/>
}
DynamicDependency
class Foo
{
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)]<br/>
readonly Type t = typeof(Bar);
public void A()
{<br/>
foreach (var prop in t.GetProperties())<br/>
{<br/>
Console.WriteLine(prop);<br/>
}<br/>
}<br/>
}
顺便说一句,这也是推荐的方法。
TrimmerRootAssembly
TrimmerRootAssembly
<ItemGroup>
<TrimmerRootAssembly Include="MyAssembly" /><br/>
</ItemGroup>
TrimmerRootDescriptor
TrimmerRootDescriptor
<ItemGroup>
<TrimmerRootDescriptor Include="link.xml" /><br/>
</ItemGroup>
TrimmerRootDescriptor 文件的文档和格式可以在这里找到。
Runtime Directives
对于泛型实例化的情况,它们无法通过 TrimmerRootAssembly 或 TrimmerRootDescriptor 来解决,这里需要一个包含 runtime directives 的文件来告诉编译器需要编译的东西。
<ItemGroup>
<RdXmlFile Include="rd.xml" /><br/>
</ItemGroup>
rd.xml
rd.xml
DynamicallyAccessedMembersDynamicDependency
结语
NativeAOT 是 .NET 中一个非常棒和强大的工具。有了 NativeAOT,你可以以可预测的性能构建你的应用,同时节省资源(更低的内存占用和更小的二进制大小)。
它还将 .NET 带到了不允许 JIT 编译器的平台,例如 iOS 和主机平台。此外,它还使 .NET 能够运行在嵌入式设备甚至裸机设备上(例如在 UEFI 上运行)。
在使用工具之前了解工具,这样你会节省很多时间。
相关文章
-
.net windows Kafka 安装与使用入门(入门笔记)
.net windows Kafka 安装与使用入门(入门笔记)
- 互联网
- 2026年03月05日
-
.net 调用R语言的函数(计算统计值pvalue 对应excel :ttest)
.net 调用R语言的函数(计算统计值pvalue 对应excel :ttest)
- 互联网
- 2026年03月05日
-
.net 微信公众号
.net 微信公众号
- 互联网
- 2026年03月05日
-
.net mvc 使用盛派Senparc.Weixin 上传图片接口
.net mvc 使用盛派Senparc.Weixin 上传图片接口
- 互联网
- 2026年03月05日
-
.Net Core 中 MemoryCache 使用
.Net Core 中 MemoryCache 使用
- 互联网
- 2026年03月05日
-
.NET Core 系列5 :使用 Nuget打包类库
.NET Core 系列5 :使用 Nuget打包类库
- 互联网
- 2026年03月05日



