如何通过字符串名称调用方法,同时指定字符串结构名称?

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

How to call method by its string name with string struct name?

问题

我有一个结构体,在结构体定义下有两个方法,我想在其他地方调用这个结构体的名称和方法的名称作为参数。

结构体代码如下:

type ArticleForm struct {
    Name     string     `required:"true" pattern:"^[A-Za-z0-9\u4e00-\u9fa5]{1,1024}$" valid:"Required;MaxSize(1024)"`
    Category []Category `class:"multis" required:"true" valid:"Required" optionqs:"GetCategoryOption"`
    Content  string     `class:"wysiwg_area" required:"true" valid:"Required"`
    Tags     []Tags     `class:"multis_create" optionqs:"GetTagOptions"`
}

方法定义如下:

func (this *ArticleForm) GetTagOptions() []Tags {
    return GetTagsOptions(nil)
}

以下是我想要调用的代码:

func main() {
    s := "models.ArticleForm"
    t := "GetTagOptions"
    //以下是问题,如何执行以下代码?
    funcall(s,t)
}

如何实现 funcall(s,t)

英文:

I have one struct with 2 methods under struct definition, I want to call in other place that use struct's name and method's name as param.

Struct code as following:

type ArticleForm struct {
Name     string     `required:"true" pattern:"^[A-Za-z0-9\u4e00-\u9fa5]{1,1024}$" valid:"Required;MaxSize(1024)"`
Category []Category `class:"multis" required:"true" valid:"Required" optionqs:"GetCategoryOption"`
Content  string     `class:"wysiwg_area" required:"true" valid:"Required"`
Tags     []Tags     `class:"multis_create" optionqs:"GetTagOptions"`
}

Method definition as following:

func (this *ArticleForm) GetTagOptions() []Tags {
return GetTagsOptions(nil)
}

Following is what I want to call:

func main() {
s := "models.ArticleForm"
t := "GetTagOptions"
//following is the question, how can i exec following?
funcall(s,t)
}

How to fulfill the funcall(s,t)?

答案1

得分: 23

调用给定名称的某个类型的方法很容易(使用反射)。看看这个例子:

type My struct{}

func (m My) MyFunc() {
    fmt.Println("MyFunc called")
}

func main() {
    m := My{}
    meth := reflect.ValueOf(m).MethodByName("MyFunc")
    meth.Call(nil)
}

输出结果(在Go Playground上尝试):

MyFunc called

根据其字符串名称来“实例化”一个类型是不可能的,因为如果你的代码没有明确引用该类型,它甚至可能不会包含在可执行二进制文件中。有关详细信息,请参阅https://stackoverflow.com/questions/37384473/call-all-functions-with-special-prefix-or-suffix-in-golang/37384665#37384665和https://stackoverflow.com/questions/38875016/splitting-client-server-code/38875901#38875901。

一种可能的解决方法是使用某种“类型注册表”,在使用之前填充它(在您想要按名称创建值之前)。类型注册表(可以是一个映射)可以保存reflect.Type值或从类型名称映射的工厂函数。

根据上述My类型声明,存储reflect.Type值的类型注册表可能如下所示(在Go Playground上尝试):

registry := map[string]reflect.Type{
    "My": reflect.TypeOf((*My)(nil)).Elem(),
}

v := reflect.New(registry["My"]).Elem()
v.MethodByName("MyFunc").Call(nil)

而存储工厂函数的注册表可能如下所示(在Go Playground上尝试):

registry := map[string]func() interface{}{
    "My": func() interface{} { return My{} },
}

v := registry["My"]()
meth := reflect.ValueOf(v).MethodByName("MyFunc")
meth.Call(nil)
英文:

Calling a method of some type given by its name is easy (using reflection). See this example:

type My struct{}

func (m My) MyFunc() {
	fmt.Println("MyFunc called")
}

func main() {
	m := My{}
	meth := reflect.ValueOf(m).MethodByName("MyFunc")
	meth.Call(nil)
}

Output (try it on the Go Playground):

MyFunc called

"Instantiating" a type given its string name is not possible, because if your code does not explicitly refer to the type, it may not even be included in the executable binary. For details, see https://stackoverflow.com/questions/37384473/call-all-functions-with-special-prefix-or-suffix-in-golang/37384665#37384665; and https://stackoverflow.com/questions/38875016/splitting-client-server-code/38875901#38875901.

A possible workaround is to use some kind of "type-registry" which you populate prior to using it (before you want to create a value by its name). The type registry (which may be a map) may hold reflect.Type values or factory functions mapped from the type name.

Following the above My type declaration, a type registry storing reflect.Type values may look like this (try it on the Go Playground):

registry := map[string]reflect.Type{
	"My": reflect.TypeOf((*My)(nil)).Elem(),
}

v := reflect.New(registry["My"]).Elem()
v.MethodByName("MyFunc").Call(nil)

And a registry that stores factory functions could look like this (try it on the Go Playground):

registry := map[string]func() interface{}{
	"My": func() interface{} { return My{} },
}

v := registry["My"]()
meth := reflect.ValueOf(v).MethodByName("MyFunc")
meth.Call(nil)

huangapple
  • 本文由 发表于 2017年3月20日 22:51:05
  • 转载请务必保留本文链接:https://go.coder-hub.com/42906759.html
匿名

发表评论

匿名网友

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

确定