英文:
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"))
}
最终,我想做类似以下的事情。其中Node
是Properties
类型,并且初始化不需要初始化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]中,类型声明和嵌入之间有很大的区别,你可以通过手动在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 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 .
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论