Golang之反射reflect包

反射规则

在计算机科学领域,反射是指一类应用,它们能够自描述和自控制。也就是说,这类应用通过采用某种机制来实现对自己行为的描述(self-representation)和监测(examination),并能根据自身行为的状态和结果,调整或修改应用所描述行为的状态和相关的语义。

每个语言的反射模型都不同(同时许多语言根本不支持反射)。
Go语言实现了反射,所谓反射就是动态运行时的状态。
我们用到的包是reflect。

从接口值到反射对象的反射

反射仅是一种用来检测存储在接口变量内部(值value,实例类型concret type)pair对的一种机制。
在reflect反射包中有两种类型让我们可以访问接口变量内容

  • reflect.ValueOf()
// ValueOf returns a new Value initialized to the concrete value
// stored in the interface i. ValueOf(nil) returns the zero Value.
func ValueOf(i interface{}) Value {...}
  • reflect.TypeOf()
// TypeOf returns the reflection Type that represents the dynamic type of i.
// If i is a nil interface value,TypeOf returns nil.
func TypeOf(i interface{}) Type {...}

一个简单的案例:

package main

import (
    "reflect"
    "fmt"
)

type ControllerInterface interface {
    Init(action string,method string)
}

type Controller struct {
    Action string
    Method string
    Tag string `json:"tag"`
}

func (c *Controller) Init(action string,method string){
    c.Action = action
    c.Method = method
}


func main(){
    //初始化
    runController := &Controller{
        Action:"Run1",Method:"GET",}

    //Controller实现了ControllerInterface方法,因此它就实现了ControllerInterface接口
    var i ControllerInterface
    i = runController

    // 得到实际的值,通过v我们获取存储在里面的值,还可以去改变值
    v := reflect.ValueOf(i)
    fmt.Println("value:",v)

    // 得到类型的元数据,通过t我们能获取类型定义里面的所有元素
    t := reflect.TypeOf(i)
    fmt.Println("type:",t)

    // 转化为reflect对象之后我们就可以进行一些操作了,也就是将reflect对象转化成相应的值
    ......

}

打印结果:
value: &{Run1 GET}
type: *main.Controller

  • v 是i接口为指向main包下struct Controller的指针(runController)目前的所存储值
  • t 是i接口为指向main包下struct Controller的指针类型。

转换reflect对象之后操作

接口i所指向的对象的值操作

// Elem返回值v包含的接口
controllerValue := v.Elem() 
fmt.Println("controllerType(reflect.Value):",controllerType)
//获取存储在第一个字段里面的值
fmt.Println("Action:",controllerValue.Field(0).String())

controllerType(reflect.Value): {Run1 GET }
Action: Run1

接口i所指向的对象的类型操作

获取定义在struct里面的标签

// Elem返回类型的元素类型。
controllerType := t.Elem()  
tag := controllerType.Field(2).Tag //Field(第几个字段,index从0开始)
fmt.Println("Tag:",tag)

Tag: json:”tag”

通过reflect.TypeOf(i)获取对象方法的信息

method,_ := t.MethodByName("Init")
fmt.Println(method)
打印结果:
{Init  func(*main.Controller,string,string) <func(*main.Controller,string) Value> 0}

它返回了一个对象方法的信息集合即Method,关于Method结构定义在reflect/type.go下具体为:

// Method represents a single method.
type Method struct {
    // Name is the method name.
    // PkgPath is the package path that qualifies a lower case (unexported)
    // method name. It is empty for upper case (exported) method names.
    // The combination of PkgPath and Name uniquely identifies a method
    // in a method set.
    // See http://golang.org/ref/spec#Uniqueness_of_identifiers
    Name    string
    PkgPath string

    Type  Type  // method type
    Func  Value // func with receiver as first argument
    Index int   // index for Type.Method
}

通过reflect.ValueOf(i)获取对象方法的信息

vMethod := v.MethodByName("Init")
fmt.Println(vMethod)

通过reflect.ValueOf(i)调用方法

