golang反射——执行函数

  在写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

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值