in用于大型只读struct的高性能传参,避免拷贝;对int、bool或class使用无收益且降低可读性;要求struct所有成员readonly、调用前初始化、方法内不可修改。

in 不是用来“教你怎么写第一个 in 参数”的,它是解决特定性能问题的工具——只在大型 struct 频繁传参时才值得用;对 int、bool 或 class 加 in 不仅没收益,还会让代码更啰嗦、更难读。
什么时候必须用 in?——不是语法强制,而是语义和性能需要
你不需要“必须”用 in,但当你遇到以下情况时,它就是最直接的解法:
- 参数是自定义的
struct,且大小 ≥ 16 字节(比如含 4 个long字段,或嵌套多个值类型) - 这个
struct被高频调用(如渲染循环、数学计算密集型逻辑) - 方法内部只读取字段或调用
readonly成员(如ToString()、GetHashCode()),从不修改它 - 你已确认该
struct的所有公共成员都标记为readonly,或本身不含可变状态(否则编译器会报错)
例如:public readonly struct Matrix4x4 { public readonly float M11, M12, ..., M44; } 这类结构体传参时加 in 才有意义。
in 参数为什么编译不过?常见错误现象
加了 in 却编译失败,通常不是语法错,而是违反了它的只读契约:
-
in参数在调用前未初始化:比如BigStruct s; Foo(in s);→ 报错 CS1612:“无法使用只读变量作为 ref 或 out 参数” - 方法体内尝试赋值:
num = 42;→ 编译错误:“不能向 in 参数赋值” - 调用了非
readonly成员:bs.SomeMethodThatModifiesState();→ 编译器拒绝,除非该方法声明为readonly - 试图取地址:
ref var r = ref bs;→ 不允许,in不支持转成可写引用
注意:class 类型加 in 是合法的,但无实际意义——引用类型本就只传引用副本,in 不改变行为,反而增加阅读负担。
in 和 ref/out 的关键区别在哪?别混用场景
三者都是按引用传递,但语义截然不同,选错会导致编译失败或逻辑错误:
-
ref:可读可写,调用前必须初始化,适合“方法要改原变量”的场景,比如Swap(ref a, ref b) -
out:只写不读(方法内必须赋值),调用前可不初始化,适合“方法负责产出值”,比如int.TryParse(s, out var i) -
in:只读不写,调用前必须初始化,**唯一目标是避免大值类型拷贝**,不是为了“返回值”或“交换”
一个典型误用:void Process(in List<int> list) —— List<int> 是引用类型,加 in 既不提升性能,又限制了后续可能的 list.Add() 调用(虽然方法里不能改 list 变量本身,但能改它指向的对象内容,这容易造成语义混淆)。
性能影响和兼容性要注意什么?
in 是 C# 7.2+ 特性,这意味着:
- 项目目标框架需 ≥ .NET Core 2.0 / .NET Standard 2.1 / .NET Framework 4.7.2
- 即使你只在内部方法用
in,只要公开 API 中出现in参数,调用方也必须用支持该特性的编译器 - 性能提升只体现在栈拷贝环节:对 32 字节的
struct,传参开销从复制 32 字节降为复制 8 字节(指针大小),但 JIT 优化后差异可能被抹平,务必实测 - 不要为了“看起来高级”而滥用:
in int、in string这类写法只会让同事皱眉
最易被忽略的一点:如果你的 struct 包含非 readonly 字段或属性 setter,哪怕只是漏标了一个 readonly,整个 in 调用链就会失效——编译器不会警告,但会在调用处静默退化为值传递,性能白优化。


















