Golang-interface(四 反射)

golang interface 与 反射 golang interface 与 反射golang interface 使用场景golang interface 数据结构golang interface 一些使用场景原理函数参数是 interface 的成本interface{}和带方法的interface的赋值过程动态类型与动态分发是如何实现的,动态分发什么时候进行,并且有什么样的调用成本如何进行类型转换如何进行断言,断言的成本有多高为什... 阅读详情

github:https://github.com/ZhangzheBJUT/blog/blob/master/reflect.md 

一 反射的规则

反射是程序运行时检查其所拥有的结构,尤其是类型的一种能力;这是元编程的一种形式。它同时也是造成混淆的重要来源。

每个语言的反射模型都不同(同时许多语言根本不支持反射)。本节将试图明确解释在 Go 中的反射是如何工作的。

1. 从接口值到反射对象的反射

在基本的层面上,反射只是一个检查存储在接口变量中的类型和值的算法。在 reflect 包中有两个类型需要了解:Type 和 Value。这两个类型使得可以访问接口变量的内容,还有两个简单的函数,reflect.TypeOf 和 reflect.ValueOf,从接口值中分别获取 reflect.Type 和 reflect.Value。(注:从 reflect.Value 也很容易能够获得 reflect.Type,不过这里让 Value 和 Type 在概念上是分离的)

从 TypeOf 开始:

package main

import (
     "fmt"
    "reflect"
)

func main() {
    var x float64 = 3.4
    fmt.Println("type:", reflect.TypeOf(x))
}

这个程序打印 type: float64 

接口在哪里呢,读者可能会对此有疑虑,看起来程序传递了一个 float64 类型的变量 x,而不是一个接口值,到 reflect.TypeOf。但是,它确实就在那里:如同 godoc 报告的那样,reflect.TypeOf 的声明包含了空接口:

// TypeOf 返回 interface{} 中的值反射的类型。
   func TypeOf(i interface{}) Type

当调用 reflect.TypeOf(x) 的时候,x 首先存储于一个作为参数传递的空接口中;reflect.TypeOf 解包这个空接口来还原类型信息。

reflect.ValueOf 函数,当然就是还原那个值(从这里开始将会略过那些概念示例,而聚焦于可执行的代码):

var x float64 = 3.4
fmt.Println("value:", reflect.ValueOf(x))

打印

value: <float64 Value>

除了reflect.Type 和 reflect.Value外,都有许多方法用于检查和操作它们。一个重要的例子是 Value 有一个 Type 方法返回 reflect.Value 的 Type。另一个是 Type 和 Value 都有 Kind 方法返回一个常量来表示类型:Uint、Float64、Slice 等等。同样 Value 有叫做 Int 和 Float 的方法可以获取存储在内部的值(跟 int64 和 float64 一样):

var x float64 = 3.4
v := reflect.ValueOf(x)
fmt.Println("type:", v.Type())
fmt.Println("kind is float64:", v.Kind() == reflect.Float64)
fmt.Println("value:", v.Float())                      

打印

type: float64                       
kind is float64: true               
value: 3.4                      

同时也有类似 SetInt 和 SetFloat 的方法,不过在使用它们之前需要理解可设置性,这部分的主题在下面的第三条军规中讨论。

反射库有着若干特性值得特别说明。

  • 为了保持 API 的简洁,“获取者”和“设置者”用 Value 的最宽泛的类型来处理值:例如,int64 可用于所有带符号整数。也就是说 Value 的 Int 方法返回一个 int64,而 SetInt 值接受一个 int64;所以可能必须转换到实际的类型:

      var x uint8 = 'x'
      v := reflect.ValueOf(x)
      fmt.Println("type:", v.Type()) // uint8.
      fmt.Println("kind is uint8: ", v.Kind() == reflect.Uint8) // true.
      x = uint8(v.Uint()) // v.Uint 返回一个 uint64.
    
    
  • 反射对象的 Kind 描述了底层类型,而不是静态类型。如果一个反射对象包含了用户定义的整数类型的值,就像

      type MyInt int
      var x MyInt = 7
      v := reflect.ValueOf(x)‘
    
    

v 的 Kind 仍然是 reflect.Int,尽管 x 的静态类型是 MyInt,而不是 int。换句话说,Kind 无法从 MyInt 中区分 int,而 Type 可以。

2. 从反射对象到接口值的反射

如同物理中的反射,在 Go 中的反射也存在它自己的镜像。

