這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
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 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)
- StructField 用於描述一個資料結構的欄位,部分欄位聲明如下:
type StructField struct { // 這個欄位的名字 Name string Tag StructTag // 這個欄位的注釋}
- StructTag StructField中有一個Tag欄位,這個欄位是對所屬欄位屬性的一種補充。比如現在有一個自訂的資料結構聲明如下:
type Person struct { Name string `json:"name"` Age int}
與C語言不同的地方是,Name欄位在聲明後,又添加了一段被反引號(`)包圍的字串,這個字串的規則是鍵名:"值"。那麼這種類型的結構在被反射時,這段被包圍的字串便會被解析到StructTag中,可以使用函數StructTag.Get(key string)來獲得某個值。
到這裡,需要用到的資料結構就都交代完畢了,其中最重要的兩個資料結構是Value、Type,一個表示值,一個表示類型。接下來再看幾個函數。