「GCTT 出品」Go 系列教程——34. 反射

pp电子托盘

c98cb3f8f6724c359cb9676f58820d2e

欢迎阅读Golang系列教程的第34篇文章。

反思是Go语言的高级主题之一。我会尽可能简单地理解。

本教程分为以下几节。

什么是反思?为什么我需要检查变量以确定变量的类型?反映包反射.Type和reflect.Valuereflect.KindNumField()和Field()方法Int()和String()方法完整的程序应该使用反射吗?

让我们逐一讨论这些章节。

反射是程序在运行时检查变量和值以查找其类型的能力。你可能还不了解它,没关系。在本教程结束时,您将清楚地理解反射,因此请按照我们的教程进行操作。

在学习反思时,每个人面临的第一个问题是,如果程序中的每个变量都是由我们自己定义的,那么变量类型可以在编译时知道。为什么我们需要在运行时检查变量并找到它?什么类型?没错,大部分时间都是如此,但并非总是如此。

让我解释一下。下面我们写一个简单的程序。

a9ce8bfcf23d474f9399cfc987e2de74

在上面的程序中,i的类型在编译时是已知的,然后我们在下一行打印出i。这里没什么特别的。

现在让我们来看看你需要在运行时找到变量类型的情况。假设我们要编写一个简单的函数,它将struct作为参数并使用它来创建SQL插入查询。

请考虑以下过程:

18a95f1643d24a3086cd29bda25e404d

在上面的程序中,我们需要编写一个接收结构变量o作为参数的函数,并返回以下SQL插入查询。

插入订单值(1234,567)

这个函数编写起来非常简单。我们现在正在编写这个函数。

994f02ce36ff4818ad13f92c0fafb950

在第12行,createQuery函数创建一个带有两个字段o(ordId和customerId)的插入查询。该程序将输出:

插入订单值(1234,567)

现在让我们升级这个查询生成器。如果我们想要使它具有通用性并适用于任何结构类型,该怎么办?我们使用该程序来理解。

8cf4a661d6114614ba2382ca18f3b20d

我们的目标是完成createQuery函数(上面程序中的第16行),它可以接受任何struct作为参数,并根据struct的字段创建一个insert查询。

例如,如果我们传入以下结构:

o:=订单{

ordId: 1234,

customerId: 567

}

createQuery函数应该返回:

插入订单值(1234,567)

同样,如果我们传入:

cbc4288e6fcf4188aef8cfae01c4d065

此函数将返回:

插入员工价值('Naveen',565,'科学园路,新加坡',90000,'新加坡')

由于createQuery函数应该适用于任何结构,因此它接收接口{}作为参数。为简单起见,我们只处理包含string和int类型字段的结构,但可以扩展为包含任何类型的字段。

createQuery函数应该适用于所有结构。因此,要编写此函数,必须检查在运行时传入的结构参数的类型,找到结构字段,然后创建查询。然后你需要使用反射。在本教程的下一步中,我们将学习如何使用reflect包实现它。

在Go语言中,反映实现运行时反射。反射包将帮助识别interface {}变量的基础具体类型和值。这正是我们所需要的。 createQuery函数接收interface {}参数,并根据其具体类型和特定值创建SQL查询。这是反射包可以帮助我们的地方。

在编写我们的通用查询生成器之前,我们首先需要了解反射包中的几种类型和方法。我们来看看每一个。

reflect.Type表示接口{}的具体类型,reflect.Value表示其特定值。 reflect.TypeOf()和reflect.ValueOf()函数可以分别返回reflect.Type和reflect.Value。这两种类型是我们创建查询生成器的基础。我们现在使用一个简单的例子来理解这两种类型。

6bacf9274c9442a98bda172c6d7c01f4

在上面的程序中,第13行的createQuery函数接收interface {}作为参数。在第14行,reflect.TypeOf接收参数interface {}并返回reflect.Type,其中包含传递的interface {}参数的具体类型。类似地,在第15行,reflect.ValueOf函数接受参数interface {}并返回reflect.Value,其中包含传递的接口{}的特定值。

上述程序将打印出来:

输入main.order

价值{456 56}

从输出中我们可以看到程序打印了接口的特定类型和特定值。

反射包中还有一个重要的类型:种类。

在反射包中,种类和类型的类型可能看起来相似,但在下面的程序中,可以清楚地看到差异。

064856bba2de4d9b970862bd649d12a4

上述程序将输出:

输入main.order

种类结构

