Go —— 反射

news2024/11/13 15:20:08

反射

反射是什么?

  • 反射是运行时检查自身结构的机制
  • 反射是困惑的源泉。

反射特性与 interface 紧密相关。

接口

1. 类型

Go是静态类型语言,比如int、float32、[]byte,等等。每个变量都有一个静态类型,而且在编译时就确定了。

考虑如下几类变量声明:

type Myint int

var i int
var j Myint

变量 i 和 j 是相同的类型吗?不是的,二者拥有不同的静态类型,尽管二者的底层类型都是 int ,但在没有类型转换的情况下是不可以相互赋值的。

Go提供了布尔、数值和字符串类型的基础类型,还有一些使用这些基本类型组合的复合类型,比如数组、结构体、指针、切片、map和 channel 等。interface 也可以称为一种复合类型。

2. interface 类型

每个 interface 类型代表一个特性的方法集,方法集中的方法称为接口。比如:

type Animal interface {
    Speak() string
}

Animal 就是一个接口类型,其包含一个 Speak() 方法

1)interface变量

就像任何其他类型一样,我们也可以声明 interface 类型的变量。比如:

var animal Animal

上面的 animal 变量的值为 nil,它没有赋值,它可以存储什么值呢?

2)实现接口

任何类型只要实现了 interface 类型的所有方法,就可以声称该类型实现了这个接口,该类型的变量就可以存储到 interface 变量中。

比如结构体 Cat 实现了Speak() 方法:

type Cat struct {
}

func (c Cat) Speak() string {
    return "Meow"
}

结构体Cat的变量就可以存储到 animal 变量中:

var animal Animal
var cat Cat
animal = cat

interface 变量可以存储到任意实现了该接口类型的变量。

3)复合类型

为什么 interface 变量可以存储任意实现了该类型接口的变量。

因为 interface 类型的变量在存储某个变量时会同时保存变量类型和变量值。

type iface struct {
    tab *itab // 保存变量类型
    data unsafe.Pointer // 变量值位于堆栈的指针
}

Go的反射就是在运行时操作 interface 中的值和类型的特性。

反射定理

interface 类型有一个(value、type)对,而反射就是操纵 interface 的这个(value,type)对的机制。具体一点说就是Go提供一组方式来获取 interface 的value 和 type。

1. reflect包

reflect包中提供了reflect.Typereflect.Value 两个类型,分别代表 interface 中的类型和value。

reflect包中同时提供两个方法来获取 interface 的 value 类型:

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

具体关系如下图所示:

在这里插入图片描述

在下面的描述中,我们称reflect.Type 和reflect.Value 为 interface 的反射对象

2. 反射定律

1)第一定律:反射可以将 interface 类型变量转换成反射对象

package main

import (
	"fmt"
	"reflect"
)

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

	v := reflect.ValueOf(x)
	fmt.Println("value:", v)
}

程序输出如下:

type: float64
value: 3.4

在上面的例子中,好像没有 interface 变量,实则不然,变量 x 在传入 reflect.TypeOf()函数时,实际上做了一次转化,x变量被转换成一个空接口传入,reflect.ValueOf(x)也是如此。该例子展示了反射的一个能力,即可以获取 interface 变量的类型和值,这是反射进一步操纵 interface变量的基础。

2)第二定律:反射可以将反射对象还原成 interface 对象

之所以叫“反射”,是因为反射对象与 interface 对象时可以相互转换的。

func foo() {
	var A interface{}
	A = 100

	v := reflect.ValueOf(A)
	B := v.Interface()
	if A == B {
		fmt.Println("A==B")
	} else {
		fmt.Println("A!=B")
	}
}

在上面的函数中,通过 reflect.ValueOf()获取接口变量A的反射对象,然后又通过反射对象的interface()获取B,结果A和B是相同的。

3)第三定律:反射对象可修改,value值必须是可设置的

通过反射可以将 interface 类型的变量转换成反射对象,可以使用该反射对象设置 interface 变量持有的值。

