一. golang 是如何实现反射的
- 参考博客Go 语言问题集(Go Questions)
- Go 语言在 reflect 包里定义了各种类型,实现了反射的各种函数,通过它们可以在运行时检测类型的信息、改变类型的值
- 反射主要与 interface{} 类型相关, 在前面我们了解到interface底层实际是Iface或Eface,其中 itab 由具体类型 _type 以及 interfacetype 组成。_type 表示具体类型,而 interfacetype 则表示具体类型实现的接口类型
- Go语言的反射实现主要基于reflect 包里定义的一个接口和一个结构,即 reflect.Type 和 reflect.Value,它们提供很多函数来获取存储在接口里的类型信息。
- reflect.Type 表示类型信息,包括类型的名称、大小、对齐方式、方法集等。在Go语言中,每个类型都对应一个Type类型的值,可以通过reflect.TypeOf函数获取,所以它和 _type 关联比较紧密;
- reflect.Value 则结合 _type 和 data 两者,表示值信息,包括值的类型、大小、对齐方式、字段和方法等。在Go语言中,每个值都对应一个Value类型的值,可以通过reflect.ValueOf函数获取,因此可以获取甚至改变类型的值。
func TypeOf(i interface{}) Type
func ValueOf(i interface{}) Value
- 获取了Type和Value之后,可以使用反射提供的方法来获取和修改对象的属性和方法。例如reflect.Value类型提供了Field和MethodByName等方法来访问结构体字段和方法
- 由于反射需要在运行时进行类型判断和转换,因此会带来一定的性能开销。为了避免这种性能开销,Go语言还提供了unsafe包,可以在一定程度上绕过类型检查和转换,从而提高程序的性能。需要注意的是,unsafe包的使用需要非常小心,避免出现安全问题和不确定性行为
如何比较两个对象完全相等
- 通常情况下我们使用"= =" 比较,但是有一些特殊情况比如: 包含 func 类型或者 float 类型的 struct, interface, array 等
- func 是不可比较的类型,只有在两个 func 类型都是 nil 的情况下,才是“深度”相等
- float 由于精度的原因,也是不能使用 == 比较的
- 对于指针而言,两个值相等的指针就是“深度”相等,因为两者指向的同一个内存地址,即使是 func,slice,map 或者 float 类型,这种情况下是不关心指针所指向的内容的
- 对于“有环”的类型,比如循环链表,比较两者是否“深度”相等的过程中,需要对已比较的内容作一个标记,一旦发现两个指针之前比较过,立即停止比较,并判定二者是深度相等的。这样做的原因是,及时停止比较,避免陷入无限循环
- 在使用"= ="时实际会执行底层的DeepEqual(),该函数底层
- 首先查看两者是否都为nil,如果是函数返回 true
- 接着使用反射,获取x,y 的反射对象,比较两者的类型,实际上是动态类型,如果类型不同返回 false
- 最核心的内容在子函数 deepValueEqual 中
func DeepEqual(x, y interface{}) bool {
if x == nil || y == nil {
return x == y
}
v1 := ValueOf(x)
v2 := ValueOf(y)
if v1.Type() != v2.Type() {
return false
}
return deepValueEqual(v1, v2, make(map[visit]bool), 0)
}
- 查看deepValueEqual()内部的部分逻辑: 核心是一个 switch 语句,识别输入参数的不同类型,分别递归调用 deepValueEqual 函数,一直递归到最基本的数据类型,比较 int,string 等可以直接得出 true 或者 false,再一层层地返回,最终得到“深度”相等的比较结果
// deepValueEqual 函数
// ……
case Map:
if v1.IsNil() != v2.IsNil() {
return false
}
if v1.Len() != v2.Len() {
return false
}
if v1.Pointer() == v2.Pointer() {
return true
}
for _, k := range v1.MapKeys() {
val1 := v1.MapIndex(k)
val2 := v2.MapIndex(k)
if !val1.IsValid() || !val2.IsValid() || !deepValueEqual(v1.MapIndex(k), v2.MapIndex(k), visited, depth+1) {
return false
}
}
return true
// ……
- 通过reflect.DeepEqual比较示例
import "reflect"
type Person struct {
Name string
Age int
}
func main() {
p1 := Person{Name: "Alice", Age: 30}
p2 := Person{Name: "Alice", Age: 30}
fmt.Println(reflect.DeepEqual(p1, p2)) // true
}