首页 > 其他分享 >go 反射

go 反射

时间:2023-09-23 17:44:18浏览次数:32  
标签:反射 Value reflect Go go Type

 

一. go 反射介绍和定义

 

在Go语言中,反射机制是一种动态获取变量类型和值信息的机制,它可以让程序在运行时动态地获取对象的类型信息、调用对象的方法、修改对象的属性等。通过反射机制,Go程序可以更加灵活和可扩展,但同时也会带来一些性能开销和复杂度。 在Go语言中,反射机制主要由标准库中的reflect包实现。该包提供了一个Type接口和一个Value结构体,分别用于表示类型信息和值信息。可以使用TypeOf()函数和ValueOf()函数获取一个值的类型信息和值信息,然后通过这些类型和值信息进行各种操作。 使用反射机制时需要注意一些细节,例如判断变量是否是可寻址的、获取变量类型必须使用指针等等。如果使用不当,可能会引起程序崩溃或产生意外的结果。因此,在使用反射机制时需要谨慎考虑各种情况,并根据具体需求选择合适的方案。 总之,反射机制是Go语言非常重要的一项特性,可以让Go程序更加灵活和可扩展。但同时也需要注意其带来的性能开销和复杂度,合理使用反射机制可以提高程序的效率和质量。

 

二.  go.反射 使用示例及使用总结

 

  Go语言反射机制中常用的关键字清单:
  1. reflect.TypeOf():获取一个值的类型信息,返回一个reflect.Type对象。
  2. reflect.ValueOf():获取一个值的元信息,返回一个reflect.Value对象。
  3. Value.Interface():将reflect.Value对象转换为接口变量,并返回接口变量中包含的原始值。
  4. Value.Int():将reflect.Value对象中的整数值转换为int64类型。
  5. Type.Field():获取一个结构体类型中指定索引位置的字段信息,返回一个reflect.StructField对象。
  6. Type.NumField():获取一个结构体类型中的字段数量。
  7. Value.Field():获取一个结构体变量中指定字段的值信息,返回一个reflect.Value对象。
  8. Value.NumField():获取一个结构体变量中的字段数量。
  9. Type.Kind():获取一个值的基础类型,例如intstringstruct等。
  10. Value.CanSet():判断一个reflect.Value对象是否可被设置。
  11. Value.Elem():获取一个指针变量指向的实际值,返回一个reflect.Value对象。
  12. Value.Set():设置一个reflect.Value对象的值。
  13. Value.MethodByName():根据方法名称查找一个结构体变量中的方法,返回一个reflect.Value对象。
  14. Value.Call():调用一个函数或方法,返回一个[]reflect.Value对象。
以上是Go语言反射机制中常用的关键字清单,可以帮助开发者对反射机制有一个更清晰的认识。   以下是一些使用Go语言反射机制中常用的关键字的实际示例:
  1. reflect.TypeOf()
go复制代码
var x int64 = 123
t := reflect.TypeOf(x)
fmt.Println(t.Name()) // "int64"
  1. reflect.ValueOf()
go复制代码
var x float64 = 3.14
value := reflect.ValueOf(x)
fmt.Println(value) // "3.14"
  1. Value.Interface()
go复制代码
var x int64 = 123
value := reflect.ValueOf(x)
i := value.Interface().(int64)
fmt.Println(i) // 123
  1. Value.Int()
go复制代码
var x int64 = 123
value := reflect.ValueOf(x)
i := value.Int()
fmt.Println(i) // 123
  1. Type.Field()
go复制代码
type User struct {
    Name string
    Age  int
}
t := reflect.TypeOf(User{})
field := t.Field(0)
fmt.Println(field.Name) // "Name"
  1. Value.CanSet()
go复制代码
type User struct {
    Name string
    Age  int
}
u := User{"Tom", 20}
v := reflect.ValueOf(u)

if v.Kind() == reflect.Struct {
    for i := 0; i < v.NumField(); i++ {
        field := v.Field(i)
        if field.CanSet() {
            field.SetString("Jerry")
        }
    }
}

fmt.Println(v.Interface()) // "{Jerry 20}"
  1. Value.Elem()
go复制代码
var x int64 = 123
value := reflect.ValueOf(&x)
elem := value.Elem()
elem.SetInt(456)
fmt.Println(x) // 456
  1. Value.Set()
