美文网首页
go基础——反射二(反射使用)

go基础——反射二(反射使用)

作者: chase_lwf | 来源:发表于2020-07-20 17:54 被阅读0次

内容

1 获取接口类型值
2 修改接口类型值
3 反射调用函数
4 反射调用方法
5 reflect包api使用

先回顾一下反射三大定律:

1 反射可以将“接口类型变量”转换为“反射类型对象”;

2 反射可以将“反射类型对象”转换为“接口类型变量”;

3 如果要修改“反射类型对象”,其值必须是“可写的”(settable)。

一 反射获取接口类型值

通过接口变量获取变量指向的值和类型信息:
1 先通过接口变量获取value反射对象
2 通过value的Interface可以获取接口变量指向的对象,是interface类型
3 断言获取接口值
4 通过value对象type()方法获取接口变量的类型信息,是静态类型信息
5 通过type对象获取接口变量的种类信息,是具体的基本类型信息,如:int slice func map string等基础类型,在reflect/type有所有的基础类型kind

type MyType int
func main() {
    var i = MyType(1)
    iValue := reflect.ValueOf(i)
    iType := reflect.TypeOf(i)

    fmt.Printf(" i value: %v", iValue.Interface()) // i value: 1
    fmt.Printf(" i type: %v", iValue.Type()) // i type: main.MyType
    fmt.Printf(" i kind: %v", iType.Kind())  //  i kind: int

}

// 获取结构体字段信息
// 结构体包含不可导出字段s,此时反射获取这个不可导出字段时,会panic
func reflectGetStructField() {
    m := MyStruct{
        A: 1,
        B: true,
        s: "test",
    }
    mValue := reflect.ValueOf(m)
    mType := reflect.TypeOf(m)
    for i:=0;  i < mValue.NumField(); i++ {
        v := mValue.Field(i)
        fmt.Printf(" field: %v {type: %v; value:%v}", mType.Field(i).Name,
            v.Type(), v.Interface())
    }
}
//panic: reflect.Value.Interface: cannot return value obtained from unexported field or method
// 将s调整为S
输出:
 field: A {type: int; value:1} field: B {type: bool; value: true} field: S {type: string; value: test}

二 修改接口类型值

要修改接口类型值,要谨记反射第三定律,其值要是可写的
总结为:
1 根据接口指针变量获取value对象;
2 根据Elem方法value变量具体指向的数据;
3 可以根据value对象的CanSet方法判断是否可写;
4 调用value对象的SetX方法进行改变;

func reflectModifyStructField() {
    m := MyStruct{
        A: 1,
        B: true,
        S: "test",
    }

    mValue := reflect.ValueOf(&m).Elem()
    // 以下两种都是不行的,获取接口指针变量的value对象后,一定要调用Elem()方法解引用获取指向的具体数据,
    // 因为我们对具体数据域修改,不是对mValue这个value 类型变量修改;
    // 1 mValue := reflect.ValueOf(&m)
    // 2 mValue := reflect.ValueOf(m)
    mType := reflect.TypeOf(&m)
    fmt.Println(fmt.Sprintf("%v can set: %v", mType, mValue.CanSet()))
    for i:=0;  i < mValue.NumField(); i++ {
        if mValue.Field(i).Kind() == reflect.String {
            mValue.Field(i).SetString("new test")
        }
    }
    fmt.Println(fmt.Sprintf("%v : %v", mType, m))
}
// 输出:
*main.MyStruct can set: true
*main.MyStruct : {1 true new test}

三 反射调用函数

1 获取方法对象变量
2 获取value反射对象
3 构造方法入参slice, reflect.Value切片
4 调用value Call方法
5 得到执行结果,是value 切片

func Add(i, j int) int {
    return i + j
}

func main() {
    a := Add
    aValue := reflect.ValueOf(a)
    aParam := make([]reflect.Value, 2)
    aParam[0] = reflect.ValueOf(1)
    aParam[1] = reflect.ValueOf(2)
    fmt.Println("reflect exe add, result: ", aValue.Call(aParam)[0])
}

// 输出
reflect exe add, result:  3

四 反射调用方法

1 获取value对象
2 构造方法入参slice, reflect.Value切片
3 根据MethodByName()获取方法value对象
4 调用call方法执行

type MyStruct struct {
    A int
    B bool
    S string
}
func (m *MyStruct) Subtract(i, j int) int {
    return i - j
}
func main() {
    m := MyStruct{}

    aParam := make([]reflect.Value, 2)
    aParam[0] = reflect.ValueOf(1)
    aParam[1] = reflect.ValueOf(2)

    //方法是指针接收器,这里必须是指针变量 
    mValue := reflect.ValueOf(&m)
    aParam[0] = reflect.ValueOf(1)
    aParam[1] = reflect.ValueOf(2)
    fmt.Println("reflect MyStruct Subtract, result: ", mValue.MethodByName("Subtract").Call(aParam)[0])

}
// 输出
reflect MyStruct Subtract, result:  -1

