【问题标题】:Assign by reference通过引用分配
【发布时间】:2020-09-27 22:27:56
【问题描述】:

我被 Go 中的一个小任务引用问题困住了。我试图在下面的代码中模仿这个问题。

package main

import (
    "encoding/json"
    "fmt"
)

type tRes struct {
    a string
    b interface{}
}

func get(t string, r interface{}) error {
    var res tRes
    switch t {
    case "string":
        res = tRes{
            a: "XYZ",
            b: "Nobody",
        }
    case "int":
        res = tRes{
            a: "XYZ",
            b: 10,
        }
    }
    // r = &res.b
    j, _ := json.Marshal(res.b)
    json.Unmarshal(j, &r)
    return nil
}

func main() {
    var b string
    get("string", &b)
    fmt.Println(b)
    
    var i int
    get("int", &i)
    fmt.Println(i)
}

在代码中,get 方法根据类型返回一个值。如果我使用 r = &res.b 它不起作用,而 j, _ := json.Marshal(res.b) json.Unmarshal(j, &r) 起作用。我不想像在实际代码中那样对一个非常大的对象执行 json marshal 和 unmarshal,我正在调用一个返回大对象的 API。我执行 json unmarshal 来创建主对象,然后将一个子对象(定义为 interface{})分配给响应(可能是不同的类型)作为对我不起作用的参考。

【问题讨论】:

  • 您的目标是在解组之前设置字段类型b 吗?如果是这样,请参阅play.golang.org/p/mLznJZX7gB2
  • @MuffinTop right 这就是我想要的,它解决了问题。

标签: go pass-by-reference


【解决方案1】:

目标是在解组之前设置字段类型b。使用以下代码:

func get(data []byte, r interface{}) error {
    res := struct {
        A string
        B interface{}
    }{
        B: r,
    }
    return json.Unmarshal(data, &res)
}

这样称呼它:

func main() {
    var b string
    get([]byte(`{"a": "XYZ", "b": "Nobody"}`), &b)
    fmt.Println(b)

    var i int
    get([]byte(`{"a": "XYZ", "b": 10}`), &i)
    fmt.Println(i)
}

Run it on the playground.

【讨论】:

    【解决方案2】:

    您正在将 interface{} 按值传递给您的 get 函数。由于它是按值传递的,因此您的 r = &res.b 赋值会覆盖函数中的值,这对引用中包含的值没有影响。

    换句话说:

    • rinterface{}
    • 指的是一个指针
    • 指向stringint

    您的代码修改了r 的本地值以引用指向stringint 的新指针,忽略并丢弃传入的原始指针。

    如果你想修改指针的目标,你需要访问它。在您的接口类型上使用类型转换(例如if rstringptr, ok := r.(*string); ok { ... } 或类型开关)以获取其中引用的适当类型的值。这甚至可以让您简化 API,因为您不需要在单独的参数中提供类型。例如:

    import "fmt"
    
    func get(iface interface{}) {
      switch concrete := iface.(type) {
      case *string:
        *concrete = "Nobody"
      case *int:
        *concrete = 10
      }
    }
    
    func main() {
        var b string
        get(&b)
        fmt.Println(b)
        
        var i int
        get(&i)
        fmt.Println(i)
    }
    

    https://play.golang.org/p/JyYRjH9Dchj

    Nobody
    10
    

    【讨论】:

    • 您没有正确阅读我的问题。 tRes 是动态的,我通过调用我无法控制的 API 来获得。我无法添加 switch 案例,而问题中的评论有助于解决问题。
    猜你喜欢
    • 1970-01-01
    • 2016-07-12
    • 1970-01-01
    • 1970-01-01
    • 2019-03-21
    • 1970-01-01
    • 2018-06-21
    • 2011-10-06
    • 2019-10-20
    相关资源
    最近更新 更多