在写golang框架的时候,免不了需要处理类似方法执行的问题,由于框架层对业务层的具体方法是不清楚了,只提供了基本的输入和输出格式,所以就需要使用一种方式,适配业务层的函数调用。
golang提供了反射,其中最终要的两个内容是Type和Value。通过反射,我们可以new一个struct,或者实现赋值等操作。可以简单理解为使用另一种方式实现对程序的处理。说了这么多,本篇文章着重介绍如何使用golang的反射机制,实现对函数的调用。
主要参考
https://studygolang.com/articles/12348?fr=sidebar
https://jimmyfrasche.github.io/go-reflection-codex/#invoke-a-function
好,下面上货。
package demo
import (
"errors"
"fmt"
"reflect"
"testing"
)
func hello() {
fmt.Println("123")
}
func xixi(a int) (string, error) {
fmt.Println(a)
return "abc", nil
}
func haha(p ...int) (string, error) {
fmt.Println(p)
return "haha", errors.New("bad bad...")
}
type msg struct {
name string
}
type specialMsg struct {
msg
age int
}
func lala(p ...int) (*msg, error) {
fmt.Println(p)
return &msg{
name: "lalalaa",
}, nil
}
func xaxa(a *msg) (string, error) {
fmt.Println(a.name)
return "ok", nil
}
func xaxa2(b *specialMsg) (string, error) {
fmt.Println(b.name)
return "lllala", nil
}
//反射调用
func invoke(f interface{}, params ...interface{}) []reflect.Value {
fv := reflect.ValueOf(f)
realParams := make([]reflect.Value, len(params)) //参数
for i, item := range params {
realParams[i] = reflect.ValueOf(item)
}
rs := fv.Call(realParams)
return rs
}
func TestNewStudent(t *testing.T) {
//无参数调用
k := invoke(hello)
fmt.Println(k)
//固定参数调用
c := invoke(xixi, 12)
fmt.Println(c)
//可变参数调用
j := invoke(haha)
fmt.Println(j)
//如何处理返回值
fmt.Printf(j[0].String())
fmt.Printf(j[0].Kind().String())
err := j[1].Interface().(error)
if err != nil {
fmt.Printf(err.Error())
}
m := invoke(lala, 1, 2, 3)
fmt.Println(m[0].Pointer())
fmt.Println(m[0].Kind().String())
//返回结构体指针(进行强制转换)
rrr := m[0].Interface().(*msg)
fmt.Println(rrr)
fmt.Println(rrr.name)
fmt.Println(m)
tempP := &msg{
name: "sdfdfdf",
}
ss := invoke(xaxa, tempP)
fmt.Println(ss)
tempSpecalMsg := &specialMsg{
msg: msg{
name: "aaaa",
},
age: 0,
}
ss2 := invoke(xaxa2, tempSpecalMsg)
fmt.Println(ss2)
}
简单说实现了对各种函数的一个反射调用。注意,这里是函数,不是方法,方法的话有些区别,可以参考上面的几个连接。
最后说一下需要注意的。
首先是这里的error,我们定义函数的返回值如果是error,那么需要注意从Value转换到error,没有直接转换的方法,需要先得到interface{}然后在转换为error才可以。同理对于自定义的参数,比如结构体,也需要先转换为interface{},然后在转换为结构体才可以。
运行结果:
GOROOT=/usr/local/go1.12.5 #gosetup
GOPATH=/Users/wuxueyou/gopath #gosetup
/usr/local/go1.12.5/bin/go test -c -o /private/var/folders/6q/swpkld2x38q9cqpkq75l18_80000gn/T/___TestNewStudent_in_study004_demo study004/demo #gosetup
/usr/local/go1.12.5/bin/go tool test2json -t /private/var/folders/6q/swpkld2x38q9cqpkq75l18_80000gn/T/___TestNewStudent_in_study004_demo -test.v -test.run ^TestNewStudent$ #gosetup
=== RUN TestNewStudent
123
[]
12
[abc <error Value>]
[]
[haha <error Value>]
hahastringbad bad...[1 2 3]
824633982992
ptr
&{lalalaa}
lalalaa
[<*demo.msg Value> <error Value>]
sdfdfdf
[ok <error Value>]
aaaa
[lllala <error Value>]
--- PASS: TestNewStudent (0.00s)
PASS
Process finished with exit code 0