上个星期,我写了篇《Function call by name in Golang》。由于是英文的,所以被人诟病(说谁,谁知道!)。好吧,现在用中文重新写一遍。
Golang 中的函数跟 C 的一样,是个代码块,不过它可以像其他类型那样赋值给一个变量。
如果你对函数不熟悉,《Codewalk: First-Class Functions in Go》应该是个不错的起点。已经有所了解?那么继续吧!
首先,来看看这段 PHP 代码:
function foobar() { echo "Hello Golang\n"; } $funcs = array( "foobar" => "foobar", "hello" => "foobar", ); $funcs["foobar"](); $funcs["hello"]();它会输出:
mikespook@mikespook-laptop:~/Desktop$ php foobar.php Hello Golang Hello Golang
用这个方法调用匹配名字的函数,非常有效。
那么,在 Golang 中是否可能用函数的名字来调用某个函数呢?
作为一个静态、编译型语言,答案是否定的……又是肯定的!
在 Golang 中,你不能这样做:
func foobar() { // bla...bla...bla... } funcname := "foobar" funcname()不过可以:
func foobar() { // bla...bla...bla... } funcs := map[string]func() {"foobar":foobar} funcs["foobar"]()但这里有一个限制:这个 map 仅仅可以用原型是“func()”的没有输入参数或返回值的函数。
如果想要用这个方法实现调用不同函数原型的函数,需要用到 interface{}。是啊!interface{},跟 C 中的 void 指针类似。还记得这个东西吗?不记得了?没事,看看这个吧:《The Go Programming Language Specification:Interface types》。
这样,就可以添加有着不同函数原型的函数到一个 map 中:
func foo() { // bla...bla...bla... } func bar(a, b, c int) { // bla...bla...bla... } funcs := map[string]interface{}{"foo":foo, "bar":bar}那么如何调用 map 中的函数呢?像这样吗:
funcs["foo"]()绝对不行!这无法工作!你不能直接调用存储在空接口中的函数。
反射走进我们的生活!在 Golang 中有着叫做“reflect”的包。你是否了解反射了呢?
如果还没有,那么阅读一下这个:《Laws of reflection》吧。哦,这里有个中文版本:《反射的规则》。func Call(m map[string]interface{}, name string, params ... interface{}) (result []reflect.Value, err error) { f = reflect.ValueOf(m[name]) if len(params) != f.Type().NumIn() { err = errors.New("The number of params is not adapted.") return } in := make([]reflect.Value, len(params)) for k, param := range params { in[k] = reflect.ValueOf(param) } result = f.Call(in) return } Call(funcs, "foo") Call(funcs, "bar", 1, 2, 3)将函数的值从空接口中反射出来,然后使用 reflect.Call 来传递参数并调用它。
没有什么是很难理解的。我已经完成了一个包来实现这个功能,在这里:https://github.com/mikespook/golib/tree/master/funcmap.
希望这有些帮助。好运,地鼠们!
这个。。。我以为真可以,结果还是要手动初始化一遍
本来就是为了方便,如果是需要自己map或者做包的话,不理解有什么意义。。。直接调用原函数不就好了……
当你的 switch 的 case 达到两位数以上时,意义就很明显了……
一直觉得变量函数用着方便,今天终于找到golang版了
要简单就这样写:
func (t *T) Foo() {}
func (t *T) Bar() {}
Call(t, “Foo”)
这个用反射也很容易实现。
bitbucket 代码中: bind函数: v.Type().NumIn() ,是必要的吗?
但这里有一个限制:这个 map 仅仅可以用原型是“func()”的没有输入参数或返回值的函数。
var Urls = map[string]func(http.ResponseWriter, *http.Request)
这样定义map是可以有传入参数和返回值的
func() 这里需要结合上下文。换句话说,如果用你的声明的话。那这个 map 就只能赋值 func(http.ResponseWriter,*http.Request) 这样的 func 了。而不是任意形式的 callable。
「Golang 中的函数跟 C 的一样,是个代码块,不过它可以像其他类型那样赋值给一个变量。」——为什么说「不过」呢?难道你认为 C 里的函数不能赋值给变量?而且可以直接调用,不需要反射什么的。
虽然在 C 中也可以通过函数指针的方式来保存函数的引用并调用函数,但 C 中的方式与 Go 中的方式在本质上是完全不同的。C 中是一个指针,Go 中是一个值。另外,这与反射无关,那是 runtime 的东西了。呵呵~
完美
这种方式带来的性能损耗太大了,而且会造成内存占用过多的问题
大哥 你那个map里面要预存对应的func,如果我想仅仅根据一个字符 去扫描文件 调用匹配上的 func,仅仅知道一个字符串行吗?
不是很理解你说的“文件”是指的什么东西。你可以把需求描述一下吗?感觉你是在寻求使用插件系统,但是不确定需求的情况下,很难说哪种形式更适合你。
同上一层的问题,我也想知道有没有办法使map的key和value是从某描述了调用关系的文本或者数据库中读取出来的,而不是在代码里定义好了函数名,但是读出来的都是字符串类型。。。
Go 本身是个静态编译型的语言,如果需要这种动态特性,我觉得最好是引入其他方式。比如 lua,python 甚至 php 什么的。可以参考这个代码,使用了 lua:https://github.com/mikespook/gleam/blob/master/lua.go
滴滴,请问文末的示例包链接怎么打不开了啊