因此,我(我想)了解了 in
参数修饰符的作用。但是它的确显得很多余。
通常,我认为使用 ref
的唯一原因是修改调用变量, in
明确禁止使用该变量/ strong>。因此,通过 in
引用传递在逻辑上等效于按值传递。
是否有某种性能优势?我认为,在后端, ref
参数必须至少复制变量的物理地址,该地址的大小应与任何典型对象引用的大小相同
那么,优点是仅在较大的结构中出现,还是在后台进行了一些编译器优化,使其在其他地方有吸引力?如果是后者,为什么我不应该将每个参数都设置为 in
?
答案 0 :(得分:49)
in
最近被引入C#语言。
in
实际上是ref readonly
。一般来说,只有in
会有用的一种用例:处理大量大型readonly struct
的高性能应用。
假设您拥有:
readonly struct VeryLarge
{
public readonly long Value1;
public readonly long Value2;
public long Compute() { }
// etc
}
和
void Process(in VeryLarge value) { }
在这种情况下,VeryLarge
结构将通过引用传递,而在Process
方法中使用该结构时(例如,调用value.Compute()
时,则不会创建防御性副本,并且编译器确保struct不变性。
请注意,使用struct
修饰符传递非只读in
会导致编译器在调用struct的方法并访问{{ 1}}方法,会对性能产生负面影响!
有一个非常好的MSDN blog entry,建议您仔细阅读。
如果您想了解Process
的更多历史背景介绍,可以在C#语言的GitHub存储库中阅读此discussion。
通常,大多数开发人员都同意将in
引入是错误的。这是一种相当奇特的语言功能,仅在高性能的情况下有用。
答案 1 :(得分:18)
通过
in
引用传递在逻辑上似乎等效于按值传递。
正确。
是否有某种性能优势?
是的
我相信,在事物的后端,
ref
参数必须至少复制变量的物理地址,该地址的大小应与任何典型对象引用的大小相同。
没有 requirement ,对对象的引用和对变量的引用都具有相同的大小,并且没有 requirement 或两者都是机器字的大小,但是可以,实际上在32位机器上都是32位,在64位机器上都是64位。
我不清楚您认为“物理地址”与它有什么关系。在Windows上,我们使用虚拟地址,而不是物理地址在用户模式代码中。我想知道在什么可能的情况下,物理地址在C#程序中是有意义的。
也没有要求将任何类型的引用实现为存储的虚拟地址。在符合CLI规范的实现中,对GC表的引用可能是不透明的句柄。
优势只是在更大的结构中吗?
降低传递较大结构的成本是该功能的激励方案。
请注意,不能保证in
实际上会使任何程序变快,并且会使程序变慢。 所有有关绩效的问题都必须通过实证研究来回答。 总是取胜的优化很少;这不是“永远获胜”的优化。
是否有一些幕后的编译器优化使其在其他地方有吸引力?
允许编译器和运行时进行任何选择的优化,前提是这样做没有违反C#规范的规则。据我所知,in
参数还没有这样的优化,但这并不排除将来的这种优化。
为什么不应该将每个参数都设为in?
好吧,假设您创建了一个int
参数而不是一个in int
参数。收取什么费用?
假设它是double
,而您将其更改为in double
。同样,现在无法将变量注册到高性能浮点寄存器中。这不仅具有 performance 的含义,还可以改变程序的行为!允许C#进行高于64位精度的浮点运算,并且通常只有在可以注册浮点数的情况下才这样做。
这不是免费的优化。您必须对照其他方案评估其性能。最好的选择是,首先不要按照设计准则的建议首先制作大型结构。
答案 2 :(得分:5)
有。传递struct
时,使用in
关键字进行优化,其中编译器只需要传递一个指针,没有方法更改内容的风险。最后一点很关键—避免了复制操作。在大型结构上,这可以带来很大的不同。
答案 3 :(得分:1)
之所以这样做是因为采用了功能编程方法。主要原理之一是函数不应具有副作用,这意味着它不应更改参数的值并应返回一些值。在C#中,没有传递结构(和值类型)的方法,只能通过引用进行复制,而引用只能更改值。只要方法开始更改其值,就会有一种hacky算法可以复制struct(它们的集合是struct BTW)。使用swift的人并非所有人都知道复制的东西。这是一个不错的c#功能,因为它的内存高效且显式。如果您查看what's new,您会发现围绕堆栈中的结构和数组所做的事情越来越多。而声明只是这些功能所必需的。其他答案中提到了一些限制,但对于了解.net的去向并不是那么重要。
答案 4 :(得分:-1)
在中,它是c#7.2中的只读引用
这意味着您不会像 ref 那样将整个对象传递给函数堆栈,而仅将对结构的引用传递给
但是尝试更改对象的值会导致编译器错误。
是的,如果使用大型结构,这将使您能够优化代码性能。