你好,Gophers!今天我们将对Go中一个叫做reflect的包进行更深入的研究。它属于中级到高级实现的范畴。在本教程中,我们将看到它是什么以及如何使用它。如果想更深入地了解,你也可以看看《在**反射中实现反射》和《Go中的类型转换**》。
什么是反射?
反射是一个程序在运行时分析其变量和值并确定其类型的能力。虽然反射可以用来打印出已知/定义的数据类型(如int、float、String等)的类型和值,但在处理任意的或抽象的组件结构时,它更有用。我们会在自己的实现中看到这一点,所以不用担心。
请看Go的开发者之一Rob Pike的《反射法则》。
空接口
正如我们在 Go中的反射和类型转换中 简单讨论的那样,空接口是Go中一个特殊的数据类型,这样如果我们存储任何值,例如x:=6.626,那么即使它被称为 "接口 "而不是 "int "或 "float",x的类型信息也不会丢失。我们也可以用x创建另一个变量,然后它的类型就不是 "interface "而是 "float64"。
func main() {
var x interface{}
x = 6.626
fmt.Printf("x: type = %T, value = %v\n", x, x)
goo := x
fmt.Printf("goo: type = %T, value = %v\n", goo, goo)
x = int(2020)
fmt.Printf("x: type = %T, value = %v\n", x, x)
}
因此,如果我们有一个空接口类型的变量,那么我们可以给这个变量分配不同的值,因为它似乎包含两个属性/字段。类型和值。所以当我们把x的值重新赋给int时,我们只是更新了这两个字段的值。
现在在 Go的反射和类型转换中 ,我们考虑了两种自定义类型--ID和Person,要求包含特定类型的变量可以通过类型断言完成。
//x.(<type>) is the format for type assertion
然而,使用类型断言的缺点是,我们必须确定该变量是否属于该类型。否则,你的程序可能会崩溃。一个变通的办法是,在断言的同时要求一个bool值。
if v, ok := x.(ID); ok
或者我们可以使用switch和case做一个类型转换。
不幸的是,它对我们不知道的类型不起作用。所以它对自定义数据类型和自定义函数输出不起作用。
反映类型/值
所以,让我们从导入reflect包开始。
package main
import (
"fmt"
"reflect"
)
然后考虑Typeof 和ValueOf 方法。
func main() {
var x interface{}
x = 6.626
t := reflect.TypeOf(x)
v := reflect.ValueOf(x)
reflect.TypeOf(x)返回一个reflect.Type类型的值,这是一个结构,隐藏了Go如何识别类型的一些细节(数据抽象),并向它添加了一些我们可以操作的方法。
类似地,reflect.ValueOf(x)返回一个reflect.Value类型的值。
从本质上讲,这两个函数在我们的空接口中寻找并提取类型和值的信息,这些信息作为一个字段并不真正存在,但却像一个字段一样。
所以现在我们可以把之前的代码改成这样,它的工作原理是一样的。
fmt.Printf("x: type = %v, value = %v\n", t, v)
goo := x
fmt.Printf("goo: type = %T, value = %v\n", goo, goo)
所以,我们可以看一下这个反射的一个简单方法--Kind()。
func main() {
var x interface{}
x = &struct{ name string }{}
t0 := reflect.TypeOf(x)
v0 := reflect.ValueOf(x)
fmt.Printf("x: type = %v, value = %v\n", t0, v0)
x = new(string)
t1 := reflect.TypeOf(x)
v1 := reflect.ValueOf(x)
fmt.Printf("x: type = %v, value = %v\n", t1, v1)
fmt.Printf("t0: type = %v, kind = %v\n", t0, t0.Kind())
fmt.Printf("t1: type = %v, kind = %v\n", t1, t1.Kind())
}
在这里,我们定义了一个指向结构的指针和一个用于比较的字符串。
所以Kind方法就像一个超级类型,也就是说,无论指针指向什么,Kind都会输出ptr。还有什么具有相同的类型?
a0 := [5]int{}
a1 := [10]int{}
//are the same kind (arrays), but very different types
//Similarly
b0 := make([]int{})
b1 := make([]float{})
//are also the same kind (slices), but very different types