iis服务器助手广告广告
返回顶部
首页 > 资讯 > 精选 >go语言中反射三定律指的是什么
  • 703
分享到

go语言中反射三定律指的是什么

2023-07-05 00:07:39 703人浏览 泡泡鱼
摘要

这篇文章主要介绍“Go语言中反射三定律指的是什么”,在日常操作中,相信很多人在go语言中反射三定律指的是什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”go语言中反射三定律指的是什么”的疑惑有所帮助!接下来

这篇文章主要介绍“Go语言中反射三定律指的是什么”,在日常操作中,相信很多人在go语言中反射三定律指的是什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”go语言中反射三定律指的是什么”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

反射三定律:1、反射可以将“接口类型变量”转换为“反射类型对象”,这里反射类型指“reflect.Type”和 “reflect.Value”;2、反射可以将“反射类型对象”转换为“接口类型变量”;3、如果要修改“反射类型对象”其值必须是“可写的”。

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

go语言中反射三定律指的是什么

两种类型:Type 和 Value

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

  • reflect.Type

  • reflect.Value

它们分别对应着真实世界里的 type 和 value,只不过在反射对象里,它们拥有更多的内容。

源码上来看,reflect.Type 是以一个接口的形式存在的

type Type interface {    Align() int    FieldAlign() int    Method(int) Method    MethodByName(string) (Method, bool)    NumMethod() int    Name() string    PkgPath() string    Size() uintptr    String() string    Kind() Kind    Implements(u Type) bool    AssignableTo(u Type) bool    ConvertibleTo(u Type) bool    Comparable() bool    Bits() int    ChanDir() ChanDir    IsVariadic() bool    Elem() Type    Field(i int) StructField    FieldByIndex(index []int) StructField    FieldByName(name string) (StructField, bool)    FieldByNameFunc(match func(string) bool) (StructField, bool)    In(i int) Type    Key() Type    Len() int    NumField() int    NumIn() int    NumOut() int    Out(i int) Type    common() *rtype    uncommon() *uncommonType}

而 reflect.Value 是以一个结构体的形式存在,

type Value struct {    typ *rtype    ptr unsafe.Pointer    flag}

同时它接收了很多的方法(见下表),这里出于篇幅的限制这里也没办法一一介绍。

AddrBoolBytesrunesCanAddrCanSetCallCallSlicecallCapCloseComplexElemFieldFieldByIndexFieldByNameFieldByNameFuncFloatIndexIntCanInterfaceInterfaceInterfaceDataisNilIsValidIsZeroKindLenMapindexMapKeysMapRangeMethodNumMethodMethodByNameNumFieldOverflowComplexOverflowFloatOverflowIntOverflowUintPointerRecvrecvSendsendSetSetBoolSetBytessetRunesSetComplexSetFloatSetIntSetLenSetCapSetMapIndexSetUintSetPointerSetStringSliceSlice3StringTryRecvTrySendTypeUintUnsafeAddrassignToConvert

通过上一节的内容(),我们知道了一个接口变量,实际上都是由一 pair 对(type 和 data)组合而成,pair 对中记录着实际变量的值和类型。也就是说在真实世界里,type 和 value 是合并在一起组成 接口变量的。

而在反射的世界里,type 和 data 却是分开的,他们分别由 reflect.Type 和 reflect.Value 来表现。

解读反射的三大定律

Go 语言里有个反射三定律,是你在学习反射时,很重要的参考:

  • Reflection goes from interface value to reflection object.

  • Reflection goes from reflection object to interface value.

  • To modify a reflection object, the value must be settable.

翻译一下,就是:

  • 反射可以将“接口类型变量” 转换为“反射类型对象”;

  • 反射可以将 “反射类型对象”转换为 “接口类型变量”;

  • 如果要修改 “反射类型对象” 其类型必须是 “可写的”;

第一定律

Reflection goes from interface value to reflection object.

为了实现从接口变量到反射对象的转换,需要提到 reflect 包里很重要的两个方法:

  • reflect.TypeOf(i) :获得接口值的类型

  • reflect.ValueOf(i):获得接口值的值

这两个方法返回的对象,我们称之为反射对象:Type object 和 Value object。

go语言中反射三定律指的是什么

举个例子,看下这两个方法是如何使用的?

