Unsafe.As 是零开销的内存重解释工具,仅在 sizeof(TFrom) == sizeof(TTo) 时允许编译,不进行类型检查、构造、装箱/拆箱,失败导致未定义行为;不是 as 关键字的 unsafe 版本,也不适用于引用类型或大小不同的类型转换。

Unsafe.As 是什么,不是什么
Unsafe.As 不是类型转换操作符,也不是 as 关键字的“unsafe 版本”。它是一个零开销的**内存重解释(bitwise reinterpret)** 工具,作用是告诉编译器:“请把这块内存当作 TTo 类型来读,不管它原本是什么类型”——不检查兼容性、不调用构造函数、不触发装箱/拆箱,甚至不关心 TFrom 和 TTo 是否有继承或转换关系。
- 它只在
TFrom和TTo占用相同大小(sizeof(TFrom) == sizeof(TTo))时才被允许编译,否则报错 - 它不进行任何运行时类型验证,失败时不会抛异常,也不会返回
null;出错直接导致未定义行为(比如读到垃圾值、崩溃、数据错乱) - 它不能用于引用类型之间的“假装转换”(如
Unsafe.As是非法的,因为引用类型大小受 GC 影响且语义不可重解释)
和常规 as / is / 强制转换比,性能差在哪?
常规类型转换(as、is、(T)obj)本质是运行时类型系统参与的**安全检查 + 可能的引用调整**;而 Unsafe.As 完全绕过整个类型系统,只是指针偏移 + 位宽断言。所以它的“性能优势”不是“快一点”,而是“没有额外开销”:
-
obj as string:检查obj是否为null或是否实际是string实例(一次虚表/类型句柄查表) -
(string)obj:同上检查,失败则抛InvalidCastException -
Unsafe.As:编译期确认(ref i) sizeof(int) == sizeof(float),生成一条mov指令(或零指令),无分支、无异常路径、无 GC 堆校验
但注意:这种“零成本”只在你**100% 确保内存布局一致且生命周期可控**时成立。一旦用错,性能再高也没意义——程序已经不可靠了。
什么时候该用 Unsafe.As?典型安全场景
它不是为了替代 as,而是为极少数需要跨类型按位复用内存的底层场景服务,比如:
创想C2C商城系统,系统功能仿照淘宝设计,采用模块标签技术和静态html生成技术 基于Asp.Net/C#+SQL的开发的创想多用户商城系统,具有智能化、高扩展、稳定安全等特性,后台可自由添加频道,自由修改界面风格,商品无限级 分类,支持在线支付整合,通过安装和使用创想C2C商城系统,就可以轻松建立起专业大型的网上交易平台。创想C2C多用户商城系统5.6.3.8版本升级功能1.网站地区设置功能的增
- 将
Span的前 4 字节快速解释为int:Span
bytes = stackalloc byte[4] { 0x01, 0x00, 0x00, 0x00 }; int value = Unsafe.As (ref bytes.DangerousGetPinnableReference()); - 在
ReadOnlySpan和ReadOnlySpan之间做 SIMD 向量对齐转换(配合Vector) - 实现高性能序列化器中字段级字节重解释(如把
long时间戳当两个int拆开处理)
⚠️ 这些场景共同点:你知道原始数据来源、长度固定、对齐明确、且全程控制内存生命周期(比如栈分配、pinning 后的数组、或 Span 背后的托管数组已确保不会移动)。
最容易踩的坑:误以为它是“更快的 as”
这是最危险的误解。下面这些写法全是错的,而且错误非常隐蔽:
-
object o = "hello"; string s = Unsafe.As→ 编译失败(引用类型不支持) -
int i = 123; long l = Unsafe.As→ 编译失败((ref i); sizeof(int) != sizeof(long)) -
var list = new List→ 即便编译通过,解引用(); int* ptr = Unsafe.As - , int*>(ref list);
ptr会读取对象头+字段,结果完全不可预测 - 在异步回调或 lambda 中捕获并长期持有
Unsafe.As得到的引用 → 可能因 GC 移动原对象而悬空
真正需要 Unsafe.As 的地方极少;绝大多数所谓“性能瓶颈”,其实是算法、缓存局部性或内存分配问题,而不是 as 多花了几个纳秒。把它当成类型转换的“加速版”,基本等于给自行车加涡轮增压——装上了,但路不对,跑不远。









