Loading... # 0x01 Go 语言中的反射使用了大量 Interface 的原理实现的。差不多是直接使用了 `interface` 里的 `eface` 和 `iface` 及其动态类型 `_type` 实现的。所以要掌握反射的原理必须先掌握 `interface` 的原理,想要细究的小伙伴可以看看我的这篇文章。 <div class="preview"> <div class="post-inser post box-shadow-wrap-normal"> <a href="https://gvoidy.cn/index.php/archives/298/" target="_blank" class="post_inser_a no-external-link no-underline-link"> <div class="inner-image bg" style="background-image: url(https://gvoidy.cn/usr/uploads/2021/07/2882018715.png);background-size: cover;"></div> <div class="inner-content" > <p class="inser-title">从源码学习 Go 的基础数据结构 —— Interface</p> <div class="inster-summary text-muted"> 0x01Interface 是 Go 里面非常有趣的一个概念,很多学习过其他面向对象语言的同学可能会下意识的觉得他... </div> </div> </a> <!-- .inner-content #####--> </div> <!-- .post-inser ####--> </div> # 什么是反射 > **[Wiki](https://zh.wikipedia.org/wiki/%E5%8F%8D%E5%B0%84%E5%BC%8F%E7%BC%96%E7%A8%8B)**: 在计算机学中,反射式编程(英语:reflective programming)或反射(英语:reflection),是指计算机程序在运行时(runtime)可以访问、检测和修改它本身状态或行为的一种能力。用比喻来说,反射就是程序在运行的时候能够“观察”并且修改自己的行为。 简单来说,就是在程序运行中(runtime)时,不通过汇编等底层方法,用语言本身的语法获取或修改对象的类型信息或内存结构的方法就叫反射。 ```go type Cat struct {} // 运行时如何给 Cat 动态的添加一个 eat() 方法? ``` # 为什么要用反射 Go 语言是一门静态语言,而静态语言变量的数据类型在编译时必须是确定的。这就导致我们编写某些处理复杂类型的程序时非常麻烦,这里借鉴《[GO 语言圣经](https://book.itsfun.top/gopl-zh/)》[反射一章](https://book.itsfun.top/gopl-zh/ch12/ch12-01.html) 的例子说明: 如果不用反射,我们设计一个类似 `fmt.Sprint` 的方法可能就如如下所示 ```go func Sprint(x interface{}) string { type stringer interface { String() string } switch x := x.(type) { case stringer: return x.String() case string: return x case int: return strconv.Itoa(x) // ...similar cases for int16, uint32, and so on... case bool: if x { return "true" } return "false" default: // array, chan, func, map, pointer, slice, struct return "???" } } ``` 因为用户自定义类型的不确定的,难道要每有一个新的自定义类型出现,就往这个方法里多加一个 `case` 吗?这显然是不合理的。我们用反射修改一下个段代码: ```go func Sprint(x interface{}) string { v := reflect.ValueOf(x) switch v.Kind() { case reflect.Invalid: return "invalid" case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: return strconv.FormatInt(v.Int(), 10) case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: return strconv.FormatUint(v.Uint(), 10) // ...floating-point and complex cases omitted for brevity... case reflect.Bool: return strconv.FormatBool(v.Bool()) case reflect.String: return strconv.Quote(v.String()) // 打印内存地址 case reflect.Chan, reflect.Func, reflect.Ptr, reflect.Slice, reflect.Map: return v.Type().String() + " 0x" + strconv.FormatUint(uint64(v.Pointer()), 16) default: // reflect.Array, reflect.Struct, reflect.Interface return v.Type().String() + " value" } } ``` # 反射是如何实现的 反射有两个重要方法 `TypeOf` 和 `ValueOf` ,分别对应着 `reflect/type.go` 和 `reflect/value.go` 。我们也分开来讲。 ## TypeOf `TypeOf` 的源码非常简单,总体如下: ```go // TypeOf 返回代表 i 的动态类型的反射类型。 // 如果 i 是一个 nil 接口值,TypeOf 返回 nil。 func TypeOf(i interface{}) Type { eface := *(*emptyInterface)(unsafe.Pointer(&i)) return toType(eface.typ) } ``` `TypeOf` 接受一个任意一个转化为空接口的对象,然后通过 `unsafe.Pointer`方法把他的指针转化为 `(*emptyInterface)` 类型,最后调用 `toType(eface.typ)` 返回一个接口类型的 `Type` 。 ```go // emptyInterface is the header for an interface{} value. type emptyInterface struct { typ *rtype word unsafe.Pointer } func toType(t *rtype) Type { if t == nil { return nil } return t } // 官方自己也说了和 _type 完全保持一致 // rtype must be kept in sync with ../runtime/type.go:/^type._type. type rtype struct { size uintptr ptrdata uintptr // number of bytes in the type that can contain pointers hash uint32 // hash of type; avoids computation in hash tables tflag tflag // extra type information flags align uint8 // alignment of variable with this type fieldAlign uint8 // alignment of struct field with this type kind uint8 // enumeration for C // function for comparing objects of this type // (ptr to object A, ptr to object B) -> ==? equal func(unsafe.Pointer, unsafe.Pointer) bool gcdata *byte // garbage collection data str nameOff // string form ptrToThis typeOff // type for pointer to this type, may be zero } ``` 我们再来看 `emptyInterface` 和 `rtype`的源码,是不是有点倍感亲切?他们竟然和 `interface` 中 `eface` 的结构几乎完全一致。 ```go type eface struct { _type *_type data unsafe.Pointer } type _type struct { size uintptr ptrdata uintptr // size of memory prefix holding all pointers hash uint32 tflag tflag align uint8 fieldAlign uint8 kind uint8 // function for comparing objects of this type // (ptr to object A, ptr to object B) -> ==? equal func(unsafe.Pointer, unsafe.Pointer) bool // gcdata stores the GC type data for the garbage collector. // If the KindGCProg bit is set in kind, gcdata is a GC program. // Otherwise it is a ptrmask bitmap. See mbitmap.go for details. gcdata *byte str nameOff ptrToThis typeOff } ``` 这竟是一个狸猫换太子的方法!把传进 `TypeOf` 的`interface{}`对象克隆进一个一模一样的 `emptyInterface` 里,然后在给假的 `_type` 类型 `rtype` 添加一堆我们熟悉的反射方法  这里的 `rtype` 实现了 `Type` 接口的,所有`reflect.TypeOf`返回对象的全部方法可以全局搜索`type Type interface` 查看。 值得注意的是 `rtype` 实现的 `Kind` 方法,会将对象转换成更具体的仿制类型,以获得特定的方法。 ```go func (t *rtype) Kind() Kind { return Kind(t.kind & kindMask) } type Kind uint const ( Invalid Kind = iota Bool Int Int8 Int16 // 省略 ... ) ```  ## ValueOf `TypeOf` 只关注对象的类型信息,而 `ValueOf` 既可以关注类型,也可以关注值。我们跟上面的套路一样,来看看 `ValueOf` 的源码。 ```go // ValueOf返回一个新的Value,初始化为具体的值。 // ValueOf(nil)返回零值。 func ValueOf(i interface{}) Value { if i == nil { return Value{} } // 内存分配到堆上,不用管 escapes(i) return unpackEface(i) } ``` ```go type Value struct { 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 } ``` 和上面的 `TypeOf` 类似,`ValueOf`把整个`interface{}` 对象的类型和值全部拷贝到假的`Value` 类型里存放了,并且 `Value` 类型也实现了一堆我们用到的反射方法。但值得注意的是,`TypeOf` 返回的是一个接口类型,而`ValueOf` 返回的是实际的`Value` 类型。 `ValueOf` 有个很有趣的地方,他有两个这样签名的方法 ```go func (v Value) Type() Type {...} func (v Value) Interface() (i interface{}) {...} ``` 就是说,`Value` 类型可以转化回原来的 `interface{}` 还可以变成 `Type` ,他们之间构成了这样一种关系。  ## 总结 `TypeOf()` 函数返回一个接口,这个接口定义了一系列方法,利用这些方法可以获取关于类型的所有信息; `ValueOf()` 函数返回一个结构体变量,包含类型信息以及实际值。 `rtype` 实现了 `Type` 接口,是所有类型的公共部分。`emptyface` 结构体和 `eface` 其实是一个东西,而 `rtype` 和` _type` 也是一个东西,只是一些字段稍微有点差别,比如 `emptyface`的` word` 字段和 `eface` 的 `data` 字段名称不同,但是数据型是一样的。 由此,我们就能完全理解官方的反射三大原则了 > 1. Reflection goes from interface value to reflection object. > 2. Reflection goes from reflection object to interface value. > 3. To modify a reflection object, the value must be settable. 1. 从 `interface{}` 对象可以反射出反射对象(`Type` 或 `Value`) 2. 反射对象可以转变回 `interface{}` 对象 (`Value` 调用 `Interface` 方法) 3. 反射对象只能修改可修改的值(`Value` 只可修改成员变量首字母大写的值) # Reference [深度解密 GO 语言之反射](https://qcrao.com/2019/05/07/dive-into-go-reflection/) [《快学 Go 语言》- 反射](https://juejin.cn/post/6844903745927528456) [Golang的反射reflect深入理解和示例](https://juejin.cn/post/6844903559335526407) [Go 语言设计与实现 - 反射](https://draveness.me/golang/docs/part2-foundation/ch04-basic/golang-reflect/) [GO 语言圣经 - 反射](https://book.itsfun.top/gopl-zh/ch12/ch12-01.html) 最后修改:2021 年 08 月 04 日 11 : 56 AM © 允许规范转载 赞赏 如果觉得我的文章对你有用,请随意赞赏 赞赏作者 支付宝微信