1. 程式人生 > >go 反射規則

go 反射規則

反射規則

在電腦科學領域,反射是指一類應用,它們能夠自描述和自控制。也就是說,這類應用通過採用某種機制來實現對自己行為的描述(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框架中用到的反射知識整理分享下,利己利人。