
本文介绍如何使用 go 的 reflect 包在运行时获取任意值(包括结构体指针、接口等)所对应的原始类型名称,并重点说明 type.name() 与 type.string() 的区别及适用场景。
在 Go 中,函数参数是按值传递的,且类型信息在编译期静态确定;若需在运行时动态获取一个值的实际类型名称(例如 User),必须借助 reflect 包。核心思路是:将任意 interface{} 值转为 reflect.Value,再通过其 Type() 方法获取 reflect.Type,进而提取名称。
以下是一个完整、健壮的实现示例:
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()
// 注意:t.Name() 仅对命名类型(如 struct、named interface)返回非空字符串
// 对指针、切片、map 等未命名类型返回空字符串
name := t.Name()
if name == "" {
// 回退到 String() 获取完整类型描述(含包名和修饰符)
name = t.String()
}
fmt.Println(name)
}
func main() {
get_struct(&crud.User{}) // 输出: "*crud.User"(因传入的是指针)
get_struct(crud.User{}) // 输出: "User"
}⚠️ 关键注意事项:
- Type.Name() 仅对顶层命名类型有效:若传入 *User,Name() 返回空字符串(因为 *User 是未命名指针类型),而 Type.String() 会返回 "*crud.User";
- 若需获取基础结构体名(忽略指针/切片等修饰),应先用 reflect.Indirect() 或 t.Elem() 层层解引用,直到得到命名类型:
func get_base_name(v interface{}) string { t := reflect.TypeOf(v) for t.Kind() == reflect.Ptr || t.Kind() == reflect.Slice || t.Kind() == reflect.Map { t = t.Elem() } return t.Name() // 此时对 *User、[]*User 均返回 "User" } - 使用 reflect 会带来轻微性能开销,不建议在高频路径中滥用;
- 确保导入 "reflect" 和 "fmt" 包,且被反射的类型需在包内可导出(首字母大写)。
总结:reflect.Type.Name() 是获取纯类型名的快捷方式,但需配合类型种类判断;reflect.Type.String() 提供更完整的调试信息;实际工程中,应根据需求选择语义明确、行为稳定的方案。










