Go利用反射reflect实现获取接口变量信息

目录
  • 引言
  • 一、反射的规则
    • 1、从实例到 Value
    • 2、从实例到 Type
    • 3、从 Type 到 Value
    • 4、从 Value 到 Type
    • 5、从 Value 到实例
    • 6、从 Value 的指针到值
    • 7、Type 指针和值的相互转换
    • 8、Value 值的可修改性
    • 9、根据 Go 官方关于反射的文档,反射有三大定律:9
  • 二、反射的使用
    • 1、已知原有类型
    • 2、未知原有类型
  • 总结

引言

反射是通过实体对象获取反射对象(Value、Type),然后可以操作相应的方法。在某些情况下,我们可能并不知道变量的具体类型,这时候就可以用反射来获取这个变量的类型或者方法。

一、反射的规则

其实反射的操作步骤非常的简单,就是通过实体对象获取反射对象(Value、Type),然后操作相应的方法即可。

下图描述了实例、Value、Type 三者之间的转换关系:

反射 API 的分类总结如下:

1、从实例到 Value

通过实例获取 Value 对象,直接使用 reflect.ValueOf() 函数。例如:

func ValueOf(i interface {}) Value

2、从实例到 Type

通过实例获取反射对象的 Type,直接使用 reflect.TypeOf() 函数。例如:

func TypeOf(i interface{}) Type

3、从 Type 到 Value

Type 里面只有类型信息,所以直接从一个 Type 接口变量里面是无法获得实例的 Value 的,但可以通过该 Type 构建一个新实例的 Value。reflect 包提供了两种方法,示例如下:

//New 返回的是一个 Value,该 Value 的 type 为 PtrTo(typ),即 Value 的 Type 是指定 typ 的指针类型
func New(typ Type) Value
//Zero 返回的是一个 typ 类型的零佳,注意返回的 Value 不能寻址,位不可改变
func Zero(typ Type) Value

如果知道一个类型值的底层存放地址,则还有一个函数是可以依据 type 和该地址值恢复出 Value 的。例如:

func NewAt(typ Type, p unsafe.Pointer) Value

4、从 Value 到 Type

从反射对象 Value 到 Type 可以直接调用 Value 的方法,因为 Value 内部存放着到 Type 类型的指针。例如:

func (v Value) Type() Type

5、从 Value 到实例

Value 本身就包含类型和值信息,reflect 提供了丰富的方法来实现从 Value 到实例的转换。例如:

//该方法最通用,用来将 Value 转换为空接口,该空接口内部存放具体类型实例
//可以使用接口类型查询去还原为具体的类型
func (v Value) Interface() (i interface{})

//Value 自身也提供丰富的方法,直接将 Value 转换为简单类型实例,如果类型不匹配,则直接引起 panic
func (v Value) Bool () bool
func (v Value) Float() float64
func (v Value) Int() int64
func (v Value) Uint() uint64

6、从 Value 的指针到值

从一个指针类型的 Value 获得值类型 Value 有两种方法,示例如下。

//如果 v 类型是接口,则 Elem() 返回接口绑定的实例的 Value,如采 v 类型是指针,则返回指针值的 Value,否则引起 panic
func (v Value) Elem() Value
//如果 v 是指针,则返回指针值的 Value,否则返回 v 自身,该函数不会引起 panic
func Indirect(v Value) Value

7、Type 指针和值的相互转换

指针类型 Type 到值类型 Type。例如:

//t 必须是 Array、Chan、Map、Ptr、Slice,否则会引起 panic
//Elem 返回的是其内部元素的 Type
t.Elem() Type

值类型 Type 到指针类型 Type。例如:

//PtrTo 返回的是指向 t 的指针型 Type
func PtrTo(t Type) Type

8、Value 值的可修改性

Value 值的修改涉及如下两个方法:

//通过 CanSet 判断是否能修改
func (v Value ) CanSet() bool
//通过 Set 进行修改
func (v Value ) Set(x Value)

Value 值在什么情况下可以修改?我们知道实例对象传递给接口的是一个完全的值拷贝,如果调用反射的方法 reflect.ValueOf() 传进去的是一个值类型变量, 则获得的 Value 实际上是原对象的一个副本,这个 Value 是无论如何也不能被修改的。

