(四十五)golang--反射

反射基本介紹:json

(1)反射能夠在運行時動態獲取變量的各類信息,好比變量的類型、類別;app

(2)若是是結構體變量,還能夠獲取結構體自己的信息(包括結構體字段、方法);函數

(3)經過反射,能夠修改變量的值,能夠調用關聯的方法;spa

(4)使用反射,須要Import "reflect:"3d

變量--空接口--relect.Value之間的轉換關係:指針

int變量與反射變量之間的轉換:code

package main

import (
    "fmt"
    "reflect"
)

func reflectTest(b interface{}) {
    rTyp := reflect.TypeOf(b)
    fmt.Printf("rTyp=%v,type=%T\n", rTyp, rTyp)
    rVal := reflect.ValueOf(b)
    fmt.Printf("rVal=%v,type=%T\n", rVal, rVal)

    iVal := rVal.Interface()
    num2 := iVal.(int)
    fmt.Printf("num2=%v,type=%T\n", num2, num2)
}

func main() {
    var num int = 100
    reflectTest(num)
}

 結構體變量與反射變量之間的轉換:blog

type student struct {
    name string
    age  int
}

func reflectTest02(b interface{}) {
    rTyp := reflect.TypeOf(b)
    fmt.Printf("rTyp=%v,type=%T\n", rTyp, rTyp)
    rVal := reflect.ValueOf(b)
    fmt.Printf("rVal=%v,type=%T\n", rVal, rVal)

    iVal := rVal.Interface()
    stu2 := iVal.(student)
    fmt.Printf("stu2=%v,type=%T\n", stu2, stu2)
    fmt.Printf("name:%v,age:%v", stu2.name, stu2.age)
}

func main() {
    stu := student{
        name: "tom",
        age:  20,
    }
    reflectTest02(stu)
}

反射注意事項和使用細節:
(1)reflect.Vale.Kind,獲取變量的類別,返回的是一個常量;排序

(2)Type是類型,Kind是類別,Type和Kind多是相同的,也多是不一樣的;接口

好比:var num int=10的type是Int,Kind也是int

     var stu Student, stu的rype是pkg1.Student,Kind是struct

(3)經過反射介意讓變量在interface{}和reflect.Value之間相互轉換;

(4)使用反射的方式來獲取變量的值,要求數據類型匹配;

(5)經過反射來修改變量,注意當使用SetXxx方法來設置須要經過對指針類型來完成,這樣才能改變傳入變量的值,同時須要使用到reflect.Value.Elem()方法;即rVal.Elem().SetInt(20)

具體實踐;

使用反射來遍歷結構體的字段,並調用結構體的方法,並獲取結構體標籤的值。

package main

import (
    "fmt"
    "reflect"
)

type monster struct {
    Name  string `json:"name"`
    Age   int    `json:"monster_age"`
    Score float32
    Sex   string
}

func (m monster) Print() {
    fmt.Println("----strat----")
    fmt.Println()
    fmt.Println("----end----")
}

func (m monster) GetSum(n1, n2 int) int {
    return n1 + n2
}

func (m monster) Set(name string, age int, score float32, sex string) {
    m.Name = name
    m.Age = age
    m.Score = score
    m.Sex = sex
}

func testStruct(b interface{}) {
    //得到類型
    rtyp := reflect.TypeOf(b)
    //得到值
    rval := reflect.ValueOf(b)
    //得到類別
    kd := rval.Kind()
    //若是kd不是指針類型且rval.Elem()不是結構體類別
    if kd != reflect.Ptr && rval.Elem().Kind() == reflect.Struct {
        fmt.Println("expect struct")
        return
    }
    //獲取結構體有幾個字段
    num := rval.Elem().NumField()
    fmt.Printf("struct has %d fields\n", num)
    //遍歷每一個字段
    for i := 0; i < num; i++ {
        //輸出該字段的值
        fmt.Printf("field %d:值爲=%v\n", i, rval.Elem().Field(i))
        //獲取該字段的json標籤
        tagval := rtyp.Elem().Field(i).Tag.Get("json")
        //若是標籤不爲空,則打印出標籤
        if tagval != "" {
            fmt.Printf("field %d:tag爲=%v\n", i, tagval)
        }
    }
    rval.Elem().Field(0).SetString("孫悟空")
    //獲取結構體有幾個方法
    numOfMethod := rval.Elem().NumMethod()
    fmt.Printf("struct has %d methods\n", numOfMethod)
    //獲取第二個方法,函數的順序是按首字母的ACSII進行排序的,因此這裏第二個方法是Print
    rval.Elem().Method(1).Call(nil)
    //定義一個reflect.Value切片
    var params []reflect.Value
    //添加兩個整型
    params = append(params, reflect.ValueOf(10))
    params = append(params, reflect.ValueOf(40))
    //調用第一個方法,並傳入參數(注意參數類型是reflect.Value切片)
    res := rval.Elem().Method(0).Call(params)
    //注意res是一個切片,裏面只有一個值
    fmt.Println("res=", res[0].Int())

}

func main() {
    var a monster = monster{
        Name:  "tom",
        Age:   400,
        Score: 20.9,
    }
    testStruct(&a)
    fmt.Println(a)
}

要在反射中修改結構體的字段的值,必須傳入的是地址,而且在反射中使用.Elem().SetXxx()

具體實踐2:經過反射完成適配器

func main() {
    test1 := func(v1 int, v2 int) {
        fmt.Printf("v1=%d,v2=%d\n", v1, v2)
    }
    test2 := func(v1 int, v2 int, s string) {
        fmt.Printf("v1=%d,v2=%d,s=%s", v1, v2, s)
    }
    bridge := func(call interface{}, args ...interface{}) {
        //獲取參數長度
        n := len(args)
        //定義參數切片
        params := make([]reflect.Value, n)
        //將參數存入到切片中
        for i := 0; i < n; i++ {
            params[i] = reflect.ValueOf(args[i])
        }
        // for i := 0; i < n; i++ {
            // fmt.Println(args[i])
        // }
        //獲取函數名
        function := reflect.ValueOf(call)
        //將參數傳給函數,並調用函數
        function.Call(params)
    }

    bridge(test1, 1, 2)
    bridge(test2, 1, 2, "test2")

}

運行結果:

具體實踐3:經過反射操做任意結構體類型

type user struct {
    UserID string
    Name   string
}

func testReflectStruct() {
    var (
        model *user
        sv    reflect.Value
    )
    model = &user{}
    sv = reflect.ValueOf(model)
    //這裏是指針類別
    fmt.Println(sv.Kind().String())
    sv = sv.Elem()
    //這裏是結構體類別
    fmt.Println(sv.Kind().String())
    sv.FieldByName("UserID").SetString("1")
    sv.FieldByName("Name").SetString("tom")
    fmt.Println(model)
}

相關文章
相關標籤/搜索