go复制代码
var x int64 = 123
value := reflect.ValueOf(&x).Elem()
value.Set(reflect.ValueOf(456))
fmt.Println(x) // 456
  1. Value.MethodByName()
go复制代码
type User struct {
    Name string
    Age  int
}
u := User{"Tom", 20}
v := reflect.ValueOf(u)
method := v.MethodByName("SayHello")
params := []reflect.Value{}
result := method.Call(params)
fmt.Println(result[0].Interface()) // "Hello, Tom!"
  1. Value.Call()
go复制代码
func Add(a, b int) int {
    return a + b
}
value := reflect.ValueOf(Add)
params := []reflect.Value{reflect.ValueOf(2), reflect.ValueOf(3)}
result := value.Call(params)
fmt.Println(result[0].Int()) // 5
以上示例涵盖了Go语言反射机制中常用的几个关键字,可以帮助开发者更好地理解和掌握反射机制的基本原理和用法  

 

三.  GO 反射的应用场景

 

Go反射机制可以在许多场景下发挥作用,以下是一些常见的应用场景:
  1. 序列化和反序列化
在将Go数据类型转换为JSON、XML等格式时,需要动态获取变量类型和属性信息,并根据这些信息生成相应的数据结构。使用反射机制可以轻松地完成这些操作。
  1. 动态调用函数和方法
在某些情况下,需要在运行时动态调用某个函数或方法,并传递不同的参数和返回值。使用反射机制可以实现这种动态调用的功能。
  1. 框架编程
在编写框架或库时,需要支持不同类型的对象,并在运行时根据对象类型进行不同的处理。使用反射机制可以方便地获取对象类型和属性信息,并进行相应的处理。
  1. 插件系统
在开发插件系统时,需要动态加载不同的插件,并根据插件类型执行不同的操作。使用反射机制可以帮助程序动态加载插件,并根据插件类型调用相应的方法。
  1. 代码生成工具
在编写代码生成工具时,需要动态创建数据结构、生成代码并编译执行。使用反射机制可以方便地进行这些操作。 总之,Go反射机制在许多场景下都能够发挥重要的作用,特别是在需要动态获取对象类型和属性信息的情况下,反射机制更是无可替代的。但同时也需要注意其带来的性能开销和复杂度,合理使用反射机制可以提高程序的效率和质量。

 

四.  go.反射 底层原理

 

Go语言的反射机制是通过reflect包来实现的,它基于两个底层数据结构:reflect.Typereflect.Value
  • reflect.Type表示类型信息,用于描述一个值的类型,
  • reflect.Value则表示值信息,用于存储和访问一个值的内容。
  在Go语言中,每种数据类型都对应着一个reflect.Type对象,可以使用reflect.TypeOf()函数来获取一个值的类型信息。reflect.Type对象提供了丰富的属性和方法, 例如Name()Kind()Field()NumMethod()等,可以帮助我们动态地获取和操作变量的类型信息。   另一方面,reflect.Value对象用于存储和访问一个值的内容,可以使用reflect.ValueOf()函数来获取一个值的元信息。reflect.Value对象也提供了丰富的属性和方法, 例如Interface()CanSet()Field()NumField()MethodByName()Call()等,可以帮助我们动态地访问和修改变量的值,并调用其方法。   在Go语言中,反射机制主要是通过这两个底层数据结构来实现的。当我们调用reflect.TypeOf()reflect.ValueOf()函数时,会返回一个reflect.Typereflect.Value对象,这些对象内部维护了原始数据的指针和类型信息。 在访问和修改变量值时,我们可以使用reflect.Value对象的各种方法来读写变量的内容,或者使用reflect.Value.Set()方法来设置变量的值。需要注意的是,在使用reflect.Value.Set()方法时,必须确保该对象是可寻址的,即它本身就是一个指针变量或可以通过Elem()方法获取到指向实际值的指针变量。 总的来说,Go语言的反射机制是基于reflect.Typereflect.Value两个底层数据结构实现的,它允许我们动态地访问和修改变量的类型和值信息,在某些情况下能够提高程序的灵活性和复用性。但是反射机制比较复杂,性能也较低,在实际开发中需要谨慎使用。  

 

五.  go.反射 核心原码分析

 

Go语言标准库中的reflect包是Go语言反射机制的核心实现。以下是reflect包中的一些核心原码:
  1. type Type interface
