我可以写一个操作具有任意数量字段的结构体的 Go 方法吗?

huangapple go评论86阅读模式
英文:

Can I write a Go method that operates on a struct with arbitrary number of fields?

问题

我有一个Go结构体,看起来像这样:

type myStruct struct {
    A bool
    B bool
    C bool
}

我想编写一个方法,将布尔值从true翻转为false,反之亦然。但我不想为字段ABC重复相同的代码3次。我想要写一个循环。

我考虑的代码如下:

func (m *myStruct) flipBools() {
    r := reflect.ValueOf(*m)
    for i := 0; i < r.NumField(); i++ {
        // 在这里写什么?
    }
}

但我不知道在循环内部应该写什么。这可行吗?我意识到对于像Go这样的强类型语言来说,这是一个很高的要求。

英文:

I have a Go struct that looks like this:

type myStruct struct {
	A bool
	B bool
	C bool
}

I want to write a method that flips the bool values from true to false and vice-versa. But I don't want to repeat the same code 3 times for fields A, B & C. I want to write a loop.

I'm thinking something like this:

func (m *myStruct) flipBools() {
	r := reflect.ValueOf(*m)
	for i := 0; i &lt; r.NumField(); i++ {
		// WHAT GOES HERE?
	}
}

But I don't know what to put inside the loop. Can it be done? I realize this is a tall order for a strongly typed language like Go.

答案1

得分: 3

不需要反射。

func (m *myStruct) flipBools() {
    ps := []*bool{&m.A, &m.B, &m.C}
    for _, p := range ps {
        *p = !*p
    }
}

你甚至可以将逻辑提取到一个可变参数的函数中,并且可以重复使用,如果你想的话:

func flipBools(ps ...*bool) {
    for _, p := range ps {
        *p = !*p
    }
}

func (m *myStruct) flipBools() {
    flipBools(&m.A, &m.B, &m.C)
}
英文:

No need for reflection.

func (m *myStruct) flipBools() {
    ps := []*bool{&amp;m.A, &amp;m.B, &amp;m.C}
    for _, p := range ps {
        *p = !*p
    }
}

https://play.golang.org/p/RrCo4Kph-hl


You can even extract the logic into a variadic func and have it be re-used, if you want to:

func flipBools(ps ...*bool) {
    for _, p := range ps {
        *p = !*p
    }
}

func (m *myStruct) flipBools() {
    flipBools(&amp;m.A, &amp;m.B, &amp;m.C)
}

https://play.golang.org/p/082lu4-VZ18

答案2

得分: 3

为了实际回答这个问题:是的,这是可能的(即使不是特别明智)。以下是实现的代码:

func (m *myStruct) flipBools() {
    r := reflect.ValueOf(m).Elem()
    for i := 0; i < r.NumField(); i++ {
        fld := r.Field(i)
        if fld.Kind() == reflect.Bool {
            fld.SetBool(!fld.Bool())
        } // 可选:否则,做其他操作?
    }
}

内部循环实际上很简单;唯一需要修复的是使用 reflect.ValueOf(*m) 会得到一个不可寻址的值,无法设置其字段;而 reflect.ValueOf(m).Elem() 通过指针访问结构体,这样就可以正常工作了。

英文:

In the spirit of actually answering the question: yes, it's possible (even if it's not particularly wise). This works:

func (m *myStruct) flipBools() {
    r := reflect.ValueOf(m).Elem()
    for i := 0; i &lt; r.NumField(); i++ {
        fld := r.Field(i)
        if fld.Kind() == reflect.Bool {
            fld.SetBool(!fld.Bool())
        } // optional: else, do something else?
    }
}

The inner loop is actually pretty easy; the only thing that needed fixing was that using reflect.ValueOf(*m) leaves you with an un-addressable value that can't have its fields set; reflect.ValueOf(m).Elem() accesses the struct through the pointer, which works fine.

huangapple
  • 本文由 发表于 2021年8月31日 12:04:03
  • 转载请务必保留本文链接:https://go.coder-hub.com/68992726.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定