关于Go语言反射机制的浅谈
刚把Go的基础语法过一遍,趁着知识还热乎,就想通过构思一个后台管理的框架,来巩固下所学技能。
在Github上扒拉了不少现有的的框架和项目,总觉得不太满意。其中最不能忍受的,就是不同包之间调用函数时,需要引入很多重复的包。
本来还是可以忍一下的,可刚有一个好的想法,却发现使用Go难以实现:
文件夹router目录下有若干个go文件,我希望后期用户可以随意增加或删除其中的go文件。
当然go文件的定义也有规定,文件内容是只定义一个函数(或结构体)且与文件名一致。
我的想法是: 当运行项目时,扫描router目录,得到每个go文件名,再通过反射执行里面的函数(或结构体的方法)。
因为router目录下的go文件名字是不确定的,按照Java或者PHP的方式是很容易实现的:
// Java方式
Class obj = Class.forName("完整.类名");
// PHP方式
$obj = new ReflectionClass("完整\类名");
然而我通过翻看反射相关的内容,发现居然,第一步“得到函数(或结构体)”是行不通的。
在Go反射相关的接口中并不能如此的获取函数或结构体。都说万事开头难,这完全是出师未捷身先死了!
我不死心的去发看了手头的书籍,又去查看了网上的许多博客,还去找了一些有工作经验的goer。无一例外的没把问题说在点上。
所有介绍go反射的文章或视频,都是建立在已经得到这个函数(或结构体)之上,然后可以剖析其中的属性或方法等等。
例如:
// 文章介绍通过字符串类型的函数名不能直接去调用函数
// 但是可以通过map方式婉转的实现该功能
func f1(){
println("f1")
}
func main() {
funcs := make(map[string]func())
// 看到将f1赋值给map时,我是比较尴尬的。
// 题目说只能得到字符串的“f1”, 后面的f1是外星人送的吗?
funcs["f1"] = f1
// 既然上面已经得到f1,这里完全不用这么费劲了,直接f1()
funcs["f1"]()
}
又例如:
type Student struct {
Name string
}
func (s Student) Info() {
println("Student Info")
}
func main() {
// 很明显,前提条件已经拿到结构体
s := Student{}
obj := reflect.ValueOf(s)
// 通过字符串找方法是可行的
infoFunc := obj.MethodByName("Info")
infoFunc.Call([]reflect.Value{})
}
结构体的反射中通过字符串找方法是可以的,但其实还是第一步 [拿到结构体] 是办不到的。
折腾良久,问题依然没有很好的解决。
最终在Github上找到一个解决这类问题的项目,作者改写了底层逻辑,已经跳出了Go的功能接口。
或许是Go的静态编译机制导致的问题吧,总之Go的反射并不能算是完整反射。
哪位道友有解决之法,还望不吝赐教!