Go語言的反射三定律
這裡先丟擲GO語言的反射三定律,然後一一進行講解:
1、反射可以將“介面型別變數”轉換為“反射型別物件”
2、反射可以將“反射型別物件”轉換為“介面型別變數”
3、如果要修改反射型別物件,其值必須是“addressable”
談到Go的反射,涉及到如下幾個概念。
(1)資料型別。go語言中的資料型別有:
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
(2)介面型別
介面型別,一種特殊的資料型別,包括一組方法集,同時,其內部維護兩個屬性:value和type。
value指的是實現了介面型別的型別值;type則是對相應型別值的型別描述。
舉例說明:
var i interface{} //定義空介面變數i,值為nil
var a integer = 10 //定義integer型別變數a
i = a //因為任何型別都實現了空介面型別interface{},所以這裡的賦值沒問題
此時,介面變數i中就包含屬性(10,integer)
(3)反射
go中的反射是通過reflect包實現的。通過反射機制,可以獲取介面變數儲存的型別以及相應的值。
reflect包定義了兩種反射型別:Type和Value
Type is the representation of a Go type.
Value is the reflection interface to a Go value.
反射定律一:反射可以將“介面型別變數”轉換為“反射型別物件”
注意:這裡的反射型別指的是reflect.Type和reflect.Value
將介面型別變數轉換為反射型別變數,是通過reflect包的TypeOf和ValueOf實現的。
func TypeOf
func TypeOf(i interface{}) Type
TypeOf returns the reflection Type that represents the dynamic type of i. If i is a nil interface value, TypeOf returns nil.
func ValueOf
func ValueOf(i interface{}) Value
ValueOf returns a new Value initialized to the concrete value stored in the interface i. ValueOf(nil) returns the zero Value.
舉例:
package main
import (
"fmt"
"reflect"
)
func main() {
var a int = 30
v := reflect.ValueOf(a)//返回Value型別物件,值為30
t := reflect.TypeOf(a)//返回Type型別物件,值為int
fmt.Println(v)
fmt.Println(t)
v = reflect.ValueOf(&a)//返回Value型別物件,值為&a,變數a的地址
t = reflect.TypeOf(&a)//返回Type型別物件,值為*int
fmt.Println(v)
fmt.Println(t)
}
執行結果:
C:/go/bin/go.exe run test.go [E:/project/go/proj/src/test]
30
int
0xc04203a1d0
*int
成功: 程序退出程式碼 0.
上面的案例通過使用reflect.ValueOf和reflect.TypeOf將介面型別變數分別轉換為反射型別物件v和t。
v中包含了介面中的實際值。
t中包含了介面中的實際型別。
大家可能對上面的案例感到疑惑,程式裡沒有介面型別變數啊,哪來的介面型別變數到反射型別物件的轉換啊?
事實上,reflect.ValueOf和reflect.TypeOf的引數型別都是interface{},空介面型別,而返回值的型別是reflect.Value和reflect.Type,中間的轉換由reflect包來實現。
反射定律二:反射可以將“反射型別物件”轉換為“介面型別變數”
根據一個 reflect.Value 型別的變數,我們可以使用 Interface 方法恢復其介面型別的值。事實上,這個方法會把 type 和 value 資訊打包並填充到一個介面變數中,然後返回。
func (Value) Interface
func (v Value) Interface() (i interface{})
Interface returns v's current value as an interface{}. It is equivalent to:
var i interface{} = (v's underlying value)
It panics if the Value was obtained by accessing unexported struct fields.
舉例:
package main
import (
"fmt"
"reflect"
)
func main() {
var a int = 30
v := reflect.ValueOf(&a) //返回Value型別物件,值為&a,變數a的地址
t := reflect.TypeOf(&a) //返回Type型別物件,值為*int
fmt.Println(v)
fmt.Println(t)
v1 := v.Interface() //返回空介面變數
v2 := v1.(*int) //型別斷言,斷定v1中type=*int
fmt.Printf("%T %v\n", v2, v2)
fmt.Println(*v2)
}
執行:
C:/go/bin/go.exe run test.go [E:/project/go/proj/src/test]
0xc042046130
*int
*int 0xc042046130
30
成功: 程序退出程式碼 0.
反射定律三:如果要修改反射型別物件,其值必須是“addressable”
通過反射定義一可以知道,反射物件包含了介面變數中儲存的值以及型別。
如果反射物件中包含的值是原始值,那麼可以通過反射物件修改原始值;
如果反射物件中包含的值不是原始值(反射物件包含的是副本值或指向原始值的地址),那麼該反射物件是不可以修改的。
通過CanSet函式可以判定反射物件是否可以修改。
func (Value) CanSet
func (v Value) CanSet() bool
CanSet reports whether the value of v can be changed. A Value can be changed only if it is addressable and was not obtained by the use of unexported struct fields. If CanSet returns false, calling Set or any type-specific setter (e.g., SetBool, SetInt) will panic.
舉例:
package main
import (
"reflect"
)
func main() {
var x float64 = 3.4
v := reflect.ValueOf(x)
v.SetFloat(7.1) // Error: will panic.
}
執行:
C:/go/bin/go.exe run test.go [E:/project/go/proj/src/test]
panic: reflect: reflect.Value.SetFloat using unaddressable value
goroutine 1 [running]:
panic(0x470400, 0xc042006150)
c:/go/src/runtime/panic.go:500 +0x1af
reflect.flag.mustBeAssignable(0x8e)
c:/go/src/reflect/value.go:228 +0x109
reflect.Value.SetFloat(0x46fa40, 0xc042006148, 0x8e, 0x401c666666666666)
c:/go/src/reflect/value.go:1388 +0x36
main.main()
E:/project/go/proj/src/test/test.go:10 +0xa1
exit status 2
錯誤: 程序退出程式碼 1.
舉例:
package main
import (
"fmt"
"reflect"
)
func main() {
var x float64 = 3.4
v := reflect.ValueOf(x)
fmt.Println("settability of v:", v.CanSet())
}
執行:
C:/go/bin/go.exe run test.go [E:/project/go/proj/src/test]
settability of v: false
成功: 程序退出程式碼 0.
上面的反射物件v不可以修改,是因為v中儲存的是3.4的副本。
舉例:
package main
import (
"fmt"
"reflect"
)
func main() {
var x float64 = 3.4
v := reflect.ValueOf(&x)
fmt.Println("settability of v:", v.CanSet())
}
執行:
C:/go/bin/go.exe run test.go [E:/project/go/proj/src/test]
settability of v: false
成功: 程序退出程式碼 0.
上面的反射物件v不可以修改,是因為v當前儲存的是x的地址,而不是x的原始空間。
舉例:
package main
import (
"fmt"
"reflect"
)
func main() {
var x float64 = 3.4
v := reflect.ValueOf(&x).Elem()
fmt.Println("settability of v:", v.CanSet())
v.SetFloat(6.6)
fmt.Println("x=", x)
}
執行:
C:/go/bin/go.exe run test.go [E:/project/go/proj/src/test]
settability of v: true
x= 6.6
成功: 程序退出程式碼 0.
從執行結果,可以看出,這時候的反射物件v是可以修改的,v的修改等同於x的修改。
上面的程式碼裡,出現了Elem函式,Elem用來獲取原始值對應的反射物件。
func (Value) Elem
func (v Value) Elem() Value
Elem returns the value that the interface v contains or that the pointer v points to. It panics if v's Kind is not Interface or Ptr. It returns the zero Value if v is nil.
關於go的反射機制,就說這麼多吧。
原文地址:http://www.jb51.net/article/90021.htm