我们可以通过 reflect.Value 的一系列 SetXXX() 方法来设置反射对象的值。先看一个失败的例子:

package main

import "reflect"

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

	v.SetFloat(7.1) 
}

在上面的代码中,通过反射对象v设置新值,会触发panic。报错如下:

panic: reflect: reflect.Value.SetFloat using unaddressable value

错误原因是v是不可修改的,为什么会如此呢?

反射对象是否可修改取决于其所存储的值。上例中传入 reflect.ValueOf()函数的其实是x的值,而非x本身,即通过v修改其值是无法影响x的,也就是无效的修改,所以会报错。

如果构建v时使用x的地址就可实现修改了,但此时v代表的是指针地址,我们要设置的是指针所指向的内容,即我们想要修改*v。那怎么通过v修改x的值呢?

reflect.Value() 提供了Elem() 方法,可以获得指向 value 的指针。修正后的代码如下:

package main

import (
	"fmt"
	"reflect"
)

func main() {
	var x float64 = 3.4
	v := reflect.ValueOf(&x)

	v.Elem().SetFloat(7.1)
    fmt.Println("x:"x)
}

输出如下:

x: 7.1

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2055812.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

自主智能体的未来:LangChain Agents如何实现复杂任务自动化

一、AI Agents 基础:ReAct 范式 在AI领域,智能体(Agents)指的是能够自主感知环境并采取行动以实现特定目标的系统。ReAct(Reasoning and Acting)范式是理解智能体的基础,它强调智能体在执行任务…

【YashanDB知识库】共享集群YAC换IP

【标题】共享集群YAC换IP 【需求分类】安装部署,配置变更 【关键字】安装部署,更换IP,运维,配置变更,高可用,YAC 【需求描述】客户需要将已经部署的YAC集群更换IP,从测试网段切换生产网段 【…

2024年AI艺术生成器精选榜单,抢先体验!

选择合适的AI艺术生成器对于设计项目的成功至关重要。无论是从设计线框到复杂的交互原型,合适的工具都能帮助顺利实现目标。本文将分享2024年最受欢迎的AI艺术生成器,让我们一起来看看! 即时设计 在2024年好用的AI艺术生成器中,…

基于STM32开发的智能家居照明系统

目录 引言环境准备工作 硬件准备软件安装与配置系统设计 系统架构硬件连接代码实现 系统初始化光线检测与自动调节手动控制与状态指示Wi-Fi通信与远程控制应用场景 家庭智能照明办公室自动化照明常见问题及解决方案 常见问题解决方案结论 1. 引言 智能家居照明系统通过集成光…

宠物空气净化器是智商税吗吗?哪款最好用?

在当今社会,随着生活节奏不断加快,许多人会感到孤独。因此养猫已成为许多家庭的生活方式之一。他们期待着家里有欢声笑语的出现,希望家里一推开门都是有猫咪等着自己,在自己无人诉说心事的时候,猫咪能给自己一份陪伴。…

图神经网络教程2——循环图神经网络-2

目录 计算下游输出 序列图数据的扩展 图长短期记忆网络 循环转换在RGNN应用于图分类时的作用 数据集 算法 结果和讨论 门控循环单元 优缺点 前文索引 本篇是GRNN的第二篇文章,点击此处可到达第一篇文章的位置。 计算下游输出 一旦我们以图中的每个顶点为…

【PyTorch】深度学习PyTorch加载数据

系列文章目录 【PyTorch】深度学习PyTorch环境配置及安装【详细清晰】 文章目录 系列文章目录前言一、Dataset与DataloaderDatasetDataloader 二、使用步骤Dataset类的使用2.读入数据 前言 pytorch的数据加载中关于如何操作数据主要涉及Dataset和DataLoader两个类&#xff0c…

桶射巡飞无人机技术详解

无人机(Launcher-Deployed Loitering Munition, LDLM)作为一种新型无人机系统,融合了远程发射、长时续航、精确打击与多任务执行能力,近年来在军事侦察、目标监视、精确打击以及民用领域如环境监测、应急救援等方面展现出巨大潜力…

