文章由作者马志国在博客园的原创,若转载请于明显处标记出处:http://www.cnblogs.com/mazg/

Go学习群:415660935

8.1概念和作用

Reflection(反射)在计算机中表示程序能够检查自身结构的能力,它是元编程的一种形式。通过反射,可以获取丰富的类型信息,并可以利用这些类型信息做非常灵活的工作。

通过反射机制在运行时能够完成如下功能:

1.确认对象的类

2.确认对象的类型的所有成员变量和方法

3.动态调用对象的方法

8.2 基本用法

本质上来说,反射就是一种检查接口变量的类型和值的机制。在Go语言中使用反射,首先要了解两个基本类型—reflect.Type和reflect.Value。对任何变量进行反射,都可以得到一个包含Type和Value的信息结构。顾名思义,Type主要表达的是被反射的这个变量本身的类型信息,而Value则为该变量的值。

1. 反射第一定律:反射可以将“接口类型对象”转换为“反射类型对象”

 

以上两个函数的参数都是接口类型,可以接收任何类型的变量。所以,称为接口类型变量。返回值是Type和Value,这两个类型称为反射类型,所以通过这两个函数可以将“接口类型变量”即参数转换为反射类型对象即返回值。

2 反射第二定律:反射可以将“反射类型对象”转换为“接口类型对象”

 

根据一个 reflect.Value 类型的变量,我们可以使用 Interface( )方法恢复其接口类型的值。事实上,这个方法会把 type 和 value 信息打包并填充到一个接口变量中,然后返回。

这两个定律互为逆操作。案例如下:

// reflect.go

package main

import (

"fmt"

"reflect"

)

func main() {

// 1 根据接口类型的变量获取反射类型对象

var pi float64 = 3.14

t := reflect.TypeOf(pi)

v := reflect.ValueOf(pi)

fmt.Println(t, v)

// 2 根据反射类型对象获取接口类型变量

pi = v.Interface().(float64)

fmt.Println(pi)

}

3 反射第三定律:如果要修改“反射类型对象”,其值必须是“可写的”

通过反射定律一可以知道,反射对象包含了接口变量中存储的值以及类型。如果反射对象中包含的值是原始值,那么可以通过反射对象修改原始值;如果反射对象中包含的值不是原始值(反射对象包含的是副本值或指向原始值的地址),那么该反射对象是不可以修改的。

 

package main

import (

"fmt"

"reflect"

)

func main() {

var num int = 10

fmt.Println(num)

/*

// 3.1

v := reflect.ValueOf(num)

// 将num作为参数传递给ValueOf后,执行SetInt函数,num的值没有改变

v.SetInt(100)

*/

/*

// 3.2 将num作为参数传递给ValueOf后,执行SetInt函数,修改的是num的地址,不是num的值

v := reflect.ValueOf(&num)

v.SetInt(100)

*/

// 3.3 调用Elem(),相等于解引用 *(&num)

v := reflect.ValueOf(&num).Elem()

v.SetInt(100)

fmt.Println(num)

}

4 对结构体的反射操作

4.1 遍历结构体的成员并修改成员的值

package main

import (

"fmt"

"reflect"

)

type Person struct {

Name string

Age  int

}

func main() {

// 1 p1是对象

p1 := Person{"李四", 20}

v := reflect.ValueOf(&p1).Elem()

// 1 p1是对象的地址

//p1 := &Person{"李四", 20}

//v := reflect.ValueOf(p1).Elem()

num := v.NumField()

for i := 0; i < num; i++ {

fmt.Println(v.Type().Field(i).Name,

v.Field(i).Type(), v.Field(i).Interface())

}

//修改结构体中字段的值

v.Field(0).SetString("王五")

v.Field(1).SetInt(30)

fmt.Println(p1)

}

4.2 遍历结构体的方法并调用方法

// reflect.go

package main

import (

"fmt"

"reflect"

"strconv"

)

type Person struct {

Name string

Age  int

}

func (p *Person) SetName(name string) {

p.Name = name

}

func (p *Person) SetAge(age int) {

p.Age = age

}

func (p *Person) ToString() string {

return "Name:" + p.Name + "Age:" + strconv.Itoa(p.Age)

}

