英文:
How to embed struct values via an embedded interface : Composable Structs
问题
这个问题最好通过一个例子来描述。
http://play.golang.org/p/bQuRr0kV-b
我正在尝试创建一个可组合的结构体。在这个例子中,我想要一个名为Person的类型,它嵌入了Female或Male的值。如果我只是处理结构体,我会像这样嵌入它们:
type Person struct{
Female
Male
}
然而,我不能这样做,因为在实际项目中,有很多嵌入的结构体,我更希望保持结构体的清晰和可组合性。但是这里也存在命名冲突 - 在这个例子中,Male和Female都包含字段'Eyes'。将冲突的值移动到Person结构体中不是一个可行的解决方案(因为许多其他嵌入的结构体不包含那个特定的值)。
我希望通过一个简单的接口来传递这些值。示例如下:运行这段代码时,我得到的结果是&{Name: Age:0 Type:male GenderType:0x10434120}
,其中GenderType是指向Male结构体的指针(在这种情况下)。我的目标是返回一个扁平的结构,看起来像&{Name: Age:0 Type:male Eyes: ChestSize:0}
。
package main
import "fmt"
type Person struct {
Name string
Age int
Type string
GenderType GenderType
}
type GenderType interface {
TypeName() string
}
type Male struct {
Eyes string
ChestSize int
}
type Female struct {
Eyes string
BustSize int
}
func (m *Male) TypeName() string {
return "male"
}
func (f *Female) TypeName() string {
return "female"
}
func main() {
d := NewHuman(new(Male))
fmt.Printf("%+v", d)
}
func NewHuman(gt GenderType) *Person {
return &Person{
Type: gt.TypeName(),
GenderType: gt,
}
}
希望这能帮到你!
英文:
This question is best described by an example
http://play.golang.org/p/bQuRr0kV-b
I am trying to make a composable struct. In this example, I want to have a Person type with embedded values from either Female or Male. If I were just dealing with structs, I would embed them like this
type Person Struct{
Female
Male
}
I cannot do this however, both because in the actual project, there are a lot of embedded structs and I would prefer to keep the struct clean and composable. But there is also a naming conflict — in this example, both Male and Female contain the field 'Eyes'. Moving the conflicting value to the Person struct is not a viable solution (as many of the other embedded structs do not contain that particular value).
I was hoping to pass these values via a simple interface. Sample Below: When running this code, I get &{Name: Age:0 Type:male GenderType:0x10434120}
where GenderType is the pointer to Male struct(in this case). My Goal is to have a flat structure returned that would look like &{Name: Age:0 Type:male Eyes: ChestSize:0}
package main
import "fmt"
type Person struct {
Name string
Age int
Type string
GenderType
}
type GenderType interface {
TypeName() string
}
type Male struct {
Eyes string
ChestSize int
}
type Female struct {
Eyes string
BustSize int
}
func (m *Male) TypeName() string {
return "male"
}
func (f *Female) TypeName() string {
return "female"
}
func main() {
d := NewHuman(new(Male))
fmt.Printf("%+v", d)
}
func NewHuman(gt GenderType) *Person {
return &Person{
Type: gt.TypeName(),
GenderType: gt,
}
}
答案1
得分: 2
我认为在一个扁平的结构中做到这一点是不可能的,因为这将涉及在运行时更改结构类型的内存结构,而Go语言不允许这样做。虽然你可以使用GenderType访问嵌入字段,但这仍然是允许的,因为你是在说这将是一个满足接口的嵌入结构的引用,而不是改变结构本身的结构。
我认为更好的方法是保留嵌入的结构,然后使Person结构成为一个Marshaler。
你可以添加自己的自定义MarshalJSON() (byte[],error)方法,并使用它来生成一个扁平的JSON输出。
如果你需要专门的反序列化,那么你可以使用与Person相关联的(UnmarshalJSON([]byte) error)方法来实现。
请参考https://golang.org/pkg/encoding/json/#Marshaler了解更多信息。
这里有一个示例,展示了我的意思:https://play.golang.org/p/qOl9WSaI3O
英文:
I don't think it is possible to do this in a flat structure because it would entail changing the memory structure of the struct type during runtime, which go doesn't allow. While you can access embedded fields using GenderType, it's still allowed because you are saying that this will be a reference to an embedded struct that satisfies the interface rather than changing the structure of the struct itself.
I think the better way to marshal into a flat json using this is to keep your embedded structure, but then make the Person struct a Marshaler
You can add your own custom MarshalJSON() (byte[],error) method and use this to make a flat json output.
If you need specialized unmarshaling, then you can do likewise with an (UnmarshalJSON([]byte) error) method tied to Person.
see https://golang.org/pkg/encoding/json/#Marshaler for further reference
Here is a playground that shows what I mean: https://play.golang.org/p/qOl9WSaI3O
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论