这是关于在 Go 中根据类型创建对象的博客系列两部分的第一部分。这部分讨论原始类型的对象创建
![58eb5a64011f558dead164433cb06050.png](https://i-blog.csdnimg.cn/blog_migrate/0073dab9a6a0e694e06b2d65509093b6.jpeg)
Go 中的 reflect 包提供了根据执行过程中对象的类型来改变程序控制流的 API。
reflect 包提供了两个重要的结构 - Type 和 Value。
Type 是一个 Go 中任何类型的代表。换句话说,它可以被用于编码任何 Go 类型(例如:int , string , bool , myCustomType 等等)。Value 是一个 Go 中任何值的代表。换句话说,它可以被用于编码、操作任何 Go 的值。
类型与类别(Types 和 Kinds)
Go 中有一个隐蔽的、鲜为人知的,使得 Type 和 Kind 含义有差别的公约。这种差别可以通过一个例子理解一下。 看一下这个结构:
![33ae8ff50ea71ea1502b41980446b70a.png](https://i-blog.csdnimg.cn/blog_migrate/94a3814b660964f76143655a05a57e3e.jpeg)
这个结构体的一个对象的 type 应该是 example。而这个对象的 kind 应该是 struct。这里的 Kind 可以被看成一个 Type 的 Type。
在 Go 里所有 structs 都是相同的 kind,但不是相同的 Type
像 Pointer、Array、Slice、Map 等等复杂类型,使得 type 和 kind 的含义产生了这样的差异。
相比之下,像 int、float、string 等等原始类型,并没有产生 type 和 kind 含义上的差别。换句话说,一个 int 变量的 kind 是 int。一个 int 变量的 type 也是 int。
根据类型创建对象
为了根据一个类型标签(type signature)创建一个对象,这个对象的 type 和 kind 都是必要的。从这里开始,当我用到 ‘type signature’ 这个术语时,我的意思就是 Go 里的 reflect.Type 类型的对象。
根据原始类型创建原始对象
原始对象可以根据它们的 type signature,通过使用它们的 Zero 值被创建。
一个类型的 Zero 值是指一个该类型、没有初始化的对象的值
这是一个 Go 中所有原始类型的列表:
BoolIntInt8Int16Int32Int64UintUint8Uint16Uint32Uint64UintptrFloat32Float64Complex64Complex128StringUnsafePointer
通过使用 reflect.Zero 函数,可以创建原始类型的对象
![3978f6ca972d09b0c1bd42b034a77c49.png](https://i-blog.csdnimg.cn/blog_migrate/b45a45260f1d2c97bd166d17797e99ca.jpeg)
这里将创建一个期望的对象,并返回一个拥有相应 Zero 值的 reflect.Value 对象。为了让这个对象可以使用,需要将它的值提取一下。
对于不同的原始类型,相应对象的值可以采用相应适合的方法来提取。
提取整数值
在 Go 里有 5 种整数类型:
IntInt8Int16Int32Int64
Int 类型表示平台定义的默认整型大小。另外 4 种类型分别是 8,16,32,64(bit单位)大小的整型。
为了获得每种不同的整数类型,reflect.Value 对象对应的整数需要被转换成相应的整数类型。
这里是如何提取 int32 类型:
![97f17c3f4b6d7bdfc2c588f42b55a1c1.png](https://i-blog.csdnimg.cn/blog_migrate/d25d952ddebc55003fa2a38ecbbf1837.jpeg)
值得注意的是:reflect.Int() 会返回 int64 类型。这是因为 int64 类型可以编码成其他所有整数类型。
这里是剩余其他整数类型的提取:
![a47817509882481ea187ccf69fc96589.png](https://i-blog.csdnimg.cn/blog_migrate/baf14c3b5c89041155d01d999aa2f002.jpeg)
提取布尔值
布尔值在 reflect 包中用常量 Bool 表示。
它们可以通过使用 Bool() 方法,从 reflect.Value 对象中提取出来:
![8e00d12d47b1793be57c1498d029a3a7.png](https://i-blog.csdnimg.cn/blog_migrate/767c95bcfd3009393e0a7f661f38d1f9.jpeg)
提取无符号整数
在 Go 中有 5 种无符号整数类型:
UintUint8Uint16Uint32Uint64
Uint 类型表示平台定义的默认无符号整型大小。另外 4 种类型分别是 8,16,32,64(bit单位)大小的无符号整型。
为了获得每种不同的无符号整数类型,reflect.Value 对象对应的无符号整数需要被转换成相应的无符号整数类型。
这里是如何提取 Uint32 类型:
![faf6bd55dbafc617f9631a2ceb9e520b.png](https://i-blog.csdnimg.cn/blog_migrate/807f4d74780f4e8827463e96492faacb.jpeg)
值得注意的是:reflect.Uint() 会返回 uint64 类型。这是因为 uint64 类型可以编码成其他所有整数类型。
这里是剩余其他无符号整数类型的提取:
![350ae2bd79b2b20cf4a73c86841312d5.png](https://i-blog.csdnimg.cn/blog_migrate/53882e256879b0e926a2dc055fc1c936.jpeg)
提取浮点数
在 Go 中有 2 种浮点数类型:
Float32Float64
Float32 类型表示 32bit 大小的浮点数。
Float64 类型表示 64bit 大小的浮点数。
为了获得每种不同的浮点数类型,reflect.Value 对象对应的浮点数需要被转换成相应的浮点数类型。
这里是如何提取 Float32 类型:
![dc989115dc1f4c96a32b1dce75a20684.png](https://i-blog.csdnimg.cn/blog_migrate/03658d82850b28ea9006431a744b9c73.jpeg)
值得注意的是:reflect.Float() 会返回 float64 类型。这是因为 float64 类型可以编码成其他所有浮点数类型。
这里是如何提取 64-bit 的浮点数值:
![92892422a2baaa123f6082877ef22321.png](https://i-blog.csdnimg.cn/blog_migrate/d0e120c6899d0ad242ccb91c62fbe542.jpeg)
提取复数值
在 Go 中有 2 种复数类型:
Complex64Complex128
Complex64 类型表示 64bit 大小的复数。Complex128 类型表示 128bit 大小的复数。
为了获得每种不同的复数类型,reflect.Value 对象对应的复数需要被转换成相应的复数类型。
这里是如何提取 Complex64 类型:
![bd2306a5f7ffd7a48bcb5ee99505ae5d.png](https://i-blog.csdnimg.cn/blog_migrate/c5fde228f669dd3d6c6f68f4203915e1.jpeg)
值得注意的是:reflect.Complex() 会返回 complex128 类型。这是因为 complex128 类型可以编码成其他所有复数类型。
这里是如何提取 128-bit 的复数值:
![b1652728d0124e25782283220d7797c1.png](https://i-blog.csdnimg.cn/blog_migrate/b411ebd9a50e7920edb82f11532c87c2.jpeg)
提取字符串值
在 reflect 包中,字符串值用常量 String 表示。
它们可以通过使用 String() 方法,从 reflect.Value 对象中提取出来:
这里是如何提取 String 类型:
![b2d4fbe29ca1b2e8f722d6013d8beda0.png](https://i-blog.csdnimg.cn/blog_migrate/093324605f1680e4e98ba70991b26e85.jpeg)
提取指针值
在 Go 中,有 2 种指针类型:
UintptrUnsafePointer
Uintptr 和 UnsafePointer 其实是程序内存中代表一个虚拟地址的 uint 值。它可以表示一个变量或函数的位置。
Uintptr 和 UnsafePointer 两者间的不同在于:Uintptr 会在 Go 运行时进行类型校验,而 UnsafePointer 不会。UnsafePointer 可以被用于 Go 中任意类型向 Go 中其他任何拥有相同内存结构的类型转换。如果这是你想要探索的,请在下面评论,我会写更多关于它的东西。
Uintptr 和 UnsafePointer 可以分别通过 Addr() 和 UnsafeAddr() 方法,从 reflect.Value 的对象中提取出来。这是一个展示 Uintptr 提取的例子:
![4fd11878a7f1c192ba99e14292e158e5.png](https://i-blog.csdnimg.cn/blog_migrate/a8a9707634a2e82c21b3ea6b2e060b0e.jpeg)
这是如何提取 UnsafePointer 类型:
![214f2caaea9a33ef4ae26ae46bed58b3.png](https://i-blog.csdnimg.cn/blog_migrate/2d9ad69092d852a985059575588733d7.jpeg)
值得注意的是:上面 v.UnsafeAddr() 会返回 uintptr 值。它应该在同一行进行类型转换,否则这个 unsafe.Pointer 的值不一定指向预期的位置。
接下来是什么
请注意:reflect.Value 结构的所有方法在使用时都需要检验它们的 kind,否则很容易引发 panic。
在下一篇中,我会写更多像 struct、pointer、chan、map、slice、array 等复杂类型对象的创建。敬请期待!