go的反射reflect


我們項目中反射一般是在特定場合獲取對象的信息, 然后動態調用對象方法 或者修改對象的屬性,但是go里面還有指針【地址】一說法, 來看看是怎么用的

package main
 
import (
    "fmt"
    "reflect"
)
 
func main() {
    var x float64 = 3.4
    p := reflect.ValueOf(&x) // Note: take the address of x.
    /*為了得到 p 指向的數據,可以調用 Value 類型的 Elem 方法。
    Elem 方法能夠對指針進行“解引用”,然后將結果存儲到反射 Value 類型對象 v 中*/
    v := p.Elem()
    v.SetFloat(7.1)
    fmt.Println(x)
}

如果是常見的類型 struct又該如何了:

package main

import (
    "fmt"
    "reflect"
)

func main() {
    type T struct {
        A int
        B string `json:"b"`
    }
    t := T{23, "skidoo"}
    s := reflect.ValueOf(&t).Elem()
    ///獲取反射信息
    typeOfT := s.Type()
    for i := 0; i < s.NumField(); i++ {
        f := s.Field(i)
        fmt.Printf("%d: %s %s = %v\n", i,
            typeOfT.Field(i).Name, f.Type(), f.Interface())
    }
    // 通過字段名, 找到字段類型信息
    if bType, ok := typeOfT.FieldByName("B"); ok {
        // 從tag中取出需要的tag
        fmt.Printf("B has tag %s\n", bType.Tag.Get("json"))
    }
    //修改對象數據
    s.Field(0).SetInt(77)
    s.Field(1).SetString("Sunset Strip")
    fmt.Println("t is now", t)
}

運行結果:

0: A int = 23
1: B string = skidoo
B has tag b
t is now {77 Sunset Strip}

 如果我們修改了程序讓 s 由 t(而不是 &t)創建,程序就會在調用 SetInt 和 SetString 的地方失敗,因為 t 的字段是不可設置的。

普通方法的調用:

package main

import (
    "fmt"
    "reflect"
)

// 普通函數
func add(a, b int) int {
    return a + b
}
func main() {
    // 將函數包裝為反射值對象
    funcValue := reflect.ValueOf(add)
    // 構造函數參數, 傳入兩個整型值
    paramList := []reflect.Value{reflect.ValueOf(10), reflect.ValueOf(20)}
    // 反射調用函數
    retList := funcValue.Call(paramList)
    // 獲取第一個返回值, 取整數值
    fmt.Println(retList[0].Int())
}

接口方法

package main

import (
    "fmt"
    "reflect"
)

type Table interface {
    TableName() string
}

type Cal interface {
    Add(a, b int) int
}

type model struct{}

func (m model) TableName() string {
    return "table_name"
}

func (m model) Add(a, b int) int {
    return a + b
}
func main() {
    var mod model
    getTableName(mod)
}

/*
reflect.New()函數的聲明 func New(typ Type) Value
根據傳入的tpye,可以獲取到對應的value
*/
func getTableName(v interface{}) {
    rt := reflect.TypeOf(v)
    rv := reflect.ValueOf(v)
    if tabler, ok := rv.Interface().(Table); ok {
        fmt.Println(tabler.TableName())
    }
    if cal, ok := rv.Interface().(Cal); ok {
        fmt.Println(cal.Add(10, 20))
    }
    ///
    if tabler, ok := reflect.New(rt).Interface().(Table); ok {
        fmt.Println(tabler.TableName())
    }
    if cal, ok := reflect.New(rt).Interface().(Cal); ok {
        fmt.Println(cal.Add(20, 30))
    }
}

對象方法:

package main

import (
    "fmt"
    "reflect"
)

type Person struct {
    Name string
    Age  int
    Sex  string
}

//如果Tool小寫后面就沒辦法反射
type Tool struct {
    Cap string
    Key string
}

func (t Tool) Print() {
    fmt.Println("tool:" + t.Cap + t.Key)
}

func (p Person) PrintInfo(t *Tool) {
    t.Cap = "green"
    t.Key = "long"
    fmt.Printf("姓名:%s, 年齡:%s, 性別:%s, 參數tool內容:%s %s\n", p.Name, p.Age, p.Sex, t.Key, t.Cap)
}

func main() {
    p1 := Person{"Rbuy", 20, "男"}
    ///調用person的PrintInfo 方法
    rtyp := reflect.TypeOf(p1)
    rcvr := reflect.ValueOf(p1)
    rmethod, ok := rtyp.MethodByName("PrintInfo")
    if ok {
        // 得到第一個此method第1參數的Type,第零個當然就是結構體本身了
        replyType := rmethod.Type.In(1).Elem()
        //實例化返回值的地址  *tool
        replyv := reflect.New(replyType)
        //rcvr 可以理解為對象實例  replyv 理解為返回值
        rmethod.Func.Call([]reflect.Value{rcvr, replyv})

        //調用tool的print
        var t Tool
        rtyp = reflect.TypeOf(t)
        rmethod, ok = rtyp.MethodByName("Print")
        if ok {
            rmethod.Func.Call([]reflect.Value{replyv.Elem()})
        }
    }

}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM