Рефлексия — сложный момент в изучении языка го, но он также очень важен для получения знаний. Рефлексия — это волшебное оружие для понимания конструкции системы типов языка Go.От нее неотделима библиотека ORM языка Go, неотделима от нее библиотека сериализации json языка Go, а также среда выполнения языка Go. неотделимо от него. Когда я изучал функцию отражения, мне потребовалось немало усилий, чтобы осмелиться сказать, что я действительно ее понял. Читателям предлагается следовать моим шагам, чтобы шаг за шагом понять функцию отражения.
цель отражения
Одной из целей отражения является получение информации о типе переменной, такой как имя типа, количество занимаемых им байтов, все списки методов, все внутренние структуры полей, базовый тип хранения и т. д.
Второй целью отражения является динамическое изменение значения внутреннего поля переменной. Например, при десериализации json у вас есть имена и соответствующие им значения внутренних полей объекта, и вам нужно циклически заливать значения этих полей в соответствующие поля объекта.
reflect.Kind
Пакет Reflect определяет более дюжины встроенных «метатипов», каждый из которых имеет целое число, представленное типом Reflect.Kind. Разные структуры относятся к разным типам, но все они относятся к одному и тому же метатипу Struct. Срезы с разными подэлементами также относятся к разным типам, но все они имеют один и тот же метатип Slice.
type Kind uint
const (
Invalid Kind = iota // 不存在的无效类型
Bool
Int
Int8
Int16
Int32
Int64
Uint
Uint8
Uint16
Uint32
Uint64
Uintptr // 指针的整数类型,对指针进行整数运算时使用
Float32
Float64
Complex64
Complex128
Array // 数组类型
Chan // 通道类型
Func // 函数类型
Interface // 接口类型
Map // 字典类型
Ptr // 指针类型
Slice // 切片类型
String // 字符串类型
Struct // 结构体类型
UnsafePointer // unsafe.Pointer 类型
)
Базовый код отражения
Пакет Reflect предоставляет два основных метода отражения, а именно методы TypeOf() и ValueOf(), которые используются для получения типа и значения переменных, соответственно, определенных следующим образом.
func TypeOf(v interface{}) Type
func ValueOf(v interface{}) Value
Ниже приведен простой пример, отражающий структурную переменную
package main
import "fmt"
import "reflect"
func main() {
var s int = 42
fmt.Println(reflect.TypeOf(s))
fmt.Println(reflect.ValueOf(s))
}
--------
int
42
Параметры этих двух методов имеют тип interface{}, что означает, что компилятор сначала преобразует целевую переменную в тип interface{} при вызове. В разделе интерфейса мы упомянули, что тип интерфейса содержит два указателя, один указывает на тип и один указывает на значение.Функция двух вышеупомянутых методов состоит в том, чтобы анализировать переменные интерфейса и отделять тип и значение.
reflect.Type
Это тип интерфейса, который определяет множество методов для получения всей информации, относящейся к этому типу. Реализация структуры этого интерфейса скрыта в пакете отражения, и каждый тип имеет связанную структуру типа для выражения своей структурной информации.
type Type interface {
...
Method(i int) Method // 获取挂在类型上的第 i'th 个方法
...
NumMethod() int // 该类型上总共挂了几个方法
Name() string // 类型的名称
PkgPath() string // 所在包的名称
Size() uintptr // 占用字节数
String() string // 该类型的字符串形式
Kind() Kind // 元类型
...
Bits() // 占用多少位
ChanDir() // 通道的方向
...
Elem() Type // 数组,切片,通道,指针,字典(key)的内部子元素类型
Field(i int) StructField // 获取结构体的第 i'th 个字段
...
In(i int) Type // 获取函数第 i'th 个参数类型
Key() Type // 字典的 key 类型
Len() int // 数组的长度
NumIn() int // 函数的参数个数
NumOut() int // 函数的返回值个数
Out(i int) Type // 获取函数 第 i'th 个返回值类型
common() *rtype // 获取类型结构体的共同部分
uncommon() *uncommonType // 获取类型结构体的不同部分
}
Все структуры типов содержат общую часть информации, которая описывается структурой rtype, реализующей все методы интерфейса Type. Остальные различные части информации различны для каждого специального типа структуры. rtype можно понимать как родительский класс, особый тип структуры — это подкласс, и там будет какая-то другая информация о полях.
// 基础类型 rtype 实现了 Type 接口
type rtype struct {
size uintptr // 占用字节数
ptrdata uintptr
hash uint32 // 类型的hash值
...
kind uint8 // 元类型
...
}
// 切片类型
type sliceType struct {
rtype
elem *rtype // 元素类型
}
// 结构体类型
type structType struct {
rtype
pkgPath name // 所在包名
fields []structField // 字段列表
}
...
reflect.Value
В отличие от интерфейса Reflect.Type, Reflect.Value является структурным типом, очень простой структурой.
type Value struct {
typ *rtype // 变量的类型结构体
ptr unsafe.Pointer // 数据指针
flag uintptr // 标志位
}
Это тело интерфейса содержит указатель структуры типа переменной, указатель адреса данных и некоторую информацию флага. Поле указателя структуры типа внутри является адресом структуры rtype выше, в которой хранится информация о типе переменной. Во флагах есть несколько битов, которые хранят «метатип» значения. Давайте рассмотрим простой пример
package main
import "reflect"
import "fmt"
func main() {
type SomeInt int
var s SomeInt = 42
var t = reflect.TypeOf(s)
var v = reflect.ValueOf(s)
// reflect.ValueOf(s).Type() 等价于 reflect.TypeOf(s)
fmt.Println(t == v.Type())
fmt.Println(v.Kind() == reflect.Int) // 元类型
// 将 Value 还原成原来的变量
var is = v.Interface()
fmt.Println(is.(SomeInt))
}
----------
true
true
42
Метод Type() структуры Value также может возвращать информацию о типе переменной, которую можно использовать вместо функции Reflect.TypeOf() без каких-либо отличий. Значение может быть восстановлено до исходного значения переменной с помощью метода Interface(), предоставляемого структурой Value.
Разобравшись с различными отношениями выше, можно получить следующую картину
func (v Value) SetLen(n int) // 修改切片的 len 属性
func (v Value) SetCap(n int) // 修改切片的 cap 属性
func (v Value) SetMapIndex(key, val Value) // 修改字典 kv
func (v Value) Send(x Value) // 向通道发送一个值
func (v Value) Recv() (x Value, ok bool) // 从通道接受一个值
// Send 和 Recv 的非阻塞版本
func (v Value) TryRecv() (x Value, ok bool)
func (v Value) TrySend(x Value) bool
// 获取切片、字符串、数组的具体位置的值进行读写
func (v Value) Index(i int) Value
// 根据名称获取结构体的内部字段值进行读写
func (v Value) FieldByName(name string) Value
// 将接口变量装成数组,一个是类型指针,一个是数据指针
func (v Value) InterfaceData() [2]uintptr
// 根据名称获取结构体的方法进行调用
// Value 结构体的数据指针 ptr 可以指向方法体
func (v Value) MethodByName(name string) Value
...
Стоит отметить, что многие методы, предоставляемые структурой Value, возвращают тип Value. Например, метод Index(i int) типа отраженного массива вернет новый объект Value, тип которого указывает на тип подэлемента внутри массива, а указатель данных объекта указывает на память подэлемента в указанной позиции массива.
Поймите три официальных закона отражения в языке Go.
Чиновник сделал абстрактное описание функции отражения языка Go и суммировал три закона, а именно:
- Reflection goes from interface value to reflection object.
- Reflection goes from reflection object to interface value.
- To modify a reflection object, the value must be settable.
Первый закон означает, что отражение преобразует переменные интерфейса в объекты отражения Type и Value, что легко понять и является функцией следующих двух методов.
func TypeOf(v interface{}) Type
func ValueOf(v interface{}) Value
Второй закон означает, что отражение может восстановить исходную переменную интерфейса через значение объекта отражения, которое ссылается на метод Interface(), предоставляемый структурой Value. Обратите внимание, что он получает переменную интерфейса.Если вы хотите заменить ее исходной переменной, вам нужно пройти моделирование.
func (v Value) Interface() interface{}
Первые два закона относительно просты, и их смысл можно выразить с помощью нарисованной ранее диаграммы отражения. Функция третьего закона не очень хорошо понята, это означает, что вы хотите использовать функцию отражения для изменения значения переменной при условии, что значение может быть изменено.
Переменные типа значения не могут быть изменены посредством отражения, потому что перед отражением переменная значения должна быть преобразована в переменную интерфейса при передаче параметров, содержимое значения будет поверхностно скопировано, а адрес памяти данных, на который указывает значение объекта отражения, не память исходного адреса переменной, а скопированный адрес памяти. Это означает, что если переменная типа значения может быть изменена посредством отражения, операция модификации вообще не повлияет на значение исходной переменной, и оно будет изменено напрасно. Таким образом, пакет Reflect напрямую запрещает модификацию переменных типа значения посредством отражения. Давайте посмотрим на пример
package main
import "reflect"
func main() {
var s int = 42
var v = reflect.ValueOf(s)
v.SetInt(43)
}
---------
panic: reflect: reflect.Value.SetInt using unaddressable value
goroutine 1 [running]:
reflect.flag.mustBeAssignable(0x82)
/usr/local/go/src/reflect/value.go:234 +0x157
reflect.Value.SetInt(0x107a1a0, 0xc000016098, 0x82, 0x2b)
/usr/local/go/src/reflect/value.go:1472 +0x2f
main.main()
/Users/qianwp/go/src/github.com/pyloque/practice/main.go:8 +0xc0
exit status 2
Попытка изменить целочисленную переменную с помощью отражения не удалась, и программа напрямую выдает исключение. Попробуем изменить значение, на которое указывает переменная-указатель, с помощью отражения, что вполне возможно.
package main
import "fmt"
import "reflect"
func main() {
var s int = 42
// 反射指针类型
var v = reflect.ValueOf(&s)
// 要拿出指针指向的元素进行修改
v.Elem().SetInt(43)
fmt.Println(s)
}
-------
43
Можно видеть, что значение переменной s действительно успешно изменено, но этот пример изменяет значение, на которое указывает указатель, а не саму переменную указателя.Если вы не используете метод Elem() для его изменения, будет выброшено такое же исключение.
Структуры также являются типами значений и также должны модифицироваться типами указателей. Ниже мы пытаемся использовать отражение для динамического изменения значения внутреннего поля структуры.
package main
import "fmt"
import "reflect"
type Rect struct {
Width int
Height int
}
func SetRectAttr(r *Rect, name string, value int) {
var v = reflect.ValueOf(r)
var field = v.Elem().FieldByName(name)
field.SetInt(int64(value))
}
func main() {
var r = Rect{50, 100}
SetRectAttr(&r, "Width", 100)
SetRectAttr(&r, "Height", 200)
fmt.Println(r)
}
-----
{100 200}
Здесь представлены основные функции отражения.В продвинутой части этой книги мы будем использовать функцию отражения для завершения простой структуры ORM.Это большое задание очень сложное, и читатели могут попробовать его после того, как заложат прочную основу.
Отсканируйте, чтобы следить за «Code Cave» и читать другие статьи, связанные с языком Go.