修改任何具有特定字段的结构的函数
Function which modifies any structure which has particular fields
我有几个结构,它们继承了一些基本结构。像这样:
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.
如果您 export the fields, then you can use the 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)
}
必须导出字段才能使用反射包,因为反射包无法设置未导出的字段。
一般的解决方案是使用反射,如 Cerise Limó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} }
如果你仍然希望它接受 "any"(某些)类型的值(这样你就不必引用嵌入的 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
)。
我有几个结构,它们继承了一些基本结构。像这样:
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.
如果您 export the fields, then you can use the 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)
}
必须导出字段才能使用反射包,因为反射包无法设置未导出的字段。
一般的解决方案是使用反射,如 Cerise Limó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} }
如果你仍然希望它接受 "any"(某些)类型的值(这样你就不必引用嵌入的 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
)。