1. 简介

Reflection in Golang is about getting to know the data types of the data that we are dealing with at runtime. There are often certain scenarios where we would want to know the data type of a certain variable that we are getting at runtime or something similar.

Golang 中的反射是关于了解我们在运行时处理的数据的数据类型。 在某些情况下,我们经常想知道我们在运行时获得的某个变量的数据类型或类似的东西。

2. 常用api

reflect.TypeOf()

示例:

  1. func main(){
  2. var name string = "TutorialsPoint"
  3. fmt.Println(reflect.TypeOf(name)) //string
  4. sl := []int{1, 2, 3, 4, 5}
  5. fmt.Println(reflect.TypeOf(sl)) //[]int
  6. num := 989
  7. fmt.Println(reflect.TypeOf(num)) //int
  8. }

TypeOf returns the reflection Type that represents the dynamic type of i. If i is a nil interface value, TypeOf returns nil.

TypeOf接收一个变量作为参数,并会参数运行时的类型。

reflect.ValueOf()

示例:

  1. func main(){
  2. var name string = "TutorialsPoint"
  3. fmt.Println(reflect.ValueOf(name)) //TutorialsPoint
  4. sl := []int{1, 2, 3, 4, 5}
  5. fmt.Println(reflect.ValueOf(sl)) //1, 2, 3, 4, 5
  6. num := 989
  7. fmt.Println(reflect.ValueOf(&num)) //0xc000022100
  8. fmt.Println(reflect.ValueOf(nil)) //<invalid reflect.Value>
  9. }

ValueOf returns a new Value initialized to the concrete value stored in the interface i.

ValueOf接收一个变量作为参数,并返回一个新值,初始化为存储在接口 i 中的具体值。

v.NumField()

示例:

  1. type Person struct {
  2. age int
  3. name string
  4. number float64
  5. isMarries bool
  6. }
  7. func main() {
  8. p := Person{10, "ABCD", 15.20, true}
  9. typeT := reflect.TypeOf(p)
  10. fmt.Println(typeT.NumField()) //4
  11. }

NumField returns the number of fields in the struct v. It panics if v’s Kind is not Struct.

NumField返回struct v的字段数量,如果v不是struct则会panic。

v.FieldByIndex()

  1. func (v Value) Field(i int) StructField
  2. 接收一个索引,返回StructField类型。如果v不是structpanic
  3. func (v Value) FieldByIndex(index []int) StructField
  4. 接收一个索引数组,返回嵌套的StructField类型。
  5. func (v Value) FieldByName(name string) StructField
  6. 接收一个name,返回对应name字段的StructField类型。
  7. // A StructField describes a single field in a struct.
  8. type StructField struct {
  9. // Name is the field name.
  10. Name string
  11. // PkgPath is the package path that qualifies a lower case (unexported)
  12. // field name. It is empty for upper case (exported) field names.
  13. // See https://golang.org/ref/spec#Uniqueness_of_identifiers
  14. PkgPath string
  15. Type Type // field type
  16. Tag StructTag // field tag string
  17. Offset uintptr // offset within struct, in bytes
  18. Index []int // index sequence for Type.FieldByIndex
  19. Anonymous bool // is an embedded field
  20. }

示例:

  1. type Person struct {
  2. age int
  3. name string
  4. number float64
  5. isMarries bool
  6. }
  7. func main() {
  8. p := Person{10, "ABCD", 15.20, true}
  9. typeT := reflect.TypeOf(p)
  10. s := typeT.Field(0)
  11. fmt.Println(s) //{age main int 0 [0] false},返回的是age的相关信息。
  12. }

StructField.Tag

  1. type StructTag string
  2. A StructTag is the tag string in a struct field.
  3. 如果StructField中含有标签字符串,可以通过StructField.Tag获得。
  4. 例如:
  5. NumberOfPatents `json:"patent.number_of_patents" bson:"number_of_patents"`
  6. 通过StructField.Tag 就可以得到 json:"patent.number_of_patents" bson:"number_of_patents"
  7. 再通过 StructTag.get("json") 就可以得到 "patent.number_of_patents"

StructField.Type.Kind()

示例:

  1. type Person struct {
  2. age int
  3. name string
  4. number float64
  5. isMarries bool
  6. }
  7. func main() {
  8. p := Person{10, "ABCD", 15.20, true}
  9. typeT := reflect.TypeOf(p)
  10. s := typeT.Field(0)
  11. fmt.Println(s.Type.Kind())
  12. if s.Type.Kind() == reflect.Int {
  13. fmt.Println("s是int类型。")
  14. }
  15. }

Kind returns the specific kind of this type.

注意,返回的是reflect.Int 等类型。

3. 使用案例

在公司任职期间,做了一个对外同步数据的需求。需要将我们公司数据库、数据表的数据全量同步到客户方数据库。最后,我们决定应用反射实现动态的读取数据库中的数据。(如果不利用反射的话,我们就要一个数据表对应一个同步文件,非常的麻烦)

  1. //1.定义reflect类型。其中,m.RecvModel是数据表对应的model。
  2. recvRecords := reflect.New(reflect.TypeOf(m.RecvModel))
  3. //2.recvRecords.Interface()是拿到recvRecords的值。
  4. err := getRecordFromDB(module.DB, ids, i, sync_manager.BatchSize, tableName, nextID, mysqlPrimaryKey).Scan(recvRecords.Interface()).Error
  5. //3.去掉指针。
  6. records := reflect.Indirect(recvRecords)
  7. //4.拿到最后一条数据的主键ID。
  8. record := records.Index(records.Len() - 1)
  9. record.FieldByName(PrimaryKey).String()