深入了解Golang中reflect反射的使用

目录
  • 1. 介绍
  • 2. 方法示例
    • 2.1 通过反射获取对象的键(类型)和值
    • 2.2 反射对象的类型和属性
  • 3. 反射对Json的操作
    • 3.1 反射与Json属性解析
    • 3.2 Json包的序列化与反序列化
  • 4. 实战巩固
    • 4.1 需求
    • 4.2 代码实现

1. 介绍

在反射的世界里,我们拥有了获取一个对象的类型,属性及方法的能力。

在 Go 反射的世界里,有两种类型非常重要,是整个反射的核心,在学习 reflect 包的使用时,先得学习下这两种类型:

  • reflect.Type
  • reflect.Value

2. 方法示例

2.1 通过反射获取对象的键(类型)和值

package main
import (
   "fmt"
   "reflect"
)
//
func reflectNum(arg interface{})  {
   fmt.Println("type:",reflect.TypeOf(arg))
   fmt.Println("value : ",reflect.ValueOf(arg))
}
func main() {
   reflectNum(1.1234)
}

type: float64
value : 1.1234

注意,Go 中所有对象都是键+值组成的,含有这种特性的对象都是可以用 Type + Value 进行抽象的,这就是万能对象

2.2 反射对象的类型和属性

package main
import (
   "fmt"
   "reflect"
)
type User struct {
   Id int
   Name string
   Age int
}
func (this User) Call()  {
   fmt.Println("user is called ..")
   fmt.Printf("#{this}\n")
}
func main() {
   user := User{1,"Aceld",18}
   //传入 user 对象
   DoFiledAndMethod(user)
}
//接收万能对象 用反射解析
func DoFiledAndMethod(input interface{})  {
   //反射获取类的类型
   inputType := reflect.TypeOf(input)
   fmt.Println("inputType is :", inputType.Name() )
  // inputType is : User
   inputValue := reflect.ValueOf(input)
   fmt.Println("inputValue is :", inputValue)
  //inputValue is : {1 Aceld 18}
   //反射获取类的属性
   for i := 0; i < inputType.NumField(); i++ {
      field := inputType.Field(i)
      value := inputValue.Field(i).Interface()
      fmt.Printf("%s : %v = %v\n",field.Name,field.Type,value)
      //Id : int = 1
      //Name : string = Aceld
      //Age : int = 18
   }
   //反射调用方法
   for i := 0; i < inputType.NumMethod(); i++ {
      m := inputType.Method(i)
      //%s 输出字符串表示(string类型或[]byte)
      //%v  相应值的默认格式。
      fmt.Printf("%s:%v\n",m.Name,m.Type)
     //Call:func(main.User)
   }
}

3. 反射对Json的操作

3.1 反射与Json属性解析

在结构体属性中 我们可以通过加 tag 以供其他程序的解析与识别

package main
import (
   "fmt"
   "reflect"
)
type resume struct {
   // ':'前后不加空格  ` != '
   Name string `info:"name"  doc:"我的名字" `
   Sex string `info:"sex" `
}
func findTag(str interface{})  {
   // Elem returns a type's element type.
   t := reflect.TypeOf(str).Elem()
  //遍历接收对象的属性
   for i := 0; i < t.NumField(); i++ {
      taginfo := t.Field(i).Tag.Get("info")
      tagdoc := t.Field(i).Tag.Get("doc")
      fmt.Println("info:",taginfo,"doc:",tagdoc)
   }
}
func main() {
   var re resume
   findTag(&re)
}
输出:
  info: name doc: 我的名字
  info: sex doc:

3.2 Json包的序列化与反序列化

go 的 json 工具要想解析 需要你在字段上添加 json tag

package main
import (
   "encoding/json"
   "fmt"
)
// 全部添加 json 的 tag
type Movie struct {
   Title string `json:"title"`
   Year int `json:"year"`
   Price int `json:"rmb"`
   Actors []string `json:"actors"`
}
func main() {
   movie := Movie{"喜剧之王", 2000, 10, []string{"zhouxingchi", "wumengda"}}
   // 序列化
   jsonStr, err := json.Marshal(movie)
   if err != nil {
      fmt.Println("json marshal error",err)
      return
   }
   fmt.Printf("jsonStr = %s\n",jsonStr)
   //反序列化
   m := Movie{}
   err = json.Unmarshal(jsonStr, &m)
   if err != nil {
      fmt.Println("json ummarshal error", err)
      return
   }
   fmt.Printf("%v\n",m)
}

4. 实战巩固

4.1 需求

