NOTE

1.2 reflection

What reflection is, how Go implements reflection through interfaces, and the relationship between Type and Value.

GoCreated Updated 1 min readhistorical

This is a historical learning note and may contain outdated or incomplete understanding.

1. What Is Reflection

Modify code -> compile -> run. Normally, code can only be changed before compilation. Reflection allows us to modify things at runtime.

2. How Go Implements Reflection

Interface. When a concrete type is assigned to an interface variable, the interface stores the type information of that concrete value. Reflection is implemented through the type information stored in the interface. interface.md

3. Type and Value

3.1. The reflect package defines a (Type, Value) pair

3.2. Obtain Type through reflect.TypeOf

It is closely related to _type (the dynamic type).

type emptyInterface struct {
	typ  *rtype// dynamic type
	word unsafe.Pointer
}

func TypeOf(i interface{}) Type {
    // emptyInterface can be regarded as eface. eface is in the runtime package, while emptyInterface is in the reflect package
	eface := *(*emptyInterface)(unsafe.Pointer(&i))
	// pass in the dynamic type
	return toType(eface.typ)
}

// It does nothing special; it simply returns the dynamic type
func toType(t *rtype) Type {
	if t == nil {
		return nil
	}
	return t


type Type interface {
    // All types can call the following functions

	// Number of bytes occupied by a variable of this type after alignment
	Align() int
	
	// If it is a struct field, number of bytes occupied after alignment
	FieldAlign() int

	// Return the i-th method in this type's method set
	Method(int) Method

	// Get a method by name
	MethodByName(string) (Method, bool)

	// Get the number of exported methods in this type's method set
	NumMethod() int

	// Type name
	Name() string

	// Return the path where the type is located, such as encoding/base64
	PkgPath() string

	// Return the size of the type, similar to unsafe.Sizeof
	Size() uintptr

	// Return the string representation of the type
	String() string

	// Return the kind value of the type
	Kind() Kind

	// Whether the type implements interface u
	Implements(u Type) bool

	// Whether it can be assigned to u
	AssignableTo(u Type) bool

	// Whether it can be converted to u
	ConvertibleTo(u Type) bool

	// Whether values of the type are comparable
	Comparable() bool

	// The following functions can only be called by specific types
	// For example, Key and Elem can only be called by Map types
	
	// Number of bits occupied by the type
	Bits() int

	// Return the direction of a channel; only chan types can call this
	ChanDir() ChanDir

	// Return whether the type is variadic; only func types can call this
	// For example, if t has type func(x int, y ... float64)
	// then t.IsVariadic() == true
	IsVariadic() bool

	// Return the internal element type; only Array, Chan, Map, Ptr, or Slice can call this
	Elem() Type

	// Return the i-th field of a struct type; only struct types can call this
	// It panics if i exceeds the total number of fields
	Field(i int) StructField

	// Return a field in a nested struct
	FieldByIndex(index []int) StructField

	// Get a field by name
	FieldByName(name string) (StructField, bool)

	// FieldByNameFunc returns the struct field with a name
	// Return the field whose name matches the func function
	FieldByNameFunc(match func(string) bool) (StructField, bool)

	// Get the type of the i-th parameter of a function type
	In(i int) Type

	// Return the key type of a map; only map types can call this
	Key() Type

	// Return the length of an Array; only Array types can call this
	Len() int

	// Return the number of fields; only Struct types can call this
	NumField() int

	// Return the number of input parameters of a function type
	NumIn() int

	// Return the number of return values of a function type
	NumOut() int

	// Return the type of the i-th return value of a function type
	Out(i int) Type

    // Return the common part of the type structure
	common() *rtype
	
	// Return the uncommon part of the type structure
	uncommon() *uncommonType
}}

3.3. Obtain Value through reflect.ValueOf

This combines both _type (the dynamic type) and data (the dynamic value).

func ValueOf(i interface{}) Value {
	if i == nil {
		return Value{}
	}
	
   // ……
	return unpackEface(i)
}

// Decompose eface
func unpackEface(i interface{}) Value {
    // Convert i to *emptyInterface
	e := (*emptyInterface)(unsafe.Pointer(&i))

	t := e.typ
	if t == nil {
		return Value{}
	}
	
	// Assemble its typ field, word field, and a flag field into a Value structure
	f := flag(t.Kind())
	if ifaceIndir(t) {
		f |= flagIndir
	}
	return Value{t, e.word, f}
}

// Value structure

// Set the len field of a slice; panic if the type is not a slice
 func (v Value) SetLen(n int)
 
 // Set the cap field of a slice
 func (v Value) SetCap(n int)
 
 // Set a map key/value pair
 func (v Value) SetMapIndex(key, val Value)

 // Return the value at index i of a slice, string, or array
 func (v Value) Index(i int) Value
 
 // Get the value of a struct field by name
 func (v Value) FieldByName(name string) Value
 
// Get an int value
func (v Value) Int() int64

// Get the number of struct fields (members)
func (v Value) NumField() int

// Try to send data to a channel (does not block)
func (v Value) TrySend(x reflect.Value) bool

// Call the function (or method) represented by v with argument list in
func (v Value) Call(in []Value) (r []Value) 

// Call a variadic function
func (v Value) CallSlice(in []Value) []Value

3.4. Value.Kind returns the built-in kind

type Student struct {
}

// TypeOf returns Student
// Kind returns struct

3.5. Obtain interface{} through Value.Interface()

func testStrType(v interface{}) {
	if str, ok := v.(string); ok {
		fmt.Println(str)
	}
}

func testType(t interface{}) {
	switch t.(type) {
	case int:
		fmt.Println("int type")
	case *factory.Stu:
		fmt.Println("Stu type")
		stu := t.(*factory.Stu)
		//stu := *factory.Stu(t)
		stu.TestPrintName()
	case factory.Learner:
		fmt.Println("Learner type")
		learner := t.(factory.Learner)
		learner.Learn()
	}
}

3.6. Modify attribute values through Value.Elem

Reflection can be used to modify the value of a variable, provided that the value can be modified.

4. API

go - Why is IsValid() returning true for the zero value of an int? - Stack Overflow

5. References

Discussion

Sign in with GitHub to comment. Discussions are stored as GitHub Issues.View on GitHub