news 2026/6/23 14:45:48

给旧版 .NET 也开一扇“私有之门“ —— ILAccess.Fody 实现原理与设计

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
给旧版 .NET 也开一扇“私有之门“ —— ILAccess.Fody 实现原理与设计

前言:从 UnsafeAccessor 说起

在 .NET 8 中, 微软引入了一个让底层开发者非常心动的新特性 —— UnsafeAccessor

它允许我们在不使用反射的情况下访问类的私有字段、方法或构造函数, 而且是强类型、零开销的.

举个例子:

class Dog

{

private string _name = "Puppy";

}

static class DogAccessors

{

[UnsafeAccessor(UnsafeAccessorKind.Field, Name = "_name")]

public static extern ref string GetName(Dog d);

}

var dog = new Dog();

ref var name = ref DogAccessors.GetName(dog);

Console.WriteLine(name); // Puppy

CLR 会在运行时将 GetName 绑定到 _name 字段, 生成直接访问的 IL 指令.性能几乎和直接访问公有字段相当.

🧩 Benchmark(来自 Sharmila Malar 的 Medium 文章)

Reflection: ~10.9 ns

UnsafeAccessor: ~1.99 ns

Direct Access: ~1.81 ns

但是, 这个特性只在 .NET 8+ 可用.

ILAccess.Fody 诞生

我希望旧版 .NET 平台(例如 .NET Framework、.NET Standard、.NET 6)也能享受这种“反射级灵活 + 原生级性能”的能力.于是我编写了 ILAccess.Fody. 它实现了和 UnsafeAccessor 几乎一致的语法和体验, 但通过 Fody + Mono.Cecil 在编译期修改 IL 来实现.

Mono.Cecil 是一个用于分析和修改 .NET 程序集的库, 它提供了强大的对象模型, 让我们能在不加载程序集的情况下读取、编辑、甚至生成新的 IL 代码.

Fody 是一个基于 Mono.Cecil 可扩展的编译期织入工具, 它让开发者能在构建过程中直接修改程序集的 IL, 而无需手动处理 MSBuild 或 Visual Studio 的复杂管线.

使用方法 (和UnsafeAccessor几乎一样)

static class DogILAccessors

{

// UnsafeAccessorAttribute -> ILAccessorAttribute

// UnsafeAccessorKind -> ILAccessorKind

[ILAccessor(ILAccessorKind.Field, Name = "_name")]

public static extern ref string GetName(Dog d);

}

var dog = new Dog();

ref var name = ref DogILAccessors.GetName(dog);

Console.WriteLine(name); // Puppy

编译后, ILAccess.Fody 会将这些标记了 ILAccessor 的桩方法体替换为直接访问私有成员的 IL :

.method public hidebysig static string& GetName(class ILAccess.Dog d) cil managed

{

IL_0000: ldarg.0 // d

IL_0001: ldflda string ILAccess.Dog::_name

IL_0006: ret

}

没有反射、没有委托、没有运行时查找.

核心实现: 编译期注入 IL

遍历所有方法, 找到带 [ILAccessor] 的桩方法.

根据不同的 ILAccessorKind 生成对应的 IL 指令.

确定目标类型, 对于构造函数目标类型就是桩方法的返回类型, 其余的就是桩方法的第一个参数的类型.

对于字段, 根据字段名匹配, 在目标类型的声明字段里找到目标字段, 然后根据是否为静态选择 Ldsfld 或 Ldfld 指令, 如果是 ref访问, 则需选择 Ldsflda 或 Ldfld

对于方法, 则需要根据方法名+泛型参数个数+参数类型列表来匹配, 在目标类型的声明方法里找到目标方法, 然后选择 callvirt 或 call 指令

对于构造函数, 其实就是一个名为 .ctor 的方法, 处理方式和普通方法类似, 不过指令是 newobj

额外处理

当想访问的私有成员的类型是定义在一个 引用程序集(Reference Assemblies) 中

比如想访问基础库中的 List<T> 的 _items, 而基础库在编译时通常会以 引用程序集 的形式进行引用, 此时这个程序集只包含这个类型的公共成员的定义, 不包括其实现以及所有的私有成员. 那么就无法根据名称找到私有成员的用于生成 IL 的元数据.

