Go 笔记 反射

Go 学习笔记 · 进阶篇 · 第三天:反射机制 反射就是程序在运行时检查其类型和值的能力。Go 提供了reflect包,可以用来动态查看、修改、调用类型。:类型信息:值信息反射让你在运行时查看并修改类型和值结构体字段和标签都能动态访问虽强大,但一定要慎用,能不用就别用!今日咒语:“写 Go 不用反射能活,用了反射可能活得更野,但也可能挂得更快。 阅读详情

Go 笔记 反射

类型和接口

go是静态类型语言,所以

type MyInt int

var i int
var j MyInt

i和j是不相容的。并且

var r io.Reader
r = os.Stdin
r = bufio.NewReader(r)
r = new(bytes.Buffer)

不论r的具体值是什么,其类型总是io.Reader,因为接口在go中也是静态的类型。尽管在运行时,一个接口变量保存的具体值的类型会变,但这个值总是要满足接口,也就是必须实现接口中声明的方法的。

一个接口类型变量保存一个(value,type)二元组:赋给这个变量的具体值,这个值的类型描述符。更精确的说,值就是具体的底层数据,类型就是这个底层数据的类型

var r io.Reader
tty, err := os.OpenFile("/dev/tty", os.O_RDWR, 0)
if err != nil {
    return nil, err
}
r = tty

于是,r包含的(value,type)二元组就是(tty,*os.File)。我们知道os.File不止实现了Read方法,所以下面这个赋值语句

var w io.Writer
w = r.(io.Writer)

是一个类型断言,判断在r中的值,注意不是r,是否实现了io.Writer,如果是,就把它赋给w。接下来,我们可以

var empty interface{}
empty = w

于是,空接口empty就包含了相同的二元组(tty,*os.File)。

这里我们不需要类型断言,因为任意的值包括w必然实现了空接口。但是把一个值从Reader转移到Writer就必须用类型断言,因为Writer的方法不是Reader的子集。

另外,一个重要的事实是一个接口包含的二元组总是(value,concrete type),而不会是(value,interface type)。一个接口并不持有接口值,换句话说,接口本身并没有值。

反射要知道的第一件事:从接口的值到反射对象的反射

从最基本的层次而言,反射只是一种机制,用来检查一个保存着一个接口的对象内部的类型和值对。

我们从reflect包中的两个类型Type和Value,以及两个函数reflect.TypeOf和reflect.ValueOf开始。

通过reflect.Type和reflect.Value我们可以得到接口变量的内容,而这两个函数可以分别从一个接口变量获取到reflect.Type和reflect.Value。当然,从reflect.Value也很容易得到reflect.Type。

我们从Typeof开始

package main

import (
    "fmt"
    "reflect"
)

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

运行结果是

type: float64

不要奇怪,这里传进去的是一个float64而不是一个接口,因为TypeOf的签名是这样的

// TypeOf returns the reflection Type of the value in the interface{}.
func TypeOf(i interface{}) Type

我们已经知道,一个接口变量会保存其具体值的二元组(value,type)。当我们调用reflect.TypeOf(x)时,x就首先保存到一个空接口中,然后作为参数传入,reflect.TypeOf再解开这个接口并恢复其类型信息。

而reflect.ValueOf函数则会恢复其值。

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

结果是

value: <float64 Value>
value: 3.4

调用String()的原因是fmt包会深入到reflect.Value的内部挖出具体的值3.4,而String()不会。

Value有一个Type方法,返回一个reflect.Value的Type。

Value和Type都有一个Kind方法,返回一个常量,表示其保存的数据项的是什么,如:Uint, Float64,Slice等等。

而Value的Int和Float等方法则可以让我们得到其内部保存的数据项的值。

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

为简单记,Value的getter和setter方法总是使用位数最长的类型,比方说所有的有符号整型都用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 returns a uint64.

如果上面最后不做类型转换,便会有个编译错误。

另外就是Kind表示的是一个反射对象的底层数据类型而不是在变量声明体现的静态类型。

type MyInt int
var x MyInt = 7
v := reflect.ValueOf(x)
fmt.Println("type:", v.Type())
fmt.Println("kind:", v.Kind())

运行结果是

type: main.MyInt
kind: int

v.Kind()仍然是reflect.Int,而不是MyInt,或者说,Kind不能区分MyInt和int,但Type可以。

