Golang语言学习拿捏Go反射示例教程

目录
  • 1. 反射简介
    • 1.1 反射是什么?
    • 1.2 为什么需要反射?
  • 2. reflect包
    • 2.1 基本反射
    • 2.2 反射与指针
    • 2.3 反射与对象
    • 2.4 反射与函数
    • 2.5 反射例子
  • 3. 总结

1. 反射简介

1.1 反射是什么?

Go语言提供了一种机制在运行时更新和检查变量的值、调用变量的方法和变量支持的内在操作,但是在编译时并不知道这些变量的具体类型,这种机制被称为反射。反射也可以让我们将类型本身作为第一类的值类型处理。

反射是指在程序运行期对程序本身进行访问和修改的能力,程序在编译时变量被转换为内存地址,变量名不会被编译器写入到可执行部分,在运行程序时程序无法获取自身的信息。

举个例子
平时我们定义变量都是正射

var a int

将变量a定义成一个int类型

现在我并不知道变量a是什么类型,但是我可以通过反射也知晓变量a是什么来历!是什么类型!

type FanOne struct {
	name string
}
func main(){
	var a int = 1
	var d FanOne
	fmt.Println(reflect.TypeOf(a))  		  // int
	// 这里就拿到了a的类型!注意是类型!不是类别!虽然这个类型和类别是一样的
	// 后面会说说类型(Type)和类别(Kind)的区别
	fmt.Println(reflect.ValueOf(a).Kind())    //int
	//这样就拿到了a的类别,是通过a的值来判断类别
	fmt.Println(reflect.TypeOf(d))			  //main.FanOne
	//类型是main.FanOne  是在main里面定义的FanOne
	fmt.Println(reflect.ValueOf(d).Kind())    //struct
	//类别是struct
	// 输出 d 的类型名称和种类,类型名称就是 FanOne
	//而 FanOne 属于一种结构体类别,因此类别为 struct
}

所以这个类别和类型有时候相同,有时候不同。

1.2 为什么需要反射?

在开发当中,当我们对于某一个函数进行值的处理的时候,但是为了保证这个函数能接受更多类型的值,因为go是强类型的语言,虽然interface可以接受所有的数据类型,但是在处理数据的时候,要对不同类型进行不同的处理的时候就会显得代码十分冗余,于是我们可以使用反射来进行对传入参数的判断与处理。

详细见例题

2. reflect包

2.1 基本反射

reflect.TypeOf()			 //获取变量的类型,返回reflect.Type类型
reflect.ValueOf()			 //获取变量的值,返回reflect.Value类型
reflect.Value.Kind()		 //获取变量的类别,返回一个常量
reflect.Value.Interface()	 //转换成interface{}类型

2.2 反射与指针

Go语言程序中对指针获取反射对象时,可以通过 reflect.Elem() 方法获取这个指针指向的元素类型,这个获取过程被称为取元素,等效于对指针类型变量做了一个*操作

reflect.ValueOf(xxx).Elem()

2.3 反射与对象

可以通过reflect.new(xxx)或是reflect.zero(xxx)来进行反射,创建原始类型的对象

func CreatePrimitiveObjects(t reflect.Type) reflect.Value {
    return reflect.Zero(t)
}

也可以使用

reflect.New()

来进行创建原始对象。

2.4 反射与函数

如果反射值对象(reflect.Value)中值的类型为函数时,可以通过reflect.Value调用该函数。使用反射调用函数时,需要将参数使用反射值对象的切片[]reflect.Value构造后传入Call()方法中,调用完成时,函数的返回值通过[]reflect.Value返回。
在反射中 函数 和 方法 的类型(Type)都是 reflect.Func,如果要调用函数的话,可以通过 Value 的 Call() 方法,例如:

package main

import (
	"fmt"
	"reflect"
)

func FanOne() string {
	return "一键三连"
}

func FanOneWoW(a string) string {
	return fmt.Sprintf("%s要给FanOne一键三连噢~",a)
}

func main() {
	FanOneNotArgs := reflect.ValueOf(FanOne).Call([]reflect.Value{})		 //无参数
	FanOneHaveArgs := reflect.ValueOf(FanOneWoW).Call([]reflect.Value{reflect.ValueOf("我")})  //有参数
	fmt.Println(FanOneNotArgs[0])
	fmt.Println(FanOneHaveArgs[0])
}

2.5 反射例子

填写fn函数使得输出为

要求不使用任何的switch 或是 if 或是其他选择语句。

