Go语言之反射(反射的简单使用,原理)

这篇具有很好参考价值的文章主要介绍了Go语言之反射(反射的简单使用,原理)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、反射的基础

1.什么是反射

  • Go语言中,反射的机制就是在运行的时候,可以获取到其变量的类型和值,且可以对其类型和值进行检查,对其值进行修改。
  • 即在不知道具体的类型的情况下,可以用反射机制来查看变量类型、更新变量的值。
  • Go中反射主要涉及到两个概念:Type和Value。对所有的接口进行反射时,都可以得到一个包含Type和Value的信息结构,Type是反射的这个变量本身的类型信息,Value是反射的这个变量本身的值信息。

2.反射的使用

(1) 获取到变量类型
  • 方法:reflect.TypeOf(x)
  • 作用:获取变量x的类型信息
    package main
    
    import (
    	"fmt"
    	"reflect"
    )
    
    func main(){
    	x := 3.1415
    	y := 3
    	z := "sheena"
    	u := true
    
    	fmt.Println("x的type为:", reflect.TypeOf(x))
    	fmt.Println("y的type为:", reflect.TypeOf(y))
    	fmt.Println("z的type为:", reflect.TypeOf(z))
    	fmt.Println("u的type为:", reflect.TypeOf(u))
    }
    
    结果:Go语言之反射(反射的简单使用,原理)
(2) 进行变量类型判断
  • 方法:xtype := reflect.TypeOf(x) xkind := xtype.Kind() if xkind == reflect.Type
  • 作用:先调用TypeOf方法获取到x的类型信息,再获取到类型信息中的类型,与类型进行判断
    package main
    
    import (
    	"fmt"
    	"reflect"
    )
    
    func main(){
    	x := 3.1415
    	y := 3
    	z := "sheena"
    	u := true
    
    	//获取变量的类型
    	xtype := reflect.TypeOf(x)
    	ytype := reflect.TypeOf(y)
    	ztype := reflect.TypeOf(z)
    	utype := reflect.TypeOf(u)
    
    	//Kind()方法的返回结果主要是用来进行类型判断的
    	xkind := xtype.Kind()
    	ykind := ytype.Kind()
    	zkind := ztype.Kind()
    	ukind := utype.Kind()
    
    	//对x进行类型判断
    	if xkind == reflect.String{
    		fmt.Println("x的type是string")
    	}else if xkind == reflect.Float64{
    		fmt.Println("x的type是float64")
    	}
    
    	//对y进行类型判断
    	if ykind == reflect.Float64{
    		fmt.Println("y的type是Float64")
    	}else if ykind == reflect.Int{
    		fmt.Println("y的type是int")
    	}
    
    	//对z进行类型判断
    	if zkind == reflect.Float64{
    		fmt.Println("z的type是Float64")
    	}else if zkind == reflect.String{
    		fmt.Println("z的type是string")
    	}
    
    	//对u进行类型判断
    	if ukind == reflect.Bool{
    		fmt.Println("u的type是bool")
    	}else if ukind == reflect.String{
    		fmt.Println("u的type是string")
    	}
    }
    
    结果:
    Go语言之反射(反射的简单使用,原理)
(3) 获取到变量值
  • 方法:reflect.ValueOf(x)
  • 作用:获取x变量的值
    package main
    
    import (
    	"fmt"
    	"reflect"
    )
    
    func main(){
    	x := 3.1415
    	y := 3
    	z := "sheena"
    	u := true
    
    	fmt.Println("x的value为:", reflect.ValueOf(x))
    	fmt.Println("y的value为:", reflect.ValueOf(y))
    	fmt.Println("z的value为:", reflect.ValueOf(z))
    	fmt.Println("u的value为:", reflect.ValueOf(u))
    }
    
    结果:
    Go语言之反射(反射的简单使用,原理)