反射要知道的第二件事:从反射对象到接口的值的反射

给定一个reflect.Value,使用Interface方法可以恢复一个接口的值,实际上,这个方法是把类型和值的信息打包进到一个接口,然后返回这个接口,注意这里说的值和类型都是针对reflect对象,而不是某个变量的值和类型,当然这个变量的值和类型的信息是分别包含在作为reflect对象的值和信息里边的。概念上这里是需要明晰的。

// Interface returns v's value as an interface{}.
func (v Value) Interface() interface{}

比如

y := v.Interface().(float64) // y will have type float64.
fmt.Println(y)

实际上,像fmt.Println, fmt.Printf这些函数的参数是空接口,在fmt内部会解包,所以只要这样就可以了

fmt.Println(v.Interface())

空接口的值包含了类型信息,Printf可以获取到。

所以,Interface方法是ValueOf函数的反函数。

反射要知道的第三件事:要改变一个反射对象,其值必须是可设置的

先看个错误的例子

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

错误信息是这样的

panic: reflect.Value.SetFloat using unaddressable value

这里的问题在于不是7.1这个值不可寻址,而是v不可设置。可设置性(Settability)是反射Value的属性,但不是所有的反射Value都有这个属性。

可以用CanSet方法来测试一个Value的可设置性

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

将会输出

settability of v: false

可设置性有一点像可寻址性,但严格些。 它是这样一个属性,表明一个反射对象能够修改其所指向的实际存储空间。可设置性取决于该反射对象是否持有原始数据项。

当我们说

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

时给reflect.ValueOf传递的是x的一个副本,因此作为ValueOf参数的接口值是根据x的这个副本创建的,而不是x本身。因此,如果v.SetFloat(7.1)是合法的,那么它修改的也只是这个副本,而不是x本身,为简便记就定为非法了,引入可设置性也是为了避免这种情况。想象一下C语言中函数参数的传递方式,当然go语言也是同样的道理。

所以我们需要这样

var x float64 = 3.4
p := reflect.ValueOf(&x) // Note: take the address of 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,如果很好的理解了C或者就是go本身的指针,这里也是很好理解的,所以我们需要使用Value的Elem方法来得到这个地方

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

注意,v不是指针变量,而是通过指针间接访问得到的x的一个别名,它是可设置的。因此

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

输出自然就是

7.1
7.1

结构Struct

前面所说的机制特别适用于对结构的操作

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

输出为

&{23 skidoo}
*main.T
{23 skidoo}
main.T
0: (A int) int = 23
1: (B string) string = skidoo

注意T的字段名都是大写的,这是有意的,因为只有大写首字母的标识符才是可设置的,当然我们知道这时候这样的标识符才是可导出的,这当然不是偶然或者说巧合。

这个例子我们可以得出这样的信息,当然具体的方法需要参看reflect包。

  • typeOfT是一个Type对象,描述的是T这个结构本身,其Field方法返回其包含的域的信息,比方说Name,Type
  • s是一个Value对象,其Field方法返回的是代表其包含的域的Value对象,所以可以通过Type()和Interface()方法分别获得这个域的类型和值。
  • f是一个Value对象,其Type方法返回这个域的Type,其Interface方法返回这个域的Value。

最后,我们可以修改这个结构的域了,当然指的是一个实际的结构变量,而不是这个结构的定义,要记得一个结构定义是不分配存储空间的因此必然也是不可寻址的,当然也就无从修改了。

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

输出

t is now {77 Sunset Strip}

注意对不同类型的域要用不同Set方法,毕竟既然语言已经提供了反射机制,想骗过类型系统那是几乎不可能的了。

结论

没有了,就上面三条注意的事项,或者说要知道的事情,翻译成法则其实并不恰当。

基于simulink的智能驾驶领域--智能车道保持辅助系统仿真 LKAS通过摄像头等传感器检测车辆相对于车道线的位置,并自动调整转向,以帮助驾驶员保持在车道内行驶。将摄像头传感器模块的输出连接到车道线检测模块的输入端口,再将车道线检测模块的输出连接到转向控制器模块的输入端口,最后将转向控制器模块的输出连接到车辆动力学模型的转向输入端口,形成闭环控制系统。上升时间是指系统从初始转向角上升到目标转向角所需的时间,调节时间是指系统进入并保持在目标转向角一定范围内的所需时间。该控制器将根据摄像头传感器的数据调整车辆的转向角,以保持车辆在车道内行驶。 阅读详情

