代码之家  ›  专栏  ›  技术社区  ›  Arne Claassen

访问ref返回的结构体上的字段是否会复制该结构体?

  •  0
  • Arne Claassen  · 技术社区  · 6 月前

    给定方法

    ref readonly State GetState();
    

    哪里 State 是a struct ,以下代码访问 State.SimTime 不复制 国家 :

    ref readonly var state = ref GetState();
    var t = state.SimTime;
    

    同时,此代码确实会复制 国家 :

    var start = GetState();
    var t =  state.SimTime;
    

    我的问题是,以下副本是否 国家 :

    var t = GetState().SimTime;
    

    我希望不会,因为 GetState() 是一名裁判,从未被分配过,但我不确定,也不知道该如何测试。

    作为额外的好处,有没有一种方法可以静态地检查代码,以确定内存是否被复制?

    2 回复  |  直到 6 月前
        1
  •  2
  •   Sweeper    6 月前

    GetState().SimTime 不创建以下内容的副本 State 。您可以通过查看上生成的IL来看到这一点 SharpLab 或其他工具。

    GetState()。SimTime 编译为2 call 说明:

    // IL pseudocode
    call GetState
    call get_SimTime
    

    使用 ref readonly var 编译为相同的两个 呼叫 说明。

    另一方面,不使用 ref只读变量 编译为:

    // IL pseudocode
    call GetState
    ldobj State
    stloc.0
    ldloca.s 0
    call get_SimTime
    

    这里的关键说明是 ldobj -这就是为什么 参考 那个 GetState 返回一个实际的结构值(即 复制 属于 国家 ). 如果你没有看到这样的指令 ldobj 调用ref return方法后,这意味着代码正在对 参考 由方法返回,而不是结构值。


    As Marc Gravell's answer 说,运行时 如果getter SimTime 有副作用。

        2
  •  1
  •   Ivan Petrov    6 月前

    不,它不会被复制。

    docs :

    使用返回的值 从方法中,调用方法可以使用 方法调用自身 在任何地方,相同类型的值都是 足够了。

    使用 GetValue() 方法调用相当于使用 ref 局部变量/参数,即。 byRefParameter .

    如果我们没有 readonly 修饰符,我们可以很容易地通过写作来证明这一点:

    GetState().SimTime = 42; // byRefParameter.SimTime = 42
    

    由于访问了一个字段 byref 参数/局部变量,不涉及复制结构

    var t = byRefParameter.SimTime;
    

    这同样适用于你的案件

    var t = GetState().SimTime;
    
        3
  •  1
  •   Marc Gravell    6 月前

    对于a 领域 ?没有。但是,为了完整性,我们应该注意,字段通常不应该在公共API表面上公开,它通常应该是一个属性getter,它仍然与您问题中的代码兼容:

    ref readonly var state = ref GetState();
    var t = state.SimTime;
    

    现在事情变得更有趣了!

    如果已知属性getter没有副作用,那么这很好,但除此之外,这里还有一个防御性副本来保护以下事实 state 意思是 ref readonly 。通常,属性getter可以做任何事情,因此获取 .SimTime 不是 本身 可靠无副作用。但我们能做到!

    有两种方法:

    1. 标记整个 struct 作为 readonly
    2. 将特定属性访问者标记为 只读

    例如;

    readonly struct Foo
    {
        public int SimTime => // some code
    }
    
    readonly struct Bar
    {
        public readonly int SimTime => // some code
    }
    

    请注意,没有什么是真正的 只读 ,而编译器将强制执行规则、代码 可以 如果你足够努力,仍然可以绕过这些规则。