1. 什么是反射

  Golang提供了一种机制,在编译时不知道类型的情况下,可更新变量、运行时查看值、调用方法以及直接对他们的布局进行操作的机制,称为反射。

2. 反射的使用

2.1 获取变量内部信息

  reflect提供了两种类型来进行访问接口变量的内容:

  类型reflect.ValueOf() 的作用是:获取输入参数接口中的数据的值,如果为空则返回0 <- 注意是0。
  类型reflect.TypeOf() 动态获取输入参数接口中的值的类型,如果为空则返回nil <- 注意是nil。

  看示例:

 package main

 import (
"fmt"
"reflect"
) func main() {
var name string = "编程菜菜" //TypeOf会返回目标数据的类型,比如int/float/struct/指针等
reflectType := reflect.TypeOf(name) //valueOf会返回目标数据的值,比如上文的“编程菜菜”
reflectValue := reflect.ValueOf(name) fmt.Println("type:", reflectType)
fmt.Println("value:", reflectValue)
}

  输出结果:

 type:  string
value: 编程菜菜

  在以上操作发生的时候,反射将“接口类型的变量”转为了“反射的接口类型的变量”,比如上文实际上返回的是reflect.Value和reflect.Type的接口对象。

2.2 struct的反射

 package main

 import (
"fmt"
"reflect"
) type Student struct {
Id int
Name string
} func (s Student) Hello(){
fmt.Println("我是一个学生")
} func main() {
s := Student{Id: , Name: "编程菜菜"} t := reflect.TypeOf(s) // 获取目标对象 fmt.Println("类型的名称是: ", t.Name()) // .Name()可以获取去这个类型的名称 v := reflect.ValueOf(s) // 获取目标对象的值类型 for i := ; i < t.NumField(); i++ { // .NumField()来获取其包含的字段的总数目 key := t.Field(i) // 从0开始获取Student所包含的key value := v.Field(i).Interface() // 通过interface方法来获取key所对应的值 fmt.Printf("第%d个字段是:%s:%v = %v \n", i+, key.Name, key.Type, value)
} for i:=;i<t.NumMethod(); i++ { // 通过.NumMethod()来获取Student里头的方法
m := t.Method(i)
fmt.Printf("第%d个方法是:%s:%v\n", i+, m.Name, m.Type)
}
}
 这个类型的名称是: Student
第1个字段是:Id:int =
第2个字段是:Name:string = 编程菜菜
第1个方法是:Hello:func(main.Student)

2.3 判断传入的类型是否是我们想要的类型

 package main

 import (
"reflect"
"fmt"
) type Student struct {
Id int
Name string
} func main() {
s := Student{Id: , Name: "编程菜菜"}
t := reflect.TypeOf(s) // 通过.Kind()来判断对比的值是否是struct类型
if k := t.Kind(); k == reflect.Struct {
fmt.Println("yes")
} num := ;
numType := reflect.TypeOf(num)
if k := numType.Kind(); k == reflect.Int {
fmt.Println("yes")
}
}
 yes
yes

2.4 通过反射修改内容

 package main

 import (
"reflect"
"fmt"
) type Student struct {
Id int
Name string
} func main() {
s := &Student{Id: , Name: "编程菜菜"} v := reflect.ValueOf(s) if v.Kind() != reflect.Ptr { // 修改值必须是指针类型否则不可行
fmt.Println("不是指针类型,没法进行修改操作")
return
} v = v.Elem() // 获取指针所指向的元素 name := v.FieldByName("Name") // 获取目标key的Value的封装 if name.Kind() == reflect.String {
name.SetString("小学生")
} fmt.Printf("%#v \n", *s) test := // 如果是整型的话
testV := reflect.ValueOf(&test)
testV.Elem().SetInt()
fmt.Println(test)
}

  输出结果:

 main.Student{Id:, Name:"小学生"}
 

2.5 通过反射调用方法

 package main

 import (
"fmt"
"reflect"
) type Student struct {
Id int
Name string
} func (s Student) EchoName(name string){
fmt.Println("我的名字是:", name)
} func main() {
s := Student{Id: , Name: "咖啡色的羊驼"} v := reflect.ValueOf(s) // 获取方法控制权
// 官方解释:返回v的名为name的方法的已绑定(到v的持有值的)状态的函数形式的Value封装
mv := v.MethodByName("EchoName") args := []reflect.Value{reflect.ValueOf("编程菜菜")} // 拼凑参数 mv.Call(args) // 调用函数
}
 我的名字是: 编程菜菜

  使用规则:

