作者:BGbiao
链接:https://www.jianshu.com/p/42c19f88df6c
來源:简书

反射reflection

  • 可以大大提高程序的灵活性,使得interface{}有更大的发挥余地
  • 反射可以使用TypeOf和ValueOf函数从接口中获取目标对象信息
  • 反射会将匿名字段作为独立字段(匿名字段的本质)
  • 想要利用反射修改对象状态,前提是interface.data是settable,即pointer-interface
  • 通过反射可以“动态”调用方法

常用的类型、函数和方法

//返回动态类型i的类型,如果i是一个空结构体类型,TypeOf将返回nil
func TypeOf(i interface{}) Type //Type 接口类型
type Type interface {
Align() int
FieldAlign() int
//指定结构体中方法的下标,返回某个方法的对象,需要注意的是返回的Method是一个独立的结构体
Method(int) Method
/*
type Method struct {
Name string
PkgPath string
Type Type
Func Value
Index int
}
*/ MethodByName(string) (Method, bool) //返回该结构体类型的方法下标
NumMethod() int
//返回类型的名称,即动态类型i的名称
Name() string
PkgPath() string
Size() uintptr
String() string
Kind() Kind
Implements(u Type) bool
AssignableTo(u Type) bool
ConvertibleTo(u Type) bool
Comparable() bool
Bits() int
ChanDir() ChanDir
IsVariadic() bool
Elem() Type
//返回结构体类型第i个字段
Field(i int) StructField
//StructField结构体
//type StructField struct {
// Name string
// PkgPath string
// Type Type
// Tag StructTag
// Offset uintptr
// Index []int
// Anonymous bool //根据结构体字段索引获取嵌入字段的结构体信息
FieldByIndex(index []int) StructField FieldByName(name string) (StructField, bool)
FieldByNameFunc(match func(string) bool) (StructField, bool)
In(i int) Type
Key() Type
Len() int
//返回动态类型i(结构体字段)的字段总数
NumField() int
NumIn() int
NumOut() int
Out(i int) Type
} //返回接口i的一个初始化的新值.ValueOf(nil)返回一个零值
func ValueOf(i interface{}) Value // Value结构体
type Value struct { }
// Value结构体的一些方法
// 返回结构体v中的第i个字段。如果v的类型不是结构体或者i超出了结构体的范围,则会出现panic
func (v Value) Field(i int) Value //以接口类型返回v的当前值
func (v Value) Interface() (i interface{})
//等价于.
var i interface{} = (v's underlying value) //通过反射方式修改结构体对象的一些方法 //返回接口v包含或者指针v包含的值
func (v Value) Elem() Value
//判断该接口v是否可以被set修改
func (v Value) CanSet() bool //使用另外一个反射接口去修改反射值
func (v Value) Set(x Value)
//其他不同类型的Set
func (v Value) SetBool(x bool)
func (v Value) SetBytes(x []byte)
func (v Value) SetFloat(x float64)
func (v Value) SetInt(x int64)
//设置结构体对象v的长度为n
func (v Value) SetLen(n int)
func (v Value) SetString(x string) //一些辅助方法
//返回反射结构体的Value的类型.如果v为零值,IsValid将返回false
func (v Value) Kind() Kind
//判断value是否为有效值,通常用在判断某个字段是否在反射体的Value中
func (v Value) IsValid() bool //Kind常量
type Kind uint
const (
Invalid Kind = iota
Bool
Int
Int8
Int16
Int32
Int64
Uint
Uint8
Uint16
Uint32
Uint64
Uintptr
Float32
Float64
Complex64
Complex128
Array
Chan
Func
Interface
Map
Ptr
Slice
String
Struct
UnsafePointer
)

反射的基本操作

通过反射来获取结构体字段的名称以及其他相关信息。

package main

import (
"fmt"
"reflect"
) //定义结构体
type User struct {
Id int
Name string
Age int
} //定义结构体方法
func (u User) Hello() {
fmt.Println("Hello xuxuebiao")
} func main() {
u := User{, "bgops", }
Info(u)
u.Hello()
} //定义一个反射函数,参数为任意类型
func Info(o interface{}) {
//使用反射类型获取o的Type,一个包含多个方法的interface
t := reflect.TypeOf(o)
//打印类型o的名称
fmt.Println("type:", t.Name()) //使用反射类型获取o的Value,一个空的结构体
v := reflect.ValueOf(o)
fmt.Println("Fields:") //t.NumField()打印结构体o的字段个数(Id,Name,Age共三个)
for i := ; i < t.NumField(); i++ {
//根据结构体的下标i来获取结构体某个字段,并返回一个新的结构体
/**
type StructField struct {
Name string
PkgPath string
Type Type
Tag StructTag
Offset uintptr
Index []int
Anonymous bool
}
**/
f := t.Field(i) //使用结构体方法v.Field(i)根据下标i获取字段Value(Id,Name,Age)
//在根据Value的Interface()方法获取当前的value的值(interface类型)
val := v.Field(i).Interface()
fmt.Printf("%6s:%v = %v\n", f.Name, f.Type, val)
} //使用t.NumMethod()获取所有结构体类型的方法个数(只有Hello()一个方法)
//接口Type的方法NumMethod() int
for i := ; i < t.NumMethod(); i++ {
//使用t.Method(i)指定方法下标获取方法对象。返回一个Method结构体
//Method(int) Method
m := t.Method(i)
//打印Method结构体的相关属性
/*
type Method struct {
Name string
PkgPath string
Type Type
Func Value
Index int
}
*/
fmt.Printf("%6s:%v\n", m.Name, m.Type)
}
}

输出

type: User
Fields:
Id:int =
Name:string = bgops
Age:int =
Hello:func(main.User)
Hello xuxuebiao

注意:我们上面的示例是使用值类型进行进行反射构造的。如果是指针类型的话,我们需要使用reflect.Struct字段进行判断接口类型的Kind()方法

if k := t.Kind();k != reflect.Struct {
fmt.Println("非值类型的反射")
return
}

匿名字段的反射以及嵌入字段

注意:反射会将匿名字段当做独立的字段去处理,需要通过类型索引方式使用FieldByIndex方法去逐个判断

//根据指定索引返回对应的嵌套字段
FieldByIndex(index []int) StructField type StructField struct {
Name string
PkgPath string
Type Type
Tag StructTag
Offset uintptr
Index []int
Anonymous bool //是否为匿名字段
}
package main

import (
"fmt"
"reflect"
) type User struct {
Id int
Name string
Age int
} type Manager struct {
User
title string
} func main() {
//注意匿名字段的初始化操作
m := Manager{User: User{, "biaoge", }, title: "hello biao"}
t := reflect.TypeOf(m) fmt.Printf("%#v\n", t.FieldByIndex([]int{}))
fmt.Printf("%#v\n", t.FieldByIndex([]int{}))
fmt.Printf("%#v\n", t.FieldByIndex([]int{, }))
fmt.Printf("%#v\n", t.FieldByIndex([]int{, })) }

输出:

reflect.StructField{Name:"User", PkgPath:"", Type:(*reflect.rtype)(0x4ac660), Tag:"", Offset:0x0, Index:[]int{}, Anonymous:true}
reflect.StructField{Name:"title", PkgPath:"main", Type:(*reflect.rtype)(0x49d820), Tag:"", Offset:0x20, Index:[]int{}, Anonymous:false}
reflect.StructField{Name:"Id", PkgPath:"", Type:(*reflect.rtype)(0x49d1a0), Tag:"", Offset:0x0, Index:[]int{}, Anonymous:false}
reflect.StructField{Name:"Name", PkgPath:"", Type:(*reflect.rtype)(0x49d820), Tag:"", Offset:0x8, Index:[]int{}, Anonymous:false}

通过反射修改目标对象

通过反射的方式去修改对象的某个值。需要注意的亮点是,首先,需要找到对象相关的名称,其次需要找到合适的方法去修改相应的值。

package main

import (
"fmt"
"reflect"
) func main() {
x :=
v := reflect.ValueOf(&x)
v.Elem().SetInt()
fmt.Println(x)
}

输出:


修改I的时候需要找到对象字段的名称;并且判断类型,使用相对正确的类型修改值
v.FieldByName("Name");f.Kind() == reflect.String {
f.SetString("test string")
}

判断是否找到正确的字段名称:

f := v.FieldByName("Name1")
//判断反射对象Value中是否找到Name1字段
if !f.IsValid() {
fmt.Println("bad field")
return
}
package main

import (
"fmt"
"reflect"
) type User struct {
Id int
Name string
Age int
} //使用反射方式对结构体对象的修改有两个条件
//1.通过指针
//2.必须是可set的方法
func main() {
num :=
numv := reflect.ValueOf(&num)
//通过Value的Elem()和SetX()方法可直接对相关的对象进行修改
numv.Elem().SetInt()
fmt.Println(num) u := User{, "biao", }
uu := reflect.ValueOf(&u)
//Set()后面的必须是值类型
//func (v Value) Set(x Value)
test := User{, "bgops", }
testv := reflect.ValueOf(test)
uu.Elem().Set(testv)
fmt.Println("Change the test to u with Set(x Value)", uu) //此时的U已经被上面那个uu通过指针的方式修改了
Set(&u)
fmt.Println(u)
} func Set(o interface{}) {
v := reflect.ValueOf(o)
//判断反射体值v是否是Ptr类型并且不能进行Set操作
if v.Kind() == reflect.Ptr && ! v.Elem().CanSet() {
fmt.Println("xxx")
return
//初始化对象修改后的返回值(可接受v或v的指针)
} else {
v = v.Elem()
}
//按照结构体对象的名称进行查找filed,并判断类型是否为string,然后进行Set
if f := v.FieldByName("Name"); f.Kind() == reflect.String {
f.SetString("BYBY")
}
}

输出:

Change the test to u with Set(x Value) &{ bgops }
{ BYBY }

通过反射进行动态方法的调用

使用反射的相关知识进行方法的动态调用

package main

import (
"fmt"
"reflect"
) type User struct {
Id int
Name string
Age int
} func (u User) Hello(name string, id int) {
fmt.Printf("Hello %s,my name is %s and my id is %d\n", name, u.Name, id)
} func main() {
u := User{, "biaoge", }
fmt.Println("方法调用:")
u.Hello("xuxuebiao", ) //获取结构体类型u的Value
v := reflect.ValueOf(u)
//根据方法名称获取Value中的方法对象
mv := v.MethodByName("Hello") //构造一个[]Value类型的变量,使用Value的Call(in []Value)方法进行动态调用method
//这里其实相当于有一个Value类型的Slice,仅一个字段
args := []reflect.Value{reflect.ValueOf("xuxuebiao"), reflect.ValueOf()}
fmt.Println("通过反射动态调用方法:")
//使用Value的Call(in []Value)方法进行方法的动态调用
//func (v Value) Call(in []Value) []Value
//需要注意的是当v的类型不是Func的化,将会panic;同时每个输入的参数args都必须对应到Hello()方法中的每一个形参上
mv.Call(args) }
方法调用:
Hello xuxuebiao,my name is biaoge and my id is
通过反射动态调用方法:
Hello xuxuebiao,my name is biaoge and my id is

golang的reflection(转)的更多相关文章

  1. Golang 反射reflection

    反射reflection 反射可大大提高程序的灵活性,使得interface{}有更大的发挥余地 反射使用TypeOf和ValueOf函数从接口中获取目标对象信息 反射会将匿名字段作为独立字段(匿名字 ...

  2. golang之reflection

    反射就是程序能够在运行时检查变量和值,求出它们的类型. reflect包实现运行时反射. 创建一个接收任何数据类型任何数值的查询string: func createQuery(q interface ...

  3. Cheatsheet: 2015 12.01 ~ 12.31

    Mobile Setting Up the Development Environment iOS From Scratch With Swift: How to Test an iOS Applic ...

  4. Golang高效实践之interface、reflection、json实践

    前言 反射是程序校验自己数据结构和类型的一种机制.文章尝试解释Golang的反射机制工作原理,每种编程语言的反射模型都是不同的,有很多语言甚至都不支持反射. Interface 在将反射之前需要先介绍 ...

  5. Golang接口(interface)三个特性(译文)

    The Laws of Reflection 原文地址 第一次翻译文章,请各路人士多多指教! 类型和接口 因为映射建设在类型的基础之上,首先我们对类型进行全新的介绍. go是一个静态性语言,每个变量都 ...

  6. golang下的grpc

    facebook的thrift也是开源rpc库,性能高出grpc一倍以上,grpc发展的较晚,期待以后有长足的进步.简单来说thrift = grpc + protobuf gRPC基于HTTP/2标 ...

  7. Go语言(golang)开源项目大全

    转http://www.open-open.com/lib/view/open1396063913278.html内容目录Astronomy构建工具缓存云计算命令行选项解析器命令行工具压缩配置文件解析 ...

  8. [转]Go语言(golang)开源项目大全

    内容目录 Astronomy 构建工具 缓存 云计算 命令行选项解析器 命令行工具 压缩 配置文件解析器 控制台用户界面 加密 数据处理 数据结构 数据库和存储 开发工具 分布式/网格计算 文档 编辑 ...

  9. golang路上的小学生系列--使用reflect查找package路径

    本文同时发布在个人博客chinazt.cc 和 gitbook 今日看到了一个有趣的golang项目--kolpa(https://github.com/malisit/kolpa). 这个项目可以用 ...

随机推荐

  1. ClickHouse之集群搭建以及数据复制

    前面的文章简单的介绍了ClickHouse,以及也进行了简单的性能测试.本次说说集群的搭建以及数据复制,如果复制数据需要zookeeper配合. 环境: 1. 3台机器,我这里是3台虚拟机.都安装了c ...

  2. Java并发编程笔记之基础总结(一)

    一.线程概念 说到线程就必须要提一下进程,因为线程是进程中的一个实体,线程本身是不会独立存在的.进程是代码在数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,线程则是进程的一个执行路径,一 ...

  3. Deep Reinforcement Learning: Pong from Pixels

    这是一篇迟来很久的关于增强学习(Reinforcement Learning, RL)博文.增强学习最近非常火!你一定有所了解,现在的计算机能不但能够被全自动地训练去玩儿ATARI(译注:一种游戏机) ...

  4. SpringBoot自动配置注解原理解析

    1. SpringBoot启动主程序类: @SpringBootApplication public class DemoApplication { public static void main(S ...

  5. Sharepoint 2010 工作流状态值

    在Sharepoint2010中,如果要使用工作流状态值进行筛选,必须使用内部值,不能使用文字,要不然是筛选不出来的. 进行中:2 已取消:4 已批准:16 拒绝:17 下边是已取消的工作流状态:

  6. Oracle入门《Oracle介绍》第一章1-4 Oracle 用户管理

    1.Oracle 默认用户 只有用合法的用户帐号才能访问Oracle数据库 Oracle 有几个默认的数据库用户 数据库中所有数据字典表和视图都存储在 SYS 模式中.SYS用户主要用来维护系统信息和 ...

  7. List排序Collections.sort 重写compare

    static List<Integer> intList = Arrays.asList(2,5,7, 3, 1); public static void main(String[] ar ...

  8. C#多线程——同步

    多个线程(不仅仅局限于相同进程)如果需要访问相同的可变资源的话就可能需要考虑到线程同步的手段.CPU的线程和进程管控我这里就不去说了,计算机组成原理里面的东西 那么既然要让线程的步调一致,那么我们首先 ...

  9. 用PHP去掉文件头的Unicode签名(BOM)

    <?php //此文件用于快速测试UTF8编码的文件是不是加了BOM,并可自动移除 //By Bob Shen $basedir="."; //修改此行为需要检测的目录,点表 ...

  10. Salesforce DX 简介

    Salesforce DX Salesforce DX (Salesforce Developer Experience) 是 Salesforce 推出的一个新的开发和部署模式,旨在提供更好的开发者 ...