修改具有特定字段的任何结构的函数

时间:2017-10-28 02:40:51

标签: go struct embedding

我有几个结构,它们继承了一些基本结构。像这样:

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"`
}

现在我需要定义一个函数,该函数可以在任何具有字段ab的结构上运行。像

这样的东西
func modifyStruct(s *s1) {
    s.a, s.b = s.b, s.a
}

但必须处理s2,s3和任何其他继承s1的结构。我试图通过界面实现这一点,但到目前为止没有运气。有没有办法实现这个目标?该模板位于go-playground

2 个答案:

答案 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)
}

必须导出字段才能使用反射包,因为反射包不能设置未导出的字段。

playground example

相关问题