func fn(callback interface{}, bytes []byte) {
		//coding
}
type aaa struct {
	Name string `json:"name"`
	Age  int    `json:"age"`
}
func Test(t *testing.T) {
		fn(func(a []*aaa) string {
		aaas := a
		for i, item := range aaas {
			fmt.Println(i, item)
		}
		fmt.Println("12312312, ", aaas)
		return "xxxx"
	}, []byte("[{\"name\":\"111\",\"age\":1}, {\"name\":\"gsjk\",\"age\":2}]"))

	fn(func(a []aaa) string {
		aaas := a
		for i, item := range aaas {
			fmt.Println(i, item)
		}

		fmt.Println("12312312, ", aaas[0])
		return "xxxx"
	}, []byte("[{\"name\":\"111\",\"age\":1}, {\"name\":\"gsjk\",\"age\":2}]"))

	fn(func(a *aaa) string {
		fmt.Println("12312312, ", a)
		aaas := a
		fmt.Println("12312312, ", aaas)
		return "xxxx"
	}, []byte("{\"name\":\"gsjk\",\"age\":2}"))

	fn(func(a string) string {
		fmt.Println("12312312, ", a)
		aaas := a
		fmt.Println("12312312, ", aaas)
		return "xxxx"
	}, []byte("\"sss\""))

	fn(func(a int) string {
		fmt.Println("-----------, ", a)
		aaas := a
		fmt.Println("-----------, ", aaas)
		return "xxxx"
	}, []byte("123"))
}

(1)首先是test的知识:
名称一定要有_test,不然好像会报错,我就是这样。

go test xxx_test.go
go test -v xxx_test.go

(2)其次是了解这个fn()里面的匿名函数
单独拿出来

func(a []*aaa) string {
		aaas := a
		for i, item := range aaas {
			fmt.Println(i, item)
		}
		fmt.Println("12312312, ", aaas)
		return "xxxx"
	}, []byte("[{\"name\":\"111\",\"age\":1}, {\"name\":\"gsjk\",\"age\":2}]"))

可以看到这是一个*aaa类型的数组。那么我们任务就是反射出fn这个函数里面的匿名函数,然后调用反射出来的这个匿名函数,并将参数传入其中。

以下都是用第一个作为例子

(3)那么我们先ValueOf和TypeOf这个interface{},然后再看这个匿名函数各种的值

func fn(callback interface{}, bytes []byte) {
	v := reflect.ValueOf(callback)  //0xbaff40
	t := reflect.TypeOf(callback)  //func([]*main.aaa) string
}

我们可以看到入参的函数的Type是func([]*main.aaa) string 所以我们可以用

paramsValue := t.In(0)  //[]*main.aaa

拿到匿名函数的传入参数

(4)重点!!
我们拿到的这个paramsValue只是[]*main.aaa名称,这个值是reflect.type 类型的!!、
我们要的是[]*main.aaa这个类型,而不是要这个名称!
所以我们要创建这个类型的对象,然后转成相应的类型

	val := reflect.New(paramsValue)
	newT := val.Interface()
	fmt.Printf("valValue:%v , valType: %T \n",val,val)    //valValue:&[] , valType: reflect.Value
	fmt.Printf("newTValue:%v , newTType: %T \n",newT,newT)//newTValue:&[] , newTType: *[]*main.aaa

我们要创建这样一个类别的对象,虽然go并不是面向对象的编程,但是这里可以这样理解。

为什么要这个类型呢?

因为后面把bytes切片反序列化成这个类型的变量,传入这个匿名函数中!

	if v.IsValid() {                       			//function valid or not
		_ = json.Unmarshal(bytes, newT)               //byte to json
	}

那么问题又来了,传入的值的类型是[]*main.aaa 但是我们拿到了*[]*main.aaa这个类型,很明显是不对的。

	fmt.Printf("调用 callback 结束 callback ret = %s \n", v.Call([]reflect.Value{reflect.ValueOf(newT)}))
	fmt.Printf("*************************\n")

报错了!类型不对!

那么我们就要进行去*操作。在反射中,并不是直接加*去除!下面这样在反射中是不行的。

package main

import (
  "fmt"
  "reflect"
)

func main(){
  var a int = 1
  var b *int = &a
  var c **int = &b
  fmt.Println(a, *b, c)
  fmt.Println(reflect.TypeOf(a))
  fmt.Println(reflect.TypeOf(*b))
  fmt.Println(reflect.TypeOf(b))
}

那么我们可以用reflect.Elem() 将这个去除*

	fmt.Printf("调用 callback 结束 callback ret = %s \n", v.Call([]reflect.Value{reflect.ValueOf(newT).Elem()}))
	fmt.Printf("*************************\n")

大功告成了!

3. 总结

以前我是很少使用反射的,基本在项目中就没用过,但是暑期实习的时候,第一个任务就是写反射接口,那么就疯狂补这方面的知识,反射对于我来说,确实有点难理解,花了我两天时间才做出来。

