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

3 回复

感谢您将其提炼成一个最小化场景。这让测试变得非常容易!

问题在于您的 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),
		})
	}
}
回到顶部