package mainimport ("fmt""reflect")func main() {    var age interface{} = 25    fmt.Printf("原始接口变量的类型为 %T,值为 %v \n", age, age)    t := reflect.TypeOf(age)    v := reflect.ValueOf(age)    // 从接口变量到反射对象    fmt.Printf("从接口变量到反射对象:Type对象的类型为 %T \n", t)    fmt.Printf("从接口变量到反射对象:Value对象的类型为 %T \n", v)}

输出如下

原始接口变量的类型为 int,值为 25 从接口变量到反射对象:Type对象的类型为 *reflect.rtype 从接口变量到反射对象:Value对象的类型为 reflect.Value 复制代码

如此我们完成了从接口类型变量到反射对象的转换。

等等,上面我们定义的 age 不是 int 类型的吗?第一法则里怎么会说是接口类型的呢?

关于这点,其实在上一节(关于接口的三个 『潜规则』)已经提到过了,由于 TypeOf 和 ValueOf 两个函数接收的是 interface{} 空接口类型,而 Go 语言函数都是值传递,因此Go语言会将我们的类型隐式地转换成接口类型。

原始接口变量的类型为 int,值为 25 从接口变量到反射对象:Type对象的类型为 *reflect.rtype 从接口变量到反射对象:Value对象的类型为 reflect.Value

第二定律

Reflection goes from reflection object to interface value.

和第一定律刚好相反,第二定律描述的是,从反射对象到接口变量的转换。

go语言中反射三定律指的是什么

通过源码可知, reflect.Value 的结构体会接收 Interface 方法,返回了一个 interface{}  类型的变量(注意:只有 Value 才能逆向转换,而 Type 则不行,这也很容易理解,如果 Type 能逆向,那么逆向成什么呢?

// Interface returns v's current value as an interface{}.// It is equivalent to://    var i interface{} = (v's underlying value)// It panics if the Value was obtained by accessing// unexported struct fields.func (v Value) Interface() (i interface{}) {    return valueInterface(v, true)}

这个函数就是我们用来实现将反射对象转换成接口变量的一个桥梁。

例子如下

package mainimport ("fmt""reflect")func main() {    var age interface{} = 25    fmt.Printf("原始接口变量的类型为 %T,值为 %v \n", age, age)    t := reflect.TypeOf(age)    v := reflect.ValueOf(age)    // 从接口变量到反射对象    fmt.Printf("从接口变量到反射对象:Type对象的类型为 %T \n", t)    fmt.Printf("从接口变量到反射对象:Value对象的类型为 %T \n", v)    // 从反射对象到接口变量    i := v.Interface()    fmt.Printf("从反射对象到接口变量:新对象的类型为 %T 值为 %v \n", i, i)}

输出如下

原始接口变量的类型为 int,值为 25 从接口变量到反射对象:Type对象的类型为 *reflect.rtype 从接口变量到反射对象:Value对象的类型为 reflect.Value 从反射对象到接口变量:新对象的类型为 int 值为 25

当然了,最后转换后的对象,静态类型为 interface{} ,如果要转成最初的原始类型,需要再类型断言转换一下,关于这点,我已经在上一节里讲解过了,你可以点此前往复习:()。

i := v.Interface().(int)

至此,我们已经学习了反射的两大定律,对这两个定律的理解,我画了一张图,你可以用下面这张图来加强理解,方便记忆。

go语言中反射三定律指的是什么

第三定律

To modify a reflection object, the value must be settable.

反射世界是真实世界的一个『映射』,是我的一个描述,但这并不严格,因为并不是你在反射世界里所做的事情都会还原到真实世界里。

第三定律引出了一个 settable (可设置性,或可写性)的概念。

其实早在以前的文章中,我们就一直在说,Go 语言里的函数都是值传递,只要你传递的不是变量的指针,你在函数内部对变量的修改是不会影响到原始的变量的。

回到反射上来,当你使用 reflect.Typeof 和 reflect.Valueof 的时候,如果传递的不是接口变量的指针,反射世界里的变量值始终将只是真实世界里的一个拷贝,你对该反射对象进行修改,并不能反映到真实世界里。

因此在反射的规则里

  • 不是接收变量指针创建的反射对象,是不具备『可写性』的

  • 是否具备『可写性』,可使用 CanSet() 来获取得知

  • 对不具备『可写性』的对象进行修改,是没有意义的,也认为是不合法的,因此会报错。

package mainimport (    "fmt"    "reflect")func main() {    var name string = "Go编程时光"    v := reflect.ValueOf(name)    fmt.Println("可写性为:", v.CanSet())}

输出如下

可写性为: false

要让反射对象具备可写性,需要注意两点

  • 创建反射对象时传入变量的指针

  • 使用 Elem()函数返回指针指向的数据

完整代码如下

package mainimport (    "fmt"    "reflect")func main() {    var name string = "Go编程时光"    v1 := reflect.ValueOf(&name)    fmt.Println("v1 可写性为:", v1.CanSet())    v2 := v1.Elem()    fmt.Println("v2 可写性为:", v2.CanSet())}

输出如下

v1 可写性为: falsev2 可写性为: true

知道了如何使反射的世界里的对象具有可写性后,接下来是时候了解一下如何对修改更新它。

反射对象,都会有如下几个以 Set 单词开头的方法

go语言中反射三定律指的是什么

这些方法就是我们修改值的入口。

来举个例子

package mainimport (    "fmt"    "reflect")func main() {    var name string = "Go编程时光"    fmt.Println("真实世界里 name 的原始值为:", name)    v1 := reflect.ValueOf(&name)    v2 := v1.Elem()    v2.SetString("python编程时光")    fmt.Println("通过反射对象进行更新后,真实世界里 name 变为:", name)}

输出如下

真实世界里 name 的原始值为: Go编程时光通过反射对象进行更新后,真实世界里 name 变为: Python编程时光

到此,关于“go语言中反射三定律指的是什么”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注编程网网站,小编会继续努力为大家带来更多实用的文章!

--结束END--

本文标题: go语言中反射三定律指的是什么

本文链接: https://www.lsjlt.com/news/348588.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • c++中int和double有什么区别
    int 和 double 是 c++ 的数据类型,用于表示整数和浮点数。它们的关键区别在于:1. 范围:int 为整数,double 为浮点数且范围更大;2. 存储大小:int 占 4 ...
    99+
    2024-05-14
    c++ 隐式转换
  • C++ 多线程程序测试的挑战和策略
    多线程程序测试面临不可重复性、并发错误、死锁和缺乏可视性等挑战。策略包括:单元测试:针对每个线程编写单元测试,验证线程行为。多线程模拟:使用模拟框架在控制线程调度的情况下测试程序。数据竞...
    99+
    2024-05-14
    c++ 多线程
  • c++中深拷贝和浅拷贝的应用时间
    浅拷贝复制对象指针或引用,仅适用于不含动态分配内存或简单数据结构的对象;深拷贝复制实际数据,包括动态分配内存,适用于包含动态分配内存或复杂数据结构的对象。 浅拷贝和深拷贝的应用时间 在...
    99+
    2024-05-14
    c++
  • 探索用于 C++ 服务器架构的高级数据结构
    在 c++++ 服务器架构中,选择适当的高级数据结构至关重要。哈希表用于快速数据查找,树用于表示数据层次结构,图用于表示对象之间的关系。这些数据结构在实践中有着广泛的应用,例如缓存系统、...
    99+
    2024-05-14
    c++ 数据结构 社交网络 键值对
  • fixed在c++中的作用
    fixed 关键字在 c++ 中用于将浮点数存储为固定小数,提供更高精度,尤其适用于需要高精度的金融计算。fixed 将浮点数表示为具有固定小数位数的小数,默认情况下使用十进制表示法,小...
    99+
    2024-05-14
    c++
  • insert在c++中怎么用
    insert() 函数在 c++ 中用于在容器(如 vector、set)中插入元素,提供了一种动态调整容器大小并添加新元素的方法。它需要两个参数:要插入元素的位置 (pos) 和要插入...
    99+
    2024-05-14
    c++ 标准库
  • 如何使用 Golang 构建 RESTful API 并处理 JSON 响应?
    如何使用 golang 构建和处理 json 响应的 restful api步骤:创建 golang 项目并安装 gorilla mux。定义路由并处理 http 请求。安装 json ...
    99+
    2024-05-14
    golang git
  • c++中int和long的区别
    int 和 long 都是 c++ 中的整型类型,主要区别在于范围和存储空间:范围:int 为 32 位整数,范围为 [-2^31, 2^31-1];long 为 64 位整数,范围为 ...
    99+
    2024-05-14
    c++ 数据丢失
  • c++中int a(n)和int a[n]的区别
    int a(n)声明一个不可变的整型变量,而int a[n]声明一个可修改元素的整型数组,用于存储和处理数据序列或集合。 int a(n) 和 int a[n] 在 C++ 中的区别 ...
    99+
    2024-05-14
    c++
  • C++ 多线程编程中调试和故障排除的技术
    c++++ 多线程编程的调试技巧包括:使用数据竞争分析器检测读写冲突,并使用同步机制(如互斥锁)解决。使用线程调试工具检测死锁,并通过避免嵌套锁和使用死锁检测机制来解决。使用数据竞争分析...
    99+
    2024-05-14
    c++ 多线程 故障排除 同步机制
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作