相关推荐

WPF的基础设施:XAML基础语法

XAML是WPF的核心标记语言,采用XML语法实现界面与逻辑分离。其基础语法包括:1)控件声明方式(特性语法和属性元素语法);2)x:Name与Name的区别(适用范围和编译行为);3)注释规范与代码折叠技巧;4)实用特性如属性继承、附加属性等。文章还提供了常见错误排查方法,建议通过实践练习掌握XAML开发技巧。全文系统介绍了XAML的核心语法与应用场景,是WPF开发者的基础学习指南。

Hare的博客 1757

Go语言反射定律

Go语言三大反射定律介绍

信道者 369

C# : 计算并输出这一天是这一年的第几天

C#之输入某年,然后输入某月,再输入某日,计算并输出这一天是这一年的第几天的功能算法

saitoDeng的博客 970

编程笔记 Golang基础 033 反射的类型与种类

反射机制的作用范围涵盖了几乎所有的类型和值的操作层面,它极大地增强了Go语言在运行时对于自身类型系统的探索和操作能力。然而,这种灵活性也带来了性能开销和安全性问题,因此应当谨慎使用,在保证代码简洁性和高效性的前提下选择性地利用反射特性。

明月看潮生的博客 1456

当MATLAB遇见ROS2:一场跨平台实时仿真的技术融合与创新实践

本文探讨了MATLAB/Simulink与ROS2在跨平台实时仿真中的技术融合与创新实践。重点介绍了在Windows+WSL2异构环境下构建高可靠性联合仿真系统的架构设计、PX4仿真环境与DDS中间件的集成方法,以及MATLAB R2024b对ROS2深度集成的实战技巧,为复杂机器人系统的开发提供了高效解决方案。

rum55的博客 245

ZYNQ千兆网UDP通信实战:从Vivado配置到LWIP优化全流程(附避坑指南)

本文详细介绍了在ZYNQ平台上实现千兆以太网UDP通信的全流程,包括Vivado硬件配置、LWIP协议栈优化及实战代码示例。通过关键参数调整和性能优化,系统吞吐量可提升至850Mbps以上,适用于高速网络通信场景。

weixin_29161785的博客 230

编程笔记 Golang基础 032 反射

反射(Reflection)是一种机制,它允许程序在运行时访问和操作任意类型对象的内部信息。具体来说,Go语言通过其内置的reflect包提供了对类型和值进行动态操作的能力,使得开发者能够在编译时不知道具体类型的情况下,依然能够检查变量的类型、结构体字段、调用方法以及修改变量的值。

明月看潮生的博客 1447

移动端实战指南:深入理解 & 接入 IAB OMSDK

OMSDK(OpenMeasurementSDK)是IABTechLab推出的标准化测量工具,用于统一移动端/原生App广告的可见性和验证流程。其核心包括Native库(采集设备数据)和OMID JavaScript层(实现通信),通过标准化接口让开发者只需接入一套SDK即可兼容多个测量厂商,减少冲突和性能损耗。v1.4版本强化了对CTV和原生App场景的支持,而v1.5新增了画中画检测等功能。集成流程需通过IAB技术实验室注册下载,并遵循严格的命名空间规则。测试阶段需嵌入验证脚本并通过指定工具验证

帅次的博客 1188

golang 反射_Golang入门笔记09反射

反射是用程序检查其所拥有的结构,尤其是类型的一种能力。反射可以在运行时(不必在编译时)检查类型和变量,例如大小、变量、方法和动态调用这些方法。方法和类型的反射reflect包提供了反射功能,它定义两个重要类型:Type和Value,分别表示动态类型和值。有两个常用的方法:reflect.TypeOf:返回对象的具体类型。reflect.ValueOf:返回对象的值。反射是先检查一个...

weixin_39846289的博客 115

用 SAP ABAP ALV 重塑 SM30:可扩展的自定义表维护视图与自动传输实践

