分享
  1. 首页
  2. 文章

golang 反射

rojas · · 10995 次点击 · · 开始浏览
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

转自:http://golanghome.com/post/546

自己在用Go写Web框架时,遇到要从接口中返回对象信息的技术问题。网上关于Go中接口反射的资料较少,所以自己学习了一段时间,特将结果与大家分享。

代码约定

import (
 "fmt"
 "reflect"
)
type boy struct {
 Name string 
 age int
}
type human interface {
 SayName()
 SayAge()
}
func (this *boy) SayName() {
 fmt.Println(this.Name)
}
func (this *boy) SayAge() {
 fmt.Println(this.age)
}
func main() {
 // 定义接口变量
 var i human
 // 初始化对象,jown持有对象指针。
 jown := &boy{
 Name: "jown",
 age: 15,
 }
 // 因为boy实现了human中的方法,所以它实现了human接口。
 // 这时,i就指向jown对象。
 i = jown 
 // 通过反射获取接口i 的类型和所持有的值。
 t := reflect.TypeOf(i)
 v := reflect.ValueOf(i)
 // ... 后续操作
}

t,v 的打印结果:

t, v 实现了String() string 所对应的接口,所以可以在fmt中打印出来。

fmt.Println(t)
fmt.Println(v)
// 打印结果
*main.boy 
<*main.boy Value>

从上面的打印结果可见:

  1. t 表示i接口的类型为指向main包下struct boy的指针类型。它可以用来存储指向boy的指针。
  2. v 表示i接口目前的所存储值为指向main包下struct boy的指针,也可以理解为上面代码中的jown

得到了这些信息,我们就可以进行后续操作:

通过接口i查询对象的名字

reflect.Type类型下有一个方法Name() string,用来返回不包含包名的类型的名字。所以,我们需要知道i所存储的对象的类型。reflect.Elem() Type可以返回类型的成员类型。这样我们就可以用这两个函数返回对象的名字。

 // 获取i所指向的对象的类型
 structType := t.Elem()
 // 获取对象的名字
 structName := structType.Name()
 fmt.Println(structName)
 // 打印结果
 boy

通过接口i查询对象的方法信息

由于在Go中,func也是一种类型,所以对象的方法存在值和类型的说法。

通过t获取对象方法的信息。

利用t的MethodByName() Method方法:

 method, _ := t.MethodByName("SayAge")
 fmt.Println(method)
 // 打印结果
 {SayAge func(*main.boy) <func(*main.boy) Value> 1}

它返回了一个对象方法的信息集合即Method,关于Method结构定义在reflect/type.go下具体为:

// Method represents a single method.
type Method struct {
 // Name is the method name.
 // PkgPath is the package path that qualifies a lower case (unexported)
 // method name. It is empty for upper case (exported) method names.
 // The combination of PkgPath and Name uniquely identifies a method
 // in a method set.
 // See http://golang.org/ref/spec#Uniqueness_of_identifiers
 Name string
 PkgPath string
 Type Type // method type
 Func Value // func with receiver as first argument
 Index int // index for Type.Method
}

可以看出该信息包含对象方法的名字,包路径(导出的方法,此路径为空),方法的类型,方法的接收者,该方法在对象中的位置从0开始。

通过v获取对象方法的信息。

利用v.MethodByName() Value方法:

 method := v.MethodByName("SayAge")
 fmt.Println(method)
 // 打印结果
 <*main.boy Value>

它又返回了一个Value类型。我们通过类型中的方法获取一些对象方法的信息:

 // 返回方法的地址
 fmt.Println(method.Pointer())
 // 返回方法的类型
 fmt.Println(method.Type())
 // 对象方法是否可以更改
 fmt.Println(method.CanSet())
 
 //打印结果
 4527472
 func()
 false

通过v调用方法。

注意,调用的方法必须是可导出的。

 // 有输入参数的方法调用。例如, func (this *boy) SayName (name string) {}。
 // 构造输入参数
 args := []reflect.Value{reflect.ValueOf("liming")}
 // 通过v进行调用
 v.MethodByName("SayName").Call(args)
 // 无输入参数的方法调用。例如,func (this *boy) SayName(){}。
 // 构造zero value
 args := make([]reflect.Value, 0)
 // 通过v进行调用
 v.MethodByName("SayName").Call(args)

总结

通过上面的代码,Go的反射比较方便和强大。在静态类型语言中实现反射是一件挺伟大的事。当然,这也是得益于Go有一层中间件Runtime,reflect包的大部分功能靠它来实现。上面只是列举了通过接口反射对象的方法信息以及调用方法。我们还可以加以变通来反射对象的字段信息,已经动态修改字段内容等等。总的来说,运用好reflect包对写通用型框架还是很有帮助的。在此只是抛砖引玉,还是望大神Carry,指导。


有疑问加站长微信联系(非本文作者)

本文来自:博客园

感谢作者:rojas

查看原文:golang 反射

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

关注微信
10995 次点击
暂无回复
添加一条新回复 (您需要 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传

用户登录

没有账号?注册
(追記) (追記ここまで)

今日阅读排行

    加载中
(追記) (追記ここまで)

一周阅读排行

    加载中

关注我

  • 扫码关注领全套学习资料 关注微信公众号
  • 加入 QQ 群:
    • 192706294(已满)
    • 731990104(已满)
    • 798786647(已满)
    • 729884609(已满)
    • 977810755(已满)
    • 815126783(已满)
    • 812540095(已满)
    • 1006366459(已满)
    • 692541889

  • 关注微信公众号
  • 加入微信群:liuxiaoyan-s,备注入群
  • 也欢迎加入知识星球 Go粉丝们(免费)

给该专栏投稿 写篇新文章

每篇文章有总共有 5 次投稿机会

收入到我管理的专栏 新建专栏