在 Golang 中用名字調用函數

上個星期,我寫了篇《Function call by name in Golang》。因爲是英文的,因此被人詬病(說誰,誰知道!)。好吧,如今用中文從新寫一遍。 php

Golang 中的函數跟 C 的同樣,是個代碼塊,不過它能夠像其餘類型那樣賦值給一個變量。 html

若是你對函數不熟悉,《Codewalk: First-Class Functions in Go》應該是個不錯的起點。已經有所瞭解?那麼繼續吧! golang

首先,來看看這段 PHP 代碼: c#

functionfoobar() {
    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

用這個方法調用匹配名字的函數,很是有效。 spa

那麼,在 Golang 中是否可能用函數的名字來調用某個函數呢? 指針

做爲一個靜態、編譯型語言,答案是否認的……又是確定的code

在 Golang 中,你不能這樣作: orm

func foobar() {
    // bla...bla...bla...
}
funcname :="foobar"
funcname()

不過能夠: htm

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, cint) {
    // 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])
    iflen(params) != f.Type().NumIn() {
        err = errors.New("The number of params is not adapted.")
        return
    }
    in := make([]reflect.Value, len(params))
    fork, 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://bitbucket.org/mikespook/golib/src/27c65cdf8a77/funcmap.

但願這有些幫助。好運,地鼠們!

相關文章
相關標籤/搜索