Elasticsearch + Search UI 构建一个文件搜索引擎

目录 Elasticsearch使用优势App Search Search UI配置engine集中管理配置和提供实用工具函数配置和初始化一个基于Elasticsearch的搜索界面应用程序Search UI 基础用法 好书推荐 Elasticsearch 使用优势 使用ElasticSearch的主要好处在于其强大的全文搜索和实时分析能力。Elas…

JVM G1垃圾回收器简介与常用配置

简介 G1 is a generational, incremental, parallel, mostly concurrent, stop-the-world, and evacuating garbage collector which monitors pause-time goals in each of the stop-the-world pauses. 在内存空间划分上,G1将堆分为等大的一块区域(regi…

手摸手系列之Linux下根据自己的jdk包构建docker镜像

项目在本地导出PDF文件正常,部署到Linux服务器docker容器中导出就报错,百撕不得姐,经查,docker依赖的openjdk版本是8u112版本,而我本地是8u421版本,那就升级jdk的小版本试试。 在docker的中央仓库[点我直达…

记录jenkins的一个错误

因为workspace 的权限多了一个s 导致构建镜像出现了失败 [rootsimetra-ecs-01 .jenkins]# ls -la | grep work -rw-r----- 1 root root 46 Aug 17 11:57 org.jenkinsci.plugins.workflow.flow.FlowExecutionList.xml drwxr-x--- 6 root root 4096 Aug 12 10:06 works…

【微信小程序】自定义组件 - 组件的生命周期

1. 组件全部的生命周期函数 2. 组件主要的生命周期函数 3. lifetimes 节点

OceanMind海睿思入选《2024中国企业数智化转型升级服务全景图/产业图谱》

近日,国内知名数据智能产业创新服务媒体数据猿携手上海大数据联盟发布了《2024中国企业数智化转型升级服务全景图/产业图谱1.0版》。中新赛克海睿思从数千家企业中脱颖而出,成功入选「底层技术服务 - 大数据」细分领域。 在历经数月的时间里,…

AI 代理参考架构

LLM Agent部署框架 围绕 ChatGPT 的讨论,现在已经演变为AI 代理。 图:AI代理平台参考架构 比尔盖茨最近设想(CNBC 采访:链接)未来我们将拥有一个能够处理和响应自然语言并完成许多不同任务的AI 代理。盖茨以计划旅行…

SD NAND的SD模式与SPI模式的初始化

最近很多客户朋友在询问我们CS创世 SD NAND能不能使用SPI接口,两者使用起来有何区别,下面为大家详细解答。 SD MODE: CS创世 SD NAND支持SD模式和SPI模式,SD NAND默认为SD模式,上电后,其初始化过程如下: …

陪跑案例 | 德国营养师吴迪:从胖妹逆袭塑形导师!公开首场发售秘籍

有目标,有心力的老师,一旦找对方法和团队,能够化解99%的问题。 今天,创客匠人【陪跑案例故事】专栏推出第四期内容,为大家介绍【梦想身型健康学院】平台创始人吴迪老师的陪跑故事。 吴迪老师是梦想身型训练营创始人、德…

Es-在java中使用

match_all match trem 范围查询 地理位置查询 算分函数 多条件bool查询 分页查询 高亮

mixly教程-ESP32接入文言一心

一、获取access_token 步骤一. 创建千帆应用 (1)登录百度智能云千帆控制台。 百度智能云-登录 (2)创建千帆应用 注意切换到旧版 (3)创建应用后,获取AppID、API Key、Secret Key。 步骤二. 服…

Leetcode 162.153.33 二分法 C++实现

Leetcode 162. 寻找峰值 问题:峰值元素是指其值严格大于左右相邻值的元素。 给你一个整数数组 nums ,找到峰值元素并返回其索引。数组可能包含多个峰值,在这种情况下,返回 任何一个峰值 所在位置即可。你可以假设 nums[-1] num…