​ 项目经常会在启动的时候会加一些环境变量参数,随着启动而触发作用加载到项目中,现如今在。利用os包设置好环境变量,利用反射实现项目启动时运行时加载进环境!

环境变量Key CONFIG_SERVER_NAME CONFIG_SERVER_IP CONFIG_SERVER_URL

4.2 代码实现

package main
import (
	"fmt"
	"os"
	"reflect"
	"strings"
)
type Config struct {
	Name    string `json:"server-name"`
	IP      string `json:"server-ip"`
	URL     string `json:"server-url"`
	Timeout string `json:"timeout"`
}
func readConfig() *Config {
	// read from xxx.json,省略
	config := Config{}
	// 1. 拿到config的反射类型
	typ := reflect.TypeOf(config)
	// 2. 拿到config的反射值对象
	value := reflect.Indirect(reflect.ValueOf(&config))
	// 3. 遍历每个字段
	for i := 0; i < typ.NumField(); i++ {
		// 3.1 拿到反射的config的第i个字段
		f := typ.Field(i)
		// 3.2 拿到赋值了json的字段tag
		if v, ok := f.Tag.Lookup("json"); ok {
			// 3.3 拼接系统环境变量Key
			key := fmt.Sprintf("CONFIG_%s", strings.ReplaceAll(strings.ToUpper(v), "-", "_"))
			// 3.4 从系统中读取环境变量对应的值
			if env, exist := os.LookupEnv(key); exist {
				// 3.5 将值设置到给定名称的字段上
				value.FieldByName(f.Name).Set(reflect.ValueOf(env))
			}
		}
	}
	return &config
}
func main() {
	os.Setenv("CONFIG_SERVER_NAME", "global_server")
	os.Setenv("CONFIG_SERVER_IP", "10.0.0.1")
	os.Setenv("CONFIG_SERVER_URL", "geektutu.com")
	c := readConfig()
	fmt.Printf("%+v", c)
}