func main() {

// 5 打印和调用结构体的方法

p2 := &Person{"李四", 20}

// 方法的接收器是指针,如果方法的接收器是对象不需要取地址

pv := reflect.ValueOf(&p2).Elem()

num := pv.NumMethod()

fmt.Println(num)

for i := 0; i < num; i++ {

fmt.Println(pv.Type().Method(i).Type) //方法类型

fmt.Println(pv.Type().Method(i).Name) //方法名称

}

params := make([]reflect.Value, 1)

params[0] = reflect.ValueOf(35)

pv.Method(0).Call(params)

params[0] = reflect.ValueOf("张飞")

pv.Method(1).Call(params)

info := pv.MethodByName("ToString").Call(nil)

fmt.Println(info[0])

}

疑问:反射结构体的方法时,采用结构体指针变量的地址作为ValueOf参数可以遍历到接收器为变量的指针的方法和为变量本身的方法。但是使用采用结构体指针变量作为ValueOf参数只能遍历到接收器为变量本身的方法。

5反射的综合例子:

// reflect.go

package main

import (

"fmt"

"reflect"

"strconv"

)

type Person struct {

Name string

Age  int

}

func (p *Person) SetName(name string) {

p.Name = name

}

func (p *Person) SetAge(age int) {

p.Age = age

}

func (p *Person) ToString() string {

return "Name:" + p.Name + "Age:" + strconv.Itoa(p.Age)

}

// 1 根据接口类型的对象获取反射类型对象

func PrintInfo(object interface{}) (v reflect.Value) {

t := reflect.TypeOf(object)

v = reflect.ValueOf(object)

fmt.Println(t, v)

return v

}

// 2 根据反射类型对象获取接口类型对象

func ValueToObject(v reflect.Value) interface{} {

return v.Interface()

}

func main() {

// 1 根据接口类型对象获取反射类型对象

var pi float64 = 3.14

v1 := PrintInfo(pi)

var p1 = Person{"张三", 18}

v2 := PrintInfo(p1)

fmt.Printf("%p\n", &p1)

// 2 根据反射类型对象Value获取接口类型对象

pi = ValueToObject(v1).(float64)

fmt.Println(pi)

p1 = ValueToObject(v2).(Person)

fmt.Println(p1)

p1.ToString()

fmt.Printf("%p\n", &p1)

// 3 如果要修改反射类型对象,其值必须是可写的

var num int = 10

fmt.Println(num)

v := reflect.ValueOf(&num).Elem()

v.SetInt(100)

fmt.Println(num)

// 4 打印和修改结构体中字段的信息

p2 := &Person{"李四", 20}

v = reflect.ValueOf(p2).Elem()

num = v.NumField()

for i := 0; i < num; i++ {

fmt.Println(v.Type().Field(i).Name,

v.Field(i).Type(), v.Field(i).Interface())

}

//修改结构体中字段的值

v.Field(0).SetString("王五")

v.Field(1).SetInt(30)

fmt.Println(*p2)

// 5 打印和调用结构体的方法

pv := reflect.ValueOf(&p2).Elem()

num = pv.NumMethod()

fmt.Println(num)

for i := 0; i < num; i++ {

fmt.Println(pv.Type().Method(i).Type) //方法类型

fmt.Println(pv.Type().Method(i).Name) //方法名称

}

params := make([]reflect.Value, 1)

params[0] = reflect.ValueOf(35)

pv.Method(0).Call(params)

params[0] = reflect.ValueOf("张飞")

pv.Method(1).Call(params)

info := pv.MethodByName("ToString").Call(nil)

fmt.Println(info[0])

}