9、根据 Go 官方关于反射的文档,反射有三大定律:9

  • Reflection goes from interface value to reflection object.
  • Reflection goes from reflection object to interface value.
  • To modify a reflection object, the value must be settable.

第一条是最基本的:反射可以从接口值得到反射对象。

反射是一种检测存储在 interface中的类型和值机制。这可以通过 TypeOf函数和 ValueOf函数得到。

第二条实际上和第一条是相反的机制,反射可以从反射对象获得接口值。

它将 ValueOf的返回值通过 Interface()函数反向转变成 interface变量。

前两条就是说 接口型变量和 反射类型对象可以相互转化,反射类型对象实际上就是指的前面说的 reflect.Type和 reflect.Value。

第三条不太好懂:如果需要操作一个反射变量,则其值必须可以修改。

反射变量可设置的本质是它存储了原变量本身,这样对反射变量的操作,就会反映到原变量本身;反之,如果反射变量不能代表原变量,那么操作了反射变量,不会对原变量产生任何影响,这会给使用者带来疑惑。所以第二种情况在语言层面是不被允许的。

二、反射的使用

从relfect.Value中获取接口interface的信息

当执行reflect.ValueOf(interface)之后,就得到了一个类型为”relfect.Value”变量,可以通过它本身的Interface()方法获得接口变量的真实内容,然后可以通过类型判断进行转换,转换为原有真实类型。不过,我们可能是已知原有类型,也有可能是未知原有类型,因此,下面分两种情况进行说明。

1、已知原有类型

已知类型后转换为其对应的类型的做法如下,直接通过Interface方法然后强制转换,如下:

realValue := value.Interface().(已知的类型)

示例代码:

package main

import (
    "fmt"
    "reflect"
)

func main() {
    var num float64 = 3.1415926

    pointer := reflect.ValueOf(&num)
    value := reflect.ValueOf(num)

    // 可以理解为“强制转换”,但是需要注意的时候,转换的时候,如果转换的类型不完全符合,则直接panic
    // Golang 对类型要求非常严格,类型一定要完全符合
    // 如下两个,一个是*float64,一个是float64,如果弄混,则会panic
    convertPointer := pointer.Interface().(*float64)
    convertValue := value.Interface().(float64)

    fmt.Println(convertPointer)
    fmt.Println(convertValue)
}

运行结果:

0xc000018080
3.1415926

说明

  • 转换的时候,如果转换的类型不完全符合,则直接panic,类型要求非常严格!
  • 转换的时候,要区分是指针还是指
  • 也就是说反射可以将“反射类型对象”再重新转换为“接口类型变量”

2、未知原有类型

很多情况下,我们可能并不知道其具体类型,那么这个时候,该如何做呢?需要我们进行遍历探测其Filed来得知,示例如下:

package main

import (
    "fmt"
    "reflect"
)

type Person struct {
    Name string
    Age int
    Sex string
}

func (p Person)Say(msg string)  {
    fmt.Println("hello,",msg)
}
func (p Person)PrintInfo()  {
    fmt.Printf("姓名:%s,年龄:%d,性别:%s\n",p.Name,p.Age,p.Sex)
}

func main() {
    p1 := Person{"王富贵",20,"男"}

    DoFiledAndMethod(p1)

}

// 通过接口来获取任意参数
func DoFiledAndMethod(input interface{}) {

    getType := reflect.TypeOf(input) //先获取input的类型
    fmt.Println("get Type is :", getType.Name()) // Person
    fmt.Println("get Kind is : ", getType.Kind()) // struct

    getValue := reflect.ValueOf(input)
    fmt.Println("get all Fields is:", getValue) //{王富贵 20 男}

    // 获取方法字段
    // 1. 先获取interface的reflect.Type,然后通过NumField进行遍历
    // 2. 再通过reflect.Type的Field获取其Field
    // 3. 最后通过Field的Interface()得到对应的value
    for i := 0; i < getType.NumField(); i++ {
        field := getType.Field(i)
        value := getValue.Field(i).Interface() //获取第i个值
        fmt.Printf("字段名称:%s, 字段类型:%s, 字段数值:%v \n", field.Name, field.Type, value)
    }

    // 通过反射,操作方法
    // 1. 先获取interface的reflect.Type,然后通过.NumMethod进行遍历
    // 2. 再公国reflect.Type的Method获取其Method
    for i := 0; i < getType.NumMethod(); i++ {
        method := getType.Method(i)
        fmt.Printf("方法名称:%s, 方法类型:%v \n", method.Name, method.Type)
    }
}