五 reflect包api使用

1 创建slice map chan

  // 反射创建map slice channel
    intSlice := make([]int, 0)
    mapStringInt := make(map[string]int)
    sliceType := reflect.TypeOf(intSlice)
    mapType := reflect.TypeOf(mapStringInt)

    // 创建新值
    intSliceReflect := reflect.MakeSlice(sliceType, 0, 0)
    mapReflect := reflect.MakeMap(mapType)

    // 使用新创建的变量
    v := 10
    rv := reflect.ValueOf(v)
    intSliceReflect = reflect.Append(intSliceReflect, rv)
    intSlice2 := intSliceReflect.Interface().([]int)
    fmt.Println(intSlice2)

    k := "hello"
    rk := reflect.ValueOf(k)
    mapReflect.SetMapIndex(rk, rv)
    mapStringInt2 := mapReflect.Interface().(map[string]int)

2 创建函数
使用reflect.Makefunc()创建函数,入参是:想要创建的函数的reflect.type和一个输入参数是[] reflect.value类型的slice,其输出参数也是类型reflect.value 切片的闭包

package main

import (
    "reflect"
    "time"
    "fmt"
    "runtime"
)
/*
将创建Func封装, 非reflect.Func类型会panic
当然makeFunc的闭包函数表达式类型是固定的,可以查阅一下文档。
细读文档的reflect.Value.Call()方法。
 */
func MakeTimedFunction(f interface{}) interface{} {
    rf := reflect.TypeOf(f)
    if rf.Kind() != reflect.Func {
        panic("非Reflect.Func")
    }
    vf := reflect.ValueOf(f)
    wrapperF := reflect.MakeFunc(rf, func(in []reflect.Value) []reflect.Value {
        start := time.Now()
        out := vf.Call(in)
        end := time.Now()
        fmt.Printf("calling %s took %v\n", runtime.FuncForPC(vf.Pointer()).Name(), end.Sub(start))
        return out
    })
    return wrapperF.Interface()
}

func time1() {
    fmt.Println("time1Func===starting")
    time.Sleep(1 * time.Second)
    fmt.Println("time1Func===ending")
}

func time2(a int) int {
    fmt.Println("time2Func===starting")
    time.Sleep(time.Duration(a) * time.Second)
    result := a * 2
    fmt.Println("time2Func===ending")
    return result
}

func main() {
    timed := MakeTimedFunction(time1).(func())
    timed()
    timedToo := MakeTimedFunction(time2).(func(int) int)
    time2Val := timedToo(5)
    fmt.Println(time2Val)
}

引用文档:Golang Reflect反射的使用详解1 https://my.oschina.net/90design/blog/1614820

相关文章

  • go基础——反射二(反射使用)

    内容 1 获取接口类型值2 修改接口类型值3 反射调用函数4 反射调用方法5 reflect包api使用 先回顾一...

  • reflect.go包学习_之二 指针操作提高反射性能 反射应用

    reflect.go包学习_之二 指针操作提高反射性能 反射应用 反射创建实例 反射信息、反射调用方法、反射修改值...

  • golang

    一、go基础 二、go应用 1 初级应用 1.1. 反射reflection 1.2. server服务 1.3....

  • Java基础之反射

    Java基础之反射 反射基本介绍 反射的使用通过反射调用属性和方法通过反射获取配置文件 反射基本介绍 Java反射...

  • Go基础——反射

    reflect包实现了运行时反射,允许程序操作任意类型的对象。典型用法是用静态类型interface{}保存一个值...

  • golang反射机制

    反射 反射就是程序能够在运行时动态的查看变量自己的所有属性和方法,能够调用他的任意方法和属性。 GO的反射基础是接...

  • Go语言基础——反射

    Go语言提供了一种机制,在编译时不知道类型的情况下,在运行时,可更新变量、查看值、调用方法以及直接对它的结构成员进...

  • go 反射

    反射的概念 反射就是程序能够在运行时动态的查看自己的状态,比关切允许修改自身的行为。1、GO的反射基础是接口和类型...

  • Java笔记之——反射

    这篇文章来谈谈Java基础——反射的内容。主要通过以下几点进行介绍:反射机制、反射的使用及有关反射的API。 一、...

  • Java基础-反射-反射的使用

    Java工程师知识树[https://www.jianshu.com/p/db77d19a25f6] / Ja...

网友评论

      本文标题:go基础——反射二(反射使用)

      本文链接:https://www.haomeiwen.com/subject/knqdkktx.html