Связь между отражением и интерфейсом в golang

Go

существуетНизкоуровневый анализ интерфейса в golangВ этой статье анализируется основной принцип интерфейса. Внутренняя структура интерфейса делится на два типа: первый — интерфейс iface, представляющий собой интерфейс с методами, и другой — eface, представляющий собой пустой интерфейс. Любой тип имеет два поля: data, _type, представляющий данные переменной интерфейса, и информацию о типе переменной. Так имеет ли это какое-то отношение к типам отражения? Сегодняшняя статья посвящена анализу связи между переменными интерфейса и переменными отражения.

Среда: go версия go1.12.5 linux/amd64

1 Метод типа Reflect.TypeOf(interface{})

Код примера 1 выглядит следующим образом:

图片.png

выход я

Тип переменной 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.

Ссылаться на:

Принцип реализации пакета отражения Голанга (Законы отражения)

Связь между интерфейсом и отражением