c# - 避免将 `in` 与结构一起使用而不会使结构成为只读而降低性能?

标签 c# optimization struct c#-7.2 in-parameters

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
    

    在这里,ldobjstloc指令创建防御性副本,以确保如果 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/

    相关文章:

    javascript - 如何优化重复的 if/else block ?

    c# - Dictionary.ContainsKey/Value 和检查某个键/值的 foreach 循环之间的速度是否存在差异

    c - 我无法在 c 中将一个结构分配给另一个结构

    c - 指针和结构

    c# - 无法从 singelton 事件取消订阅 signalR 集线器功能

    oracle - where rownum=1 查询在 Oracle 中花费时间

    c# - 你调用的对象是空的。我需要构造函数吗?

    c - 标准 C 是否接受 `{0}` 作为任何结构的初始值设定项?

    c# - 如何以 PDF 格式呈现 ASP.NET MVC View

    c# - 数据层的异步是否要求整个调用栈也是异步的?