Go语言 8 反射的更多相关文章

  1. go语言通过反射获取和设置结构体字段值的方法

    本文实例讲述了go语言通过反射获取和设置结构体字段值的方法.分享给大家供大家参考.具体实现方法如下: type MyStruct struct { N int } n := MyStruct{ 1 } ...

  2. Go语言之反射(一)

    反射 反射是指在程序运行期对程序本身进行访问和修改的能力.程序在编译时,变量被转换为内存地址,变量名不会被编译器写入到可执行部分.在运行程序时,程序无法获取自身的信息.支持反射的语言可以在程序编译期将 ...

  3. 深度解密Go语言之反射

    目录 什么是反射 为什么要用反射 反射是如何实现的 types 和 interface 反射的基本函数 反射的三大定律 反射相关函数的使用 代码样例 未导出成员 反射的实际应用 json 序列化 De ...

  4. Go语言之反射(二)

    反射的值对象 反射不仅可以获取值的类型信息,还可以动态地获取或者设置变量的值.Go语言中使用reflect.Value获取和设置变量的值. 使用反射值对象包装任意值 Go语言中,使用reflect.V ...

  5. Go语言的反射

    反射是语言里面是非常重要的一个特性,我们经常会看见这个词,但是对于反射没有一个很好的理解,主要是因为对于反射的使用场景不太熟悉. 一.理解变量的内在机制 1.类型信息,元信息,是预先定义好的,静态的. ...

  6. 列举java语言中反射的常用方法

    package review;/*12:43 2019/7/21*/ import model.AnotherClass; import model.OneClassMore; import mode ...

  7. Go语言之反射(三)

    结构体转JSON JSON格式是一种用途广泛的对象文本格式.在Go语言中,结构体可以通过系统提供的json.Marshal()函数进行序列化.为了演示怎么样通过反射获取结构体成员以及各种值的过程,下面 ...

  8. go语言之反射

    ---恢复内容开始--- 一 :并发基础 1 并发和并行 并发和并行是两个不同的概念: 并行意味着程序在任意时刻都是同时运行的: 并发意味着程序在单位时间内是同时运行的 详解: 并行就是在任一粒度的时 ...

  9. Go语言基础之反射

    Go语言基础之反射 本文介绍了Go语言反射的意义和基本使用. 变量的内在机制 Go语言中的变量是分为两部分的: 类型信息:预先定义好的元信息. 值信息:程序运行过程中可动态变化的. 反射介绍 反射是指 ...

随机推荐

  1. Spring Cloud构建微服务架构

    Dalston版本 由于Brixton和Camden版本的教程已经停止更新,所以笔者计划在2017年上半年完成Dalston版本的教程编写(原计划完成Camden版本教程,但由于写了两篇Dalston ...

  2. LoadRunner录制用户操作

    先说明一点,使用录制的手段拿到的测试脚本和工程师自己编写的测试脚本其实是一样的,不要觉得录制的方式low,而自己编写脚本就显得高大上,这是不对的.除非工程师本身对开发们写的代码逻辑很熟,对业务上的各个 ...

  3. Visual Studio 中设置npm

    VS2017自带的npm会去国外的镜像下载文件, 奇慢无比, 还是马云家淘宝的镜像适合国内用户. 淘宝npm镜像地址:  https://registry.npm.taobao.org VS中使用淘宝 ...

  4. Java Machine Learning Tools & Libraries--转载

    原文地址:http://www.demnag.com/b/java-machine-learning-tools-libraries-cm570/?ref=dzone This is a list o ...

  5. BZOJ1996 HNOI2010合唱队(区间dp)

    设f[i][j][0/1]表示i~j这段区间上一次选择的是最左/最右人的方案数.转移显然. #include<iostream> #include<cstdio> #inclu ...

  6. C++手动开O2优化

    O2优化能使程序的编译效率大大提升. 从而减少程序的运行时间,达到优化的效果. C++程序中的O2开关如下所示: #pragma GCC optimize(2) 同理O1.O3优化只需修改括号中的数即 ...

  7. Django新手图文教程-转发

    转发自:http://www.cnblogs.com/Leo_wl/p/5824541.html 一.Django简介 百度百科:开放源代码的Web应用框架,由Python语言编写...... 重点: ...

  8. hive 连接(join)查询

    1.内连接 hive> select b.*,a.name from userinfo2 b,userinfo a where a.userid=b.userid; hive> selec ...

  9. linux 操作swap分区

    Swap是Linux下的交换分区,类似Windows的虚拟内存,当物理内存不足时,系统可把一些内存中不常用到的程序放入Swap,解决物理内存不足的情况. 若系统安装时开辟的Swap空间太小,可通过手动 ...

  10. 【2016北京集训】Mushroom

    Portal --> broken qwq Description 一开始有个蘑菇,蘑菇里面有\(n\)个房间,是一棵有根树,\(1\)号是根,每个房间里面都有杂草,现在要支持以下操作:将某个指 ...