到此这篇关于深入了解Golang中reflect反射的使用的文章就介绍到这了,更多相关Go reflect反射内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

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

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

  • Golang反射模块reflect使用方式示例详解

    Golang的反射功能,在很多场景都会用到,最基础的莫过于rpc.orm跟json的编解码,更复杂的可能会到做另外一门语言的虚拟机.通过反射模块,我们可以在编程语言的runtime运行时期间去访问内部产生对象的信息.了解反射模块的实现,对我们了解Golang对象机制本身,也是莫大的帮助. 今天,恰逢阳康+新年,就决定来探究一下Golang的反射模块——reflect. 从最基础的开始,reflect模块,以获取整数对象的类型信息为例,我们可以这么用: func TestReflect_Integ

  • 深入理解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包就是反射相关的,只要包含这个包就可以使用. 多插一句,

  • 一文带你了解Golang中reflect反射的常见错误

    目录 获取 Value 的值之前没有判断类型 没有传递指针给 reflect.ValueOf 在一个无效的 Value 上操作 什么时候 IsValid 返回 false 其他情况下 IsValid 返回 false 通过反射修改不可修改的值 在错误的 Value 上调用 Elem 方法 调用了一个其类型不能调用的方法 总结 go 的反射是很脆弱的,保证反射代码正确运行的前提是,在调用反射对象的方法之前, 先问一下自己正在调用的方法是不是适合于所有用于创建反射对象的原始类型. go 反射的错误大

  • 关于golang中map使用的几点注意事项总结(强烈推荐!)

    目录 前言 1 使用 map 记得初始化 2 map 的遍历是无序的 3 map 也可以是二维的 4 获取 map 的 key 最好使用这种方式 5 map 是并发不安全的 ,sync.Map 才是安全的 总结 前言 日常的开发工作中,map 这个数据结构相信大家并不陌生,在 golang 里面,当然也有 map 这种类型 关于 map 的使用,还是有蛮多注意事项的,如果不清楚,这些事项,关键时候可能会踩坑,我们一起来演练一下吧 1 使用 map 记得初始化 写一个 demo 定义一个 map[

  • Golang 中反射的应用实例详解

    目录 引言 Golang类型设计原则 Golang 中为什么要使用反射/什么场景可以(应该)使用反射 举例场景: 反射的基本用法 反射的性能分析与优缺点 测试反射结构体初始化 测试结构体字段读取/赋值 测试结构体方法调用 优缺点 反射在 okr 中的简单应用 结论 引言 首先来一段简单的代码逻辑热身,下面的代码大家觉得应该会打印什么呢? type OKR struct { id int content string } func getOkrDetail(ctx context.Context,

  • Golang中反射的常见用法分享

    目录 根据类型做不同处理 标准库 json 中的示例 基本类型的反射 数组类型的反射 chan 反射 map 反射 迭代反射 map 对象 slice 反射 string 反射 interface/Pointer 反射 结构体的反射 遍历结构体字段 根据名称或索引获取结构体字段 修改结构体字段 结构体方法调用 是否实现接口 结构体的 tag 修改结构体未导字段 方法的反射 入参和返回值 通过反射调用方法 总结 在之前的两篇文章 <深入理解 go reflect - 反射基本原理>.<深入

  • Golang学习之反射机制的用法详解

    目录 介绍 TypeOf() ValueOf() 获取接口变量信息 事先知道原有类型的时候 事先不知道原有类型的时候 介绍 反射的本质就是在程序运行的时候,获取对象的类型信息和内存结构,反射是把双刃剑,功能强大但可读性差,反射代码无法在编译阶段静态发现错误,反射的代码常常比正常代码效率低1~2个数量级,如果在关键位置使用反射会直接导致代码效率问题,所以,如非必要,不建议使用. 静态类型是指在编译的时候就能确定的类型(常见的变量声明类型都是静态类型):动态类型是指在运行的时候才能确定的类型(比如接

  • Golang泛型与反射的应用详解

    目录 1. 泛型 1.1 定义 1.2 例子 1.3 自定义泛型类型 1.4 泛型与switch结合使用 1.5 泛型实战 2. 反射 2.1 定义 2.2 方法 2.3 反射读取 2.4 反射操作 2.5 判断 1. 泛型 1.1 定义 泛型生命周期只在编译期,旨在为程序员生成代码,减少重复代码的编写 在比较两个数的大小时,没有泛型的时候,仅仅只是传入类型不一样,我们就要再写一份一模一样的函数,如果有了泛型就可以减少这类代码 1.2 例子 // SumInts 将map的值相加,如果需要添加的

  • 详解如何让Go语言中的反射加快

    目录 切入点案例 反射基本版 优化一:加入缓存策略 优化二:利用字段偏移量 优化三:更改缓存 key 类型 优化四:引入描述符 总结 最近读到一篇关于 Go 反射的文章,作者通过反射给结构体填充字段值的案例,充分利用 Go 的各种内在机理,逐步探讨让代码运行得更快的姿势. 文章(原文地址:https://philpearl.github.io/post/aintnecessarilyslow/)非常有学习价值,故翻译整理了下来. 不要使用反射,除非你真的需要.但是当你不使用反射时,不要认为这是因

  • 实例讲解Java编程中数组反射的使用方法

    什么是反射 "反射(Reflection)能够让运行于JVM中的程序检测和修改运行时的行为."这个概念常常会和内省(Introspection)混淆,以下是这两个术语在Wikipedia中的解释: 内省用于在运行时检测某个对象的类型和其包含的属性: 反射用于在运行时检测和修改某个对象的结构及其行为. 从它们的定义可以看出,内省是反射的一个子集.有些语言支持内省,但并不支持反射,如C++. 内省示例:instanceof 运算符用于检测某个对象是否属于特定的类. if (obj inst

  • Java中的反射机制详解

    Java中的反射机制详解 反射,当时经常听他们说,自己也看过一些资料,也可能在设计模式中使用过,但是感觉对它没有一个较深入的了解,这次重新学习了一下,感觉还行吧! 一,先看一下反射的概念: 主要是指程序可以访问,检测和修改它本身状态或行为的一种能力,并能根据自身行为的状态和结果,调整或修改应用所描述行为的状态和相关的语义. 反射是Java中一种强大的工具,能够使我们很方便的创建灵活的代码,这些代码可以再运行时装配,无需在组件之间进行源代码链接.但是反射使用不当会成本很高! 看概念很晕的,继续往下

  • java中利用反射调用另一类的private方法的简单实例

    我们知道,Java应用程序不能访问持久化类的private方法,但Hibernate没有这个限制,它能够访问各种级别的方法,如private, default, protected, public. Hibernate是如何实现该功能的呢?答案是利用JAVA的反射机制,如下: import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class ReflectDemo {

  • Go语言中使用反射的方法

    本文实例讲述了Go语言中使用反射的方法.分享给大家供大家参考.具体实现方法如下: 复制代码 代码如下: // Data Model type Dish struct {   Id  int   Name string   Origin string   Query func() } 创建实例如下: 复制代码 代码如下: shabushabu = Dish.new shabushabu.instance_variables # => [] shabushabu.name = "Shabu-S

随机推荐