嵌入式结构体

huangapple go评论68阅读模式
英文:

Embedded struct

问题

有没有办法在不使用嵌入结构的情况下继承类型的方法?

第一个代码片段是一个工作代码,它在Node中嵌入了Property结构体,我可以调用Properties上的node.GetString方法。我不喜欢的是,当我初始化Node时,我必须初始化其中的Properties结构体。有没有办法绕过这个问题?

package main

import "fmt"

type Properties map[string]interface{}

func (p Properties) GetString(key string) string {
	return p[key].(string)
}

type Nodes map[string]*Node

type Node struct {
	*Properties
}

func main() {
	allNodes := Nodes{"1": &Node{&Properties{"test": "foo"}}} // :(
	singleNode := allNodes["1"]
	fmt.Println(singleNode.GetString("test"))
}

最终,我想做类似以下的事情。其中NodeProperties类型,并且初始化不需要初始化Property结构体。以下代码不起作用,但可能清楚我的目标是什么。

package main

import "fmt"

type Properties map[string]interface{}

func (p Properties) GetString(key string) string {
	return p[key].(string)
}

type Nodes map[string]*Node

type Node Properties

func main() {
	allNodes := Nodes{"1": &Node{"test": "foo"}} // :)
	singleNode := allNodes["1"]
	fmt.Println(singleNode.GetString("test")) // :D
}

我将添加更多使用Properties方法的结构体,这就是我提问的原因。如果只有Node,我只需为Node添加方法就可以了。但是因为我将有多个不同的结构体嵌入Properties,我觉得在所有嵌入Properties的结构体中添加相同的方法有点多余。

我想更准确地说,我希望在Node中使用Properties的方法,而不必初始化Properties

英文:

Is it possible to inherit methods of a type without using embedded structs?

The first snippet of code is working code that embeds the Property struct in Node and I'm able to call node.GetString that's a method on Properties. The thing I don't like about this is when I initialize Node I have(?) to initialize the Properties struct within it. Is there a way around this?

package main

import "fmt"

type Properties map[string]interface{}

func (p Properties) GetString(key string) string {
	return p[key].(string)
}

type Nodes map[string]*Node

type Node struct {
	*Properties
}

func main() {
	allNodes := Nodes{"1": &Node{&Properties{"test": "foo"}}} // :'(
	singleNode := allNodes["1"]
	fmt.Println(singleNode.GetString("test"))
}

Ultimately, I would like to do something like the following. Where Node is of type Properties and initializing does not require initializing a Property struct too. The following code doesn't work but may be clear what my goal is.

package main

import "fmt"

type Properties map[string]interface{}

func (p Properties) GetString(key string) string {
	return p[key].(string)
}

type Nodes map[string]*Node

type Node Properties

func main() {
	allNodes := Nodes{"1": &Node{"test": "foo"}} // :)
	singleNode := allNodes["1"]
	fmt.Println(singleNode.GetString("test")) // :D
}

I'll be adding more structs that will use Properties's methods which is why I'm asking. If I only had Node, I would just have methods for Node and be done. But because I'll have more than Node I find it kind of redundant to add the same methods to all the structs that embed Properties

I guess more to the exact problem, I want to use Properties methods from Node without having to initialize Properties.

答案1

得分: 4

所以你在这里遇到了Go语言的一个特殊情况。嵌入是一种方法,通过它,一个结构体的方法可以“提升”到另一个结构体中,使其看起来存在于另一个结构体中。虽然type Node Properties应该让Node暴露Properties的方法,但这种语法的效果是让Node采用Properties的内存布局,但不包含任何方法。

这并没有解释为什么会做出这样的设计选择,但是Go语言规范至少是具体而干燥的。如果你按照它的原文阅读,没有进行解释,它是非常准确的:

> 接口类型的方法集就是它的接口。其他类型T的方法集包括所有以T为接收者声明的方法。

GetString的接收者类型是Properties而不是Node,认真地按照规范来解释,就像你是一个没有想象力的会计师一样。有了这个前提:

