go 反射規則
阿新 • • 發佈:2018-12-25
反射規則
在電腦科學領域,反射是指一類應用,它們能夠自描述和自控制。也就是說,這類應用通過採用某種機制來實現對自己行為的描述(self-representation)和監測(examination),並能根據自身行為的狀態和結果,調整或修改應用所描述行為的狀態和相關的語義。
每個語言的反射模型都不同(同時許多語言根本不支援反射)。
Go語言實現了反射,所謂反射就是動態執行時的狀態。
我們用到的包是reflect。
從介面值到反射物件的反射
反射僅是一種用來檢測儲存在介面變數內部(值value,例項型別concret type)pair對的一種機制。
在reflect反射包中有兩種型別讓我們可以訪問介面變數內容
- reflect.ValueOf()
// ValueOf returns a new Value initialized to the concrete value
// stored in the interface i. ValueOf(nil) returns the zero Value.
func ValueOf(i interface{}) Value {...}
- reflect.TypeOf()
// TypeOf returns the reflection Type that represents the dynamic type of i.
// If i is a nil interface value, TypeOf returns nil.
func TypeOf(i interface{}) Type {...}
一個簡單的案例:
package main
import (
"reflect"
"fmt"
)
type ControllerInterface interface {
Init(action string, method string)
}
type Controller struct {
Action string
Method string
Tag string `json:"tag"`
}
func (c *Controller) Init(action string, method string){
c.Action = action
c.Method = method
}
func main(){
//初始化
runController := &Controller{
Action:"Run1",
Method:"GET",
}
//Controller實現了ControllerInterface方法,因此它就實現了ControllerInterface介面
var i ControllerInterface
i = runController
// 得到實際的值,通過v我們獲取儲存在裡面的值,還可以去改變值
v := reflect.ValueOf(i)
fmt.Println("value:",v)
// 得到型別的元資料,通過t我們能獲取型別定義裡面的所有元素
t := reflect.TypeOf(i)
fmt.Println("type:",t)
// 轉化為reflect物件之後我們就可以進行一些操作了,也就是將reflect物件轉化成相應的值
......
}
列印結果:
value: &{Run1 GET}
type: *main.Controller
- v 是i介面為指向main包下struct Controller的指標(runController)目前的所儲存值
- t 是i介面為指向main包下struct Controller的指標型別。
轉換reflect物件之後操作
介面i所指向的物件的值操作
// Elem返回值v包含的介面
controllerValue := v.Elem()
fmt.Println("controllerType(reflect.Value):",controllerType)
//獲取儲存在第一個欄位裡面的值
fmt.Println("Action:", controllerValue.Field(0).String())
controllerType(reflect.Value): {Run1 GET }
Action: Run1
介面i所指向的物件的型別操作
獲取定義在struct裡面的標籤
// Elem返回型別的元素型別。
controllerType := t.Elem()
tag := controllerType.Field(2).Tag //Field(第幾個欄位,index從0開始)
fmt.Println("Tag:", tag)
Tag: json:”tag”
通過reflect.TypeOf(i)獲取物件方法的資訊
method, _ := t.MethodByName("Init")
fmt.Println(method)
列印結果:
{Init func(*main.Controller, string, string) <func(*main.Controller, string, string) Value> 0}
它返回了一個物件方法的資訊集合即Method,關於Method結構定義在reflect/type.go下具體為:
// Method represents a single method.
type Method struct {
// Name is the method name.
// PkgPath is the package path that qualifies a lower case (unexported)
// method name. It is empty for upper case (exported) method names.
// The combination of PkgPath and Name uniquely identifies a method
// in a method set.
// See http://golang.org/ref/spec#Uniqueness_of_identifiers
Name string
PkgPath string
Type Type // method type
Func Value // func with receiver as first argument
Index int // index for Type.Method
}
通過reflect.ValueOf(i)獲取物件方法的資訊
vMethod := v.MethodByName("Init")
fmt.Println(vMethod)
通過reflect.ValueOf(i)呼叫方法
package main
import (
"reflect"
"fmt"
)
type ControllerInterface interface {
Init(action string, method string)
}
type Controller struct {
Action string
Method string
Tag string `json:"tag"`
}
func (c *Controller) Init(action string, method string){
c.Action = action
c.Method = method
//增加fmt列印,便於看是否呼叫
fmt.Println("Init() is run.")
fmt.Println("c:",c)
}
//增加一個無引數的Func
func (c *Controller) Test(){
fmt.Println("Test() is run.")
}
func main(){
//初始化
runController := &Controller{
Action:"Run1",
Method:"GET",
}
//Controller實現了ControllerInterface方法,因此它就實現了ControllerInterface介面
var i ControllerInterface
i = runController
// 得到實際的值,通過v我們獲取儲存在裡面的值,還可以去改變值
v := reflect.ValueOf(i)
fmt.Println("value:",v)
// 有輸入引數的方法呼叫
// 構造輸入引數
args1 := []reflect.Value{reflect.ValueOf("Run2"),reflect.ValueOf("POST")}
// 通過v進行呼叫
v.MethodByName("Init").Call(args1)
// 無輸入引數的方法呼叫
// 構造zero value
args2 := make([]reflect.Value, 0)
// 通過v進行呼叫
v.MethodByName("Test").Call(args2)
}
列印結果:
value: &{Run1 GET }
//有引數的Func
Init() is run.
c: &{Run2 POST }
//無引數的Func
Test() is run.
從反射物件到介面值的反射
修改反射物件
最近在寫ATC框架,路由第一版構思中用到了反射就整理了下,不過經過幾個莫名夜晚糾結,最終第二版構思實踐中又把所有反射干掉了(淚…, 討論群中一致認為反射對效能影響較大,能避免則避免,不能避免就少用。),目前思路和框架已定型,一個輕量級簡單的RESTful go 框架 即將釋出…
不早了,先到這裡吧,米西米西了。空了在補充,還有很多反射內容待進一步完善……
有空的話就把beego框架中用到的反射知識整理分享下,利己利人。