解决方法: 先通过引用程序集的路径找到其对应的 实现程序集(Implementation Assemblies) 的路径, 并从其中读取所需的元数据.

当想访问的私有成员的类型并不定义在当前被编译的程序集中

这种情况如果直接使用 IL 指令访问其私有成员会触发 MethodAccessException 或者 FieldAccessException 之类的异常.

解决方法: 使用 IgnoresAccessChecksToAttribute 跳过对想访问的程序集的权限检查. 例如

[assembly: IgnoresAccessChecksTo("System.Private.CoreLib")] // 跳过对基础库的访问检查

不过在使用 ILAccess.Fody 的时候无需手动添加这些代码, 它会自动生成并注入到被编译的程序集中.

访问私有成员方法的对比

特性 反射 UnsafeAccessor ILAccess.Fody

支持平台 所有 .NET 平台 仅 .NET 8+ 全部 .NET 平台

实现方式 运行时查找 CLR 运行时注入 Fody 编译期注入

性能 慢 几乎接近直接访问 几乎接近直接访问

编译期验证 ❌ ❌ ✅

AOT支持 ⚠️ 受限支持 ✅ ✅

结语

ILAccess.Fody 的目标很纯粹:

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/23 11:21:12

《构建游戏实时流失预警模型的核心逻辑》

玩家流失预警的关键痛点从来不是捕捉显性的行为衰减,而是解码藏在时序流转里的隐性流失信号—那些散落在跨模块交互、行为节奏变化中的序列异动,往往比单纯的在线时长缩短、任务参与度下降更早暴露玩家的离开倾向,也是实时预警模型能否实现“提前干预、精准留客”的核心突破…

作者头像 李华
网站建设 2026/6/23 19:52:29

两个步骤,打包war,tomcat使用war包

资源代码&#xff1a; https://download.csdn.net/download/hashiqimiya/92455258 如上 了解资源代码&#xff1a; 写了一个controller代码&#xff0c;控制接口对应该运行的函数。 package org.example.testproducttomcatwar;import org.springframework.web.bind.annotat…

作者头像 李华
网站建设 2026/6/23 14:19:12

idea修改maven的刷新引入依赖快捷键

在 IntelliJ IDEA 里&#xff0c;Load Maven Changes 对应的是当你修改 pom.xml 或者依赖后&#xff0c;手动刷新 Maven 项目的操作。你也可以给它自定义快捷键。具体步骤如下&#xff1a;1. 打开快捷键设置Windows/Linux: File → Settings → KeymapmacOS: IntelliJ IDEA → …

作者头像 李华
网站建设 2026/6/23 19:51:17

纯电动汽车Simulink仿真模型建模详细步骤。 通过文档的形式,跟着文档一步一步操作,既可以...

纯电动汽车Simulink仿真模型建模详细步骤。 通过文档的形式&#xff0c;跟着文档一步一步操作&#xff0c;既可以提高自己的建模能力&#xff0c;又可以对整个建模思路进行借鉴&#xff0c;形成设计能力。 附带模型。纯电动汽车仿真模型在Simulink里搭建就像搭乐高——找准核心…

作者头像 李华
网站建设 2026/6/23 19:48:46

同花顺平衡多空看图操作多空理论

{}多空:50,COLORFFFFFF,POINTDOT; J:(C-LLV(L,9))/(HHV(H,9)-LLV(L,9))*100,colormagenta; K:SMA(J,3,1),colorwhite; D:SMA(K,3,1),coloryellow; 动能:100 * (LLV(LOW,60)-EMA(CLOSE,3))/(LLV(LOW,60)-HHV(HIGH,60)),colorligray,LINETHICK2; 多:IF(动能>多空,动能,DRAWNUL…

作者头像 李华
网站建设 2026/6/23 19:52:38

通达信222222测试帖别下载

{} Z1:STRCAT(【,STRCAT(CODE,STKNAME));Z2:STRCAT( 行业&#xff1a;,HYBLOCK);Z3:STRCAT( 地域&#xff1a;,DYBLOCK);Z4:STRCAT(STRCAT( 概念&#xff1a;,GNBLOCK),】);Z5:STRCAT(Z1,Z2);Z6:STRCAT(Z3,Z4);Z7:STRCAT(Z5,Z6);DRAWTEXT_FIX(1,0.35,0.030,0,Z7),COLORGREEN…

作者头像 李华