从 reflect.Value 可以使用 Interface 方法还原接口值; 此方法可以高效地打包类型和值信息到接口表达中,并返回这个结果:

// Interface 以 interface{} 返回 v 的值。
func (v Value) Interface() interface{}

可以这样作为结果

y := v.Interface().(float64) // y 将为类型 float64。
fmt.Println(y)

通过反射对象 v 可以打印 float64 的表达值。

然而,还可以做得更好。fmt.Println,fmt.Printf 等其他所有传递一个空接口值作为参数的函数,在 fmt 包内部解包的方式就像之前的例子这样。因此正确的打印 reflect.Value 的内容的方法就是将 Interface 方法的结果进行格式化打印(formatted print routine).

fmt.Println(v.Interface())

为什么不是 fmt.Println(v)?因为 v 是一个 reflect.Value;这里希望获得的是它保存的实际的值。

由于值是 float64,如果需要的话,甚至可以使用浮点格式化:

fmt.Printf("value is %7.1e\n", v.Interface())

输出: 3.4e+00 

再次强调,对于 v.Interface() 无需类型断言其为 float64;空接口值在内部有实际值的类型信息,而 Printf 会发现它。 

简单来说,Interface 方法是 ValueOf 函数的镜像,除了返回值总是静态类型 interface{}。

回顾:反射可以从接口值到反射对象,也可以反过来。

3. 为了修改反射对象,其值必须可设置

var x float64 = 3.4
v := reflect.ValueOf(x)
v.SetFloat(7.1) // Error: will panic.

如果运行这个代码,它报出神秘的 panic 消息

panic: reflect.Value.SetFloat using unaddressable value

问题不在于值 7.1 不能地址化;在于 v 不可设置。设置性是反射值的一个属性,并不是所有的反射值有此特性。

Value的 CanSet 方法提供了值的设置性;在这个例子中,

var x float64 = 3.4
v := reflect.ValueOf(x)
fmt.Println("settability of v:" , v.CanSet())

打印

settability of v: false

对不可设置值调用 Set 方法会有错误。

但是什么是设置性? 

设置性有一点点像地址化,但是更严格。这是用于创建反射对象的时候,能够修改实际存储的属性。设置性用于决定反射对象是否保存原始项目。当这样

var x float64 = 3.4
v := reflect.ValueOf(x)

就传递了一个 x 的副本到 reflect.ValueOf,所以接口值作为 reflect.ValueOf 参数创建了 x 的副本,而不是 x 本身。因此,如果语句

v.SetFloat(7.1)

允许执行,虽然 v 看起来是从 x 创建的,它也无法更新 x。反之,如果在反射值内部允许更新 x 的副本,那么 x 本身不会收到影响。这会造成混淆,并且毫无意义,因此这是非法的,而设置性是用于解决这个问题的属性。

这很神奇?其实不是。这实际上是一个常见的非同寻常的情况。考虑传递 x 到函数:

f(x) 由于传递的是 x 的值的副本,而不是 x 本身,所以并不期望 f 可以修改 x。如果想要 f 直接修改 x,必须向函数传递 x 的地址(也就是,指向 x 的指针):

f(&x) 这是清晰且熟悉的,而反射通过同样的途径工作。如果希望通过反射来修改 x,必须向反射库提供一个希望修改的值的指针。

来试试吧。首先像平常那样初始化 x,然后创建指向它的反射值,叫做 p。

var x float64 = 3.4
p := reflect.ValueOf(&x) // 注意:获取 X 的地址。
fmt.Println("type of p:", p.Type())
fmt.Println("settability of p:" , p.CanSet())

这样输出为

type of p: *float64
settability of p: false

反射对象 p 并不是可设置的,而且我们也不希望设置 p,实际上是 *p。为了获得 p 指向的内容,调用值上的 Elem 方法,从指针间接指向,然后保存反射值的结果叫做 v:

v := p.Elem()
fmt.Println("settability of v:" , v.CanSet())

现在 v 是可设置的反射对象,如同示例的输出,

settability of v: true

而由于它来自 x,最终可以使用 v.SetFloat 来修改 x 的值:

v.SetFloat(7.1)
fmt.Println(v.Interface())
fmt.Println(x)

得到期望的输出

7.1
7.1

反射可能很难理解,但是语言做了它应该做的,尽管底层的实现被反射的 Type 和 Value 隐藏了。务必记得反射值需要某些内容的地址来修改它指向的东西。

