何时在ReadOnlyMemory/ReadOnlySpan中使用in关键字?
关于ReadOnlyMemory/ReadOnlySpan中in关键字的适用场景,以及和C++ const&的对比
咱们先从你的代码示例入手,一步步拆解问题,再延伸到适用场景和C++的对比~
一、先分析你的代码示例
先看你给出的三个方法:
void Method(ReadOnlyMemory<int> memory) { // 修改memory不会影响外部,因为是值类型的副本 memory = memory.Slice(3); } void Method(in Memory<int> memory) { // 和上面效果类似,但避免了值类型复制 memory.Slice(3); } void Method(in ReadOnlyMemory<int> memory) { // 这里有没有性能收益?当然有! }
- 第一个方法:
ReadOnlyMemory<int>是值类型,默认传值会复制整个结构体(64位系统下大概16字节)。你修改memory(比如调用Slice后赋值)只是修改副本,外部完全不受影响,但每次调用都会产生一次结构体复制的开销。 - 第二个方法:
in Memory<int>用了in关键字,相当于把值类型按只读引用传递,避免了结构体复制。因为in参数是只读的,你不能给memory本身赋值(比如memory = memory.Slice(3)会报错),但调用Slice返回新的视图是没问题的——而且关键是,你依然可以通过memory.Span修改内存里的元素(因为in限制的是结构体本身,不是它指向的内存)。 - 第三个方法:
in ReadOnlyMemory<int>的核心收益就是避免值类型复制。ReadOnlyMemory<int>本身是只读的内存视图,加上in后既保证了不能修改参数结构体本身,又省去了每次传值的复制开销——在高频调用的方法里,这种累积的性能提升会很明显。
二、in关键字在ReadOnlyMemory/ReadOnlySpan中的适用场景
总结下来,适合用in的场景主要有两类:
- 高频调用的方法:不管是
ReadOnlyMemory<T>还是ReadOnlySpan<T>,它们都是值类型,虽然本身尺寸不大(ReadOnlySpan<T>在64位下是8字节),但如果方法被几万、几十万次调用,每次的复制开销累积起来就很可观。用in传只读引用,能彻底消除这部分开销。 - 不需要修改参数本身的场景:
in参数是只读的,你不能给参数赋值(比如把它改成Slice后的新视图),但如果你的方法只是读取内存内容、做计算,不需要修改这个视图结构体本身,那in就非常合适——既安全(避免误改参数)又高效。
另外要注意ReadOnlySpan<T>的特殊点:它是栈绑定的类型,用in传递时要确保它的生命周期不会超出栈范围(比如不能把in ReadOnlySpan<T>参数存在类的字段里),否则会出现内存安全问题。
三、和C++中const typename &的对比
两者确实非常相似,但也有细微差别:
- 核心作用一致:都是为了避免值类型复制,同时保证参数的只读性——C++的
const&是把值类型/对象按只读引用传递,C#的in是把值类型按只读引用传递,本质都是减少复制开销+防止误改参数。 - 语义细节有区别:
- C#的
ReadOnlyMemory<T>/ReadOnlySpan<T>本身已经自带“内存只读”的语义,而in关键字是“参数结构体本身只读”——也就是说,in ReadOnlyMemory<int>是双重只读:既不能修改内存内容,也不能修改视图结构体本身;而C++的const std::vector<int>&是不能修改vector的内容,也不能让引用指向另一个vector,和这个逻辑类似,但C#的“内存只读”是类型本身的属性,不是in带来的。 - C++的
const&可以绑定到临时对象,C#的in参数也支持接受临时值,编译器会自动处理生命周期,这点是一致的。
- C#的
内容的提问来源于stack exchange,提问作者Bercovici Adrian
相关产品推荐
相关产品推荐

