Golang 通过反射的方式调用结构体方法

在Go语言中,反射就是用来检查储存在接口变量内部pair对的一种机制,pair对是以值(value)和实际类型(concrete type)组成.在go中提供两种方法让我们可以轻松地访问接口变量的内容,分别是 reflect.ValueOf()和 reflect.TypeOf()

reflect.ValueOf(i interface{} )

用来获取输入参数接口中的数据的值,如果接口为空则返回0

reflect.TypeOf(i interface{} )

用来获取输入参数接口中的值的类型,如果接口为空则返回nil

var num float64 = 1.2345
fmt.Println("type : " , reflect.TypeOf(num)) //float64
fmt.Println("type : " , reflect.ValueOf(num)) //1.2345

这说明反射可以将“接口类型变量”转换为“反射类型变量” , 反射类型指的就是reflect.Type和reflect.Value
在构建框架工程的时候,需要可以随意扩展的方法,或者说在Web程序框架设计中编写调度分发控制器的时候,往往需要用到反射(reflect)来完成相关工作
以下例子是演示通过反射来调用结构体方法:

package main
import (
    "fmt"
    "reflect"
)
type User struct{
    Id int 
    Name string
    Age int
}
//ToString方法
func (u User) String() string {
    return "User[ Id " + string(u.Id) +"]"
}
//设置Name方法
func (u *User) SetName(name string) string{
  oldName := u.Name
  u.Name = name
  return oldName 
}
//年龄数+1
func (u *User) AddAge() bool {
    u.Age++
    return true
}
//测试方法
func (u User) TestUser() {
  fmt.Println("我只是输出某些内容而已....")
}

func main(){
    //通过反射的方式调用结构体类型的方法
    var setNameStr string = "SetName"
    var addAgeStr string = "AddAge"
    user := User{
        Id : 1,
        Name : "env107" , 
        Age : 18 ,
    }
    //1.获取到结构体类型变量的反射类型
    refUser:= reflect.ValueOf(&user)  //需要传入指针,后面再解析
    fmt.Println(refUser)
    //2.获取确切的方法名
    //带参数调用方式
    setNameMethod := refUser.MethodByName( setNameStr  )
    args := []reflect.Value{ reflect.ValueOf("Mike")  } //构造一个类型为reflect.Value的切片
    setNameMethod.Call(args) //返回Value类型
    //不带参数调用方式
    addAgeMethod := refUser.MethodByName( addAgeStr )
    addAgeMethod.Call( make([]reflect.Value , 0) )
    
    fmt.Println("User.Name = ",user.Name)
    fmt.Println("User.Age = ",user.Age)

}

上述代码运行的结果将会是

<*main.User Value>
User.Name =  Mike
User.Age =  19

可见,通过反射的方式成功的将user的名字更改为Mike并将Age的数+1
在这里需要思考一个问题,方法TestUser接收者的类型是User类型而非User的指针类型,在结构体当中,接收者类型的区别将影响该结构体方法的可见性。假设接收者类型为指针类型则该方法称为指针方法,假如是值类型,则该方法称为值方法。
如果将代码改成

refUser:= reflect.ValueOf(user)

在我们后续通过反射调用TestUser的时候,将会引起恐慌

panic: reflect: call of reflect.Value.Call on zero Value

造成该恐慌的原因是refUser.MethodByName( setNameStr )并没有返回一个reflect.Value而是一个nil
原因在于,User类型是*User的基底类型,在Go的指针知识中,有一条规则:一个指针类型拥有它以及它的基底类型为接收者类型的所有方法,而它的基底类型却只能拥有以它本身为接收者类型的方法。

也就是说,User为*User基底类型,所以User类型只能拥有以它本身为接收者类型(User类型)的方法,也就是TestUser , 而指针方法(SetName和AddAge)只有*User类型才拥有,而 refUser:= reflect.ValueOf(user) 拿到的是User类型的反射类型对象,因此并没有指针方法,引起恐慌。

这里补充一下指针方法和值方法的区别

type User struct{
    Id int 
    Name string
    Age int
}
func (u *User) test1() {}
user := User{
        Id : 1,
        Name : "env107" , 
        Age : 18 ,
    }

以接收者类型为*User的结构体方法,其中变量u是user的值的指针的副本,但如果接收者类型为User的结构体方法,也就是值方法,则变量u是user的一个副本,如果尝试改变u的副本的属性值,则对user的属性值是不会造成影响的

参考文章:https://studygolang.com/articles/12348?fr=sidebar

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 222,104评论 6 515
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,816评论 3 399
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 168,697评论 0 360
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,836评论 1 298
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,851评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,441评论 1 310
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,992评论 3 421
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,899评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,457评论 1 318
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,529评论 3 341
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,664评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,346评论 5 350
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 42,025评论 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,511评论 0 24
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,611评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 49,081评论 3 377
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,675评论 2 359