
本文介绍如何使用 go 的 `reflect` 包在运行时获取任意值(包括结构体指针、接口等)的真实类型名称,重点解决“如何从 `interface{}` 参数中提取结构体名”这一常见反射需求。
在 Go 中,当一个结构体(如 crud.User)以 interface{} 形式传入函数时,其原始类型信息会被擦除。若需在运行时动态获取其类型名称(例如 "User"),必须借助 reflect 包进行类型检查与解析。
以下是一个完整、健壮的实现示例:
package main
import (
"fmt"
"reflect"
"./crud"
)
func get_struct(value interface{}) {
v := reflect.ValueOf(value)
if !v.IsValid() {
fmt.Println("nil")
return
}
t := v.Type()
// 处理指针:解引用至实际类型
for t.Kind() == reflect.Ptr {
t = t.Elem()
}
// 使用 t.Name() 获取未带包路径的类型名(如 "User")
// 若需完整标识符(如 "*crud.User"),可用 t.String()
name := t.Name()
if name == "" {
// 非命名类型(如 struct{}、[]int)Name() 返回空字符串
fmt.Println(t.String()) // fallback to full description
} else {
fmt.Println(name)
}
}
func main() {
get_struct(&crud.User{}) // 输出: User
get_struct(crud.User{}) // 输出: User
get_struct(42) // 输出: int(Name() 为空,故打印 String())
}? 关键要点说明:
- reflect.ValueOf(value).Type() 返回 reflect.Type,是类型元数据的核心入口;
- Type.Name() 仅对命名类型(即源码中显式声明的 type User struct {...})返回非空字符串;匿名类型(如 struct{X int})将返回空,此时应改用 Type.String();
- 若传入的是指针(如 *crud.User),Type.Name() 会返回空(因指针本身是内置类型,无名称),需通过循环调用 Type.Elem() 解引用,直至抵达底层命名类型;
- 始终检查 reflect.Value.IsValid(),避免对 nil 接口或零值调用 Type() 导致 panic;
- Type.String() 返回带包路径的完整类型描述(如 "*crud.User" 或 "crud.User"),适用于调试;而 Type.Name() 更适合提取简洁类型标识,常用于泛型模拟、ORM 映射或日志标记等场景。
✅ 总结:要安全、准确地提取结构体名称,请结合 IsValid() 校验、指针解引用逻辑,并根据是否需要命名类型灵活选用 Name() 或 String() —— 这是 Go 反射实践中最典型也最实用的类型识别模式。