1. 使用反射时需要先确定要操作的值是否是期望的类型,是否是可以进行“赋值”操作的,否则reflect包将会毫不留情的产生一个panic。

2. 反射主要与Golang的interface类型相关,只有interface类型才有反射一说。看下TypeOf和ValueOf,会发现其实传入参数的时候已经被转为接口类型了。

3. reflect有关的部分源码分析

  // 部分源代码
func TypeOf(i interface{}) Type {
eface := *(*emptyInterface)(unsafe.Pointer(&i))
return toType(eface.typ)
} func ValueOf(i interface{}) Value {
if i == nil {
return Value{}
}
escapes(i) return unpackEface(i)
}

  TypeOf函数动态获取输入参数接口中的值的类型,如果接口为空则返回nil。

  转换为emptyinterface,emptyInterface是interface {}值的标头。

 // emptyInterface is the header for an interface{} value.
// emptyInterface是interface {}值的标头。
type emptyInterface struct {
typ *rtype
word unsafe.Pointer
}

rtype结构体,实现了Type接口

  size: 存储这个类型的一个值所需要的字节数(值占用的字节数)

  algin: 这个类型的一个变量在内存中的对齐后的所用的字节数 (变量占的字节数)

  FieldAlign: 这种类型的变量如果是struct中的字段,那么它对齐后所用的字节数

 // rtype是大多数值的通用实现。
//它嵌入在其他结构类型中。
//
// rtype必须与../runtime/type.go:/^type._type保持同步。
type rtype struct {
size uintptr
ptrdata uintptr // 类型中可以包含指针的字节数
hash uint32 // hash of type; avoids computation in hash tables
tflag tflag // 类型的哈希; 避免在哈希表中进行计算
align uint8 // 变量与此类型的对齐
fieldAlign uint8 // 结构域与该类型的对齐
kind uint8 // C的枚举
alg *typeAlg // 算法表
gcdata *byte // 垃圾收集数据
str nameOff // 字符串形式
ptrToThis typeOff // 指向此类型的指针的类型,可以为零
}

Value

  Value描述对象的值信息,并不是所有的方法对任何的类型都有意义,特定的方法只适用于特定的类型。

 type Value struct {
// typ包含由值表示的值的类型。
typ *rtype // 指针值的数据;如果设置了flagIndir,则为数据的指针。
         //在设置flagIndir或typ.pointers()为true时有效。
ptr unsafe.Pointer // 标志保存有关该值的元数据。
        //最低位是标志位:
        //-flagStickyRO:通过未导出的未嵌入字段获取,因此为只读
        //-flagEmbedRO:通过未导出的嵌入式字段获取,因此为只读
        //-flagIndir:val保存指向数据的指针
        //-flagAddr:v.CanAddr为true(表示flagIndir)
        //-flagMethod:v是方法值。
        //接下来的五位给出值的种类。
        //重复typ.Kind(),方法值除外。
        //其余的23+位给出方法值的方法编号。
        //如果flag.kind()!= Func,则代码可以假定flagMethod未设置。
        //如果是ifaceIndir(typ),则代码可以假定设置了flagIndir。
flag //方法值代表一个经过咖喱的方法调用像r.Read为某些接收者r。 typ + val + flag位描述接收者r,但标志的Kind位表示Func(方法是函数),并且标志的高位给出方法号在r的类型的方法表中。
}

总结

  • 涉及到内存分配以及后续的GC
  • reflect实现里面有大量的枚举,也就是for循环,比如类型之类的

