我有几个结构,它们继承了一些基本结构。像这样:
type s1 struct {
a string `json:"a"`
b string `json:"b"`
}
type s2 struct {
s1
c string `json:"c"`
d string `json:"d"`
}
type s3 struct {
s1
c string `json:"c"`
d string `json:"d"`
e string `json:"d"`
f string `json:"d"`
}
现在我需要定义一个函数,该函数可以在任何具有字段a
,b
的结构上运行。像
func modifyStruct(s *s1) {
s.a, s.b = s.b, s.a
}
但必须处理s2,s3和任何其他继承s1的结构。我试图通过界面实现这一点,但到目前为止没有运气。有没有办法实现这个目标?该模板位于go-playground。
答案 0 :(得分:4)
一般的解决方案是使用CeriseLimón的答案中所示的反射。使用反射的缺点是你必须导出字段,并且它比应该或可能的速度慢。
在您的示例中,尽管函数采用*s1
类型的值完全正常且足够,因为嵌入s1
的所有类型都具有值s1
(显式) 。非限定类型名称(没有包名称)充当嵌入字段的字段名称:
s2 := s2{s1: s1{"A", "B"}}
fmt.Println(s2)
modifyStruct(&s2.s1)
fmt.Println(s2)
输出(在Go Playground上尝试):
{{A B} }
{{B A} }
如果您仍希望它接受“任何”(某些)类型的值(因此您不必引用嵌入的s1
字段),但不想导出字段,那么你可以使用接口。使用接口可以保留两个解决方案的优点(保持快速,灵活,您无需导出字段):
type S1 interface {
AB() (string, string)
SetAB(a, b string)
}
type s1 struct {
a string `json:"a"`
b string `json:"b"`
}
func (s s1) AB() (string, string) { return s.a, s.b }
func (s *s1) SetAB(a, b string) { s.a, s.b = a, b }
func modifyStruct(s S1) {
a, b := s.AB()
s.SetAB(b, a)
}
测试它:
s2 := s2{s1: s1{"A", "B"}}
fmt.Println(s2)
modifyStruct(&s2)
fmt.Println(s2)
输出相同(在Go Playground上尝试):
{{A B} }
{{B A} }
注意(除*s1
类型本身之外)任何自动(隐式)嵌入*s1
的结构类型(以及任何指向结构类型的指针)实现S1
接口,类似地任何嵌入s1
的结构类型的指针也会在您的示例中实现S1
(因此*s2
和*s3
。“
答案 1 :(得分:3)
如果您export这些字段,那么您可以使用reflect包来交换值:
type s1 struct {
A string `json:"a"`
B string `json:"b"`
}
...
func modifyStruct(s interface{}) {
v := reflect.ValueOf(s).Elem()
a := v.FieldByName("A")
b := v.FieldByName("B")
t := reflect.New(a.Type()).Elem()
t.Set(a)
a.Set(b)
b.Set(t)
}
必须导出字段才能使用反射包,因为反射包不能设置未导出的字段。