当前位置:网站首页>GO语言-反射reflect
GO语言-反射reflect
2022-06-28 21:43:00 【一边学习一边哭】
概述
在计算机科学中,反射是指计算机程序在运行时可以访问、检测和修改它本身状态或行为的一种能力。反射实在Java出现后迅速流行起来的一种概念,通过反射可以获取丰富的类型信息,可以利用这些类型信息做比较灵活的工作。
如果不用反射也是可以的,那我们就需要用到汇编语言,与计算机的内存打交道。但我们常用的都是高级语言,则需要通过反射来实现。不同的语言实现反射的机制也是不同的,而且有些语言也不支持反射。《Go语言圣经》中是这样定义反射的:
Go语言提供了一种机制在运行时更新变量和检查它们的值、调用它们的方法。但是在编译时并不直到这些变量的具体类型。这称为反射机制。
什么情况需要反射?
需要反射的2个常见场景:
- 不能明确函数传入的参数的类型,需要在运行时对传入的具体参数进行类型判断。
- 不确定接口调用哪些方法,需要在运行时根据传入参数确定。
为什么不建议用反射?
反射是一把双刃剑,虽然它的功能很强大。
- 反射相关的代码,一般是难以阅读的。
- GO语言作为一门静态语言,编译器能在编码过程中提前发现一些类型错误。但是对于反射代码是无能为力的,所以包含反射相关的代码,往往运行一段时间以后才会发现错误。这往往会导致比较严重的后果。
- 反射对于性能影响是比较大的,比正常代码运行速度慢一到两个数量级。出于运行效率来讲,尽量避免使用反射。
反射如何实现的?
Go语言中我们学习过interface,他是Go语言实现抽象的一个非常强大的工具。当给接口变量赋予一个实体类型的时候,接口会存储实体类型的信息。反射就是通过接口的类型信息实现的,反射建立在类型的基础之上。
Go语言在reflect包中定义了各种类型,实现了反射的各种函数。通过它们可以在运行时检测类型的信息、改变类型的值。
回顾两个知识点:
go语言是静态类型语言。在代码编译的时候,所有类型都已经确定了。
interface{}——空接口。空接口是可以代表任意类型的。
Go语言的反射是建立在类型之上的。
- Go语言中指定类型创建的变量叫做是静态类型,在创建变量的时候类型就已经确定。
- 反射主要与interface{}类型相关,它是具体类型,只有interface类型才有反射的说法。
- Go语言中,变量包括(type、value)两部分,type又分为静态类型和具体类型。每个interface变量都有一个对应的pair,pair中记录了实际变量的(type、value)。interface{}包含两个指针,一个指针指向type,一个指针指向value。其中type是一个具体类型。
- 如果将一个interface{}类型,赋值给另一个interface{},则实际pair的信息是不会变的。也就是说值和类型都不会改变。
反射,本质上就是用来检测存储在interface{}中的pair的一种机制。pair中本质上就是存储的type和value。type和value也是反射包中最重要的两个类型。
反射reflect的使用
reflect的基本功能TypeOf和ValueOf
reflect反射包提供了两种方法,reflect.TypeOf()和reflect.ValueOf()
- reflect.TypeOf()用来动态获取输入参数接口中的值的类型,如果接口为空返回nil。实际就是获取interface{}的pair中的type。
- reflect.ValueOf()用来获取输入参数中的数据的值,如果为空返回0。实际就是获取interface{}的pair中的value。
所有的具体类型都可以看作是interface{}空接口的具体实现。
var x interface{}
var y string
func main() {
x = 11.11
fmt.Println("type x:", reflect.TypeOf(x))
fmt.Println("value x:", reflect.ValueOf(x))
y = "liqi-test"
fmt.Println("type y:", reflect.TypeOf(y))
fmt.Println("value y:", reflect.ValueOf(y))
}
reflect.TypeOf()返回的类型是Type,reflect.ValueOf()返回的类型是Vlue。对于Type和Vlue下,都包含了大量的方法。
反射获取结构体数据
type Person struct {
Name string
Age int
Sex string
}
func (p Person) Say(msg string) {
fmt.Println("hello,", msg)
}
func (p Person) PrintInfo() {
fmt.Printf("姓名: %s, 年龄: %d, 性别: %s\n", p.Name, p.Age, p.Sex)
}
func main() {
p1 := Person{"xiaoming", 18, "男"}
getMsg(p1)
}
func getMsg(msg interface{}) {
getType := reflect.TypeOf(msg)
fmt.Println("Type name is:", getType.Name())
fmt.Println("Kind is:", getType.Kind())
getValue := reflect.ValueOf(msg)
fmt.Println("get all Fields:", getValue)
//获取结构体字段
fmt.Println("---获取结构体字段信息---")
for i := 0; i < getType.NumField(); i++ {
field := getType.Field(i)
fieldValue := getValue.Field(i).Interface()
fmt.Printf("结构体Field:%v, 值:%v, 值的类型: %T\n", field.Name, fieldValue, fieldValue)
}
//获取方法
fmt.Println("---获取结构体方法信息---")
for i := 0; i < getType.NumMethod(); i++ {
getMethod := getType.Method(i)
fmt.Printf("方法名称: %s, 方法类型: %s\n", getMethod.Name, getMethod.Type)
}
}
定义了一个Person结构体,字段有Name、Age、Sex,还定义了结构体的两个方法。
- reflect.TypeOf(xx).Name() 获取类型的名称
- reflect.TypeOf(xx).Kind() 获取类型的种类
- reflect.TypeOf(xx).NumField() 获取结构体字段的数量
- reflect.ValueOf(xx).NumField() 获取结构体字段对应的值的数量
- reflect.TypeOf(xx).Field(index) 按下标获取结构体字段
- reflect.ValueOf(xx).Field(i).Interface() 按下标获取结构体内字段的值;如果不加.Interface()得到的类型是Vlue类型,interface()方法相当于将Vlue类型强制转换为字段本身类型。
- reflect.TypeOf(xx).Method(index) 按下表获取结构体方法
- reflect.TypeOf(xx).Method(index).Name() 方法的名称(函数名称)
- reflect.TypeOf(xx).Method(index).Type() 方法的类型(函数类型)
反射设置实际变量的值
需要修改实际变量的值,也是通过reflect.ValueOf(X)获取reflect.Value对象,然后进行变量值的修改。但需要注意的是reflect.ValueOf(X)中的X必须是一个指针对象,然后使用Elem().Set()进行更改,Set()传入的数值必须是Value类型。
func main() {
var num float64 = 11.11
fmt.Println("num的数值:", num)
numValue := reflect.ValueOf(&num)
fmt.Println("num的类型:", numValue.Elem().Type())
fmt.Println("num是否可以修改:", numValue.Elem().CanSet())
SetNum := reflect.ValueOf(22.22)
numValue.Elem().Set(SetNum)
fmt.Println("修改后num的数值:", num)
fmt.Println("---修改结构体对象数值---")
p1 := &Person{"xiaoming", 18, "男"}
fmt.Println("修改前的结构体:", *p1)
personValue := reflect.ValueOf(p1)
fmt.Println("结构体对象是否可以修改?:", personValue.Elem().CanSet())
personValue.Elem().FieldByName("Name").SetString("xiaohong")
personValue.Elem().FieldByName("Age").SetInt(20)
personValue.Elem().FieldByName("Sex").SetString("女")
fmt.Println("修改后的结构体:", *p1)
} 
反射调用方法
type Person struct {
Name string
Age int
Sex string
}
func (p Person) Say(msg string) {
fmt.Println("hello,", msg)
}
func (p Person) PrintInfo() {
fmt.Printf("姓名: %s, 年龄: %d, 性别: %s\n", p.Name, p.Age, p.Sex)
}
func main() {
p1 := Person{"xiaoming", 18, "男"}
value := reflect.ValueOf(p1)
method1 := value.MethodByName("Say")
method2 := value.MethodByName("PrintInfo")
//调用方法
args1 := []reflect.Value{reflect.ValueOf("反射执行有参数的方法")}
method1.Call(args1) //有参数
method2.Call(nil) //无参数
}
反射调用函数
函数的调用和方法类似。
func fun1() {
fmt.Println("反射调用无参函数")
}
func fun2(msg string) {
fmt.Println("hello,", msg)
}
func fun3(i int, s string) (a int, b string) {
a = i + 1
b = s + ",从函数中return"
return a, b
}
func main() {
//反射调用无参数函数
funValue1 := reflect.ValueOf(fun1)
if funValue1.Kind() == reflect.Func {
funValue1.Call(nil)
}
//反射调用有参数函数
funValue2 := reflect.ValueOf(fun2)
if funValue2.Kind() == reflect.Func {
funValue2.Call([]reflect.Value{reflect.ValueOf("反射调用有参函数")})
}
//反射调用有返回值函数
funValue3 := reflect.ValueOf(fun3)
if funValue3.Kind() == reflect.Func {
resultValue := funValue3.Call([]reflect.Value{reflect.ValueOf(10), reflect.ValueOf("函数有返回值")})
fmt.Println("有返回值函数的返回值:", resultValue[0].Interface(), resultValue[1].Interface())
}
}
边栏推荐
- 阿海的忠告
- Pat 1054 the dominiant color (20 points)
- Is it safe to open an account for stocks on mobile phones in 2022? Who can I ask?
- Security dilemma of NFT liquidity agreement - Analysis of the hacked event of NFT loan agreement xcarnival
- 在亿学学堂开通证券账户是安全可靠的吗?
- Which software is safer to open an account on and what is the account opening process?
- Google search is dying | DKB
- Binomial distribution (a discrete distribution)
- Lua源码剖析:一. lua变量类型可变特性在C代码中实现。
- Comprehensive evaluation of easy-to-use and powerful PDF reading software: PDF expert, marginnote, liquidtext, notability, goodnotes, Zotero
猜你喜欢