原来我的想法是用if判断类型的,或是断言然后用switch判断类型的,但是这样实用性不高,换了一个名称的话就要改代码了。比如现在是结构体aaa,换成bbb就不管用了。

现在这种的话,直接将这个类型的反射成一个对象,然后再对这个对象进行赋值操作,就更加灵活!

学到了!

实习很痛苦!但是学到了很多新知识!还有好多大佬带!还有工资拿!也舒服!

以上就是Golang语言学习拿捏Go反射示例教程的详细内容,更多关于Go反射教程的资料请关注我们其它相关文章!

(0)

相关推荐

  • golang之反射和断言的具体使用

    1. 反射 反射这个概念绝大多数语言都有,比如Java,PHP之类,golang自然也不例外,反射其实程序能够自描述和自控制的一类机制. 比如,通过PHP的反射,你可以知道一个类有什么成员,有什么方法.而golang,也能够通过官方自带的reflect包来了解各种变量类型及其信息. 下面我们通过一个例子查看反射的基本用法. 话不多说,直接贴代码: package main import ( "fmt" "reflect" ) type Order struct {

  • Go系列教程之反射的用法

    反射是 Go 语言的高级主题之一.我会尽可能让它变得简单易懂. 本教程分为如下小节. 什么是反射? 为何需要检查变量,确定变量的类型? reflect 包 reflect.Type 和 reflect.Value reflect.Kind NumField() 和 Field() 方法 Int() 和 String() 方法 完整的程序 我们应该使用反射吗? 让我们来逐个讨论这些章节. 什么是反射? 反射就是程序能够在运行时检查变量和值,求出它们的类型.你可能还不太懂,这没关系.在本教程结束后,

  • Go语言中反射的正确使用

    介绍 反射是元数据编程的一种形式,指的是程序获得本身结构的一种能力.不同语言的反射模型实现不一样,本文中的反射,仅仅指的是Go语言中的反射模型. 反射有两个问题,在使用前需要三思: 大量的使用反射会损失一定性能 Clear is better than clever. Reflection is never clear. Go的类型设计上有一些基本原则,理解这些基本原则会有助于你理解反射的本质: 变量包括 <type, value> 两部分.理解这一点你就知道为什么nil != nil了. t

  • 学习使用Go反射的用法示例

    什么是反射 大多数时候,Go中的变量,类型和函数非常简单直接.当需要一个类型.变量或者是函数时,可以直接定义它们: type Foo struct { A int B string } var x Foo func DoSomething(f Foo) { fmt.Println(f.A, f.B) } 但是有时你希望在运行时使用变量的在编写程序时还不存在的信息.比如你正在尝试将文件或网络请求中的数据映射到变量中.或者你想构建一个适用于不同类型的工具.在这种情况下,你需要使用反射.反射使您能够在

  • Go语言学习笔记之反射用法详解

    本文实例讲述了Go学习笔记之反射用法.分享给大家供大家参考,具体如下: 一.类型(Type) 反射(reflect)让我们能在运行期探知对象的类型信息和内存结构,这从一定程度上弥(mi)补了静态语言在动态行为上的不足.同时,反射还是实现元编程的重要手段. 和 C 数据结构一样,Go 对象头部并没有类型指针,通过其自身是无法在运行期获知任何类型相关信息的.反射操作所需要的全部信息都源自接口变量.接口变量除存储自身类型外,还会保存实际对象的类型数据. func TypeOf(i interface{

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

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

  • Golang语言学习拿捏Go反射示例教程

    目录 1. 反射简介 1.1 反射是什么? 1.2 为什么需要反射? 2. reflect包 2.1 基本反射 2.2 反射与指针 2.3 反射与对象 2.4 反射与函数 2.5 反射例子 3. 总结 1. 反射简介 1.1 反射是什么? Go语言提供了一种机制在运行时更新和检查变量的值.调用变量的方法和变量支持的内在操作,但是在编译时并不知道这些变量的具体类型,这种机制被称为反射.反射也可以让我们将类型本身作为第一类的值类型处理. 反射是指在程序运行期对程序本身进行访问和修改的能力,程序在编译

  • C语言共用体union作用使用示例教程

    目录 共用体 union 开锅解构 小结一手 共用体 union 什么是共用体 union?这个共用体,估计大家平时在代码也比较少见,我去看了;其实这个共用体 union(也叫联合体)跟结构体定义是非常像的,比如说:类型定义.变量定义.使用方法上很相似.就像下面两个例子一样,把许多类型联合在一起 union st{ char a; int b; } 共用体也是一种自定义类型,可以通过它来创建变量,例如: union num{ int n; char ch; double f; }; union

  • C语言学习笔记之VS2022安装使用教程

    目录 一.安装VS2022 二.创建项目测试程序 三.遇到问题 四.解决办法 一.安装VS2022 参照B站安装教程安装. Tips:选择安装信息的时候,学习C语言勾选“通用Windows平台开发”,考虑到后续会用到C++,所以勾选了“使用C++的桌面开发”.值得一提的是,安装完成之后在后续学习过程中此安装信息也是可以修改的. 二.创建项目测试程序 点击最上方状态栏调试-开始执行(不调试),运行程序. 成功! 三.遇到问题 在VS2022中,在使用scanf函数编译出错:error C4996:

  • Go语言快速入门指针Map使用示例教程

    目录 1. 指针 1.1 指针地址和指针类型 1.2 指针取值 1.3 空指针 1.4 new 的使用 1.5 new与make的区别 2. Map 2.1 什么是Map key,value存储 hash冲突 hash冲突的常见解决方法 开放定址(线性探测)和拉链的优缺点 2.2 Map 定义 2.3 map基本使用 2.4 map的遍历 2.5 map判断某个键是否存在 2.6 map使用delete()函数删除键值对 1. 指针 区别于C/C++中的指针,Go语言中的指针不能进行偏移和运算,

  • Go语言学习教程之反射的示例详解

    目录 介绍 反射的规律 1. 从接口值到反射对象的反射 2. 从反射对象到接口值的反射 3. 要修改反射对象,该值一定是可设置的 介绍 reflect包实现运行时反射,允许一个程序操作任何类型的对象.典型的使用是:取静态类型interface{}的值,通过调用TypeOf获取它的动态类型信息,调用ValueOf会返回一个表示运行时数据的一个值.本文通过记录对reflect包的简单使用,来对反射有一定的了解.本文使用的Go版本: $ go version go version go1.18 dar

  • Go语言基础反射示例详解

    目录 概述 语法 一.基本操作 二.修改目标对象 三.动态调用方法 总结 示例 概述 在程序运行期对程序动态的进行访问和修改 reflect godoc: https://golang.org/pkg/reflect/ reflect包有两个数据类型: Type:数据类型 [reflect.TypeOf():是获取Type的方法] Value:值的类型[reflect.ValueOf():是获取Value的方法] 语法 一.基本操作 获取变量类型 func TypeOf(i interface{

  • Go语言学习之反射的用法详解

    目录 1. reflect 包 1.1 获取变量类型 1.2 断言处理类型转换 2. ValueOf 2.1 获取变量值 2.2 类型转换 3. Value.Set 3.1 设置变量值 3.2 示例 4. 结构体反射 4.1 查看结构体字段数量和方法数量 4.2 获取结构体属性 4.3 更改属性值 4.4 Tag原信息处理 5. 函数反射 6. 方法反射 6.1 使用 MethodByName 名称调用方法 6.2 使用 method 索引调用方法 反射指的是运行时动态的获取变量的相关信息 1.

  • Go语言学习教程之结构体的示例详解

    目录 前言 可导出的标识符 嵌入字段 提升 标签 结构体与JSON相互转换 结构体转JSON JSON转结构体 练习代码步骤 前言 结构体是一个序列,包含一些被命名的元素,这些被命名的元素称为字段(field),每个字段有一个名字和一个类型. 结构体用得比较多的地方是声明与数据库交互时需要用到的Model类型,以及与JSON数据进行相互转换.(当然,项目中任何需要多种数据结构组合在一起使用的地方,都可以选择用结构体) 代码段1:声明一个待办事项的Model类型: type Todo struct

  • Go语言学习教程之goroutine和通道的示例详解

    目录 goroutine 通道 Range 和 Close Select 官方留的两道练习题 等价的二叉树 网络爬虫 源码地址 goroutine goroutine是由Go运行时管理的轻量级线程. go f(x, y, z)在一个新的goroutine中开始执行f(x, y,z). goroutines运行在相同的地址空间中,所以对共享的内存访问必须同步.sync包提供了基本的同步原语(synchronization primitives),比如互斥锁(mutual exclusion loc

  • R语言编程学习绘制动态图实现示例

    在讨论级数时,可能需要比对前 n n n项和的变化情况,而随着 n n n的递增,通过动态图来反映这种变化会更加直观,而通过R语言绘制动态图也算是一门不那么初级的技术,所以在此添加一节,补充一下R语言的绘图知识. 绘图需要用到ggplot2,为多张图加上时间轴则需要用到gganimate,为了让这些动态图片被渲染,需要用到av.此外,ggplot2绘图需要输入的数据格式为tibble. install.packages("ggplot2") install.packages("

随机推荐