Go 基础
参考链接
[Golang教程 - 菜鸟教程](Go 基础教程 - Golang教程 - 菜鸟教程l)
Hello world
package main // 包名称
import "fmt"
func main() { // main: 函数名
fmt.Println("Hello world") // Hello world
fmt.Println("1+1=",1+1) // 1+1= 2
var name = "golong" // 变量名
fmt.Println(name)
}Go 标识符规则
标识符的名称必须以字母或下划线(_)开头。并且名称中可能包含字母“ a-z”或“ A-Z”或数字0-9,以及字符“
标识符的名称不能以数字开头。
标识符的名称区分大小写。
关键字不能用作标识符名称。
标识符名称的长度没有限制,但是建议仅使用4到15个字母的最佳长度。
例如:
Go 内置常量、类型、函数
Go 关键字(25个)
GO 数据类型
基本类型:数字(int),字符串(string)和布尔值(bool)属于此类别。
聚合类型:数组和结构属于此类别。
引用类型:指针,切片,map集合,函数和Channel属于此类别。
接口类型
GO println和printf区别
Println :可以打印出字符串,和变量
Printf : 只可以打印出格式化的字符串,可以输出字符串类型的变量
Go 变量
变量命名规则: 变量名称必须以字母或下划线(_)开头。并且名称中可能包含字母“ a-z”或“ A-Z”或数字0-9,以及字符" _"
创建变量的方法
使用var关键字
使用短变量声明运算符(:=)---> 只能在局部使用,不能全局
声明变量
}
变量类型: int%!(EXTRA int=0, string=, float64=0, int=111, int=222, int=333, int=2, string=abc, float64=11.44, int=2, string=3, float64=11.11, int=800, string=NHOOO, float64=47.56)
Go 语言常量
常量就像变量一样声明,但是使用 const 关键字作为前缀来声明具有特定类型的常量。不能使用 := 语法声明。
典型
Go 运算符
算数运算符
+ - * / %
% 当第一个操作数除以第二个操作数时,'%'运算符返回余数。例如,x%y。
关系运算符
== , != , > , < , >= , <=
逻辑运算符
&&(逻辑AND), ||(逻辑或), !(逻辑非)
按位运算符(没看懂)
&(按位与):将两个数字作为操作数,并对两个数字的每一位进行“与”运算。仅当两个位均为1时,AND的结果才为1。
| (按位或):将两个数字作为操作数,并对两个数字的每一位进行“或”运算。两个位中的任何一个为1,OR的结果为1。
^(按位XOR):将两个数字作为操作数,并对两个数字的每一位进行XOR。如果两个位不同,则XOR的结果为1。
<<(左移):取两个数字,左移第一个操作数的位,第二个操作数决定移位的位数。
(右移):取两个数字,右移第一个操作数的位,第二个操作数决定移位的位数。
&^(AND NOT):(按位清除)运算符,该运算符的实际操作为&(^)操作。
赋值运算符
赋值运算符用于为变量赋值(运算) 注意: 右侧的值必须与左侧的变量具有相同的数据类型,否则编译器将抛出错误
= , += , -= , *= , /= , %= , &= , ^= , |=
杂项运算符
&:此运算符返回变量的地址。
*:此运算符提供指向变量的指针。
<-:该运算符的名称为接收。它用于从通道接收值。
Go 类型转换
注意:由于Golang具有强大的类型系统,因此不允许在表达式中混合使用数字类型(例如加,减,乘,除等),并且不允许在两个混合类型之间执行赋值类型。
Go 条件判断
Go 循环语句
如果数组,字符串,切片或map集合为空,则for循环不会抛出错误并继续其流程。换句话说,如果数组,字符串,切片或map为nil,则for循环的迭代次数为零。
语法:
initialization: 可选,循环开始之前执行(如变量声明,递增,赋值,函数)
condition: 布尔表达式,该表达式在循环的每次迭代开始时计算。如果条件语句的值为true,则执行循环。
post: for循环体之后执行。在post语句之后,条件语句再次计算条件语句的值是否为false,然后循环结束
for简单循环
for无线循环
for循环用作while循环
for循环数组
for循环字符串
for循环map的键和值对
for通道
for控制语句
break ---> 终止其所在的循环或语句
continue ---> 在循环中,表示结束本次循环,进行下次循环。
goto ---> 表示无条件跳转到标号处执行。
Go Switch几种用法
表达式 switch
类型 switch
语法:
示例一: 可选语句结合表达式
示例二: 无可选,无表达式
switch语句不带可选语句和表达式
示例三: case多个值
示例四: 类型switch
Go 避免死锁
语法
设置默认值
Go 函数
简单函数
语法
func: Go关键字,用于创建函数
function_name: 函数名称
Parmeter_list: 包含函数参数名称和类型
Return_type: 可选,包含函数返回值类型,如果在函数中使用return_type,则必须在函数中使用return语句。
可变参函数
当您要在函数中传递切片时,使用可变参数函数。
当我们不知道参数的数量时,使用可变参数函数。
在程序中使用可变参数函数时,它可以增加程序的可读性。
匿名函数
定义格式
在定义时调用匿名函数
将匿名函数赋值给变量
匿名函数用作回调函数
实现对切片的遍历操作,遍历中访问每个元素的操作使用匿名函数来实现,用户传入不同的匿名函数体可以实现对元素不同的遍历操作
简单实用
匿名函数作为参数传递给其他函数。
从另一个函数返回匿名函数。(常用于回调函数)
Go 函数调用
按值调用
函数内部进行的任何更改都不会反映在调用者的实际参数中
引用调用
指针概念: 在函数调用中,我们传递变量的地址,并使用解引用运算符*修改值
函数返回值
首先先看下函数语法
function_name:它是函数的名称。
parameter-list:它包含函数参数的名称和类型。
return_type_list:这是可选的,它包含函数返回的值的类型。如果在函数中使用return_type,则必须在函数中使用return语句。
返回多个值
返回值命名
允许为返回值提供名称。你也可以在代码中使用这些变量名。
语法
Go 空白标识符
只需使用_(下划线)。它允许编译器忽略该特定变量的错误(declared and not used)。
Go 上下文函数
init()函数的主要目的是初始化无法在全局上下文中初始化的全局变量。
Go Defer关键字
同一程序中允许多个defer语句,并且它们按LIFO(后进先出)顺序执行
在defer语句中,将在执行defer语句时(而不是在调用它们时)评估参数。
defer语句通常用于确保在完成文件处理后关闭文件,关闭通道或捕获程序中的紧急情况。
语法
后进先出延迟执行
Go 方法(看不懂)
Go 结构体(看不懂)
Go 数组
语法
var创建数组
简声明创建数组
多维数组
数组是一维的,但是允许创建多维数组。多维数组是相同类型数组的数组
语法
省略号
... 在长度位置处可见,则数组的长度由初始化的元素确定
数组比较
Go Defer关键字
同一程序中允许多个defer语句,并且它们按LIFO(后进先出)顺序执行
在defer语句中,将在执行defer语句时(而不是在调用它们时)评估参数。
defer语句通常用于确保在完成文件处理后关闭文件,关闭通道或捕获程序中的紧急情况。
语法
后进先出延迟执行
Go 方法(看不懂)
Go 结构体(看不懂)
Go 数组
语法
使用var关键字创建数组
使用简声明
多维数组
数组是一维的,但是允许创建多维数组。多维数组是相同类型数组的数组
语法
省略号
... 在长度位置处可见,则数组的长度由初始化的元素确定
数组比较
数组复制
语法
数组作为函数参数
将1或多维数组传递给该函数
语法
切片的使用
数组是固定大小
切片大小可伸缩
在内部,切片和数组相连,切片是对基础数组的引用
切片中的第一个索引位置始终为0,而最后一个索引位置将为(切片的长度– 1)。
切片声明: []T 或 []T{} 或 []T{value1, value2, value3, ...value n}
make()函数
make()函数用于创建一个空切片
语法:
func make([]T, len, cap) []T ;
[]T 类型, len 长度, cap 容量(可选,默认和长度一样))
遍历切片方法
多维切片使用
切片比较判断
判断两个[]byte是否相等
使用reflect判断slice是否相等
深度比较两个对象包括它们内部包含的元素是否都相等: 语法: func DeepEqual(x, y interface{}) bool
牺牲了性能
手写切片是否相等判断
性能比 reflect 快
切片排序
Go语言的标准库提供了sort包,其中包含用于对int,float64和字符串切片进行排序的不同类型的排序方法。 这些函数始终按升序对可用元素进行切片排序
切片分割
语法
切片传递给函数
字符串操作
字符串比较
是否存在某个字符或子串
语法
子串出现次数 ( 字符串匹配 )
当 sep 为空时,Count 的返回值是:utf8.RuneCountInString(s) + 1 Count 是计算子串在字符串中出现的无重叠的次数
语法
Fields 和 FieldsFunc
该包提供了六个三组分割函数:Fields 和 FieldsFunc、Split 和 SplitAfter、SplitN 和 SplitAfterN。
语法
Fields
用一个或多个连续的空格分隔字符串 s,返回子字符串的数组(slice)如果字符串 s 只包含空格,则返回空列表 ([]string 的长度为 0)
FieldsFunc
可以通过回调函数来指定分隔字符串 s 的字符, 也可自定义回调
Split 和 SplitAfter、 SplitN 和 SplitAfterN
这四个函数都是通过 sep 进行分割,返回[]string。如果 sep 为空,相当于分成一个个的 UTF-8 字符 Split(s, sep) 和 SplitN(s, sep, -1) 等价;SplitAfter(s, sep) 和 SplitAfterN(s, sep, -1) 等价。
语法
字符串是否有某个前缀或后缀
字符或子串在字符串中出现的位置
字符串 JOIN 操作
将字符串数组(或 slice)连接起来可以通过 Join 实现,函数签名如下:
字符串重复几次
将 s 重复 count 次,如果 count 为负数或返回值长度 len(s)*count 超出 string 上限会导致 panic
语法
字符替换
字符串子串替换
语法
大小写转换
语法
修剪
Replacer 类型
进行多个替换
替换结果写入到文件
Go 语言结构体(struct)
不支持继承但支持组合的轻量级类。
声明结构、 访问结构、修改结构、指针地址
Go 结构体比较
如果结构彼此相等(就其字段值而言),则运算符和方法均返回true;否则,返回false。并且,如果比较的变量属于不同的结构,则编译器将给出错误
Go 嵌套结构体
语法
Go 匿名结构和字段
匿名结构
匿名字段
不允许
结构体内嵌类型
函数用作结构体字段
Go指针
指针的引用
指针解引用
传递指针给函数
函数返回指针地址
结构体指针
指针比较
== 运算符:如果两个指针都指向同一个变量,则此运算符返回true。或如果两个指针都指向不同的变量,则返回false。
!= 运算符:如果两个指针都指向同一个变量,则此运算符返回false。或如果两个指针都指向不同的变量,则返回true。
指针容量和长度
Go 方法接收器
方法简单示例
等同于
在结构上定义接收器方法
指针接收器与值接收器
指针接收器: 考虑下一个结构体有很多的字段。在方法内使用这个结构体做为值接收器需要拷贝整个结构体,这是很昂贵的。在这种情况下使用指针接收器,结构体不会被拷贝,只会传递一个指针到方法内部使用。
值接收器: 除了指针接收器外,在其他的所有情况,都可以被使用值接收器
匿名字段的方法
在方法中使用值接收器 与 在函数中使用值参数
当一个函数有一个值参数,它只能接受一个值参数
当一个方法有一个值接收器,它可以接受值接收器和指针接收器。
在方法中使用指针接收器 与 在函数中使用指针参数
和值参数相类似,函数使用指针参数只接受指针,而使用指针接收器的方法可以使用值接收器和指针接收器。
在非结构体上的方法
Go 接口
静态接口
动态接口
语法:
接口的声明与实现
接口的实际用途
根据公司员工的个人薪资,计算公司的总支出。为了简单起见,我们假定支出的单位都是美元。 公司员工有着不同的薪资结构,以永久工和合同工薪酬为例
接口的内部表示
把接口看作内部的一个元组 (type, value)。 type 是接口底层的具体类型(Concrete Type),而 value 是具体类型的值。
Test 接口只有一个方法 Tester(),而 MyFloat 类型实现了该接口。 我们把变量 f(MyFloat 类型)赋值给了 t(Test 类型)。现在 t 的具体类型为 MyFloat,而 t 的值为 89.7。
空接口
没有包含方法的接口称为空接口。空接口表示为interface{}。由于空接口没有方法,因此所有类型都实现了空接口。
类型断言
类型断言的语法是 i.(T)
类型选择(Type Switch)
i.(T) , 而对于类型选择,类型 T 由关键字 type 代替
类型与接口比较
还可以将一个类型和接口相比较。如果一个类型实现了接口,那么该类型与其实现的接口就可以互相比较。
指针接受者与值接受者
实现多个接口
接口的零值
接口的零值是 nil。对于值为 nil 的接口,其底层值(Underlying Value)和具体类型(Concrete Type)都为 nil。
结合值和接口使用
指针类型实现接口:
值类型实现接口:
通过函数封装接口实现:
Go 并发
Go 编程语言原生支持并发。Go 使用 Go 协程(Goroutine) 和信道(Channel)来处理并发
Go 协程
启动一个新的协程时,协程的调用会立即返回。与函数不同,程序控制不会去等待 Go 协程执行完毕。在调用 Go 协程之后,程序控制会立即返回到代码的下一行,忽略该协程的任何返回值。
如果希望运行其他 Go 协程,Go 主协程必须继续运行着。如果 Go 主协程终止,则程序终止,于是其他 Go 协程也不会继续运行。
启动多个 Go 协程(并发)
启动了两个 Go 协程。现在,这两个协程并发地运行。numbers 协程首先休眠 250 微秒,接着打印 1,然后再次休眠,打印 2,依此类推,一直到打印 5 结束。alphabete 协程同样打印从 a 到 e 的字母,并且每次有 400 微秒的休眠时间。 Go 主协程启动了 numbers 和 alphabete 两个 Go 协程,休眠了 3000 微秒后终止程序。
信道(channel)
无缓冲信道
Go 协程之间的管道,通过使用信道,数据也可以从一端发送,在另一端接收。实现 Go 协程间的通信。
信道的声明
所有信道都关联了一个类型。信道只能运输这种类型的数据,而运输其他类型的数据都是非法的。
信道进行发送和接收
默认是阻塞
在一个单独的 Go 协程计算平方和,而在另一个协程计算立方和,最后在 Go 主协程把平方和与立方和相加。
死锁
当 Go 协程给一个信道发送数据时,照理说会有其他 Go 协程来接收数据。如果没有的话,程序就会在运行时触发 panic,形成死锁。
单向信道
信道转换,把一个双向信道转换成唯送信道或者唯收(Receive Only)信道都是行得通的
我们创建了一个双向信道 cha1。在第 11 行 cha1 作为参数传递给了 sendData 协程。在第 5 行,函数 sendData 里的参数 sendch chan<- int 把 cha1 转换为一个唯送信道。于是该信道在 sendData 协程里是一个唯送信道,而在 Go 主协程里是一个双向信道。该程序最终打印输出 10。
关闭信道和使用 for range 遍历信道
成功接收信道所发送的数据,那么 ok 等于 true。而如果 ok 等于 false
v, ok := <- ch
producer 协程会从 0 到 9 写入信道 chn1,然后关闭该信道。 主函数有一个无限的 for 循环,使用变量 ok 检查信道是否已经关闭。如果 ok 等于 false,说明信道已经关闭,于是退出 for 循环。如果 ok 等于 true,会打印出接收到的值和 ok 的值。
优化
for range 循环用于在一个信道关闭之前,从信道接收数据。
缓冲信道
创建一个有缓冲(Buffer)的信道。只在缓冲已满的情况,才会阻塞向缓冲信道(Buffered Channel)发送数据。同样,只有在缓冲为空的时候,才会阻塞从缓冲信道接收数据。
语法
死锁
在上面程序里,我们向容量为 2 的缓冲信道写入 3 个字符串。当在程序控制到达第 3 次写入时,由于它超出了信道的容量,因此这次写入发生了阻塞。现在想要这次写操作能够进行下去,必须要有其它协程来读取这个信道的数据。但在本例中,并没有并发协程来读取这个信道,因此这里会发生死锁(deadlock)。程序会在运行时触发 panic
长度 vs 容量
缓冲信道的容量是指信道可以存储的值的数量。我们在使用 make 函数创建缓冲信道的时候会指定容量大小。
缓冲信道的长度是指信道中当前排队的元素个数。
WaitGroup 工作池
WaitGroup 用于等待一批 Go 协程执行结束。程序控制会一直阻塞,直到这些协程全部执行完毕
假设我们有 3 个并发执行的 Go 协程(由 Go 主协程生成)。Go 主协程需要等待这 3 个协程执行结束后,才会终止。这就可以用 WaitGroup 来实现。
工作池缓冲信道实现
1、创建一个 Go 携程池,监听一个等待作业分配的输入型缓冲信道
2、将作业添加到该输入型缓冲信道中
3、作业完成后,再将结果写入一个输出型缓冲信道
4、从输出型缓冲信道读取并打印结果
随着工作协程数量增加,完成作业的总时间会减少。你们可以练习一下:在 main 函数里修改 noOfJobs 和 noOfWorkers 的值,并试着去分析一下结果。
select(信道操作)
select 语句用于在多个发送/接收信道操作中进行选择。select 语句会一直阻塞,直到发送/接收操作准备就绪。如果有多个信道操作准备完毕,select 会随机地选取其中之一执行。该语法与 switch 类似,所不同的是,这里的每个 case 语句都是信道操作
默认情况
在没有 case 准备就绪时,可以执行 select 语句中的默认情况(Default Case)。这通常用于防止 select 语句一直阻塞。
死锁与默认情况
如果存在默认情况,就不会发生死锁,因为在没有其他 case 准备就绪时,会执行默认情况。我们用默认情况重写后
如果 select 只含有值为 nil 的信道,也同样会执行默认情况。
随机选取
当 select 由多个 case 准备就绪时,将会随机地选取其中之一去执行。
空 select
除非有 case 执行,select 语句就会一直阻塞着。在这里,select 语句没有任何 case,因此它会一直阻塞,导致死锁。
Mutex(加锁)
临界区: 当程序并发地运行时,多个 Go 协程不应该同时访问那些修改共享资源的代码。这些修改共享资源的代码称为临界区
Mutex 用于提供一种加锁机制(Locking Mechanism),可确保在某时刻只有一个协程在临界区运行,以防止出现竞态条件。
方法
mutex.Lock()
mutex.Unlock()
使用 Mutex
我们创建了 1000 个 Go 协程。如果每个协程对 x 加 1,最终 x 期望的值应该是 1000, 使用 Mutex,修复竞态条件的问题。
使用信道处理竞态条件
信道来处理竞态条件
Mutex vs 信道
当 Go 协程需要与其他协程通信时,可以使用信道。
而当只允许一个协程访问临界区时,可以使用 Mutex。
63 + 49 + 39.60
Go实现面向对象
使用结构体,而非类
Go 不支持类,而是提供了结构体。结构体中可以添加方法。这样可以将数据和操作数据的方法绑定在一起,实现与类相似的效果。
employee.go
main.go
Go 组合取代继承
Go 不支持继承,但它支持组合(Composition)。组合一般定义为“合并在一起”。汽车就是一个关于组合的例子:一辆汽车由车轮、引擎和其他各种部件组合在一起。
通过嵌套结构体进行组合
例如: 组合的典型例子就是博客帖子。每一个博客的帖子都有标题、内容和作者信息
结构体切片的嵌套
进一步处理这个示例,使用博客帖子的切片来创建一个网站
63 + 49 + 39.60
多态
所有实现了接口的类型,都可以把它的值保存在一个接口类型的变量中。在 Go 中,我们使用接口的这种特性来实现多态。
使用接口实现多态
假设某个虚构的组织所获得的收入来源于两个项目:fixed billing 和 time and material。该组织的净收益等于这两个项目的收入总和
新增收益流(动态添加了新项目)
尽管我们新增了收益流,但却完全没有修改 calculateNetIncome 函数。这就是多态带来的好处。由于新的 Advertisement 同样实现了 Income 接口,所以我们能够向 incomeStreams 切片添加 Advertisement。calculateNetIncome 无需修改,因为它能够调用 Advertisement 类型的 calculate() 和 source() 方法。
错误处理
示例
错误类型的表示
获取错误详细信息的各种方法
1、断言底层结构体类型,使用结构体字段获取更多信息
2、断言底层结构体类型,调用方法获取更多信息
编写一个程序,断言 *DNSError 类型,并调用这些方法来确定该错误是临时性错误,还是由超时导致的。
3、直接比较
查询了模式为 [ 的文件,然而这个模式写的不正确。我们检查了该错误是否为 nil。为了获取该错误的更多信息,将 error 直接与 filepath.ErrBadPattern 相比较。如果该条件满足,那么该错误就是由模式错误导致的
4、不可忽略错误
使用 New 函数创建自定义错误
使用 Errorf 给错误添加更多信息
上面错误是固定的,如果我们想给错误信息添加额外的信息,比如把半径值打印出来
Errorf 函数会根据格式说明符,规定错误的格式,并返回一个符合该错误的字符串。
使用结构体类型和字段提供错误的更多信息
使用结构体类型的方法来提供错误的更多信息
编写一个计算矩形面积的程序。如果长或宽小于零,程序就会打印出错误。
panic|recover
有些情况,当程序发生异常时,无法继续运行。在这种情况下,我们会使用 panic 来终止程序
可以认为 panic 和 recover 与其他语言中的 try-catch-finally 语句类似
什么时候应该使用 panic
发生了一个不能恢复的错误,此时程序不能继续运行。 一个例子就是 web 服务器无法绑定所要求的端口。在这种情况下,就应该使用 panic,因为如果不能绑定端口,啥也做不了。
panic 有两个合理的用例。
1、发生了一个不能恢复的错误,此时程序不能继续运行。 一个例子就是 web 服务器无法绑定所要求的端口。在这种情况下,就应该使用 panic,因为如果不能绑定端口,啥也做不了。
2、发生了一个编程上的错误。 假如我们有一个接收指针参数的方法,而其他人使用 nil 作为参数调用了它。在这种情况下,我们可以使用 panic,因为这是一个编程错误:用 nil 参数调用了一个只能接收合法指针的方法。
panic 示例
发生 panic 时的 defer
当函数发生 panic 时,它会终止运行,在执行完所有的延迟函数后,程序控制返回到该函数的调用方。这样的过程会一直持续下去,直到当前协程的所有函数都返回退出,然后程序会打印出 panic 信息,接着打印出堆栈跟踪,最后程序终止。
发生 panic 时,首先执行了延迟函数,接着控制返回到函数调用方,调用方的延迟函数继续运行,直到到达顶层调用函数。
recover
recover 是一个内建函数,用于重新获得 panic 协程的控制。
只有在延迟函数的内部,调用 recover 才有用
在延迟函数内调用 recover,可以取到 panic 的错误信息,并且停止 panic 续发事件(Panicking Sequence),程序运行恢复正常
如果在延迟函数的外部调用 recover,就不能停止 panic 续发事件。
panic,recover 和 Go 协程
只有在相同的 Go 协程中调用 recover 才管用。recover 不能恢复一个不同协程的 panic
由 go b() 修改为 b(),就可以恢复 panic 了, 因为 panic 发生在与 recover 相同的协程里。
恢复后获得堆栈跟踪
当我们恢复 panic 时,我们就释放了它的堆栈跟踪。实际上,在上述程序里,恢复 panic 之后,我们就失去了堆栈跟踪。
有办法可以打印出堆栈跟踪,就是使用 Debug 包中的 PrintStack 函数。
头等函数
可以把函数赋值给变量,也可以把函数作为其他函数的参数或者返回值
匿名函数
1、 匿名函数赋值变量
2、匿名函数立即调用
3、向匿名函数传递参数
自定义的函数类型
高阶函数
高阶函数(Hiher-order Function)定义为:满足下列条件之一的函数:
接收一个或多个函数作为参数
返回值是一个函数
把函数作为参数,传递给其它函数
在其它函数中返回函数
闭包
当一个函数所访问的变量定义在函数体的外部时,称这样的匿名函数为闭包
每一个闭包都会绑定一个它自己的外围变量
头等函数的实际用途
示例二: 对切片的每个元素执行相同的操作,并返回结果
反射
反射就是程序能够在运行时检查变量和值,求出它们的类型
reflect 包
reflect.Type: 表示 interface{} 具体类型
reflect.Value: 表示它的具体值
reflect.TypeOf() 返回 reflect.Type
reflect.ValueOf() 返回 reflect.Value
reflect.Kind: 表示该类型的特定类别
NumField(): 返回结构体中字段的数量
Field(): 返回字段 i 的 reflect.Value。
reflect 实现了运行时反射。reflect 包会帮助识别 interface{} 变量的底层具体类型和具体值
reflect.Type 和 reflect.Value
reflect.Kind
Type 表示 interface{} 的实际类型(在这里是 main.Order),而 Kind 表示该类型的特定类别
NumField() 和 Field() 方法
Int() 和 String() 方法
Int 和 String 可以帮助我们分别取出 reflect.Value 作为 int64 和 string。
完整实例(通用查询器)
Map(集合)
Map 是一种无序的键值对的集合。Map 最重要的一点是通过 key 来快速检索数据,key 类似于索引,指向数据的值。
语法
map 示例
delete 函数
delete() 函数用于删除集合的元素, 参数为 map 和其对应的 key
Last updated