二结构体

在之前的例子中 v 本身不是指针,它只是从一个指针中获取的。这种情况更加常见的是当使用反射修改结构体的字段的时候。也就是当有结构体的地址的时候,可以修改它的字段。

这里有一个分析结构值 t 的简单例子。由于希望对结构体进行修改,所以从它的地址创建了反射对象。设置了 typeOfT 为其类型,然后用直白的方法调用来遍历其字段(参考 reflect 包了解更多信息)。注意从结构类型中解析了字段名字,但是字段本身是原始的 reflect.Value 对象。

type T struct {
  A int
  B string
}
t := T{23, "skidoo"}
s := reflect.ValueOf(&t).Elem()
typeOfT := s.Type()
for i := 0; i < s.NumField(); i++ {
    f := s.Field(i)
    fmt.Printf("%d: %s %s = %v\n", i,
    typeOfT.Field(i).Name, f.Type(), f.Interface())
}

程序输出:

0: A int = 23
1: B string = skidoo

还有一个关于设置性的要点:T 的字段名要大写(可导出),因为只有可导出的字段是可设置的。

由于 s 包含可设置的反射对象,所以可以修改结构体的字段。

s.Field(0).SetInt(77)
s.Field(1).SetString("Sunset Strip")
fmt.Println("t is now", t)

这里是结果:

t is now {77 Sunset Strip}

如果修改程序使得 s 创建于 t,而不是 &t,调用 SetInt 和 SetString 会失败,因为 t 的字段不可设置。

三 总结

反射的规则如下: 

从接口值到反射对象的反射 

从反射对象到接口值的反射 

为了修改反射对象,其值必须可设置 

一旦理解了 Go 中的反射的这些规则,就会变得容易使用了,虽然它仍然很微妙。这是一个强大的工具,除非真得有必要,否则应当避免使用或小心使用。

还有大量的关于反射的内容没有涉及到——channel 上的发送和接收、分配内存、使用 slice 和 map、调用方法和函数。

事例代码: https://github.com/ZhangzheBJUT/GoProject/blob/master/reflect/main.go

参考:  http://blog.golang.org/laws-of-reflection

Golang笔记——Interface类型 接口定义了类型应该具备的行为(即方法)。Go 的接口与其他语言(如 Java 或 C++)中的接口有一些不同之处,特别是Go 的接口不需要显式声明实现,即只要类型实现了接口的方法,就自动实现了该接口。Method1() // 方法签名Method2() // 方法签名类型断言:用于在运行时提取接口的具体类型值,如果类型不匹配,可以使用ok变量避免运行时错误。类型开关:允许你对接口值的动态类型进行多分支判断,可以在多个可能的类型之间选择。反射:通过reflect包可以。 阅读详情

相关推荐

Go语言反射机制详解:interface{}与reflect的妙用

本文旨在全面解析Go语言反射机制,特别关注interface{}与reflect包的交互原理和实际应用。我们将覆盖从基础概念到高级用法的完整知识体系,帮助读者掌握这一强大但容易被误解的语言特性。文章首先介绍反射的基本概念,然后深入解析interface{}的实现原理,接着详细讲解reflect包的核心API。随后通过实际案例展示反射的应用场景,最后讨论性能优化和最佳实践。反射(Reflection): 程序在运行时检查、修改自身结构和行为的能力: Go语言中的空接口类型,可以容纳任何值Type。

AI Agent Harness Engineering 1168

Go语言标准库学习之reflect——Go语言中如何通过反射获取interface{}类型数据的值、类型以及方法调用

