反射加强了语言的动静形容能力,你要问我什么是动静,我只能说,所有可能产生意料之外情理之中的变动,都是动静。
概述
反射这个词并不是特定语言持有的,相同很多语言领有着本人的反射模型。诚实说,我并不喜爱去用业余的术语去解释一些概念性的货色,这样往往观看的人也云里雾里,这些概念性的货色,每个人脑海中都有本人的“解释语言“,随他去吧。
我次要想谈谈为什么须要反射,利用场景是什么?其实在我看来,这两个问题严格意义上是等价的,即 为什么 = 利用场景,利用场景 = 为什么。
go 作为动态类型语言,如果没有反射,很多可能作用于 “ 任意类型 ” 的函数,实现起来会很麻烦。我举一个最简略的场景:
package main
// 会员
type member struct {
Name string
Level int
}
// 游客
type visitor struct {NickName string}
func main() {
m := member{
Name: "wqq",
Level: 520,
}
v := visitor{NickName: "curry",}
checkSomeTing(m)
checkSomeTing(v)
}
func checkSomeTing(v interface{}) {
if "如果是会员的话" {// ...} else {// ...}
}
下面 member 和 visitor 两种构造体示意两种身份,他们都须要通过公共的 checkSomeTing 操作,咱们心愿在这个函数中能依据不同的构造体,操作不同的逻辑。如果没有反射,如何晓得传进来具体是什么类型的值。(我只是单纯的指出没有反射状况下的问题,而不是吐槽下面这个设计)。
因而,咱们须要能有一种办法,它能够在程序运行时获取传入参数真正的类型,如果是 struct 那么这个 struct 有哪些属性,哪些办法 ……。
Interface
说起 go 反射,就必然绕不开 interface 类型。在 go 中 interface 是一种非凡的类型,能够寄存任何实现了其办法的值。如果是一个空 interface,意味着能够传递任意类型值。interface 类型有(value,type) 对,而反射就是查看 interface 的 (value,type) 对,所有的反射包中的办法,都是围绕 reflect.Type 和 reflect.Value 进行的。reflect.Type 是一个接口类型,提供了一系列获取 interface 信息的接口。源码地位在 src/reflect/type.go。
type Type interface {
// Methods applicable to all types.
Align() int
// FieldAlign returns the alignment in bytes of a value of
// this type when used as a field in a struct.
FieldAlign() int
Method(int) Method
String() string
........
}
而 reflect.Value 的类型被申明成构造体。源码地位 src/reflect/value.go
type Value struct {
// typ holds the type of the value represented by a Value.
typ *rtype
// Pointer-valued data or, if flagIndir is set, pointer to data.
// Valid when either flagIndir is set or typ.pointers() is true.
ptr unsafe.Pointer
flag
}
能够看到,这个构造体的三个字段都是公有的,没有对外裸露任何字段,然而它提供了一系列获取数据和写入等操作的办法。
反射三大定律
go 官网提供了三大定律来阐明反射,咱们也从这三个定律中学习如何 应用反射。
- 定律一: 反射能够将 interface 变量转换为反射对象。
package main
import (
"fmt"
"reflect"
)
func main() {
var a float64 = 32
var b int64 = 32
doSomeTing(a)
doSomeTing(b)
}
func doSomeTing(res interface{}) {t := reflect.TypeOf(res)
v := reflect.ValueOf(res)
fmt.Println("类型是:", t)
fmt.Println("值是:", v)
}
程序打印后果:
咱们定义了两个变量,他们的类型别离是 float64 和 int64,传入 doSomeTing 函数,此函数参数类型为空的 interface,因而能够接管任意类型参数,最终咱们通过 reflect.TypeOf 获取了变量的实在类型,通过 reflect.ValueOf 获取变量实在的值。
咱们能够再试试通过构造体应用其余操作方法。
package main
import (
"fmt"
"reflect"
)
type user struct {
Name string
Age int
}
func main() {
var u = user{
Age: 18,
Name: "wuqq",
}
v := reflect.ValueOf(u)
t := reflect.TypeOf(u)
for i := 0; i < t.NumField(); i++ {filed := t.Field(i)
value := v.Field(i).Interface()
fmt.Printf("field:%v type:%v value:%v\n", filed.Name, filed.Type, value)
}
}
运行后果:
下面就不解释了,次要解释循环外面的,咱们通过 reflect.type 的 NumField 获取构造体中个数,通过 reflect.type 的 Field 办法下标获取属性名,通过 interface() 获取对应属性值。
- 定律二: 反射能够将反射对象还原成 interface 对象。
package main
import (
"fmt"
"reflect"
)
type user struct {
Name string
Age int
}
func main() {
var u = user{
Age: 18,
Name: "wuqq",
}
v := reflect.ValueOf(u)
var user2 user = v.Interface().(user)
fmt.Printf("用户:%+v\n",user2)
}
u 变量转换成反射对象 v,v 又通过 interface() 接口转换成 interface 对象,再通过显性类型转换成 user 构造体对象,赋值给类型为 user 的变量 user2。
- 定律三: 反射对象是否可批改,取决于 value 值是否可设置
咱们在通过反射将任意类型的变量 (不论什么类型最终传递到 reflect.TypeOf 或者 reflect.ValueOf 都会隐式转换成 interface) 转换成反射对象,那么实践上咱们就能够基于反射对象设置其所持有的值。
type user struct {
Name string
Age int
}
func main() {
var u = user{
Age: 18,
Name: "wuqq",
}
v := reflect.ValueOf(u)
v.FieldByName("Name").SetString("curry")
fmt.Printf("v 的值:%+v",v)
}
下面代码咱们想的是通过反射对象批改构造体属性 Name 值为 curry。当运行这段代码时,会报运行恐慌(panic)。
谬误的起因正是值是不可批改的。
反射对象是否可批改取决于其所存储的值,下面 reflect.ValueOf 传入的其实是 u 的值,而非它的自身。(想想函数传值的时候是传值还是传址),既然是传值,那么通过批改 v 的值当然不可能批改到 u 的值,咱们要设置的应该是指针所指向的内容,即 *u。
type user struct {
Name string
Age int
}
func main() {
var u = user{
Age: 18,
Name: "wuqq",
}
v := reflect.ValueOf(&u)
v.Elem().FieldByName("Name").SetString("curry")
fmt.Printf("v 的值:%+v",v)
}
首先咱们通过 &u 传入 u 变量理论存储的地址。而后通过反射对象中的 Elem() 取得指针所指向的 value。
运行后果值未然被批改。
结尾
对于反射,我还想说说它不好的中央:
- 作为动态语言,编码过程中,编译器能够提前发现一些类型谬误,然而反射代码是不行的(如果能够请告知)。可能会因为 bug 导致运行恐慌。
- 反射对性能影响比拟大,对于一些重视运行效率的关键点,尽量避免应用反射。
还有其余乏味的操作,举荐先多看几遍官网的一篇博客:
https://blog.golang.org/laws-…
参考资料:
https://draveness.me/golang/d…
https://www.bookstack.cn/read…