(4) 修改变量值
  • 方法一:使用具体类型的值的设置方法,如SetString()、SetFloat64()等(可直接接收具体要修改的值)
    package main
    
    import (
    	"fmt"
    	"reflect"
    )
    
    func main(){
    	x := 3.1415
    	y := 3
    	z := "sheena"
    	u := true
    
    	fmt.Println("x修改前的value为:", reflect.ValueOf(x))
    	fmt.Println("y修改前的value为:", reflect.ValueOf(y))
    	fmt.Println("z修改前的value为:", reflect.ValueOf(z))
    	fmt.Println("u修改前的value为:", reflect.ValueOf(u))
    
    	//通过反射传入变量x的地址,并且通过Ele
    	rex := reflect.ValueOf(&x).Elem()
    	rey := reflect.ValueOf(&y).Elem()
    	rez := reflect.ValueOf(&z).Elem()
    	reu := reflect.ValueOf(&u).Elem()
    
    	//判断是否可以修改变量x的值,若可以,则用SetFLoat64()方法进行修改
    	if rex.CanSet(){
    		rex.SetFloat(61.23466)
    		fmt.Println("x修改后的value为:", reflect.ValueOf(x))
    	}else {
    		fmt.Println("该变量不能修改")
    	}
    
    	if rey.CanSet(){
    		rey.SetInt(10000)
    		fmt.Println("y修改后的value为:", reflect.ValueOf(y))
    	}else {
    		fmt.Println("该变量不能修改")
    	}
    
    	if rez.CanSet(){
    		rez.SetString("hello world")
    		fmt.Println("z修改后的value为:", reflect.ValueOf(z))
    	}else{
    		fmt.Println("该变量不能修改")
    	}
    
    	if reu.CanSet(){
    		reu.SetBool(false)
    		fmt.Println("u修改后的value为:", reflect.ValueOf(u))
    	}else {
    		fmt.Println("该变量不能修改")
    	}
    }
    
    结果:
    Go语言之反射(反射的简单使用,原理)
  • 方法二:是用Set方法直接设置值(注意set方法接收的是ValueOf的返回值)
    package main
    
    import (
    	"fmt"
    	"reflect"
    )
    
    func main(){
    	x := 3.1415
    	y := 3
    	z := "sheena"
    	u := true
    
    	fmt.Println("x修改前的value为:", reflect.ValueOf(x))
    	fmt.Println("y修改前的value为:", reflect.ValueOf(y))
    	fmt.Println("z修改前的value为:", reflect.ValueOf(z))
    	fmt.Println("u修改前的value为:", reflect.ValueOf(u))
    
    	//通过反射传入变量x的地址,并且通过Ele
    	rex := reflect.ValueOf(&x).Elem()
    	rey := reflect.ValueOf(&y).Elem()
    	rez := reflect.ValueOf(&z).Elem()
    	reu := reflect.ValueOf(&u).Elem()
    
    	//判断是否可以修改变量x的值,若可以,则用Set()方法进行修改
    	if rex.CanSet(){
    		ax := reflect.ValueOf(61.23466) // 使用Set方法修改值,Set方法接收的是ValueOf的返回值
    		rex.Set(ax)
    		fmt.Println("x修改后的value为:", reflect.ValueOf(x))
    	}else {
    		fmt.Println("该变量不能修改")
    	}
    
    	if rey.CanSet(){
    		ay := reflect.ValueOf(10000)// 使用Set方法修改值,Set方法接收的是ValueOf的返回值
    		rey.Set(ay)
    		fmt.Println("y修改后的value为:", reflect.ValueOf(y))
    	}else {
    		fmt.Println("该变量不能修改")
    	}
    
    	if rez.CanSet(){
    		az := reflect.ValueOf("hello world")// 使用Set方法修改值,Set方法接收的是ValueOf的返回值
    		rez.Set(az)
    		fmt.Println("z修改后的value为:", reflect.ValueOf(z))
    	}else{
    		fmt.Println("该变量不能修改")
    	}
    
    	if reu.CanSet(){
    		au := reflect.ValueOf(false)// 使用Set方法修改值,Set方法接收的是ValueOf的返回值
    		reu.Set(au)
    		fmt.Println("u修改后的value为:", reflect.ValueOf(u))
    	}else {
    		fmt.Println("该变量不能修改")
    	}
    }
    
    结果:
    Go语言之反射(反射的简单使用,原理)
