0x00 写在前面
reflect.Typeof 和 reflect.Valueof 这俩方法都是 reflect 包提供的用以获取变量动态类型的方法,也就是 golang 中的反射。
0x01 同异
首先看看这俩的源码
// TypeOf returns the reflection Type that represents the dynamic type of i.
// If i is a nil interface value, TypeOf returns nil.
// TypeOf返回反映i的动态类型的反射类型。
// 如果i是nil接口值,TypeOf返回nil。
func TypeOf(i any) Type {
eface := *(*emptyInterface)(unsafe.Pointer(&i))
// Noescape so this doesn't make i to escape. See the comment
// at Value.typ for why this is safe.
return toType((*abi.Type)(noescape(unsafe.Pointer(eface.typ))))
}
// 这边我们再继续跟进 toType 的方法调用,能够看到最后的 toRType
func toRType(t *abi.Type) *rtype {
return (*rtype)(unsafe.Pointer(t))
}
从上述 Typeof 的源码中能够很清晰的看到, Typeof 返回的结果中指保留了原参数的动态类型信息——*rtype ,也就是我们无法再通过返回值获取到原先传入参数的原始值是多少(其余跟原参数值相关的操作当然也没办法进行)。
// ValueOf returns a new Value initialized to the concrete value
// stored in the interface i. ValueOf(nil) returns the zero Value.
// ValueOf返回一个存储在接口中初始化为具体值的新值
// 。ValueOf(nil)返回零值。
func ValueOf(i any) Value {
if i == nil {
return Value{}
}
if !go121noForceValueEscape {
escapes(i)
}
return unpackEface(i)
}
// unpackEface converts the empty interface i to a Value.
func unpackEface(i any) Value {
// 这一步可以看到跟 Typeof 是一致的
e := (*emptyInterface)(unsafe.Pointer(&i))
// NOTE: don't read e.word until we know whether it is really a pointer or not.
t := e.typ
if t == nil {
return Value{}
}
// 这一步拿到基础类型
f := flag(t.Kind())
if t.IfaceIndir() {
f |= flagIndir
}
return Value{t, e.word, f}
}
从 Valueof 的源码能看出,Valueof 返回值中保留了原参数的动态类型以及原始值信息,这也就代表着我们不仅能够针对参数的动态类型做一些操作 reflect.Valueof(xx).Type()
,对其原始值也能够进行处理。
0x02 写在后面
综上,Valueof 似乎要比 Typeof 拓展性更强。但当只需要判断参数类型时,往往 Typeof 就已经够用,没必要使用 Valueof 再绕一道弯子。浅薄见解,欢迎指正。