You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET为何为JIT方法中的readonly struct生成防御性副本?

为什么.NET会为readonly struct生成防御性副本?

首先要明确:readonly struct的readonly修饰符和参数传递的副本机制是两个独立的概念,不能混为一谈。

核心原因:

  1. 值类型的默认传递规则
    .NET中值类型(包括readonly struct)作为方法参数时,默认遵循按值传递的规则——不管结构体有没有readonly修饰,都会创建一个完整的实例副本,传递给目标方法的栈帧。这是CLR的基础调用约定,目的是保持值类型的“值语义”:每个方法拿到的都是独立的实例,确保方法内的操作不会意外影响到外部的原实例。

  2. readonly struct的语义边界
    readonly修饰符的作用是约束结构体自身的成员无法被修改,同时告诉编译器可以做一些内存优化(比如紧凑布局、避免生成写操作指令),但它并没有改变值类型参数传递的默认行为。也就是说,readonly只负责“限制结构体实例的可修改性”,不负责“改变参数传递的方式”。

结合你的示例分析

你的代码里,M方法调用M1(Foo f)时,因为是默认按值传递,CLR会把M栈帧里的Foo实例完整复制到M1的栈帧中——对应汇编里的vmovdqu等复制指令。而如果把M1的参数改成in Foo f,就会切换为只读引用传递,CLR直接传递原实例的地址,不需要复制,自然也就没有防御性副本了。

为什么不直接传递栈指针?

默认按值传递是为了坚守值类型的设计初衷:值类型的实例是“值”,不是“引用”,每个方法都应该拿到属于自己的独立副本。如果默认就传递栈指针(引用),会打破值语义的一致性,即使readonly struct本身不可修改,也会让值类型的行为变得和引用类型混淆,违背.NET类型系统的设计原则。

内容的提问来源于stack exchange,提问作者Irdis

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 07:20:09