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

内容

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,732评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,496评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,264评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,807评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,806评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,675评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,029评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,683评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,704评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,666评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,773评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,413评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,016评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,204评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,083评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,503评论 2 343