文章摘要: 本文提出了一种基于ALV Grid的动态表维护框架,用于替代传统SM30维护视图。该方案通过输入表名自动生成维护界面,支持动态筛选、批量操作和自动传输管理。核心设计包括:利用BAdI实现扩展点、通过行状态字段简化数据变更跟踪、使用Style Table控制单元格编辑权限、集成CTS实现自动传输。相比SM30标准维护,该框架显著提升了交互体验(支持ALV排序/过滤/汇总)和业务扩展性(统一校验与权限控制),同时解决了"维护+传输"流程割裂的问题。文中详细阐述了技术实现方案,并提

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 318

震惊!用刚发布的GPT-5写研究现状,文献综述写作一整个大逆袭(附步骤+10条AI提示词)

每类分别给出:国内重点(3 条)、国外重点(3 条)、代表性方法(2–3 条)、代表成果(各 1–2 条)、主要问题(2 条)。每个场景给出:技术方案(2–3 种)、研究目标、实际成效/案例、当前挑战(3 点)。指令:基于国内外现状,系统列出当前学界的研究空白、关键未解问题与争议焦点(每项配简要证据/示例),并针对每个空白提出 1–2 条可行的研究方向或方法路径。三、差异与研究空白分析(≥300 字)。对每个小节提供:小标题、中心论点(1 句)、拟写要点(3–5 条)、建议引用类型(理论/方法/案例)。

qq_64378896的博客 114

Go反射学习笔记

包含了一些go的学习笔记,留给自己用,理论不少,代码也有

执念斩长河 999

【数据集】全球大气空气柱平均甲烷(XCH₄)和一氧化碳(XCO)浓度数据集 TROPOMI/WFMD

全球大气空气柱平均甲烷(XCH₄)和一氧化碳(XCO)浓度数据集 TROPOMI/WFMD

WW、forever的博客 388

Go语言学习笔记(九)反射

《Go语言反射机制详解》摘要: Go语言的反射机制通过reflect包实现,主要包含类型信息和值信息两部分。反射的起点是值或对象,可获取运行时精准的类型信息(通过TypeOf)和内容信息(通过ValueOf)。类型信息用于动态分支判断(Kind分类),值信息用于修改内容或动态调用方法。反射可实现指针修改基本数据类型、结构体字段访问(仅公共字段)以及方法的动态调用(需公共方法)。

不愿言弃-念何的博客 435

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

Golang 学习笔记—— 反射

转载请注明出处:Golang 学习笔记(11)—— 反射 Golang 介绍 反射是程序执行时检查其所拥有的结构。尤其是类型的一种能力。这是元编程的一种形式。它同一时候也是造成混淆的重要来源。 每一个语言的反射模型都不同(同一时候很多语言根本不支持反射)。本节将试图明白解释在 Go 中的反射是怎样工作的 实现 package import "reflect" Type和Va...

mojolang 819

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...

Golang笔记——反射

先看一个问题,反射的使用场景 使用反射机制,编写函数的适配器, 桥连接 反射的基本介绍 基本介绍 反射可以在运行时动态获取变量的各种信息, 比如变量的类型(type),类别(kind) 如果是结构体变量,还可以获取到结构体本身的信息(包括结构体的字段、方法) 通过反射,可以修改变量的值,可以调用关联的方法。 使用反射,需要 import (“reflect”) 5) 示意图 反射的应用场景 反射重要的函数和概念 3) 变量、interface{} 和 reflect.Value 是可以相互转换

weixin_44023658的博客 329

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

Golang学习笔记5——反射

文章目录1. 反射是什么1.1 概念1.2 功能2. 反射的实现2.1 interface 和 反射2.2 Golang的反射reflect2.2.1 reflect的基本功能TypeOf和ValueOf2.3 获取接口的属性和方法2.3.1 从relfect.Value中获取接口interface的信息2.3.2 已知原有类型2.3.3 未知原有类型【遍历探测其Filed】2.4 通过reflect.Value设置实际变量的值2.5 通过reflect.ValueOf来进行方法的调用3. Golang的反

Overvautious的博客 886

Golang基础笔记十六之反射

本文介绍了Golang中反射(reflect)的基本使用方法。反射允许程序在运行时检查、修改自身类型和值。主要内容包括:1)通过reflect.TypeOf()和reflect.ValueOf()获取变量的类型和值;2)使用指针和Elem()方法修改变量值;3)遍历结构体字段并获取标签信息;4)通过reflect.ValueOf()和Call()方法动态调用函数。文章提醒反射会降低代码可读性和性能,应谨慎使用。这些基础操作可用于实现结构体字段打印、动态验证等功能。