> 对于包含匿名字段的结构体,还有其他规则,详见结构体类型的章节。
>
> ...
>
> 结构体x中的匿名字段f被称为提升字段,如果x.f是一个合法的选择器,表示该字段或方法f。
>
> 提升字段的行为类似于结构体的普通字段,除了不能在结构体的复合字面值中用作字段名。
>
> 给定一个结构体类型S和一个名为T的类型,提升方法包括在结构体的方法集中,如下所示:
>
> - 如果S包含一个匿名字段T,则S和S的方法集都包括以T为接收者的提升方法。S的方法集还包括以T为接收者的提升方法。
> - 如果S包含一个匿名字段
T,则S和S的方法集都包括以T或T为接收者的提升方法。

关于复合字面值的那一行是强制你在创建每个Node时声明Properties的原因。

附:嗨,Jeff!

英文:

So you're running into an idiosyncrasy of Go here. Embedding is the only way in which methods of one struct can get "promoted" to appear to exist on another struct. While it feels intuitive that type Node Properties should expose the Properties methods on Node, that effect of that syntax is for Node to take on the memory layout of Properties but not any of its methods.

It doesn't explain why this design choice was made but the Go Spec is at least specific if dry. If you read it exactly as it appears, with no interpretation, it is very accurate:

> The method set of an interface type is its interface. The method set of any other type T consists of all methods declared
> with receiver type T

GetString has a receiver of type Properties not Node, seriously, interpret the spec like you're an accountant with no imagination. With that said:

> Further rules apply to structs containing anonymous fields, as described in the section on struct types.
>
> ...
>
> A field or method f of an anonymous field in a struct x is called promoted if x.f is a legal selector that denotes that field or method f.
>
> Promoted fields act like ordinary fields of a struct except that they
> cannot be used as field names in composite literals of the struct.
>
> Given a struct type S and a type named T, promoted methods are
> included in the method set of the struct as follows:
>
> - If S contains an anonymous field T, the method sets of S and *S both
> include promoted methods with receiver T. The method set of *S also
> includes promoted methods with receiver *T.
> - If S contains an anonymous
> field *T, the method sets of S and *S both include promoted methods
> with receiver T or *T.

That line about composite literals is this thing that forces you to declare Properties inside every Node you create.

p.s. Hi Jeff!

答案2

得分: 1

对你最后一个问题的简短回答是不可以

在[golang]中,类型声明和嵌入之间有很大的区别,你可以通过手动在NodeProperties之间进行类型转换来使你的最后一个示例工作:

package main

import "fmt"

type Properties map[string]interface{}

func (p Properties) GetString(key string) string {
    return p[key].(string)
}

type Nodes map[string]*Node

type Node Properties

func main() {
    allNodes := Nodes{"1": &Node{"test": "foo"}} // :)
    singleNode := allNodes["1"]
    fmt.Println(Properties(*singleNode).GetString("test")) // :D
}

但很明显这不是你想要的,你想要的是一种带有类型别名语法的结构体嵌入,这在[golang]中是不可能的。我认为你应该坚持你的第一种方法,忽略代码冗余和丑陋的事实。

英文:

The short answer to your last question is simply No.

There is a big difference between type declaration and embedding in [tag:golang], you can make your last example working by manually make a type conversion between Node and Properties:

package main

import "fmt"

type Properties map[string]interface{}

func (p Properties) GetString(key string) string {
	return p[key].(string)
}

type Nodes map[string]*Node

type Node Properties

func main() {
	allNodes := Nodes{"1": &Node{"test": "foo"}} // :)
	singleNode := allNodes["1"]
	fmt.Println(Properties(*singleNode).GetString("test")) // :D
}

But it's clearly that is not what you want, you want a struct embedding with a syntax of type aliasing, which is not possible in [tag:golang], I think that you should stuck with the your first approach and ignore the the fact the code is redundant and ugly .

huangapple
  • 本文由 发表于 2015年12月4日 09:34:22
  • 转载请务必保留本文链接:https://go.coder-hub.com/34079466.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定