Type接口定义了类型信息的抽象表示,包含了各种类型元数据的方法和属性,例如名称、大小、内存对齐方式、方法集等。
go复制代码
type Type interface {
    Name() string
    Size() uintptr
    Align() int
    Field(int) StructField
    NumField() int
    Method(int) Method
    NumMethod() int
    Kind() Kind
    ...
}
  1. type Value struct
Value结构体定义了值信息的抽象表示,包含了各种值元数据的方法和属性,例如值本身、是否可寻址、是否可设置等。
go复制代码
type Value struct {
    // contains unexported fields
}
func ValueOf(i interface{}) Value {}
func (v Value) Interface() interface{}
func (v Value) CanSet() boolfunc (v Value) Set(x Value)
func (v Value) Elem() Value
func (v Value) Field(i int) Value
func (v Value) NumField() intfunc (v Value) MethodByName(name string) Value
func (v Value) Call(args []Value) []Value
...
  1. func ValueOf(i interface{}) Value
ValueOf()函数用于获取一个值的元信息,返回一个Value对象。
go复制代码
func ValueOf(i interface{}) Value {
    return unpackEface(i).data
}
  1. func TypeOf(i interface{}) Type
TypeOf()函数用于获取一个值的类型信息,返回一个Type对象。
go复制代码
func TypeOf(i interface{}) Type {
    if i == nil {
        return nil
    }
    e := packEface(i)
    return toType(e.typ)
}
  1. func New(typ Type) Value
New()函数用于创建一个新的变量,并返回一个指向该变量的Value对象。
go复制代码
func New(typ Type) Value {
    if typ == nil {
        panic("reflect: New using nil Type")
    }
    var v Value
    newobject(typ, unsafe.Pointer(&v))
    return v
}
  1. func MakeFunc(typ Type, fn func(args []Value) (results []Value)) Value
MakeFunc()函数用于创建一个新的函数类型,并返回一个指向该函数的Value对象。
go复制代码
func MakeFunc(typ Type, fn func(args []Value) (results []Value)) Value {
    if typ.Kind() != Func {
        panic("reflect: call of MakeFunc with non-func type")
    }
    ft := typ.(*rtype)
    ...
}
以上是reflect包中的一些核心原码,它们提供了丰富的功能和灵活性,可以满足各种场景下的需求。但是反射机制比较复杂,性能也较低,在实际开发中应该尽量避免过度使用反射。  

 

 

六.  go.反射 vs java.反射

 

 

特性 Go反射 Java反射
类型系统 动态确定变量类型,需要更多元数据描述 强类型系统,编译时即可确定变量类型
性能表现 反射操作性能相对较差 JVM提供强大的反射支持,性能相对较好
语法简洁性 方法和属性比较冗长和复杂 方法和属性比较丰富和清晰
以上是Go反射和Java反射的一些不同点的简单总结,这些不同点主要集中在类型系统、性能表现和语法简洁性等方面。需要注意的是,这只是两种反射机制的一些基本特征,具体实现还会受到其他因素的影响,例如编程工具、开发环境、程序架构等等。  

 

七.  go 反射最佳实践

 

  使用反射机制可以让Go程序更加灵活和可扩展,但同时也会带来一些潜在的问题和性能开销。以下是Go反射的一些最佳实践:
  1. 优先考虑使用类型断言
在大多数情况下,使用类型断言比使用反射机制要更加直接和高效。只有在必须动态确定变量类型或进行一些特殊处理时,才应该考虑使用反射机制。
  1. 缓存类型信息
对于需要频繁访问的类型信息,应该将其缓存起来,避免重复获取造成的性能开销。
  1. 尽可能使用指针类型
由于Go语言中的值类型默认是不可寻址的,因此在使用反射机制设置变量值时,必须使用指向该变量的指针对象。因此,尽可能使用指针类型来表示变量,可以方便地使用反射机制修改变量内容。
  1. 避免错误使用反射方法
在使用反射方法时,需要注意一些细节,例如判断变量是否是可寻址的、获取变量类型必须使用指针等等。如果使用不当,可能会引起程序崩溃或产生意外的结果。
  1. 谨慎使用反射机制
反射机制本身比较复杂,在使用时需要谨慎考虑各种情况,并评估其带来的性能开销复杂度。对于一些简单的场景,使用反射机制可能并不是最好的选择。 总的来说,Go反射机制具有一定的灵活性和可扩展性,但在使用时需要注意一些细节和最佳实践,以确保程序的正确性和性能。

