go-反射

反射

反射的基本介紹

17.3.1 基本介紹
1) 反射能夠在運行時 動態獲取變量的各類信息, 好比變量的類型(type),類別(kind)
2) 若是是結構體變量,還能夠獲取到結構體自己的信息(包括結構體的 字段、 方法)
3) 經過反射,能夠修改變量的值,能夠調用關聯的方法。
4) 使用反射,須要 import (「reflect」)json

反射重要的函數和概念

1) reflect.TypeOf(變量名),獲取變量的類型,返回reflect.Typeapp

2) reflect.Value(變量名),獲取變量的值,返回reflect.Value(是一個結構體類型)
3) 變量、interface{} 和 reflect.Value 是能夠相互轉換的,這點在實際開發中,會常常使用到函數

反射的快速入門

快速入門說明

請編寫一個案例,演示對(基本數據類型、interface{}、reflect.Value)進行反射的基本操做指針

代碼演示,見下面的表格:
請編寫一個案例,演示對(結構體類型、interface{}、reflect.Value)進行反射的基本操做code

package main
import (
    "reflect"
    "fmt"
)


//專門演示反射
func reflectTest01(b interface{}) {

    //經過反射獲取的傳入的變量的 type , kind, 值
    //1. 先獲取到 reflect.Type
    rTyp := reflect.TypeOf(b)
    fmt.Println("rType=", rTyp)

    //2. 獲取到 reflect.Value
    rVal := reflect.ValueOf(b)
    
    n2 := 2 + rVal.Int()
    //n3 := rVal.Float()
    fmt.Println("n2=", n2)
    //fmt.Println("n3=", n3)
    
    fmt.Printf("rVal=%v rVal type=%T\n", rVal, rVal)
    fmt.Printf("rVal=%v rTyp type=%T\n", rVal, rTyp)

    //下面咱們將 rVal 轉成 interface{}
    iV := rVal.Interface()
    //將 interface{} 經過斷言轉成須要的類型
    num2 := iV.(int)
    fmt.Println("num2=", num2)


}

//專門演示反射[對結構體的反射]
func reflectTest02(b interface{}) {

    //經過反射獲取的傳入的變量的 type , kind, 值
    //1. 先獲取到 reflect.Type
    rTyp := reflect.TypeOf(b)
    fmt.Println("rType=", rTyp)

    //2. 獲取到 reflect.Value
    rVal := reflect.ValueOf(b)

    //3. 獲取 變量對應的Kind
    //(1) rVal.Kind() ==> 
    kind1 := rVal.Kind()
    //(2) rTyp.Kind() ==>
    kind2 := rTyp.Kind()
    fmt.Printf("kind =%v kind=%v\n", kind1, kind2)
    


    //下面咱們將 rVal 轉成 interface{}
    iV := rVal.Interface()
    fmt.Printf("iv=%v iv type=%T \n", iV, iV)
    //將 interface{} 經過斷言轉成須要的類型
    //這裏,咱們就簡單使用了一帶檢測的類型斷言.
    //同窗們可使用 swtich 的斷言形式來作的更加的靈活
    stu, ok := iV.(Student)
    if ok {
        fmt.Printf("stu.Name=%v\n", stu.Name)
    }

}

type Student struct {
    Name string
    Age int
}

type Monster struct {
    Name string
    Age int
}

func main() {

    //請編寫一個案例,
    //演示對(基本數據類型、interface{}、reflect.Value)進行反射的基本操做

    //1. 先定義一個int
    var num int = 100
    reflectTest01(num)

    //2. 定義一個Student的實例
    stu := Student{
        Name : "tom",
        Age : 20,
    }
    reflectTest02(stu)


}

反射的注意事項和細節

1) reflect.Value.Kind,獲取變量的類別,返回的是一個常量
2) Type 和 Kind 的區別:排序

Type 是類型, Kind 是類別, Type 和 Kind 多是相同的,也 多是不一樣的.
好比: var num int = 10 num 的 Type 是 int , Kind 也是 int
好比: var stu Student stu 的 Type 是 pkg1.Student , Kind 是 struct

3) 經過反射可讓變量在interface{}和reflect.Value之間互相轉換。
4) 經過反射獲取的變量的值,要求數據類型配,好比x是int,那麼就應該用reflect.Value.int(),而不能用其餘,不然會報panic的錯誤。
5) 經過反射的來修改變量, 注意當使用 SetXxx 方法來設置須要經過對應的指針類型來完成, 這樣才能改變傳入的變量的值, 同時須要使用到 reflect.Value.Elem()方法接口

package main
import (
    "reflect"
    "fmt"
)

//經過反射,修改,
// num int 的值
// 修改 student的值

