【问题标题】:How to initialize private fields that are default nil when making the zero value useful?当使零值有用时,如何初始化默认为零的私有字段?
【发布时间】:2021-04-15 08:25:23
【问题描述】:

我正在尝试遵循 Go 谚语“使零值有用”,但遇到了我的结构上默认为 nil(如 map)的私有字段的问题。如果我的结构的零值是有用的,在某些时候它需要初始化私有字段。考虑以下情况,我想取消Response 的构造函数,并使Response 的零值有用。显然需要初始化字段bag(即map[string]interface{})。当在响应上调用 getter Bag() 时,我尝试初始化字段 bag,这有效,然后允许在包上调用 Set() 以设置值。但不知何故,当响应最终回到主函数时,我的价值就丢失了。 我认为问题在于我提出的初始化替换了响应副本上的指针,而不是更改指针指向的位置。所以旧指针仍然指向nil,但我不确定。如何在没有构造函数的情况下正确初始化私有字段?

func main() {
    response := getResponse()
    number := response.Bag().Get("NiceNumber")
    fmt.Println(response.Name, "tells me that your nice number today is:", number)
}

func getResponse() (response Response) {
    response = newResponse() // I am trying to do away with this line.
    response.Name = "MyResponse"
    response.Bag().Set("NiceNumber", 100)
    return
}

type Response struct {
    Name string
    bag  Bag
}

func (r Response) Bag() Bag {
    // I have added this piece of code as an attempt to initialize r.bag
    if r.bag == nil {
        r.bag = Bag(make(map[string]interface{}))
    }
    // ---
    return r.bag
}

type Bag map[string]interface{}

func (b Bag) Set(name string, value interface{}) {
    b[name] = value
}

func (b Bag) Get(name string) interface{} {
    return b[name]
}

// This constructor would be deleted if possible.
func newResponse() Response {
    return Response{
        Name: "",
        bag:  Bag(make(map[string]interface{})),
    }
}

现在的输出是MyResponse tells me that your nice number today is: 100,当我删除对Response的构造函数的调用时,输出是MyResponse tells me that your nice number today is: <nil>

【问题讨论】:

  • “我认为问题在于我提出的初始化替换了 Response 副本上的指针,而不是更改指针指向的位置。” --是的,你是对的。如果您更改方法确实有一个指针接收器,即func (r *Response) Bag() Bag {,它应该会得到您期望的结果。但请记住,由于Bag() 修改了它的接收器,如果您的代码在Response 而不是*Response 周围传递,它仍然会以不一致的状态结束。
  • 这可能会起作用,但它无助于使Response 的零值有用,即Response{} 仍然无法正常工作。它还改变了 getResponse() 函数的签名,我希望尽可能避免。
  • 我认为对于像这样的复杂场景,constructor 是最好的方法:stackoverflow.com/questions/27553399/… 或正如@mkopriva 所说,更改方法以接受 ptr 接收器跨度>
  • @mkopriva 我只是按照你说的方式尝试了它,它似乎有效,但我不明白为什么。如何在 Response 的非指针变量上调用 getter Bag()?您能否将您的建议表述为一个带有一点解释的答案,以便我接受?
  • @Bazzz 编译器(或运行时?我忘了)会自动执行此操作。

标签: go


【解决方案1】:

您可以将Bag() 方法的接收者更改为指针。

func (r *Response) Bag() Bag {
    if r.bag == nil {
        r.bag = Bag(make(map[string]interface{}))
    }
    return r.bag
}

请注意,只要值是可寻址,您仍然可以对非指针值调用指针接收器方法。请参阅Calls 上的文档:

如果 x 的(类型)方法集,则方法调用 x.m() 是有效的 包含 m 并且参数列表可以分配给参数列表 米。如果 x 是可寻址的并且 &x 的方法集包含 m,则 x.m() 是 (&x).m() 的简写

【讨论】:

    猜你喜欢
    • 2014-12-29
    • 2015-06-28
    • 1970-01-01
    • 2020-05-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-22
    • 2020-05-01
    • 2011-04-06
    相关资源
    最近更新 更多