我想你应该非常清楚两者之间的区别。 Type表示interface {}的实际类型(这里是main.Order),Kind表示此类型的特定类别(此处为struct)。

NumField()方法返回结构中的字段数,Field(i int)方法返回字段i的reflect.Value。

afac6dd5653044f39032b6ceac84ab79

在上面的程序中,因为NumField方法只能在结构上使用,所以我们首先在第14行检查q的类是struct。该程序的其他代码易于理解,不予解释。该程序将输出:

字段数2

字段: 0类型: reflect.Value值: 456

字段: 1类型: reflect.Value值: 56

Int和String帮助我们将reflect.Value分别作为int64和string。

30eb37a6f43a42d481addc26a2d400e5

在上面程序的第10行,我们采用reflect.Value并将其转换为int64,在第13行,我们采用reflect.Value并将其转换为字符串。该程序将输出:

键入: int64值: 56

类型:字符串值: Naveen

完整的程序

现在我们已经具备足够多的知识,来完成我们的查询生成器了,我们来实现它把

5375b166c8d34ea78e6bd09b1749c709

在第22行,我们首先检查了传来的参数是否是一个结构体。在第23行,我们使用了Name()方法,从该结构体的reflect.Type获取了结构体的名字。接下来一行,我们用来来创建查询。

在第28行,case语句检查了当前字段是否为reflect.Int,如果是的话,我们会取到该字段的值,并使用Int()方法转换为int64.if else语句用于处理边界情况。请添加日志来理解为什么需要它。在第34行,我们用来相同的逻辑来取到字符串。

我们还作了额外的检查,以防止createQuery函数传入不支持的类型时,程序发生崩溃。程序的其他代码是自解释性的。我建议你在合适的地方添加日志,检查输出,来更好地理解这个程序。

该程序会输出:

插入订单值(456,56)

插入员工价值('Naveen',565,'Coimbatore',90000,'India')

不支持的类型

XX至于在输出查询中添加字段名称,我们将其作为练习留给读者。请尝试修改程序以下列格式打印出查询。

插入订单(ordId,customerId)值(456,56)

我们已经展示了反射的实际应用,现在考虑一个非常现实的问题。我们应该使用反射吗?我想引用Rob Pike关于使用反射回答这个问题的格言。

明确胜过聪明。反射并不是一目了然。

反思是Go语言中一个非常强大和先进的概念,我们应该谨慎使用它。使用反射编写清晰且可维护的代码非常困难。您应该尽可能避免使用它,并且只在必须使用时才使用反射。

本教程在此结束。希望你喜欢。祝你幸福。

上一个教程 - “GCTT制作”Go系列教程 33.功能是一等公民(一等功能)

上一个教程 - 阅读文件

历史文章:

“GCTT”Go系列教程 1.简介和安装

“GCTT”Go系列教程 2. Hello World

“GCTT制作”Go系列教程 3.变量

“GCTT”Go系列教程 4.输入

“GCTT制作”Go系列教程 5.常数

“GCTT制作”Go系列教程 6.功能(功能)

“GCTT制作”Go系列教程 7.包

Go Series Tutorial 8. if-else Statement

“GCTT制作”Go系列教程 9.循环

“GCTT制作”Go系列教程 10.开关语句

“GCTT”Go系列教程 11.数组和切片

“GCTT制作”Go系列教程 12.变量参数函数

“GCTT制作”Go系列教程 13.地图

“GCTT”Go系列教程 14.字符串

“GCTT制作”Go系列教程 15.指针

“GCTT制作”Go系列教程 16.结构,这一个就够了

“GCTT制作”Go系列教程 17.超完整方法教程

“GCTT制作”Go系列教程 18.接口(1)

“GCTT制作”Go系列教程 19.接口(2)

“GCTT”Go系列教程 20.入门

“GCTT”Go系列教程 21. Go Coroutine

“GCTT”Go系列教程 22.频道

“GCTT”Go系列教程 23.缓冲通道和工作池

“GCTT制作”Go系列教程 24.选择

“GCTT制作”Go系列教程 25. Mutex

“GCTT”Go系列教程 26.结构替换类

“GCTT制作”Go系列教程 27.组合替换继承

“GCTT制作”Go系列教程 28.多态性

“GCTT制作”Go系列教程 29.推迟

“GCTT”Go系列教程 30.错误处理

“GCTT”Go系列教程 31.自定义错误

“GCTT”Go系列教程 32.恐慌和恢复

“GCTT制作”Go系列教程 33.功能是一等公民(头等功能)