package main
import (
    "reflect"
    "fmt"
)
type ControllerInterface interface {
    Init(action string,method string)
}
type Controller struct {
    Action string
    Method string
    Tag string `json:"tag"`
}
func (c *Controller) Init(action string,method string){
    c.Action = action
    c.Method = method
    //增加fmt打印,便于看是否调用
    fmt.Println("Init() is run.")
    fmt.Println("c:",c)
}
//增加一个无参数的Func
func (c *Controller) Test(){
    fmt.Println("Test() is run.")
}

func main(){
    //初始化
    runController := &Controller{
        Action:"Run1",v)

    // 有输入参数的方法调用
    // 构造输入参数
    args1 := []reflect.Value{reflect.ValueOf("Run2"),reflect.ValueOf("POST")}
    // 通过v进行调用
    v.MethodByName("Init").Call(args1)

    // 无输入参数的方法调用
    // 构造zero value
    args2 := make([]reflect.Value, 0)
    // 通过v进行调用
    v.MethodByName("Test").Call(args2)

}
打印结果:
value: &{Run1 GET }
//有参数的Func
Init() is run.
c: &{Run2 POST }
//无参数的Func
Test() is run.

从反射对象到接口值的反射

修改反射对象

最近在写ATC框架,路由第一版构思中用到了反射就整理了下,不过经过几个莫名夜晚纠结,最终第二版构思实践中又把所有反射干掉了(泪…,讨论群中一致认为反射对性能影响较大,能避免则避免,不能避免就少用。),目前思路和框架已定型,一个轻量级简单的RESTful go 框架 即将发布…

不早了,先到这里吧,米西米西了。空了在补充,还有很多反射内容待进一步完善……

有空的话就把beego框架中用到的反射知识整理分享下,利己利人。

参考资料

示例代码:
https://github.com/lxmgo/learngo/blob/master/reflect/reflect.go

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


类型转换 1、int转string 2、string转int 3、string转float 4、用户结构类型转换
package main import s &quot;strings&quot; import &quot;fmt&quot; var p = fmt.Println func main() { p(&quot;Contains: &quot;, s.Contains(&quot;test&quo
类使用:实现一个people中有一个sayhi的方法调用功能,代码如下: 接口使用:实现上面功能,代码如下:
html代码: beego代码:
1、读取文件信息: 2、读取文件夹下的所有文件: 3、写入文件信息 4、删除文件,成功返回true,失败返回false
配置环境:Windows7+推荐IDE:LiteIDEGO下载地址:http://www.golangtc.com/downloadBeego开发文档地址:http://beego.me/docs/intro/ 安装步骤: 一、GO环境安装 二、配置系统变量 三、Beego安装 一、GO环境安装 根
golang获取程序运行路径:
Golang的文档和社区资源:为什么它可以帮助开发人员快速上手?
Golang:AI 开发者的实用工具
Golang的标准库:为什么它可以大幅度提高开发效率?
Golang的部署和运维:如何将应用程序部署到生产环境中?
高性能AI开发:Golang的优势所在
本篇文章和大家了解一下go语言开发优雅得关闭协程的方法。有一定的参考价值,有需要的朋友可以参考一下,希望对大家有所帮助。1.简介本文将介绍首先为什么需要主...
这篇文章主要介绍了Go关闭goroutine协程的方法,具有一定借鉴价值,需要的朋友可以参考下。下面就和我一起来看看吧。1.简介本文将介绍首先为什么需要主动关闭gor...
本篇文章和大家了解一下go关闭GracefulShutdown服务的几种方法。有一定的参考价值,有需要的朋友可以参考一下,希望对大家有所帮助。目录Shutdown方法Regi...
这篇文章主要介绍了Go语言如何实现LRU算法的核心思想和实现过程,具有一定借鉴价值,需要的朋友可以参考下。下面就和我一起来看看吧。GO实现Redis的LRU例子常
今天小编给大家分享的是Go简单实现多租户数据库隔离的方法,相信很多人都不太了解,为了让大家更加了解,所以给大家总结了以下内容,一起往下看吧。一定会...
这篇“Linux系统中怎么安装NSQ的Go语言客户端”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希
本文小编为大家详细介绍“怎么在Go语言中实现锁机制”,内容详细,步骤清晰,细节处理妥当,希望这篇“怎么在Go语言中实现锁机制”文章能帮助大家解决疑惑,下面...
今天小编给大家分享一下Go语言中interface类型怎么使用的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考