weixin_43354181的博客 330

golang反射学习笔记

reflect包 任何接口由两部分组成 类型 在反射中可以理解为reflect.Type 值 在反射中可以理解为reflect.Value reflect.TypeOf() 作用:获取变量的类型对象 使用:v := reflect.TypeOf(a) 返回值:变量的类型对象,是reflect.Type类型 返回值有两个属性: v.Name() 获取类型,跟%T一样 v.Kind() 获取底层类型,底层类型有: reflect.Invalid // 非法

2024路在何方 438

golang学习笔记27-反射【重要】

本节也是GO核心部分,很重要。包括基本类型的反射,结构体类型的反射,类别方法Kind(),修改变量的值。

技术卷的博客 1360

Go 学习笔记(39)— Go 反射

Go 学习笔记(39)— Go 反射 本文转自 http://c.biancheng.net/golang/reflect/ 反射是把双刃剑,功能强大但代码可读性并不理想,若非必要并不推荐使用反射。 1. 反射概念 反射是指在程序运行期对程序本身进行访问和修改的能力。程序在编译时,变量被转换为内存地址,变量名不会被编译器写入到可执行部分。在运行程序时,程序无法获取自身的信息。 支持反射的语言可以在程序编译期将变量的反射信息,如字段名称、类型信息、结构体信息等整合到可执行文件中,并给程序提供接口访问反射信

wohu1104的专栏 517

golang笔记-反射reflect-01

反射概念 “反射”一词,对于它的第一印象就是有一面镜子,通过镜子我们能看到在当前光照范围内,镜子所反射出的本质。但是反射在计算机领域,它则是指某一类应用,通过反射,可以得到自身的描述,通过得到描述,从而可以进行自我控制。也就是说,通过反射,我们可以将我们的一些具体的应用采用某种机制和协商来得到自身的行为的描述,依此来实现对自身行为和状态调整和修改,从而达到自我控制的一种机制。 ...

zhongcanw的博客 430

golang 并发、反射知识笔记

1. 性能降低,但是高效率的并发安全map import ( "sync" ) func main(){ var mymap sync.Map for i:=0;i<100;i++{ go writeM(mymap,i,i+1) go readM(mymap,i) } } func readM(mymap sync.Map,key,int)int{ if v,ok :=mymap.Load(key);ok{ // 用Load来获取 return v.(int) // 获取的v是

a1007942636的博客 351

Golang学习笔记-反射

反射让我们可以在运行时获取对象的类型信息,比如查看一个结构体有多少字段,查看函数的入参类型和返回值类型等。 Go提供了reflect.TypeOf()和reflect.ValueOf获取任意对象的reflect.Value和reflect.Type,其中reflect.Type是一个接口类型,该接口提供了很多方法让我们获取类型信息,而reflect.Value是一个结构体,它提供了很多方法让我们获...

天道酬勤 534

Go 反射 学习笔记

反射reflection 反射可大大提高程序的灵活性,使得 interface{} 有更大的发挥余地 反射使用 TypeOf 和 ValueOf 函数从接口中获取目标对象信息 反射会将匿名字段作为独立字段(匿名字段本质) 列想要利用反射修改对象状态,前提是 interface.data 是 settable, 即 pointer-interface 通过反射可以“动态”调用方法 1、获取目标对象信

勿忘初心 934

Go反射—Go语言圣经学习笔记

Go反射—Go语言圣经 Go语言提供了一种反射机制,能够在运行时更新变量和检查它们的值、调用它们的方法和它们支持的内在操作,而不需要在编译时就知道这些变量的具体类型。反射也可以让我们将类型本身作为第一类的值类型处理。 在 go 语言中,实现反射能力的是 reflect包,能够让程序操作不同类型的对象。其中,在反射包中有两个非常重要的 类型和 函数,两个函数分别是: reflect.TypeOf - 能获取对象的类型的信息 reflect.ValueOf - 能获取对象的数据 为何需要反射 有时候我们需要

weixin_41335923的博客 202
上一篇: Python笔记
下一篇: 《C++ Templates》笔记 PartI:The Basics
MYTCHITOS
博客等级 码龄7年 5粉丝 43原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值