Reflection 反射
反射在電腦方面代表的意思是程序認識自己的結構(structure)
而其中最重要最常使用的是型別(type)與 interface
下面是認識 reflection 需要的一些重點
type
type MyInt int
var i MyInt // i 目前為 0
n := 1 // 被默認宣告為 int
// i = n // cannot use n (type int) as type MyInt in assignment
// fmt.Println(i + n) // error (mismatched types MyInt and int)
fmt.Println(i + 1) // 1
fmt.Println(n + 1) // 2
interface
io.Reader 是一個 interface
var r io.Reader
// 以下型別都提供了 Read(p []byte) (int, error) 方法
// 所以都能賦值給 r
r = os.Stdin
r = bufio.NewReader(r)
r = new(bytes.Buffer)
下方程式碼重點在於說明一個變數在底層的儲存結構
valueName (value, type)
而當 empty 變數被宣告為 interface{} 後賦值時,編譯器將不會檢察 empty 的 type
// *os.File
f, err := os.OpenFile("some/file", os.O_RDWR, 0)
if err != nil {
fmt.Println(err)
return
}
defer f.Close()
var r io.Reader
// 將 f 指給 r 變數
r = f
var w io.Writer
// 將 f 指給 w 變數
w = f
// 無法將 r 指給 w
// w = r // cannot use r (type io.Reader) as type io.Writer ...
// 但因為 r 儲存的實際值是 *os.File 內部是有實作 Write(b []byte)(int, error)
// 所以可以以此方法將 r值 指給 w 變數
w, ok := r.(io.Writer)
fmt.Printf("%v, %T\n", ok, w) // true *os.File
var empty interface{}
empty = f
empty = r
empty = w
fmt.Printf("%T\n", empty) // *os.File
package reflect
反射有三個原則
1: 從 interface value
轉為 reflection object
var x float64 = 3.4
// reflect.Type
y := reflect.TypeOf(x)
fmt.Printf("%s \n%s \n%T \n%#v\n", y, y.Name(), y, y)
2: 從 reflection object
轉為 interface value
var x float64 = 3.4
// reflect.Value
y := reflect.ValueOf(x)
fmt.Println("toString:", y.String())
fmt.Println("type:", y.Type())
fmt.Println("kind is float64:", y.Kind() == reflect.Float64)
fmt.Println("value:", y.Float())
type MyInt int
var x MyInt = 7
v := reflect.ValueOf(x)
fmt.Printf("%T\n", v) // reflect.Value
fmt.Printf("%T\n", v.Interface()) // main.MyInt
簡單來說, Interface
是 ValueOf
的反函數
3: reflection object
必須是可修改
var x float64 = 3.4
v := reflect.ValueOf(x)
// v.SetFloat(7.1) // panic: reflect: reflect.Value.SetFloat using unaddressable value
fmt.Println("settability of v:", v.CanSet())
阿不是說可以修改??
因為 reflect.ValueOf(x)
是將 x
值拷貝進 reflection object
,
所以就算改動了物件內的值,也影響不到 x 變數的值
這樣的設計是為了可用性的考量
那如何取得一個可修改的 reflection object
?
var x float64 = 3.4
p := reflect.ValueOf(&x)
v := p.Elem()
fmt.Println(v.CanSet())
v.SetFloat(1.1)
fmt.Printf("%f", x) // 1.1
然而, 我們面臨到大部分狀況都會是需要查找 struct
內部屬性
type MyStruct struct {
A, B int
}
// 建立一個自訂結構,並將其指標存在 x 變數
x := &MyStruct{1, 2}
// 然後建立一個指標反射物件
p := reflect.ValueOf(x)
// 接著取得指標反射物件的 Value 物件
v := p.Elem()
// 再取得其 Type 物件
t := v.Type()
fmt.Printf("%#v\n", t.Name()) // 最後,我們找回最初的 type
// 欄位屬性取得
n := v.NumField()
fmt.Println(n)
for i := 0; i < n; i++ {
f := v.Field(i)
fmt.Printf("%d: %s %s = %v\n",
i, // index
t.Field(i).Name, // Type 物件的欄位名稱
f.Type(), // Value 物件 "第 i 個屬性" 的類型
f.Interface()) // Value 物件 "第 i 個屬性" 的值
}
v.Field(0).SetInt(9)
fmt.Printf("%#v\n", x) // &main.MyStruct{A:9, B:2}
在上面這個例子中 v 是"可修改的"反射物件, 就不再多說了
最後, 官網建議盡量減少或非必要時避免使用...