栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Go语言

Go语言反射

Go语言 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Go语言反射

什么是反射:

维基百科上的定义:在计算机科学中,反射是指计算机程序在运行时(Run time)可以访问、检测和修改它本身状态或行为的一种能力。用比喻来说,反射就是程序在运行的时候能够“观察”并且修改自己的行为。

《Go 语言圣经》中是这样定义反射的:Go 语言提供了一种机制在运行时更新变量和检查它们的值、调用它们的方法,但是在编译时并不知道这些变量的具体类型,这称为反射机制。

不用反射不行吗:

实际上,它的本质是程序在运行期探知对象的类型信息和内存结构,不用反射能行吗?可以的!使用汇编语言,直接和内层打交道,什么信息不能获取?但是,当编程迁移到高级语言上来之后,像java/python/go,就不行了!就只能通过反射来达到此项技能。

为什么要用反射:

1、有时需要编写一个函数,但是并不知道传给你的参数类型是什么,可能是没约定好;也可能是传入的类型很多,这些类型并不能统一表示。这时反射就会用的上了,在程序运行中去动态识别这些参数类型。

2、获取结构体的Tag信息等。

3、显示一个类型的方法集。

4、....

不使用反射的理由:
  1. 与反射相关的代码,经常是难以阅读的。
  2. Go 语言作为一门静态语言,编码过程中,编译器能提前发现一些类型错误,但是对于反射代码是无能为力的。所以包含反射相关的代码,很可能会运行很久,才会出错,这时候经常是直接 panic,可能会造成严重的后果。
  3. 反射对性能影响还是比较大的,比正常代码运行速度慢一到两个数量级。所以,对于一个项目中处于运行效率关键位置的代码,尽量避免使用反射特性。

go语言如何使用反射:

go语言反射里最重要的两个概念是Type和Value。

Type用于获取类型相关的信息(比如Slice的长度,struct的成员,函数的参数个数)

Value用于获取和修改原始数据的值(比如修改slice和map中的元素,修改struct的成员变量)

1、通过TypeOf()得到Type类型:

获取struct成员变量的信息

typeUser := reflect.TypeOf(common.User{}) //需要用struct的Type,不能用指针的Type
fieldNum := typeUser.NumField()           //成员变量的个数
for i := 0; i < fieldNum; i++ {
	field := typeUser.Field(i)
	fmt.Printf("%d %s offset %d anonymous %t type %s exported %t json tag %sn", i,
		field.Name,            //变量名称
		field.Offset,          //相对于结构体首地址的内存偏移量,string类型会占据16个字节
		field.Anonymous,       //是否为匿名成员
		field.Type,            //数据类型,reflect.Type类型
		field.IsExported(),    //包外是否可见(即是否以大写字母开头)
		field.Tag.Get("json")) //获取成员变量后面``里面定义的tag
}
fmt.Println()

//可以通过FieldByName获取Field
if nameField, ok := typeUser.FieldByName("Name"); ok {
	fmt.Printf("Name is exported %tn", nameField.IsExported())
}
//也可以根据FieldByIndex获取Field
thirdField := typeUser.FieldByIndex([]int{2}) //参数是个slice,因为有struct嵌套的情况
fmt.Printf("third field name %sn", thirdField.Name)

获取struct成员方法的信息、获取函数的信息、判断类型是否实现了某接口

# TODO

2、通过ValueOf()得到Value

userPtrValue := reflect.ValueOf(&common.User{
	Id:     7,
	Name:   "杰克逊",
	Weight: 65,
	Height: 1.68,
})
fmt.Println(userPtrValue) //&{7 杰克逊  65 1.68}


//指针Value和非指针Value互相转换
userValue := userPtrValue.Elem()   //Elem() 指针Value转为非指针Value

得到Value对应的原始数据

通过Interface()函数把Value转为interface{},再从interface{}强制类型转换,转为原始数据类型。或者在Value上直接调用Int()、String()等一步到位。

user := userValue.Interface().(common.User)
user2 := userPtrValue.Interface().(*common.User)
fmt.Println(user.Name)
fmt.Println(user2.Name)

通过Value修改原始数据的值

user := common.User{
	Id:     7,
	Name:   "杰克逊",
	Weight: 65.5,
	Height: 1.68,
}

valueUser := reflect.ValueOf(&user) //由于go语言所有函数传的都是值,所以要想修改原来的值就需要传指
 
//由于valueI对应的原始对象是指针,通过Elem()返回指针指向的对象
valueUser.Elem().FieldByName("Weight").SetFloat(68.0) //FieldByName()通过Name返回类的成员变量

更详细用法可以参考:知乎:反射

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/991680.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号