существуетНизкоуровневый анализ интерфейса в golangВ этой статье анализируется основной принцип интерфейса. Внутренняя структура интерфейса делится на два типа: первый — интерфейс iface, представляющий собой интерфейс с методами, и другой — eface, представляющий собой пустой интерфейс. Любой тип имеет два поля: data, _type, представляющий данные переменной интерфейса, и информацию о типе переменной. Так имеет ли это какое-то отношение к типам отражения? Сегодняшняя статья посвящена анализу связи между переменными интерфейса и переменными отражения.
Среда: go версия go1.12.5 linux/amd64
1 Метод типа Reflect.TypeOf(interface{})
Код примера 1 выглядит следующим образом:
выход я
Тип переменной x — I. После передачи x в функцию TypeOf(), как функция Name() получает информацию о типе переменной x? Далее разберем пошагово, как функция Name() 12-й строки кода получает тип I.
Взгляните на реализацию функции TypeOf(interface):
func TypeOf(i interface{}) Type {
eface := *(*emptyInterface)(unsafe.Pointer(&i))
return toType(eface.typ)
}
Мы обнаружили, что параметр TypeOf является типом интерфейса, что означает, что копия переменной x завернута в eface (пустой интерфейс), определенный в runtime/runtime2.go. Затем преобразуйте eface в emptyInterface следующим образом: В пакетах Reflect и Runtime определены два пустых интерфейса:
//reflect/type.go
type emptyInterface struct {
typ *rtype
word unsafe.Pointer
}
//runtime/runtime2.go
type eface struct {
_type *_type
data unsafe.Pointer
}
Выяснилось, что он очень похож на пустой интерфейс в runtime-пакете.Типы полей emptyInterface.word и runtime.eface одинаковы. Затем посмотрите, совпадают ли rtype и _type?
//reflect/type.go
type rtype struct {
size uintptr
ptrdata uintptr // number of bytes in the type that can contain pointers
hash uint32 // hash of type; avoids computation in hash tables
tflag tflag // extra type information flags
align uint8 // alignment of variable with this type
fieldAlign uint8 // alignment of struct field with this type
kind uint8 // enumeration for C
alg *typeAlg // algorithm table
gcdata *byte // garbage collection data
str nameOff // string form
ptrToThis typeOff // type for pointer to this type, may be zero
}
//runtime/type.go
type _type struct {
size uintptr
ptrdata uintptr // size of memory prefix holding all pointers
hash uint32
tflag tflag
align uint8
fieldalign uint8
kind uint8
alg *typeAlg
// gcdata stores the GC type data for the garbage collector.
// If the KindGCProg bit is set in kind, gcdata is a GC program.
// Otherwise it is a ptrmask bitmap. See mbitmap.go for details.
gcdata *byte
str nameOff
ptrToThis typeOff
}
Точно так же, так что вы можете переключаться без беспокойства. Другими словами, структура emptyInterface.rtype уже содержит информацию о типе x. Далее продолжайте смотреть, как функция Name() получает строковую информацию типа: В функции Type(interface{}) есть функция toType(), взгляните:
//reflect/type.go
func toType(t *rtype) Type {
if t == nil {
return nil
}
return t
}
Приведенный выше код напрямую преобразует *rtype в тип Type Что такое тип Type?
//reflect/type.go
type Type interface {
......
Name() string
......
}
По сути, Type — это интерфейсный тип.
Этот *rtype должен реализовывать методы этого интерфейса, включая метод Name(). Функция реализации Name() находится следующим образом. Если вы не смотрите сначала на реализацию Name(), вы можете догадаться: это из*rtype
Процесс поиска данных в типе для получения данных и возврата их вызывающей стороне, поскольку*rtype
Он содержит такую информацию, как тип переменной значения.
func (t *rtype) Name() string {
if t.tflag&tflagNamed == 0 {
return ""
}
s := t.String()
i := len(s) - 1
for i >= 0 {
if s[i] == '.' {
break
}
i--
}
return s[i+1:]
}
Сосредоточьтесь на t.String()
func (t *rtype) String() string {
s := t.nameOff(t.str).name()
if t.tflag&tflagExtraStar != 0 {
return s[1:]
}
return s
}
Имя перефокусировки взглядаВыкл():
func (t *rtype) nameOff(off nameOff) name {
return name{(*byte)(resolveNameOff(unsafe.Pointer(t), int32(off)))}
}
Из названия можно догадаться, что Off — это аббревиатура Offset (исследуется конкретная логика в этой функции) для смещения для получения значения соответствующего адреса памяти. Функция name() в функции String() выглядит следующим образом:
func (n name) name() (s string) {
if n.bytes == nil {
return
}
b := (*[4]byte)(unsafe.Pointer(n.bytes))
hdr := (*stringHeader)(unsafe.Pointer(&s))
hdr.Data = unsafe.Pointer(&b[3])
hdr.Len = int(b[1])<<8 | int(b[2])
return s
}
Логика функции name() состоит в том, чтобы вычислить позиции Data и Len строки в соответствии с *byte (то есть первым адресом информации о типе), возвращенным nameOff(), а затем обернуть stringHeader (прототип строки ) через возвращаемое значение &s и преобразование данных Len присваивается прототипу строки, тем самым присваивается возвращаемое значение s.
Суммировать : Обычная переменная => Тип в отражении => Получить информацию о типе переменной.
1. Копия переменной упакована в пустой интерфейсruntime.eface
.
2, будетruntime.eface
Перевести вreflat.emptyInterface
(структура та же).
3, будет*emptyInterface.rtype
Перевести вreflect.Type
Тип интерфейса (обернутый в тип структуры runtime.iface).
4. Переменная типа интерфейса основана наruntime.iface.tab.fun
Найдите функцию reflat.Name().
5, Reflect.Name() в соответствии с*rtype
Структура str (тип nameoff) находит смещение.
6. По смещению и базовому адресу (базовый адрес не указан в*rtype
, этот будет пропущен первым). Найдите тип блока памяти.
7. Упаковано вstringHeaderТип возвращается вызывающей стороне.
На самом деле ядро состоит в том, чтобы скопировать данные структуры eface из пакета среды выполнения в emptyInterface в пакете Reflect, а затем получить из него соответствующую информацию о типе значения.
Другие методы интерфейса refact.Type здесь не упоминаются, основная идея заключается в поиске и других операциях с данными в reflat.emptyInterface.
2 Метод значения Reflect.ValueOf(interface{})
package main
import (
"reflect"
"fmt"
)
func main() {
var a = 3
v := reflect.ValueOf(a)
i := v.Interface()
z := i.(int)
fmt.Println(z)
}
Взгляните на реализацию Reflect.ValueOf():
func ValueOf(i interface{}) Value {
....
return unpackEface(i)
}
Возвращаемое значение имеет тип Value:
type Value struct {
typ *rtype
ptr unsafe.Pointer
flag //先忽略
}
Значение — это тип структуры, который содержит тип и указатель данных переменной значения.
func unpackEface(i interface{}) Value {
e := (*emptyInterface)(unsafe.Pointer(&i))
t := e.typ
if t == nil {
return Value{}
}
f := flag(t.Kind())
if ifaceIndir(t) {
f |= flagIndir
}
return Value{t, e.word, f}
}
Конкретная реализация находится в unpackEface(interface{}):
e := (*emptyInterface)(unsafe.Pointer(&i))
то же, что и выше из*runtime.eface
Перевести в*reflect.emptyInterface了
.
Наконец упаковано как Value:
return Value{t, e.word, f}
Продолжайте смотреть пример кода:
i := v.Interface()
Реализация:
func (v Value) Interface() (i interface{}) {
return valueInterface(v, true)
}
func valueInterface(v Value, safe bool) interface{} {
......
return packEface(v)
}
func packEface(v Value) interface{} {
t := v.typ
var i interface{}
e := (*emptyInterface)(unsafe.Pointer(&i))
switch {
case ifaceIndir(t):
if v.flag&flagIndir == 0 {
panic("bad indir")
}
//将值的数据信息指针赋值给ptr
ptr := v.ptr
if v.flag&flagAddr != 0 {
c := unsafe_New(t)
typedmemmove(t, c, ptr)
ptr = c
}
//为空接口赋值
e.word = ptr
case v.flag&flagIndir != 0:
e.word = *(*unsafe.Pointer)(v.ptr)
default:
e.word = v.ptr
}
//为空接口赋值
e.typ = t
return i
}
Наконец, вызывается функция packEface(), из названия которой понятно, что она упакована в пустой интерфейс. Логика такова: оберните информацию о структуре Reflect.emptyInterface из информации value.typ, а затем запишите Reflect.eface в переменную i, и поскольку i имеет тип interface{}, компилятор преобразует i в тип runtime.eface.
z := i.(int)
По литералу int компилятор проверит, соответствует ли значение int из runtime.eface._type, если не совпадает с panic, то значение, соответствующее i, присваивается z.
Резюме: из переменной значения => переменная отражения значения => переменная интерфейса:
1, упакованный вvalue
Типы.
2, изvalue
Типы получают rtype, упакованные какreflect.emptyInterface
Типы.
3,reflect.eface
компилятор преобразует вruntime.eface
Типы.
4. По программе z :=i(int) изruntime.eface._type
Найдите совпадение в .
5. Совпадение присваивает значение переменной z.
Резюме. Суть типа отражения значения в тип интерфейса{} — это взаимное преобразование между reflet.emptyInterface и runtime.eface.
Ссылаться на:
Принцип реализации пакета отражения Голанга (Законы отражения)