Golang中如何通过反射获取嵌入式类型的函数
Golang中如何通过反射获取嵌入式类型的函数 我正在尝试从一个在多层方法调用中嵌入的类型中获取一个方法。我这里有代码(已清理,所以你只需要看问题所需的部分)https://play.golang.org/p/p1nhHxJe2e2。
我试图使其工作的行是 m := vField.MethodByName("Validate"),以访问方法 func (tz *TZConfig) Validate(...)。
这只是某些初始化的一部分,因此使用反射并且可能比其他编写方式稍慢一点不是问题。
如果能提供关于我遗漏了什么才能使这工作的线索,将不胜感激。
以下是playground链接中的内容:
package main
import (
`fmt`
`os`
`reflect`
`time`
)
var Config Configuration
type Configuration struct {
Core CoreConfig
}
type CoreConfig struct {
// 其他内容
Database DatabaseConfig
}
type DatabaseConfig struct {
User string `validate:"required=true,min=1,max=80"`
Loc TZConfig ``
}
type TZConfig struct {
String string
TZ *time.Location
}
func main() {
if err := Config.validate(); err != nil {
os.Exit(1)
}
}
func (c *Configuration) validate() error {
return c.Core.validate()
}
func (core *CoreConfig) validate() error {
var err error
errs := 0
ancestry := `core`
core.Database.validate(core, ancestry, &errs)
return err
}
func (db *DatabaseConfig) validate(parent interface{}, ancestry string, errs *int) {
ancestry = ancestry+`.Database`
veParent := reflect.ValueOf(db).Elem()
for i := 0; i < veParent.NumField(); i++ {
vField := veParent.Field(i)
sField := veParent.Type().Field(i)
if sField.Name == `Loc` {
m := vField.MethodByName(`Validate`)
fmt.Println(m)
} else {
// 其他处理
}
}
_ = ancestry // 仅用于设置断点
}
func (tz *TZConfig) Validate(ancestry string, required bool, errs *int) {
if tz.TZ == nil {
if required {
// 显示错误
}
if len(tz.String) > 0 {
// 显示错误
}
}
}
更多关于Golang中如何通过反射获取嵌入式类型的函数的实战教程也可以访问 https://www.itying.com/category-94-b0.html
感谢您将其提炼成一个最小化场景。这让测试变得非常容易!
问题在于您的 Loc 字段是一个 TZConfig 结构体值,但 Validate 方法操作的是 TZConfig 指针。要使其正常工作,您只需将第60行改为:
m := vField.Addr().MethodByName(`Validate`)
更多关于Golang中如何通过反射获取嵌入式类型的函数的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
太棒了,谢谢!
我猜想这是因为方法有一个指针接收器 func (tz *TZConfig)...?我原本以为,如果我把 Loc 定义为 *TZConfig,我就需要这样做。如果我那样做了,是不是意味着我需要通过类似下面的方式来访问它:
m := vField.Addr().Addr().MethodByName(`Validate`)
直觉上,感觉需要这样做似乎不太对劲。
// 问题在于vField是reflect.Value类型,它代表的是TZConfig结构体的值,
// 而Validate方法的接收器是指针类型(*TZConfig)。需要通过Addr()获取指针的反射值。
// 修改后的代码:
func (db *DatabaseConfig) validate(parent interface{}, ancestry string, errs *int) {
ancestry = ancestry + `.Database`
veParent := reflect.ValueOf(db).Elem()
for i := 0; i < veParent.NumField(); i++ {
vField := veParent.Field(i)
sField := veParent.Type().Field(i)
if sField.Name == `Loc` {
// 获取字段的指针值,因为Validate方法的接收器是指针类型
vFieldPtr := vField.Addr()
m := vFieldPtr.MethodByName(`Validate`)
if m.IsValid() {
// 调用Validate方法
m.Call([]reflect.Value{
reflect.ValueOf(ancestry),
reflect.ValueOf(true),
reflect.ValueOf(errs),
})
} else {
fmt.Println("Validate method not found")
}
} else {
// 其他处理
}
}
}
// 或者,如果字段本身已经是指针类型,可以直接使用方法查找:
func (db *DatabaseConfig) validate(parent interface{}, ancestry string, errs *int) {
ancestry = ancestry + `.Database`
veParent := reflect.ValueOf(db).Elem()
for i := 0; i < veParent.NumField(); i++ {
vField := veParent.Field(i)
sField := veParent.Type().Field(i)
if sField.Name == `Loc` {
// 尝试直接查找方法
m := vField.MethodByName(`Validate`)
if !m.IsValid() {
// 如果值类型没有找到,尝试指针类型
m = vField.Addr().MethodByName(`Validate`)
}
if m.IsValid() {
m.Call([]reflect.Value{
reflect.ValueOf(ancestry),
reflect.ValueOf(true),
reflect.ValueOf(errs),
})
}
} else {
// 其他处理
}
}
}
// 完整示例:
package main
import (
"fmt"
"reflect"
)
type TZConfig struct {
String string
}
func (tz *TZConfig) Validate(ancestry string, required bool, errs *int) {
fmt.Printf("Validate called: %s, required=%v\n", ancestry, required)
}
func main() {
db := struct {
Loc TZConfig
}{
Loc: TZConfig{String: "test"},
}
v := reflect.ValueOf(&db).Elem()
field := v.FieldByName("Loc")
// 方法1:通过指针获取方法
method1 := field.Addr().MethodByName("Validate")
if method1.IsValid() {
errs := 0
method1.Call([]reflect.Value{
reflect.ValueOf("test"),
reflect.ValueOf(true),
reflect.ValueOf(&errs),
})
}
// 方法2:通过类型的方法集查找
fieldType := field.Type()
ptrType := reflect.PtrTo(fieldType)
if method, ok := ptrType.MethodByName("Validate"); ok {
errs := 0
method.Func.Call([]reflect.Value{
field.Addr(),
reflect.ValueOf("test"),
reflect.ValueOf(true),
reflect.ValueOf(&errs),
})
}
}