运行结果:

get Type is : Person
get Kind is :  struct
get all Fields is: {王富贵 20 男}
字段名称:Name, 字段类型:string, 字段数值:王富贵 
字段名称:Age, 字段类型:int, 字段数值:20 
字段名称:Sex, 字段类型:string, 字段数值:男 
方法名称:PrintInfo, 方法类型:func(main.Person) 
方法名称:Say, 方法类型:func(main.Person, string)

总结

获取未知类型的interface的具体变量及其类型的步骤为:

  • 先获取interface的reflect.Type,然后通过NumField进行遍历
  • 再通过reflect.Type的Field获取其Field
  • 最后通过Field的Interface()得到对应的value

获取未知类型的interface的所属方法(函数)的步骤为:

  • 先获取interface的reflect.Type,然后通过NumMethod进行遍历
  • 再分别通过reflect.Type的Method获取对应的真实的方法(函数)
  • 最后对结果取其Name和Type得知具体的方法名
  • 也就是说反射可以将“反射类型对象”再重新转换为“接口类型变量”
  • struct 或者 struct 的嵌套都是一样的判断处理方式

以上就是Go利用反射reflect实现获取接口变量信息的详细内容,更多关于Go reflect获取接口信息的资料请关注我们其它相关文章!

(0)

相关推荐

  • golang 如何用反射reflect操作结构体

    背景 需要遍历结构体的所有field 对于exported的field, 动态set这个field的value 对于unexported的field, 通过强行取址的方法来获取该值(tricky?) 思路 下面的代码实现了从一个strct ptr对一个包外结构体进行取值的操作,这种场合在笔者需要用到反射的场合中出现比较多 simpleStrtuctField 函数接受一个结构体指针,因为最后希望改变其值,所以传参必须是指针.然后解引用. 接下来遍历结构体的每个field, exported字段是

  • Go语言reflect.TypeOf()和reflect.Type通过反射获取类型信息

    在 Go语言中通过调用 reflect.TypeOf 函数,我们可以从一个任何非接口类型的值创建一个 reflect.Type 值.reflect.Type 值表示着此非接口值的类型.通过此值,我们可以得到很多此非接口类型的信息.当然,我们也可以将一个接口值传递给一个 reflect.TypeOf 函数调用,但是此调用将返回一个表示着此接口值的动态类型的 reflect.Type 值. 实际上,reflect.TypeOf 函数的唯一参数的类型为 interface{},reflect.Type

  • 深入理解Golang的反射reflect示例

    目录 编程语言中反射的概念 interface 和 反射 Golang的反射reflect reflect的基本功能TypeOf和ValueOf 说明 从relfect.Value中获取接口interface的信息 已知原有类型[进行“强制转换”] 说明 未知原有类型[遍历探测其Filed] 说明 通过reflect.Value设置实际变量的值 说明 通过reflect.ValueOf来进行方法的调用 说明 Golang的反射reflect性能 小结 总结 参考链接 编程语言中反射的概念 在计算

  • 详解Golang利用反射reflect动态调用方法

    编程语言中反射的概念 在计算机科学领域,反射是指一类应用,它们能够自描述和自控制.也就是说,这类应用通过采用某种机制来实现对自己行为的描述(self-representation)和监测(examination),并能根据自身行为的状态和结果,调整或修改应用所描述行为的状态和相关的语义. 每种语言的反射模型都不同,并且有些语言根本不支持反射.Golang语言实现了反射,反射机制就是在运行时动态的调用对象的方法和属性,官方自带的reflect包就是反射相关的,只要包含这个包就可以使用. 多插一句,

  • Go利用反射reflect实现获取接口变量信息

    目录 引言 一.反射的规则 1.从实例到 Value 2.从实例到 Type 3.从 Type 到 Value 4.从 Value 到 Type 5.从 Value 到实例 6.从 Value 的指针到值 7.Type 指针和值的相互转换 8.Value 值的可修改性 9.根据 Go 官方关于反射的文档,反射有三大定律:9 二.反射的使用 1.已知原有类型 2.未知原有类型 总结 引言 反射是通过实体对象获取反射对象(Value.Type),然后可以操作相应的方法.在某些情况下,我们可能并不知道

  • 利用百度地图API获取当前位置信息的实例

    利用百度地图API可以做很多事情,个人感觉最核心也是最基础的就是定位功能了.这里分享一个制作的JS可以实现登录网页后定位: <script type="text/javascript"> var map; var gpsPoint; var baiduPoint; var gpsAddress; var baiduAddress; var x; var y; function getLocation() { //根据IP获取城市 var myCity = new BMap.

  • java反射之获取类的信息方法(推荐)

    本文接上文"老生常谈反射之Class类的使用(必看篇)",以编写一个用来获取类的信息(成员函数.成员变量.构造函数)的工具类来讲解"反射之获取类的信息" 1.获取成员函数信息 /** * 获取成员函数信息 * @param obj */ public static void printClassMethodMessage(Object obj){ //获取类的信息 获取类的类类型 Class c = obj.getClass(); //获取类的名称 System.o

  • java 利用反射获取内部类静态成员变量的值操作

    昨晚,一同事问到我,怎么利用java反射解析内部类静态成员变量的值,于是顺手写下了. 废话不多说,直接上代码! 待解析类结构如下: /** * @Author changle * @Time 17/6/13. * @Desc to do */ public class Goods { static class apple{ public static String version = "iphone6s[是手机不是吃的苹果]"; public static String date =

  • 利用反射获取Java类中的静态变量名及变量值的简单实例

    JAVA可以通过反射获取成员变量和静态变量的名称,局部变量就不太可能拿到了. public class Test { public static void main(String[] args) throws Exception { // TODO Auto-generated method stub //获取所有变量的值 Class clazz = Class.forName("com.qianmingxs.ScoreTable"); Field[] fields = clazz.g

  • c#反射机制学习和利用反射获取类型信息

    1..NET可执行应用程序结构 程序代码在编译后生成可执行的应用,我们首先要了解这种可执行应用程序的结构. 应用程序结构分为应用程序域-程序集-模块-类型-成员几个层次,公共语言运行库加载器管理应用程序域,这种管理包括将每个程序集加载到相应的应用程序域以及控制每个程序集中类型层次结构的内存布局. 程序集包含模块,而模块包含类型,类型又包含成员,反射则提供了封装程序集.模块和类型的对象.我们可以使用反射动态地创建类型的实例,将类型绑定到现有对象或从现有对象中获取类型,然后调用类型的方法或访问其字段

  • Java利用反射获取object的属性和值代码示例

    在看反射顺便做个笔记,目前知道的反射的Object都是要有对象的也就是实体Bean. referance:Java反射简易教程 import java.lang.reflect.Field; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * 反射处理Bean,得到里面的属性值 * * @author liulinsen * */ publ

  • asp.net实现利用反射,泛型,静态方法快速获取表单值到Model的方法

    本文实例讲述了asp.net实现利用反射,泛型,静态方法快速获取表单值到Model的方法.分享给大家供大家参考,具体如下: 这是初级的,很简单,牛人可以不看了.不过还算实用. 在项目中经常需要处理表单,给model赋值,很烦人的一些重复代码.如下边的代码: News news = new News(); news.Id = int.Parse(Request.Form["Id"]); news.Category = int.Parse(Request.Form["Catego

  • C#使用反射(Reflect)获取dll文件中的类型并调用方法

    使用反射(Reflect)获取dll文件中的类型并调用方法,具体内容如下 需引用:System.Reflection; 1. 使用反射(Reflect)获取dll文件中的类型并调用方法(入门案例) static void Main(string[] args) { //dll文件路径 string path = @"D:\VS2015Project\001\Computer\bin\Debug\computer.dll"; //加载dll文件 Assembly asm = Assemb

随机推荐