(5) 获取变量的指针所指向的对象
  • 方法:Elem()方法
  • 作用:获取变量x的指针所指向的对象
    package main
    
    import (
    	"fmt"
    	"reflect"
    )
    
    func main(){
    	x := 3.1415
    	y := 3
    	z := "sheena"
    	u := true
    
    	//传入变量地址
    	px := reflect.ValueOf(&x)
    	py := reflect.ValueOf(&y)
    	pz := reflect.ValueOf(&z)
    	pu := reflect.ValueOf(&u)
    
    	fmt.Println("x的地址是", px)
    	fmt.Println("y的地址是", py)
    	fmt.Println("z的地址是", pz)
    	fmt.Println("u的地址是", pu)
    
    	//通过变量地址获取到变量的值
    	xe := px.Elem()
    	ye := py.Elem()
    	ze := pz.Elem()
    	ue := pu.Elem()
    	
    	fmt.Println("x的值是", xe)
    	fmt.Println("y的值是", ye)
    	fmt.Println("z的值是", ze)
    	fmt.Println("u的值是", ue)
    }
    
    结果:
    Go语言之反射(反射的简单使用,原理)
(6) 获取结构体变量的类型和值
package main

import (
	"fmt"
	"reflect"
)

type Stu struct{
	Name string
	Age int
	Sex string
	IsCan bool
}

func main(){
	s1 := Stu{Name: "王一", Age: 18, Sex: "男", IsCan: false}
	s2 := Stu{Name: "王二", Age: 19, Sex: "女", IsCan: true}
	s3 := Stu{Name: "张三", Age: 20, Sex: "男", IsCan: false}

	//反射获取结构体的类型和值
	fmt.Println("s1的类型", reflect.TypeOf(s1))
	fmt.Println("s1的值", reflect.ValueOf(s1))

	fmt.Println("s2的类型", reflect.TypeOf(s2))
	fmt.Println("s2的值", reflect.ValueOf(s2))

	fmt.Println("s3的类型", reflect.TypeOf(s3))
	fmt.Println("s3的值", reflect.ValueOf(s3))


	fmt.Println("TypeOf()和Kind()方法输出的区别")
	fmt.Println("TypeOf(s1):", reflect.TypeOf(s1))
	s1tp := reflect.TypeOf(s1)
	fmt.Println("Kind(s1):", s1tp.Kind())
}

结果:
Go语言之反射(反射的简单使用,原理)文章来源地址https://www.toymoban.com/news/detail-424991.html

二、反射的原理

1.反射如何获取类型信息

  • reflect包中提供TypeOf接口用于获取一个变量的类型信息。它接收一个空接口类型的参数,并返回一个reflect.Type类型的返回值
  • TypeOf接口:
    func TypeOf(i interface{}) Type{
    	eface := *(*emptyInterface)(unsafe.Pointer(&i))
    	return toType(eface.type)
    }
    
    //Type接口提供了一系列方法
    type Type interface{
    	Align()  int                      //对齐边界
    	FieldAlign()  int
    	Method(int) Method
    	MethodByName(string)  (Method, bool)       //方法
    	NumMethod()  int         //类型名称
    	Name()  string
    	PkgPath()  string         //包路径
    	Slize()  uintptr
    	String()  string
    	Kind()  Kind
    	Implements(u Type)  bool         //是否实现指定接口
    	AssginableTo(u Type)  bool
    	ConvertibleTo(u Type)  bool
    	Comparable()  bool                //是否可比较
    }
    
    Go语言之反射(反射的简单使用,原理)

2.反射如何修改变量值

  • 用反射修改变量值,需要用到reflect.Value类型了,如下所示结构体,有三个元素,type字段存储反射变量的类型元数据指针;ptr字段存储数据地址;flag字段是一个位标识符,存储反射变量的描述信息,例如是否为指针,是否为方法或是否只读等等。
    type Value struct {
          type *rtype
          ptr unsafe.Pointer
          flag
    }
    
    func ValueOf(i interface{}) Value{
          if i == nill{
               return Value()
          }
          escapes(i)
          return unpackEface(i)
    }
    
  • 通常会有reflect.ValueOf方法来拿到反射类型的值,注意这里的参数i和TypeOf函数一样是个空接口类型,所以处理方式一样。(编译阶段会增加一个临时变量作为拷贝)
    Go语言之反射(反射的简单使用,原理)