go中的关键字-reflect 反射的更多相关文章

  1. JAVA 构造器, extends[继承], implements[实现], Interface[接口], reflect[反射], clone[克隆], final, static, abstrac

    记录一下: 构造器[构造函数]: 在java中如果用户编写类的时候没有提供构造函数,那么编译器会自动提供一个默认构造函数.它会把所有的实例字段设置为默认值:所有的数字变量初始化为0;所有的布尔变量设置 ...

  2. python的reflect反射方法

    核心内容专自:http://www.liujiangblog.com/course/python/48 在自动化测试的时候,需要从excel中读取关键字,此关键字对应一个方法,如何使用该关键字去调用真 ...

  3. golang中的标准库反射

    反射 反射是指程序在运行期对程序本身访问和修改的能力 变量的内在机制 变量包含类型信息和值信息 var arr [10]int arr[0] = 10 类型信息:是静态的元信息,是预先定义好的 值信息 ...

  4. 【进阶】Spring中的注解与反射

    [进阶]Spring中的注解与反射 目录 [进阶]Spring中的注解与反射 前言 一.内置(常用)注解 1.1@Overrode 1.2@RequestMapping 1.3@RequestBody ...

  5. Java中的关键字 transient

    先解释下Java中的对象序列化 在讨论transient之前,有必要先搞清楚Java中序列化的含义: Java中对象的序列化指的是将对象转换成以字节序列的形式来表示,这些字节序列包含了对象的数据和信息 ...

  6. js中this关键字测试集锦

    参考:阮一峰<javascript的this用法>及<JS中this关键字详解> this是Javascript语言的一个关键字它代表函数运行时,自动生成的一个内部对象,只能在 ...

  7. 【转载】C/C++中extern关键字详解

    1 基本解释:extern可以置于变量或者函数前,以标示变量或者函数的定义在别的文件中,提示编译器遇到此变量和函数时在其他模块中寻找其定义.此外extern也可用来进行链接指定. 也就是说extern ...

  8. 【转】java中volatile关键字的含义

    java中volatile关键字的含义   在java线程并发处理中,有一个关键字volatile的使用目前存在很大的混淆,以为使用这个关键字,在进行多线程并发处理的时候就可以万事大吉. Java语言 ...

  9. 深入解析Javascript中this关键字的使用

    深入解析Javascript中面向对象编程中的this关键字 在Javascript中this关键字代表函数运行时,自动生成的一个内部对象,只能在函数内部使用.比如: function TestFun ...

随机推荐

  1. flex一些属性

    // 改变主轴的方向 flex-direction: column; // display:flex的子元素无法设置宽度 // 子元素有个flex-shrink属性,表示在父元素宽度不够的情况下是否自 ...

  2. jinjia2

    ansible-playbook --become --become-method=su -K copy.yml - hosts: web remote_user: ansible tasks: - ...

  3. vue—自定义指令

    今日分享—自定义指令 需要学习的点: modifiers属性的具体实例就是v-on:click.stop=”handClick” 一样,为指令添加一个修饰符. 全局指令:新建一个newDir.js i ...

  4. 【集训Day2 哈希表】【NHOI2015】【Luogu P2421】差

    LuoguP2421 原题来自NHOI2015 [解题思路] 本题的解题方法有三种,一种为枚举减数,二分查找被减数.第二种为利用数据单调性用尺取法进行查找,第三种为运用哈希表以快速查找数据. [解题反 ...

  5. python核心编程笔记(转)

    解释器options: 1.1 –d   提供调试输出 1.2 –O   生成优化的字节码(生成.pyo文件) 1.3 –S   不导入site模块以在启动时查找python路径 1.4 –v   冗 ...

  6. python的匿名函数

    在Python,有两种函数,一种是def定义,一种是lambda函数. lambda函数是Python一种生成函数对象的表达式形式.匿名函数通常是创建了可以被调用的函数,它返回了函数,而并没有将这个函 ...

  7. CTF中遇到的php

    1.if(eregi("hackerDJ",$_GET[id])) {   //eregi字符串对比 echo("<p>not allowed!</p& ...

  8. 我的 Input框 不可能这么可爱

    <input /> 标签是我们日常开发中非常常见的替换元素了,但是最近在刷 whattwg 跟 MDN 的时候发现 跟 <input /> 有很多相关的属性,选择器都没怎么用过 ...

  9. Spring Boot通过ImportBeanDefinitionRegistrar动态注入Bean

    在阅读Spring Boot源码时,看到Spring Boot中大量使用ImportBeanDefinitionRegistrar来实现Bean的动态注入.它是Spring中一个强大的扩展接口.本篇文 ...

  10. java this,super简单理解

    *****this****** 表示对当前对象的引用. 作用:1.区分实例变量和局部变量(this.name----->实例变量name) 2.将当前对象当做参数传递给其它对象和方法.利用thi ...