func reflect01(b interface{}) {
    //2. 獲取到 reflect.Value
    rVal := reflect.ValueOf(b)
    // 看看 rVal的Kind是 
    fmt.Printf("rVal kind=%v\n", rVal.Kind())
    //3. rVal
    //Elem返回v持有的接口保管的值的Value封裝,或者v持有的指針指向的值的Value封裝
    rVal.Elem().SetInt(20)
}

func main() {

    var num int = 10
    reflect01(&num)
    fmt.Println("num=", num) // 20


    //你能夠這樣理解rVal.Elem()
    // num := 9
    // ptr *int = &num
    // num2 := *ptr  //=== 相似 rVal.Elem()
}
package main
import (
    "fmt"
    "reflect"
)
func main() {
    var str string = "tom"   //ok
    fs := reflect.ValueOf(&str) //ok fs -> string
    fs.Elem().SetString("jack") //ok
    fmt.Printf("%v\n", str) // jack
}

反射最佳實踐

1) 使用 反射來遍歷結構體的字段, 調用結構體的方法,並 獲取結構體標籤的值
2) 使用反射的方式來獲取結構體的 tag 標籤, 遍歷字段的值,修改字段值,調用結構體方法(要求:經過傳遞地址的方式完成, 在前面案例上修改便可)開發

3) 定義了兩個函數 test1 和 test2,定義一個適配器函數用做統一處理接口(略,用反射實現便可)
4) 使用反射操做任意結構體類型:
5) 使用反射建立並操做結構體string

package main
import (
    "fmt"
    "reflect"
)
//定義了一個Monster結構體
type Monster struct {
    Name  string `json:"name"`
    Age   int `json:"monster_age"`
    Score float32 `json:"成績"`
    Sex   string
    
}

//方法,返回兩個數的和
func (s Monster) GetSum(n1, n2 int) int {
    return n1 + n2
}
//方法, 接收四個值,給s賦值
func (s Monster) Set(name string, age int, score float32, sex string) {
    s.Name = name
    s.Age = age
    s.Score = score
    s.Sex = sex
}

//方法,顯示s的值
func (s Monster) Print() {
    fmt.Println("---start~----")
    fmt.Println(s)
    fmt.Println("---end~----")
}
func TestStruct(a interface{}) {
    //獲取reflect.Type 類型
    typ := reflect.TypeOf(a)
    //獲取reflect.Value 類型
    val := reflect.ValueOf(a)
    //獲取到a對應的類別
    kd := val.Kind()
    //若是傳入的不是struct,就退出
    if kd !=  reflect.Struct {
        fmt.Println("expect struct")
        return
    }

    //獲取到該結構體有幾個字段
    num := val.NumField()

    fmt.Printf("struct has %d fields\n", num) //4
    //變量結構體的全部字段
    for i := 0; i < num; i++ {
        fmt.Printf("Field %d: 值爲=%v\n", i, val.Field(i))
        //獲取到struct標籤, 注意須要經過reflect.Type來獲取tag標籤的值
        tagVal := typ.Field(i).Tag.Get("json")
        //若是該字段於tag標籤就顯示,不然就不顯示
        if tagVal != "" {
            fmt.Printf("Field %d: tag爲=%v\n", i, tagVal)
        }
    }
    
    //獲取到該結構體有多少個方法
    numOfMethod := val.NumMethod()
    fmt.Printf("struct has %d methods\n", numOfMethod)
    
    //var params []reflect.Value
    //方法的排序默認是按照 函數名的排序(ASCII碼)
    val.Method(1).Call(nil) //獲取到第二個方法。調用它

    
    //調用結構體的第1個方法Method(0)
    var params []reflect.Value  //聲明瞭 []reflect.Value
    params = append(params, reflect.ValueOf(10))
    params = append(params, reflect.ValueOf(40))
    res := val.Method(0).Call(params) //傳入的參數是 []reflect.Value, 返回[]reflect.Value
    fmt.Println("res=", res[0].Int()) //返回結果, 返回的結果是 []reflect.Value*/

}
func main() {
    //建立了一個Monster實例
    var a Monster = Monster{
        Name:  "黃鼠狼精",
        Age:   400,
        Score: 30.8,
    }
    //將Monster實例傳遞給TestStruct函數
    TestStruct(a)   
}

const介紹

package main
import (
    
    "fmt"
)
func main() {

    var num int
    num = 9 //ok
    //常量聲明的時候,必須賦值。
    const tax int = 0 
    //常量是不能修改
    //tax = 10
    fmt.Println(num, tax)
    //常量只能修飾bool、數值類型(int, float系列)、string 類型
    
    //fmt.Println(b)

    const (
        a = iota
        b 
        c
        d
    )


    fmt.Println(a, b, c, d)//0 1 2 3
}
相關文章
相關標籤/搜索