Golang - 教學 tutorial

GoLang - Reflect 反射

在這幾篇,會以 Go 語言的入門基礎進行逐步說明,本篇針對 reflect 進行說明

在 Go 語言使用 reflect 可以用來取得資料自身的物件結構,動態的調用物件的方法和屬性,機制與 Java 的反射機制類似。

reflect 流程可分成三個步驟

  1. 將資料本身透過 reflect 轉換為物件結構
  2. 將物件轉化為對應的值以及類型
  3. 可修改 reflect 欄位

首先,將資料本身透過 reflect 轉換為物件結構,可以透過 ```reflect.TypeOf````

透過 reflect.ValueOf CVT2HUGO: 取得 Type 的實例,

CVT2HUGO: 取得值可直接參考下面的範例
package main

import (
  "fmt"
  "reflect"
)

type Member struct {
  id   int
  name string
  age  int
}

func main() {
  member := Member{1, "Adam", 100}

  //將資料本身透過 reflect 轉換為物件結構
  t := reflect.TypeOf(member)  //取得所有元素
  v := reflect.ValueOf(member) //獲得值
  
	fmt.Println(t) //output main.Member
	fmt.Println(v) //output {1 Adam 100}
}

在前面提到的 TypeOf 回傳實例,是基於 Type Interface 定義進行實作,關於 Type Interface 主要的方法如下:

(可參考 source https://golang.org/src/reflect/type.go)

type Type interface {
	Align() int
	FieldAlign() int
	Method(int) Method
	MethodByName(string) (Method, bool)
	NumMethod() int
	Name() string
	PkgPath() string
	Size() uintptr
	String() string
	Kind() Kind
	Implements(u Type) bool
	AssignableTo(u Type) bool
	ConvertibleTo(u Type) bool
	Comparable() bool
	Bits() int
	ChanDir() ChanDir
	IsVariadic() bool
	Elem() Type
	Field(i int) StructField
	FieldByIndex(index []int) StructField
	FieldByName(name string) (StructField, bool)
	FieldByNameFunc(match func(string) bool) (StructField, bool)
	In(i int) Type
	Key() Type
	Len() int
	NumField() int
	NumIn() int
	NumOut() int
	Out(i int) Type
	common() *rtype
	uncommon() *uncommonType
}

因此,透過 TypeOf 獲得中繼資料後,就可使用上面這些方法,

有一些方法需指定的類型才能使用,例如,當我們定義了 Struct ,並且取得他的 reflect 之後,就可以透過 NumField

但是,在如果不是用 struct 呼叫 NumFiled ,則會 panics。

底下範例我們透過 NumField CVT2HUGO: 取得 struct field 的數量,

CVT2HUGO: 取得 struct field 數量以及 Field(i) 取得 i 位置的元素在透過 Name, Type 取得 struct 的欄位以及類型
package main

import (
  "fmt"
	"reflect"
)

type Member struct {
  id   int
  name string
  age  int
}

func main() {
  member := Member{1, "Adam", 100}

  //將資料本身透過 reflect 轉換為物件結構
  t := reflect.TypeOf(member) //取得所有元素

  for i := 0; i < t.NumField(); i++ {
    file := t.Field(i)
    fmt.Println(file.Name, file.Type)
  }
}

//output
//id int
//name string
//age int

Kind

在 reflect 的 Kind 方法可以用來取得型別,在之前的章節有提到 comma-ok 來幫助判斷型別,以及透過 switch 的篩選方式。

透過 reflect.TypeOf 的 Kind() 方法,結合 switch 則可以更簡單的做型別判斷,範例如下:

func JudgeType(DataObj interface{}) {

  v := reflect.TypeOf(DataObj)

  switch v.Kind() {
  case reflect.Int:
    fmt.Println("int:")
  case reflect.String:
    fmt.Println("string:")
  default:
    fmt.Println("no match")
  }
}

func main() {
  n := 10
  JudgeType(n)

  a := "hello"
  JudgeType(a)
}

Kind 的結構如下:

// A Kind represents the specific kind of type that a Type represents.
// The zero Kind is not a valid kind.
type Kind uint

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
)

其他更多延伸內容,可參考 The Laws of Reflection