Aus*_*yan 5 c# optimization struct c#-7.2 in-parameters
C# 7.2 添加了两个新特性:
输入参数
使用in的参数让我们通过引用传递,但随后阻止我们将值分配给它。然而,性能实际上会变得更糟,因为它创建了结构的“防御性副本”,复制了整个内容
只读结构
解决此问题的一种方法是使用readonlyfor a struct。当您将它传递给一个in参数时,编译器会看到它readonly并且不会创建防御性副本,从而使其成为更好的性能替代方案。
这一切都很棒,但该领域的每个领域struct都必须如此readonly。这不起作用:
public readonly struct Coord
{
    public int X, Y;    // Error: instance fields of readonly structs must be read only
}
自动属性也必须是readonly。
有没有办法获得in参数的好处(编译时检查以强制参数不被更改,通过引用传递)同时仍然能够修改 的字段struct,而不会in因创建防御副本?
当您将 [a
readonly struct]传递给in 参数时,编译器会看到它是只读的并且不会创建防御性副本。
我想你误会了。编译器创建了一个只读变量的防守副本,包含struct(这可能是一个in参数,也是一个readonly字段),当你调用该方法struct。
考虑以下代码:
struct S
{
    int x, y;
    public void M() {}
}
class C
{
    static void Foo()
    {
        S s = new S();
        Bar(s);
    }
    static void Bar(in S s)
    {
        s.M();
    }
}
您可以检查为上述代码生成的 IL,以了解实际会发生什么。
对于Foo,IL 是:
ldloca.s 0 // load address of the local s to the stack
initobj S  // initialize struct S at the address on the stack
ldloca.s 0 // load address of the local s to the stack again
call void C::Bar(valuetype S&) // call Bar
ret        // return
请注意,没有复制:本地s被初始化,然后该本地的地址直接传递给Bar。
IL 为Bar:
ldarg.0     // load argument s (which is an address) to the stack
ldobj S     // copy the value from the address on the stack to the stack
stloc.0     // store the value from the stack to an unnamed local variable
ldloca.s 0  // load the address of the unnamed local variable to the stack
call instance void S::M() // call M
ret         // return
在这里,ldobj和stloc指令创建了防御性副本,以确保如果M发生变异struct,s不会发生变异(因为它是只读的)。
如果你更改代码,使S一个readonly struct,那么IL为Foo保持不变,但Bar其改变为:
ldarg.0 // load argument s (which is an address) to the stack
call instance void S::M() // call M
ret     // return
请注意,这里不再复制。
这是将您标记struct为readonly避免的防御性副本。但是如果你不在结构上调用任何实例方法,就不会有任何防御性副本。
还要注意,语言规定当代码执行时,它必须表现得好像防御性副本就在那里。如果 JIT 可以确定副本实际上不是必需的,则允许避免它。