C# 7.2 添加了两个新特性:
使用
in
对于参数,让我们通过引用传递,但随后阻止我们为其分配值。然而,性能实际上会变得更糟,因为它创建了结构的“防御性副本”,复制了整个内容解决此问题的一种方法是使用
readonly
对于 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
的显着性能损失由创建防御性副本引起的?
最佳答案
When you pass [a
readonly struct
] into an in parameter, the compiler sees that it's readonly and won't create the defensive copy.
我想你误会了。编译器创建一个只读变量的防御性副本,其中包含
struct
(这可能是一个 in
参数,也可能是一个 readonly
字段)当你调用一个方法时 struct
.考虑 the following code :
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
.Bar
的 IL是: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
不会被改变(因为它是只读的)。如果您 change the code to make
S
a readonly struct
,然后是 Foo
的 IL保持不变,但对于 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 可以确定副本实际上不是必需的,则允许避免它。
关于c# - 避免将 `in` 与结构一起使用而不会使结构成为只读而降低性能?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50539764/