over-golang/01-基础语法/15-反射-1-概述.md
2021-07-02 18:11:59 +08:00

4.6 KiB
Raw Permalink Blame History

一 反射简介

反射是指在程序运行期对程序本身进行访问和修改的能力即可以在运行时动态获取变量的各种信息比如变量的类型type类别kind如果是结构体变量还可以获取到结构体本身的信息字段与方法通过反射还可以修改变量的值可以调用关联的方法。

反射常用在框架的开发上一些常见的案例如JSON序列化时候tag标签的产生适配器函数的制作等都需要用到反射。反射的两个使用常见使用场景

  • 不知道函数的参数类型:没有约定好参数、传入类型很多,此时类型不能统一表示,需要反射
  • 不知道调用哪个函数:比如根据用户的输入来决定调用特定函数,此时需要依据函数、函数参数进行反射,在运行期间动态执行函数

Go程序的反射系统无法获取到一个可执行文件空间中或者是一个包中的所有类型信息需要配合使用标准库中对应的词法、语法解析器和抽象语法树( AST) 对源码进行扫描后获得这些信息。

贴士:

  • CC++没有支持反射功能,只能通过 typeid 提供非常弱化的程序运行时类型信息。
  • Java、 C#等语言都支持完整的反射功能。
  • Lua、JavaScript类动态语言由于其本身的语法特性就可以让代码在运行期访问程序自身的值和类型信息因此不需要反射系统。

注意:

  • 在编译期间,无法对反射代码进行一些错误提示。
  • 反射影响性能

二 反射是如何实现的

反射是通过接口的类型信息实现的,即反射建立在类型的基础上:当向接口变量赋予一个实体类型的时候,接口会存储实体的类型信息。

Go中反射相关的包是reflect,在该包中,定义了各种类型,实现了反射的各种函数,通过它们可以在运行时检测类型的信息、改变类型的值。

变量包括type、value两个部分所以 nil != nil type包括两部分

  • static type在开发时使用的类型如int、string
  • concrete type是runtime系统使用的类型

类型能够断言成功,取决于 concrete type 如果一个reader变量如果 concrete type 实现了 write 方法那么它可以被类型断言为writer。

Go中反射与interface类型相关其type是 concrete type只有interface才有反射每个interface变量都有一个对应的pairpair中记录了变量的实际值和类型value, type。即一个接口类型变量包含2个指针一个指向对应的 concrete type ,另一个指向实际的值 value。

示例:

var r io.Reader				// 定义了一个接口类型
r, err := os.OpenFile()		// 记录接口的实际类型、实际值

var w io.Writer				// 定义一个接口类型
w = r.(io.Writer)			// 赋值时接口内部的pair不变所以 w 和 r 是同一类型

三 Go中反射初识

3.1 reflect包的两个函数

reflect 提供了2个重要函数

  • ValueOf()获取变量的值即pair中的 value
  • TypeOf()获取变量的类型即pair中的 concrete type
	type Person struct {
		Name string
		Age int
	}
	p := Person{ "lisi", 13}

	fmt.Println(reflect.ValueOf(p))			// {lisi 13}  变量的值
	fmt.Println(reflect.ValueOf(p).Type())	// main.Person 变量类型的对象名

	fmt.Println(reflect.TypeOf(p))			//  main.Person	变量类型的对象名

	fmt.Println(reflect.TypeOf(p).Name())	// Person:变量类型对象的类型名
	fmt.Println(reflect.TypeOf(p).Kind())	// struct:变量类型对象的种类名

	fmt.Println(reflect.TypeOf(p).Name() == "Person")	// true
	fmt.Println(reflect.TypeOf(p).Kind() == reflect.Struct)	//true

类型与种类的区别:

  • Type是原生数据类型 int、string、bool、float32 ,以及 type 定义的类型,对应的反射获取方法是 reflect.Type 中 的 Name()
  • Kind是对象归属的品种Int、Bool、Float32、Chan、String、Struct、Ptr指针、Map、Interface、Fune、Array、Slice、Unsafe Pointer等

3.2 静态类型与动态类型

静态类型:变量声明时候赋予的类型

	type MyInt int			// int 是静态类型
	var i *int				// *int 是静态类型

动态类型运行时给这个变量赋值时这个值的类型即为动态类型为nil时没有动态类型

	var A interface{}		// 空接口 是静态类型,必须是接口类型才能实现类型动态变化
	A = 10					// 此时静态类型为 interface{} 动态为int
	A = "hello"				// 此时静态类型为 interface{} 动态为string