如何从interface{}中获取结构体成员的指针

10
我希望将结构体指针传递给期望接口{}的函数。然后通过反射获取结构体成员的指针,然后使用该指针修改它。我已经阅读了很多问答并尝试了很多变化,但仍然无法使其正常工作。
让我们考虑下面的例子:
type Robot struct {
    Id int
}
f := func(i interface {}) {
    v :=  reflect.ValueOf(i).Elem().FieldByName("Id")
    ptr := v.Addr().Pointer()
    *ptr = 100
    //^ it needs to me for functions expecting the pointer: Scan(&pointerToValue)
}

robot := &Robot{}
f(robot)
println(robot.Id) //I want to get here 100

我认为问题在于不太理解反射包中 Addr()Pointer() 方法的实际作用。

1个回答

21

这里是函数f的可运行版本:

func f(i interface{}) {
  v := reflect.ValueOf(i).Elem().FieldByName("Id")
  ptr := v.Addr().Interface().(*int)
  *ptr = 100
}

playground示例

将其转换为整数指针的方法如下:

  • v 是表示int字段的reflect.Value
  • v.Addr() 是表示指向int字段的指针的reflect.Value
  • v.Addr().Interface() 是包含int指针的interface{}
  • v.Addr().Interface().(*int)interface{}进行类型断言,转换为*int

您可以直接设置字段而无需获取指针:

func f(i interface{}) {
  v := reflect.ValueOf(i).Elem().FieldByName("Id")
  v.SetInt(100)
}

playground example

如果您将值传递给期望接口{}(例如db/sql Scan方法)的函数,则可以删除类型断言:

func f(i interface{}) {
  v := reflect.ValueOf(i).Elem().FieldByName("Id")
  scan(v.Addr().Interface())
}

playground示例


1
非常感谢。我的大脑已经超负荷了,我明天再尝试检查!我认为我需要时间和实践才能真正理解这里发生了什么。尤其是这里!v.Addr().Interface().(*int)。你可以分享一下我在哪里可以掌握这种知识吗? - Timur Fayzrakhmanov
2
@TimurFayzrakhmanov 我更新了答案,并解释了表达式 v.Addr().Interface().(*int) - Charlie Tumahai
1
v.Addr().Interface() 对于数据库操作非常好用。非常感谢您提供这个! - WhiteAngel
如果字段具有某个接口类型,则无法正常工作。 - wvxvw
@wvxvw 这是将 int 字段替换为接口类型的示例:https://play.golang.org/p/AMhp4KFv9C。它可以正常工作。 - Charlie Tumahai

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接