CVPR 2022|极具创意&美感的文字生成方法!支持任意输入

Alist+raidrive gives the computer a complete 8billion GB hard disk drive

Architecture design of e-commerce secsha system

Anti rabbit dylight 488 abbkine universal immunofluorescence (if) toolbox

華為雲的AI深潜之旅

Lumiprobe lumizol RNA extraction reagent solution

BOE was brilliant for the Winter Olympics, but revealed another Chinese technology enterprise dominating the world

Adding a markdown editor to lavel

Webrtc audio and video development - experience

PHP login problem
随机推荐
PHP login problem
Pat 1054 the dominiant color (20 points)
Leetcode: merge K ascending linked lists_ twenty-three
Building web automation environment
External parameter calibration method for 16 line mechanical radar and monocular camera based on solid state lidar
在亿学学堂开通证券账户是安全可靠的吗?
華為雲的AI深潜之旅
LeetCode986. Intersection of interval lists
LeetCode122. The best time to buy and sell stocks II
LeetCode121. 买卖股票的最佳时机
Is it safe to open an account on great wisdom
Hyperjumptech/grule-rule-engine: rule engine implementation of golang
BOE was brilliant for the Winter Olympics, but revealed another Chinese technology enterprise dominating the world
PHP自学Go日记(四):GO的变量声明方式
Un voyage profond d'IA dans Huawei Cloud
Smarca2 antibody study: abnova smarca2 monoclonal antibody protocol
LeetCode123. The best time to buy and sell stocks III
运动App如何实现端侧后台保活,让运动记录更完整?
Definition and precautions of genuine St link/v2 j-link jtag/swd pin
Multinomial distribution (a discrete distribution)