在Go语言中我们常常会用到接口类型来编写万能程序,函数接收到参数后,我们需要分析出参数类型,这就需要用到类型反射了,这篇文章向大家介绍Go语言标准库 reflect 的使用,希望对你有帮助。 一、interface和反射 1. Go语言中类型设计原则 学习反射前,我们先了解一下Golang关于类型设计的一些原则: 变量包括(type,value)两部分。 type包括static type和concrete type,简单来说static type是在编码看得见的类型(int、string、float.

random_w的博客 9264

免费获取船舶轨迹数据?手把手教你用Python处理中国海洋卫星AIS数据

本文详细介绍了如何免费获取中国海洋卫星AIS数据,并使用Python进行船舶轨迹数据的处理与分析。从数据源评估到自动化处理流水线,再到高级分析与可视化技巧,手把手教你避开常见问题,实现高效数据处理。特别适合航运数据分析、物流优化和海上安全研究等领域的研究者和开发者。

weixin_42616115的博客 320

golang的interface与反射实现原理

1、什么是interface ​ interface通过定义一系列的方法集合指定了其他类型的行为。对于一个类型,当且仅当它实现了某一个interface方法集的所有方法,我们称这个类型满足了这个interface。可以理解为interface是一个抽象的类型,这个类型定义了一系列的方法,当某个其他类型完全实现了该interface的所有的方法,可以称这个类型是当前抽象interface的一个实例。可以把interface理解为一个合约,方法即是合同细节,当其他类型满足了所有细节(方法),即可认为该类型满足了

hust_joker的博客 2017

【随机过程】Markov Chain的定义及发展历程与实际应用

本模块书写的初衷:由于在学习张波张景肖等学者所著的《应用随机过程》存在着不甚了解的知识点,经过个人查找资料并整理、分享,希望能够为有同样疑惑的读者贡献绵薄之力,同时如果个人编写内容有错误,恳请读者指正。本文为对《应用随机过程》第42页中Markov Chain基本概念的补充。1.Markov Chain的定义俄国数学家 Andrey Andreyevich Markov 研究并提出一个用数学方法就能解释自然变化的一般规律模型,被命名为马尔科夫链(Markov Chain)。

qq_49657150的博客 3746

QCustomPlot实现毫秒级时间轴数据的动态可视化与实时刷新优化

本文深入探讨了如何利用QCustomPlot库高效实现毫秒级时间轴数据的动态可视化与实时刷新。文章从基础配置讲起,详细介绍了时间轴初始化、数据平滑滚动、增量更新与内存管理等核心优化技巧,并分享了应对高频数据的缓冲策略及OpenGL硬件加速等高级优化手段,旨在解决实时图表卡顿问题,提升流畅度。

weixin_29012677的博客 424

Go语言 interface 理解 & 与反射的关系

理解 interface 1、interface 是一种类型 准确来说,interface 是带有一组方法的一种类型,这些方法定义了 interface 的行为。如果一个类型实现了一个 interface 中所有方法,则该类型实现了该 interface。又因为 go 允许不带任何方法的interface存在,这种interface成为空interface。所以所有类型都实现了 empty interface,因为任何一种类型至少实现了 0 个方法。 go 没有显式的关键字用来实现 interfa

judgejames的博客 1403

别再踩坑了!Win10下用阿狸狗V3.2.6安装Cadence SPB 17.4的保姆级避坑指南

本文提供了Win10系统下使用阿狸狗V3.2.6安装Cadence SPB 17.4的详细避坑指南,涵盖环境准备、安装失败清理、分步安装与问题诊断、授权配置及典型故障排除等内容,帮助用户顺利完成安装并避免常见错误。

weixin_32576389的博客 359

Go 反射

GORM 也是类似,它将模型元数据称为Schema。文章转载自本人的掘金社区: https://juejin.cn/post/7135610007030562847

weixin_48943299的博客 789

GPU架构(1.1)--介绍 GPU的 VPU(Vision Processing Unit)架构

基于自研的 VPU(Vision Processing Unit)架构,专为高性能图形处理和深度学习设计。VPU 架构通过优化计算单元、内存层次结构、同步机制和指令流水线,提供了卓越的性能和能效。VPU 架构通过优化计算单元、内存层次结构、同步机制和指令流水线,提供了卓越的性能和能效。这种架构不仅适用于高性能图形处理,还广泛应用于深度学习、科学计算和边缘计算等领域。通过了解 VPU 架构的详细设计和工作原理,读者可以更好地利用这些先进的 GPU 技术,开发出高效、可靠的计算系统。

MHD0815的博客 1300

开关电源中的高频振荡噪声及其抑制方法

在开关电源的工作过程中,随着开关管的开关动作,电流和电压波形会出现剧烈的波动,尤其是在开关管切换的瞬间,往往会导致较强的过冲现象。开关电源中的高频振荡噪声是导致电磁兼容问题的主要源头之一,尤其是MOS管的通断和整流二极管的反向恢复过程中,会产生较强的高频振荡噪声。通过合理调节R和C的值,可以大大缓解高频振荡带来的EMC问题,从而有效提升电源的电磁兼容性。整流二极管反向恢复时的振荡会导致高频噪声产生,这些高频噪声源可被传导到电源的输出端,甚至可能通过电源的共地路径扩散到其他电路,影响系统的稳定性和可靠性。

硬核科技 1531

Go语言学习笔记(四)结构体struct & 接口Interface & 反射

加 Golang学习 QQ群共同学习进步成家立业工作 ^-^ 群号:96933959 结构体struct struct 用来自定义复杂数据结构,可以包含多个字段(属性),可以嵌套; go中的struct类型理解为类,可以定义方法,和函数定义有些许区别; struct类型是值类型。 struct定义 type User struct { Name string A

linux12a的博客 437

【银河麒麟服务器操作系统】安装英伟达4090D显卡驱动操作指南

本文详细介绍了在银河麒麟高级服务器操作系统V10SP3-2403-X86版本上安装NVIDIA RTX4090D显卡驱动的完整流程。主要内容包括:准备工作检查、开源驱动禁用、运行级别切换、依赖包安装以及交互式安装过程中的关键选项选择(32位库选Yes、X配置选No)。通过禁用nouveau驱动、重建内核引导等步骤确保驱动安装成功,最后通过nvidia-smi命令验证驱动安装情况。文档提供了操作检查表和安装流程图,帮助用户在银河麒麟系统上顺利完成显卡驱动部署。

信创运维工程师 · 银河麒麟 & 容器实战 | 金融生产环境迁移·等保加固 | 142篇原创·17万+阅读 3104

Go语言学习笔记(四)结构体struct & 接口Interface & 反射reflect

加 Golang学习 QQ群共同学习进步成家立业工作 ^-^ 群号:96933959 结构体struct struct 用来自定义复杂数据结构,可以包含多个字段(属性),可以嵌套; go中的struct类型理解为类,可以定义方法,和函数定义有些许区别; struct类型是值类型。 struct定义 type User struct { Name string ...

weixin_30706691的博客 202

GEE训练教程:如何批量从Google assets中将时序影像批量导出到Google drive

本文介绍了一种使用Google Earth Engine(GEE)批量导出2000-2023年叶绿素a(Chl-a)浓度数据的方法。通过JavaScript代码自动生成多个年份的导出任务,将数据保存至Google Drive指定文件夹。文章详细说明了代码参数配置、使用步骤和注意事项,包括资产命名规则验证、导出任务启动方式,以及分辨率、区域范围等关键参数的调整建议。同时提供了常见问题的解决方案,如处理缺失年份数据、解决导出像素超限等问题。该方法可显著提高多年份Chl-a数据批量导出的效率,适用于水体环境研究的

此星光明博客 91

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

go struct 静态函数_Go语言学习笔记(四)结构体struct & 接口Interface & 反射reflect...

加 Golang学习 QQ群共同学习进步成家立业工作 ^-^ 群号:96933959结构体structstruct 用来自定义复杂数据结构,可以包含多个字段(属性),可以嵌套;go中的struct类型理解为类,可以定义方法,和函数定义有些许区别;struct类型是值类型。struct定义type User struct{NamestringAge int32messstring}varuser ...

weixin_39734048的博客 441

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

go struct 静态函数_go struct 静态函数_Go语言学习笔记(四)结构体struct & 接口Interface & 反射reflect......

加 Golang学习 QQ群共同学习进步成家立业工作 ^-^ 群号:96933959结构体structstruct 用来自定义复杂数据结构,可以包含多个字段(属性),可以嵌套;go中的struct类型理解为类,可以定义方法,和函数定义有些许区别;struct类型是值类型。struct定义type User struct{NamestringAge int32messstring}varuser U...

weixin_35439783的博客 527

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

golang interface 转 struct 反射

golang interface 转 struct 反射 type YYSUserInfo struct { UserInfo string `json:"user_info"`//用户表 MacPC string `json:"mac_pc"`//MAC:PC名 ID string `json:"id"`//id:账号 Online bool `json:"online"`//是否在线 Ping int `json:"ping"`//心跳次数 LastPing time.Duration `

hbmask的博客 1555

GO的interface的使用和反射

李某人带你了解GO的interface的使用和反射(面试版)

分享包括但不限于计算机基础知识、数据结构与算法、Golang技术栈。年与时驰,意与日去,遂成枯落,多不接世,悲守穷庐,将复何及! 1680
上一篇: Golang-interface(三 接口类型)
下一篇: Golang-函数式编程(闭包)
张哲BJUT
博客等级 码龄15年 151粉丝 111原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值