深入理解go语言反射机制,看这一篇就够了 您所在的位置:网站首页 go是什么类型的语言英语 深入理解go语言反射机制,看这一篇就够了

深入理解go语言反射机制,看这一篇就够了

2024-03-14 01:28| 来源: 网络整理| 查看: 265

golang的反射机制

在计算机学中,反射式编程(英语:reflective programming)或反射(英语:reflection),是指计算机程序(runtime)可以访问、检测和修改它本身状态或行为的一种能力。用比喻来说,反射就是程序在运行的时候能够“观察”并且修改自己的行为。

一、go语言的类型系统以及接口

​ 在go语言中,反射是在类型系统中构建的,类型包括系统内自带的底层类型和自定义类型比方说:

type MyInt int var i int var j MyInt

上述代码中i是系统自带类型,j是自定义类型,这代表i和j拥有不同的静态类型但是其底层的系统类型是相同的。

还有一种重要的类型是接口类型,在go语言中接口分为iface和eface分别对应着有方法接口表示一系列方法的集合,还有无方法结构,就是所谓的interface{},只有类型和值的信息不包含方法,这也可以通过查看runtime.iface和runtime.eface查看其中的信息。

type iface struct { tab *itab // 这里面包含着fun,fun是指向接口具体实现的方法的地址 data unsafe.Pointer } type eface struct { // 空接口只含有类型和值 _type *_type data unsafe.Pointer }

让我看一个例子更好的理解接口!

var r io.Reader tty, err := os.OpenFile("/dev/tty", os.O_RDWR, 0) if err != nil { return nil, err } r = tty var w io.Writer w = r.(io.Writer) var empty interface{} empty = w

上述代码中,os.OpenFile返回的tty的类型是*os.File这个类型实现了io.Reader和io.Writer两个接口的方法,当用io.Reader接口定义出的值去接tty时候就相当于把*os.File实现的方法取了一次视图(数据库概念),对外只能表现为io.Reader所包含的方法。用r.(io.Writer)可以把接口值重新对外表现为io.Writer。用空接口empty去接w相当于empty只表现了w的类型(*os.File)和值(tty)这两种特性,并不能对外表现出任何方法,因为eface中根本没有指向实现方法的指针。

无论是eface还是iface都包含着两个信息,该接口内包含的值以及其类型

二、reflect.Type && reflect.Value

所有go接口都包含type和value这两个信息,使用reflect包中的Typeof和Valueof可以将这两个信息从接口中剥离出来。

package main import ( "fmt" "reflect" ) func main() { var x float64 = 3.4 fmt.Println("type:", reflect.TypeOf(x)) fmt.Println("value:", reflect.ValueOf(x).String()) }

Typeof和Valueof方法的入参都是interface{}当传入x时候会做类型转换,将float64类型转换为空接口类型。

查看reflect包源码我们可以看到Typeof和Valueof究竟返回的是什么,Typeof返回的是实现了Type接口的类型值,Valueof返回的是Value结构体,通过使用特定的方法可以获取这个接口所包值的一些信息,也可以修改其值。

type Type interface { Method(int) Method MethodByName(string) (Method, bool) // 返回方法 NumMethod() int // 这个类型有有多少个方法 Name() string // 类型名 PkgPath() string // 定义该类型的包路径 Kind() Kind // type Kind uint -> 把基本类型做了一个枚举 Implements(u Type) bool // 该类型是否实现了接口u的方法 AssignableTo(u Type) bool ConvertibleTo(u Type) bool Elem() Type // 返回type所包含的元素的类型,这意味着type必须 Array, Chan, Map, Ptr, Slice其中一种 Field(i int) StructField // type必须是struct然后返回struct第i个元素的信息 FieldByIndex(index []int) StructField // 用于返回嵌套字段,例如{1,2,3}就是返回第1个结构体中的第2个结构体中的第3元素 FieldByName(name string) (StructField, bool) // 用名字范会字段 FieldByNameFunc(match func(string) bool) (StructField, bool) In(i int) Type // 如果type是函数类型返回第i个入参类型 Out(i int) Type // 如果type是函数类型返回第i个返回值类型 } type Value struct { typ *rtype // *rtype实现了Type接口的方法 ptr unsafe.Pointer // 指向具体值的指针 } 三、将反射对象转成接口

反射对象Value可以转换成接口

// Interface returns v's value as an interface{}. func (v Value) Interface() interface{} v := reflect.ValueOf(0.5) y := v.Interface().(float64) // y will have type float64. fmt.Println(y) 四、 Value包含修改接口具体值的方法,但前提Value必须是可修改类型

什么是可修改类型,比如C语言中的地址int *p,中的p并不是可修改类型,p是指针,这是一个具体值,可修改类型是*p也就是该指针指向的值,所以一个值如果是可以修改值必须包含地址信息。

下面举个例子:

var x float64 = 3.4 v := reflect.ValueOf(x) v.SetFloat(7.1) // 不可以修改,因为v中的值是x的拷贝 fmt.Println("settability of v:", v.CanSet()) // false var x float64 = 3.4 p := reflect.ValueOf(&x) // 这里传入的是一个地址的值 fmt.Println("type of p:", p.Type()) //type of p: *float64,是一个地址的值但不是可修改类型 fmt.Println("settability of p:", p.CanSet()) // 不可修改,指针本身只是一个数,相当于&x这个值的拷贝 fmt.Println("settability of v:", v.Elem().CanSet())//.Elem()在这里相当于*p,这样就可以修改了

[1] [维基百科——反射式编程][https://zh.wikipedia.org/wiki/%E5%8F%8D%E5%B0%84%E5%BC%8F%E7%BC%96%E7%A8%8B]

[2] [go官方文档][https://go.dev/blog/laws-of-reflection]

在这里插入图片描述



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有