到了这里,关于Go语言之反射(反射的简单使用,原理)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【Go】四、rpc跨语言编程基础与rpc的调用基础原理

    早期 Go 语言不使用 go module 进行包管理,而是使用 go path 进行包管理,这种管理方式十分老旧,两者最显著的区别就是:Go Path 创建之后没有 go.mod 文件被创建出来,而 go module 模式会创建出一个 go.mod 文件用于管理包信息 现在就是:尽量使用 Go Modules 模式 另外,我们在引入包

    2024年02月19日
    浏览(28)
  • go语言的反射和泛型

    反射可以在程序的运行时获取变量的各种信息。Go语言中光反射在 reflect 包下。 http://c.biancheng.net/view/4407.html Go语言中通过 断言 转化为指定类型。 但是这并不具有通用性,通过断言的判断必须是已有定义的类型,未定义的就不可用,因此没有通用性。泛型就是来解决这一问题

    2024年02月10日
    浏览(37)
  • 深入理解 go reflect - 反射为什么慢

    我们选择 go 语言的一个重要原因是,它有非常高的性能。但是它反射的性能却一直为人所诟病,本篇文章就来看看 go 反射的性能问题。 在开始之前,有必要先了解一下 go 的性能测试。在 go 里面进行性能测试很简单,只需要在测试函数前面加上 Benchmark 前缀, 然后在函数体

    2024年02月01日
    浏览(35)
  • GO基础进阶篇 (十二)、反射

    Go语言中的反射是指在程序运行时检查程序的结构,比如变量的类型、方法的签名等。Go语言的反射包是reflect。通过反射,你可以动态地检查类型信息、获取字段和方法、调用方法等。 反射可以在运行时动态获取变量的各种信息,比如变量的类型、值等 如果是结构体,还可以

    2024年02月02日
    浏览(35)
  • 【go语言开发】redis简单使用

    本文主要介绍redis安装和使用。首先安装redis依赖库,这里是v8版本;然后连接redis,完成基本配置;最后测试封装的工具类 欢迎大家访问个人博客网址:https://www.maogeshuo.com,博主努力更新中… 参考文件: Yaml文件配置,Config使用 Log日志封装 常用工具类封装 命令行安装redis

    2024年03月12日
    浏览(48)
  • Go语言通过反射获取各种类型变量的值

    反射是程序在运行期间获取变量的类型和值、或者执行变量的方法的能力。 反射是程序在运行期间获取变量的类型和值、或者执行变量的方法的能力。 Golang 反射包中有两对非常重要的函数和类型,两个函数分别是: reflect.TypeOf 能获取类型信息 reflect.Type; reflect.ValueOf 能获取

    2024年02月15日
    浏览(38)
  • Go 语言之 zap 日志库简单使用

    log:https://pkg.go.dev/log log 包是一个简单的日志包。 Package log implements a simple logging package. It defines a type, Logger, with methods for formatting output. It also has a predefined \\\'standard\\\' Logger accessible through helper functions Print[f|ln], Fatal[f|ln], and Panic[f|ln], which are easier to use than creating a Logger manually. Th

    2024年02月09日
    浏览(25)
  • 【设计模式】使用 go 语言实现简单工厂模式

    最近在看《大话设计模式》,这本书通过对话形式讲解设计模式的使用场景,有兴趣的可以去看一下。 第一篇讲的是 简单工厂模式 ,要求输入两个数和运算符号,得到运行结果。 这个需求不难,难就难在类要怎么设计,才能达到可复用、维护性强、可拓展和灵活性高。 运

    2024年02月05日
    浏览(32)
  • Go 语言为什么很少使用数组?

    大家好,我是 frank,「Golang 语言开发栈」公众号作者。 01 介绍 在 Go 语言中,数组是一块连续的内存,数组不可以扩容,数组在作为参数传递时,属于值传递。 数组的长度和类型共同决定数组的类型,不同类型的数组之间不可以比较,否则在编译时会报错。 因为数组的一些

    2024年02月04日
    浏览(43)
  • GO语言使用最简单的UI方案govcl

    接触go语言有一两年时间了。 之前用Qt和C#写过桌面程序,C#会被别人扒皮,极度不爽;Qt默认要带一堆dll,或者静态编译要自己弄或者找库,有的库还缺这缺那,很难编译成功。 如果C# winform可以编译成二进制原生exe的话,给人感觉是开发效率最好的。 C#有nuget可以用别人的库

    2024年02月15日
    浏览(23)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包