func (orm *Model) ScanPK(output interface{}) *Model { if reflect.TypeOf(reflect.Indirect(reflect.ValueOf(output)).Interface()).Kind() == reflect.Slice { sliceValue := reflect.Indirect(reflect.ValueOf(output)) sliceElementType := sliceValue.Type().Elem() for i := 0; i < sliceElementType.NumField(); i++ { bb := sliceElementType.Field(i).Tag if bb.Get("beedb") == "PK" || reflect.ValueOf(bb).String() == "PK" { orm.PrimaryKey = sliceElementType.Field(i).Name } } } else { tt := reflect.TypeOf(reflect.Indirect(reflect.ValueOf(output)).Interface()) for i := 0; i < tt.NumField(); i++ { bb := tt.Field(i).Tag if bb.Get("beedb") == "PK" || reflect.ValueOf(bb).String() == "PK" { orm.PrimaryKey = tt.Field(i).Name } } } return orm }
开始看谢大的beedb,结果在Model.ScanPk(interface{})
就卡住了,主要是对函数中一些反射相关的API不了解,那就从反射开始研究吧。
先来介绍ScanPK
中会涉及到的几个数据结构,这里只会写到跟这个函数相关的数据结构的相关字段,如果有兴趣进一步研究的话请移步golang reflect
type Type interface {
// 这个Type的种类
Kind() Kind
// 查找这个Type的第 i 个字段
Field(i int) StructField
// 字段的数量
NumField() int
}
Value 表示数据结构值的数据结构,部分字段的声明如下:
Kind Go语言内置类型的枚举值,声明如下:
const (
Invalid Kind = iota
Bool
Int
Int8
Int16
Int32
Int64
Uint
Uint8
Uint16
Uint32
Uint64
Uintptr
Float32
Float64
Complex64
Complex128
Array
Chan
Func
Interface
Map
Ptr
Slice
String
Struct
UnsafePointer
)
type StructField struct {
// 这个字段的名字
Name string
Tag StructTag // 这个字段的注释
}
type Person struct {
Name string `json:"name"`
Age int
}
与C语言不同的地方是,Name字段在声明后,又添加了一段被反引号(`)包围的字符串,这个字符串的规则是键名:"值"
。那么这种类型的结构在被反射时,这段被包围的字符串便会被解析到StructTag
中,可以使用函数StructTag.Get(key string)
来获得某个值。
到这里,需要用到的数据结构就都交代完毕了,其中最重要的两个数据结构是Value、Type
,一个表示值,一个表示类型。接下来再看几个函数。