标签:反射,Value,reflect,Go,go,Type
From: https://www.cnblogs.com/shoshana-kong/p/17724791.html

相关文章

  • 2023-09-23:用go语言,假设每一次获得随机数的时候,这个数字大于100的概率是P。 尝试N次,其
    2023-09-23:用go语言,假设每一次获得随机数的时候,这个数字大于100的概率是P。尝试N次,其中大于100的次数在A次~B次之间的概率是多少?0<P<1,P是double类型,1<=A<=B<=N<=100。来自左程云。答案2023-09-23:首先,我们可以使用动态规划来解决这个问题。我们可以定义一个二......
  • 2023-09-23:用go语言,假设每一次获得随机数的时候,这个数字大于100的概率是P。 尝试N次,其
    2023-09-23:用go语言,假设每一次获得随机数的时候,这个数字大于100的概率是P。尝试N次,其中大于100的次数在A次~B次之间的概率是多少?0<P<1,P是double类型,1<=A<=B<=N<=100。来自左程云。答案2023-09-23:首先,我们可以使用动态规划来解决这个问题。我们可以定义一个二维数组d......
  • Docker 安装 MongoDB
    Docker安装MongoDB使用Docker-Compose安装MongoDB-MasonLee-博客园(cnblogs.com)[root@localhost~]#dockerpullmongo:latestlatest:Pullingfromlibrary/mongoDigest:sha256:5be752bc5f2ac4182252d0f15d74df080923aba39700905cb26d9f70f39e9702Status:Imagei......
  • 使用 cargo expand 查看被宏隐藏的代码
    使用cargoexpand查看被宏隐藏的代码使用VScode安装扩展RustMacroExpand需要安装以下软件:cargo-expandAcargocrateforeasierhandlingofcompilercommandsRustnightlycompiler,youcaninstallitwithrustuptoolchaininstallnightlycargoexpand简......
  • godot基础.md
    Godot目录Godot介绍安装介绍一款开源游戏引擎。以节点为思想进行搭建。节点阻止方式是场景。安装paru-Sgodotvulkan-radeon......
  • Google拟放弃博通自行研发AI芯片 | 百能云芯
    谷歌计划自行研发人工智能(AI)芯片,考虑将博通(Broadcom)从其供应商名单中剔除,但谷歌强调双方的合作关系不会受到影响。根据美国网络媒体《TheInformation》的报道,谷歌高层正在讨论可能在2027年放弃博通作为AI芯片供应商的计划,改为自行开发芯片。这一举措预计每年能为谷歌省下数十亿美......
  • Google拟放弃博通自行研发AI芯片 | 百能云芯
    谷歌计划自行研发人工智能(AI)芯片,考虑将博通(Broadcom)从其供应商名单中剔除,但谷歌强调双方的合作关系不会受到影响。根据美国网络媒体《TheInformation》的报道,谷歌高层正在讨论可能在2027年放弃博通作为AI芯片供应商的计划,改为自行开发芯片。这一举措预计每年能为谷歌省下数十亿美......
  • Django跨域问题解决
    Django跨域问题解决今天在学习前端Vue框架的过程中,遇到了跨域相关问题问题1详情:AccesstoXMLHttpRequestat'http://127.0.0.1:8000/book/'fromorigin'http://localhost:63342'hasbeenblockedbyCORSpolicy:No'Access-Control-Allow-Origin'headerispre......
  • Go每日一库之20:copier
    简介上一篇文章介绍了mergo库的使用,mergo是用来给结构体或map赋值的。mergo有一个明显的不足——它只能处理相同类型的结构!如果类型不同,即使字段名和类型完全相同,mergo也无能为力。今天我们要介绍的copier库就能处理不同类型之间的赋值。除此之外,copier还能:调用同名方法为字段......
  • math 库中常用的数学运算和常量【GO 基础】
    〇、关于mathGO语言的math库是一个内置的标准库,其中包含了许多数学函数和常量,用于计算各种数学运算和统计学计算。日常开发中,计算当然是少不了的,那么今天来梳理下备查。一、测试示例1.1小数位的:Round-四舍五入、RoundToEven-四舍/五至偶数funcRound(xfloat64)float6......