golang 反射

  Golang的指定类型的变量的类型是静态的(也就是指定int、string这些的变量,它的type是static type),在创建变量的时候就已经确定,有静态那么有没有动态呢??

  说起动态目前也就只能想起 接口Interface;在Golang的实现中,每个interface变量都有一个对应pair,pair中记录了实际变量的值和类型:(value, type)--value是实际变量值,type是实际变量的类型。一个interface{}类型的变量包含了2个指针,一个指针指向值的类型【对应concrete type】,另外一个指针指向实际的值【对应value】。

 

什么是反射?

--->反射就是程序能够在运行时检查变量和值,求出它们的类型??------什么意思----??自古以来越是简单描述的东西越难懂,但是理解后又是豁然开朗。

在 Golang中,reflect 实现了运行时反射。reflect 包会帮助识别 interface{} 变量的底层具体类型和具体值。

reflect.Type 和 reflect.Value

reflect.Type 表示 interface{} 的具体类型,而 reflect.Value 表示它的具体值。reflect.TypeOf()reflect.ValueOf() 两个函数可以分别返回 reflect.Typereflect.Value。这两种类型是我们创建查询生成器的基础;

 

reflect 包中还有一个重要的类型:Kind

在反射包中,KindType 的类型可能看起来很相似,

 

package main

import (
    "fmt"
    "reflect"
)

type order struct {
    ordId      int
    customerId int
}

func createQuery(q interface{}) {
    t := reflect.TypeOf(q)
    v := reflect.ValueOf(q)
    fmt.Println("Type ", t)
    fmt.Println("Value ", v)


}

func createQuery_kind(q interface{}) {
    t := reflect.TypeOf(q)
    k := t.Kind()
    fmt.Println("Type ", t)
    fmt.Println("Kind ", k)


}

func main() { o :
= order{ ordId: 456, customerId: 56, } createQuery(o) } -----------------------------------结果--- Type main.order Value {456 56}

Type  main.order
Kind  struct

Value 的NumField() 和 Field() 方法

NumField() 方法返回结构体中字段的数量,而 Field(i int) 方法返回字段 ireflect.Value

Int() 和 String() 方法

IntString 可以帮助我们分别取出 reflect.Value 作为 int64string

package main

import (
    "fmt"
    "reflect"
    "strings"
)

type Address struct {
    City  string `json:"city" xml:"city"`
    State string `json:"state" xml:"state"`
}

type Person struct {
    Name    string  `json:"name_index" xml:"name_index"`
    Age     int     `json:"age_index" xml:"age_index"`
    Address Address `json:"address_index" xml:"address_index"`
}

func main() {
    // 创建一个 Person 实例
    p := Person{Name: "Alice", Age: 30, Address: Address{City: "New York", State: "NY"}}
    v := reflect.ValueOf(p)

    // NumField: 获取字段数量
    numFields := v.NumField()
    fmt.Println("Number of fields:", numFields)

    // Field: 获取指定索引处的字段
    nameField := v.Field(0)
    fmt.Println("Field 0 (Name):", nameField)

    ageField := v.Field(1)
    fmt.Println("Field 1 (Age):", ageField)

    // FieldByIndex: 获取嵌套字段
    cityField := v.FieldByIndex([]int{2, 0})
    fmt.Println("Field Address.City:", cityField)

    stateField := v.FieldByIndex([]int{2, 1})
    fmt.Println("Field Address.State:", stateField)

    // FieldByName: 根据字段名获取字段
    nameFieldByName := v.FieldByName("Name")
    fmt.Println("Field 'Name':", nameFieldByName)

    ageFieldByName := v.FieldByName("Age")
    fmt.Println("Field 'Age':", ageFieldByName)

    addressFieldByName := v.FieldByName("Address")
    fmt.Println("Field 'Address':", addressFieldByName)

    // FieldByNameFunc: 根据自定义函数获取字段
    fullNameField := v.FieldByNameFunc(func(name string) bool {
        return strings.HasPrefix(name, "Nam")
    })
    fmt.Println("Field with prefix 'Nam':", fullNameField)
    
    t := reflect.TypeOf(p)

    // NumField: 获取字段数量
    numFields = t.NumField()
    fmt.Println("Number of fields:", numFields)

    // 遍历结构体的字段并打印字段名称
    for i := 0; i < numFields; i++ {
        field := t.Field(i)
        fmt.Printf("Field %d: %s, Tag: %s\n", i, field.Name, field.Tag)
        jsonTag := field.Tag.Get("json")
        xmlTag := field.Tag.Get("xml")
        fmt.Printf("Field %d: %s, JSON Tag: %s, XML Tag: %s\n", i, field.Name, jsonTag, xmlTag)
        
    }
    fmt.Println("Nested Struct Tags:")
    printStructTags(reflect.TypeOf(p), "")
}
// 递归打印结构体的标签,包括嵌套的结构体
func printStructTags(t reflect.Type, prefix string) {
    for i := 0; i < t.NumField(); i++ {
        field := t.Field(i)
        jsonTag := field.Tag.Get("json")
        xmlTag := field.Tag.Get("xml")
        fmt.Printf("%sField: %s, JSON Tag: %s, XML Tag: %s\n", prefix, field.Name, jsonTag, xmlTag)

        // 检查嵌套结构体
        if field.Type.Kind() == reflect.Struct && field.Anonymous == false {
            printStructTags(field.Type, prefix+"  ")
        }
    }
}
/*
Number of fields: 3
Field 0 (Name): Alice
Field 1 (Age): 30
Field Address.City: New York
Field Address.State: NY
Field 'Name': Alice
Field 'Age': 30
Field 'Address': {New York NY}
Field with prefix 'Nam': Alice
*/

/*
Number of fields: 3
Field 0: Name, Tag: json:"name_index" xml:"name_index"
Field 0: Name, JSON Tag: name_index, XML Tag: name_index
Field 1: Age, Tag: json:"age_index" xml:"age_index"
Field 1: Age, JSON Tag: age_index, XML Tag: age_index
Field 2: Address, Tag: json:"address_index" xml:"address_index"
Field 2: Address, JSON Tag: address_index, XML Tag: address_index
Nested Struct Tags:
Field: Name, JSON Tag: name_index, XML Tag: name_index
Field: Age, JSON Tag: age_index, XML Tag: age_index
Field: Address, JSON Tag: address_index, XML Tag: address_index
  Field: City, JSON Tag: city, XML Tag: city
  Field: State, JSON Tag: state, XML Tag: state

*/

转载自https://halfrost.com/go_reflection/#toc-11

   Go 语言是强类型语言,编译时对每个变量的类型信息做强校验,所以每个类型的元信息要用一个结构体描述。再者 Go 的反射也是基于类型的元信息实现的。_type 就是所有类型最原始的元信息。

// rtype is the common implementation of most values.
// It is embedded in other struct types.
//
// rtype must be kept in sync with ../runtime/type.go:/^type._type.
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  基础类型枚举值
	// function for comparing objects of this type
	// (ptr to object A, ptr to object B) -> ==?
	equal     func(unsafe.Pointer, unsafe.Pointer) bool // 比较两个形参对应对象的类型是否相等
	gcdata    *byte   // garbage collection data
	str       nameOff // string form 类型名称字符串在二进制文件段中的偏移量
	ptrToThis typeOff // type for pointer to this type, may be zero类型元信息指针在二进制文件段中的偏移量
}
  • kind,这个字段描述的是如何解析基础类型。在 Go 语言中,基础类型是一个枚举常量,有 26 个基础类型,如下。枚举值通过 kindMask 取出特殊标记位。
const (
	kindBool = 1 + iota
	kindInt
	kindInt8
	kindInt16
	kindInt32
	kindInt64
	kindUint
	kindUint8
	kindUint16
	kindUint32
	kindUint64
	kindUintptr
	kindFloat32
	kindFloat64
	kindComplex64
	kindComplex128
	kindArray
	kindChan
	kindFunc
	kindInterface
	kindMap
	kindPtr
	kindSlice
	kindString
	kindStruct
	kindUnsafePointer

	kindDirectIface = 1 << 5
	kindGCProg      = 1 << 6
	kindMask        = (1 << 5) - 1
)
  • str 和 ptrToThis,对应的类型是 nameoff 和 typeOff。这两个字段的值是在链接器段合并和符号重定向的时候赋值的。

1. reflect.Type 通用方法

以下这些方法是通用方法,可以适用于任何类型。

// Type 是 Go 类型的表示。
//
// 并非所有方法都适用于所有类型。
// 在调用 kind 具体方法之前,先使用 Kind 方法找出类型的种类。因为调用一个方法如果类型不匹配会导致 panic
//
// Type 类型值是可以比较的,比如用 == 操作符。所以它可以用做 map 的 key
// 如果两个 Type 值代表相同的类型,那么它们一定是相等的。
type Type interface {
	
	// Align 返回该类型在内存中分配时,以字节数为单位的字节数
	Align() int
	
	// FieldAlign 返回该类型在结构中作为字段使用时,以字节数为单位的字节数
	FieldAlign() int
	
	// Method 这个方法返回类型方法集中的第 i 个方法。
	// 如果 i 不在[0, NumMethod()]范围内,就会 panic。
	// 对于一个非接口类型 T 或 *T,返回的 Method 的 Type 和 Func。
	// fields 字段描述一个函数,它的第一个参数是接收方,而且只有导出的方法可以访问。
	// 对于一个接口类型,返回的 Method 的 Type 字段给出的是方法签名,没有接收者,Func字段为nil。
	// 方法是按字典序顺序排列的。
	Method(int) Method
	
	// MethodByName 返回类型中带有该名称的方法。
	// 方法集和一个表示是否找到该方法的布尔值。
	// 对于一个非接口类型 T 或 *T,返回的 Method 的 Type 和 Func。
	// fields 字段描述一个函数,其第一个参数是接收方。
	// 对于一个接口类型,返回的 Method 的 Type 字段给出的是方法签名,没有接收者,Func字段为nil。
	MethodByName(string) (Method, bool)

	// NumMethod 返回使用 Method 可以访问的方法数量。
	// 请注意,NumMethod 只在接口类型的调用的时候,会对未导出方法进行计数。
	NumMethod() int

	// 对于定义的类型,Name 返回其包中的类型名称。
	// 对于其他(非定义的)类型,它返回空字符串。
	Name() string

	// PkgPath 返回一个定义类型的包的路径,也就是导入路径,导入路径是唯一标识包的类型,如 "encoding/base64"。
	// 如果类型是预先声明的(string, error)或者没有定义(*T, struct{}, []int,或 A,其中 A 是一个非定义类型的别名),包的路径将是空字符串。
	PkgPath() string

	// Size 返回存储给定类型的值所需的字节数。它类似于 unsafe.Sizeof.
	Size() uintptr

	// String 返回该类型的字符串表示。
	// 字符串表示法可以使用缩短的包名。
	// (例如,使用 base64 而不是 "encoding/base64")并且它并不能保证类型之间是唯一的。如果是为了测试类型标识,应该直接比较类型 Type。
	String() string

	// Kind 返回该类型的具体种类。
	Kind() Kind

	// Implements 表示该类型是否实现了接口类型 u。
	Implements(u Type) bool

	// AssignableTo 表示该类型的值是否可以分配给类型 u。
	AssignableTo(u Type) bool

	// ConvertibleTo 表示该类型的值是否可转换为 u 类型。
	ConvertibleTo(u Type) bool

	// Comparable 表示该类型的值是否具有可比性。
	Comparable() bool
}

 

2、reflect.Type 专有方法

以下这些方法是某些类型专有的方法,如果类型不匹配会发生 panic。在不确定类型之前最好先调用 Kind() 方法确定具体类型再调用类型的专有方法。

 

Kind Methods applicable
Int* Bits
Uint* Bits
Float* Bits
Complex* Bits
Array Elem, Len
Chan ChanDir, Elem
Func In, NumIn, Out, NumOut, IsVariadic
Map Key, Elem
Ptr Elem
Slice Elem
Struct Field, FieldByIndex, FieldByName,FieldByNameFunc, NumField

专有方法的说明如下:

type Type interface {

	// Bits 以 bits 为单位返回类型的大小。
	// 如果类型的 Kind 不属于:sized 或者 unsized Int, Uint, Float, 或者 Complex,会 panic。
	//大小不一的Int、Uint、Float或Complex类型。
	Bits() int

	// ChanDir 返回一个通道类型的方向。
	// 如果类型的 Kind 不是 Chan,会 panic。
	ChanDir() ChanDir


	// IsVariadic 表示一个函数类型的最终输入参数是否为一个 "..." 可变参数。如果是,t.In(t.NumIn() - 1) 返回参数的隐式实际类型 []T.
	// 更具体的,如果 t 代表 func(x int, y ... float64),那么:
	// t.NumIn() == 2
	// t.In(0)是 "int" 的 reflect.Type 反射类型。
	// t.In(1)是 "[]float64" 的 reflect.Type 反射类型。
	// t.IsVariadic() == true
	// 如果类型的 Kind 不是 Func.IsVariadic,IsVariadic 会 panic
	IsVariadic() bool

	// Elem 返回一个 type 的元素类型。
	// 如果类型的 Kind 不是 Array、Chan、Map、Ptr 或 Slice,就会 panic
	Elem() Type

	// Field 返回一个结构类型的第 i 个字段。
	// 如果类型的 Kind 不是 Struct,就会 panic。
	// 如果 i 不在 [0, NumField()] 范围内,也会 panic。
	Field(i int) StructField

	// FieldByIndex 返回索引序列对应的嵌套字段。它相当于对每一个 index 调用 Field。
	// 如果类型的 Kind 不是 Struct,就会 panic。
	FieldByIndex(index []int) StructField

	// FieldByName 返回给定名称的结构字段和一个表示是否找到该字段的布尔值。
	FieldByName(name string) (StructField, bool)

	// FieldByNameFunc 返回一个能满足 match 函数的带有名称的 field 字段。布尔值表示是否找到。
	// FieldByNameFunc 先在自己的结构体的字段里面查找,然后在任何嵌入结构中的字段中查找,按广度第一顺序搜索。最终停止在含有一个或多个能满足 match 函数的结构体中。如果在该深度上满足条件的有多个字段,这些字段相互取消,并且 FieldByNameFunc 返回没有匹配。
	// 这种行为反映了 Go 在包含嵌入式字段的结构的情况下对名称查找的处理方式
	FieldByNameFunc(match func(string) bool) (StructField, bool)

	// In 返回函数类型的第 i 个输入参数的类型。
	// 如果类型的 Kind 不是 Func 类型会 panic。
	// 如果 i 不在 [0, NumIn()) 的范围内,会 panic。
	In(i int) Type

	// Key 返回一个 map 类型的 key 类型。
	// 如果类型的 Kind 不是 Map,会 panic。
	Key() Type

	// Len 返回一个数组类型的长度。
	// 如果类型的 Kind 不是 Array,会 panic。
	Len() int

	// NumField 返回一个结构类型的字段数目。
	// 如果类型的 Kind 不是 Struct,会 panic。
	NumField() int

	// NumIn 返回一个函数类型的输入参数数。
	// 如果类型的 Kind 不是Func.NumIn(),会 panic。
	NumIn() int

	// NumOut 返回一个函数类型的输出参数数。
	// 如果类型的 Kind 不是 Func.NumOut(),会 panic。
	NumOut() int

	// Out 返回一个函数类型的第 i 个输出参数的类型。
	// 如果类型的类型不是 Func.Out,会 panic。
	// 如果 i 不在 [0, NumOut()) 的范围内,会 panic。
	Out(i int) Type

	common() *rtype
	uncommon() *uncommonType
}

3. reflect.Value 数据结构

  在 reflect 包中,并非所有的方法都适用于所有类型的值。具体的限制在方法说明注释里面有写。在调用特定种类的方法之前,最好使用 Kind 方法找出 Value 的种类。和 reflect.Type 一样,调用类型不匹配的方法会导致 panic。需要特殊说明的是 zero Value,zero Value 代表没有值。它的 IsValid() 方法返回 false,Kind() 方法返回 Invalid,String() 方法返回 “”,而剩下的所有其他方法均会 panic。大多数函数和方法从不返回 invalid value。如果确实返回了 invalid value,则其文档会明确说明特殊条件。

reflect 包里的 Value数据结构如下:

type Value struct {
	// typ 包含由值表示的值的类型。
	typ *rtype

	// 指向值的指针,如果设置了 flagIndir,则是指向数据的指针。只有当设置了 flagIndir 或 typ.pointers()为 true 时有效。
	ptr unsafe.Pointer

	// flag 保存有关该值的元数据。最低位是标志位:
	//	- flagStickyRO: 通过未导出的未嵌入字段获取,因此为只读
	//	- flagEmbedRO:  通过未导出的嵌入式字段获取,因此为只读
	//	- flagIndir:    val保存指向数据的指针
	//	- flagAddr:     v.CanAddr 为 true (表示 flagIndir)
	//	- flagMethod:   v 是方法值。
    // 接下来的 5 个 bits 给出 Value 的 Kind 种类,除了方法 values 以外,它会重复 typ.Kind()。其余 23 位以上给出方法 values 的方法编号。如果 flag.kind()!= Func,代码可以假定 flagMethod 没有设置。如果 ifaceIndir(typ),代码可以假定设置了 flagIndir。
	flag
}

 

 

reflect.TypeOf() 底层实现

  在 reflect 包中有一个重要的方法 TypeOf(),利用这个方法可以获得一个 Type 的 interface。通过 Type interface 可以获取对象的类型信息。

// TypeOf() 方法返回的 i 这个动态类型的 Type。如果 i 是一个 nil interface value, TypeOf 返回 nil.
func TypeOf(i interface{}) Type {
	eface := *(*emptyInterface)(unsafe.Pointer(&i))
	return toType(eface.typ)
}

func toType(t *rtype) Type {
	if t == nil {
		return nil
	}
	return t
}

emptyInterface 数据结构如下:

// emptyInterface is the header for an interface{} value.
type emptyInterface struct {
	typ  *rtype
	word unsafe.Pointer
}

emptyInterface 其实就是 reflect 版的 eface,数据结构完全一致,所以此处强制类型转换没有问题。

  TypeOf() 入参,入参类型是 i interface{},可以是 2 种类型,一种是 interface 变量,另外一种是具体的类型变量。如果 i 是具体的类型变量,TypeOf() 返回的具体类型信息;如果 i 是 interface 变量,并且绑定了具体类型对象实例,返回的是 i 绑定具体类型的动态类型信息;如果 i 没有绑定任何具体的类型对象实例,返回的是接口自身的静态类型信息。例如下面这段代码:

import (
	"fmt"
	"reflect"
)

func main() {
	ifa := new(Person)
	var ifb Person = Student{name: "halfrost"}
    // 未绑定具体变量的接口类型 
	fmt.Println(reflect.TypeOf(ifa).Elem().Name())
	fmt.Println(reflect.TypeOf(ifa).Elem().Kind().String())
    // 绑定具体变量的接口类型 
	fmt.Println(reflect.TypeOf(ifb).Name())
	fmt.Println(reflect.TypeOf(ifb).Kind().String())
}

Person
interface

Student
struct

  在第一组输出中,reflect.TypeOf() 入参未绑定具体变量的接口类型,所以返回的是接口类型本身 Person。对应的 Kind 是 interface。

  在第二组输出中,reflect.TypeOf() 入参绑定了具体变量的接口类型,所以返回的是绑定的具体类型 Student。对应的 Kind 是 struct。

 

 reflect.ValueOf() 底层实现

 ValueOf() 方法返回一个新的 Value,根据 interface i 这个入参的具体值进行初始化。ValueOf(nil) 返回零值。

 

反射三定律

1、反射可以从接口值中得到反射对象

  • 通过实例获取 Value 对象,使用 reflect.ValueOf() 函数。
  • 通过实例获取反射对象 Type,使用 reflect.TypeOf() 函数。

 

2. 反射可以从反射对象中获得接口值

  

  • 将 Value 转换成空的 interface,内部存放具体类型实例。使用 interface() 函数。
// Interface returns v's current value as an interface{}.
// It is equivalent to:
//	var i interface{} = (v's underlying value)
// It panics if the Value was obtained by accessing
// unexported struct fields.
func (v Value) Interface() (i interface{}) {
	return valueInterface(v, true)
}

 

  • Value 也包含很多成员方法,可以将 Value 转换成简单类型实例,注意如果类型不匹配会 panic。

  

// Int returns v's underlying value, as an int64.
// It panics if v's Kind is not Int, Int8, Int16, Int32, or Int64.
func (v Value) Int() int64 {
	k := v.kind()
	p := v.ptr
	switch k {
	case Int:
		return int64(*(*int)(p))
	case Int8:
		return int64(*(*int8)(p))
	case Int16:
		return int64(*(*int16)(p))
	case Int32:
		return int64(*(*int32)(p))
	case Int64:
		return *(*int64)(p)
	}
	panic(&ValueError{"reflect.Value.Int", v.kind()})
}

从反射对象到接口值的过程是从接口值到反射对象的镜面过程,两个过程都需要经历两次转换:

  • 从接口值到反射对象:
    • 从基本类型到接口类型的类型转换;
    • 从接口类型到反射对象的转换;
  • 从反射对象到接口值:
    • 反射对象转换成接口类型;
    • 通过显式类型转换变成原始类型;

3、若要修改反射对象,值必须可修改

  • 指针类型 Type 转成值类型 Type。指针类型必须是 *Array、*Slice、*Pointer、*Map、*Chan 类型,否则会发生 panic。Type 返回的是内部元素的 Type。
// Elem returns element type of array a.
func (a *Array) Elem() Type { return a.elem }

// Elem returns the element type of slice s.
func (s *Slice) Elem() Type { return s.elem }

// Elem returns the element type for the given pointer p.
func (p *Pointer) Elem() Type { return p.base }

// Elem returns the element type of map m.
func (m *Map) Elem() Type { return m.elem }

// Elem returns the element type of channel c.
func (c *Chan) Elem() Type { return c.elem }

 

  • 值类型 Type 转成指针类型 Type。PtrTo 返回的是指向 t 的指针类型 Type。

 针对反射三定律的这个第三条,还需要特殊说明的是:Value 值的可修改性是什么意思。举例

  

func main() {
	var x float64 = 3.4
	v := reflect.ValueOf(x)
	v.SetFloat(7.1) // Error: will panic.
}

  崩溃信息是 panic: reflect: reflect.Value.SetFloat using unaddressable value,为什么这里 SetFloat() 会 panic 呢?这里给的提示信息是使用了不可寻址的 Value。在上述代码中,调用 reflect.ValueOf 传进去的是一个值类型的变量,获得的 Value 其实是完全的值拷贝,这个 Value 是不能被修改的。如果传进去是一个指针,获得的 Value 是一个指针副本,但是这个指针指向的地址的对象是可以改变的。将上述代码改成这样:

nc main() {
	var x float64 = 3.4
	p := reflect.ValueOf(&x)
	fmt.Println("type of p:", p.Type())
	fmt.Println("settability of p:", p.CanSet())

	v := p.Elem()
	v.SetFloat(7.1)
	fmt.Println(v.Interface()) // 7.1
	fmt.Println(x)             // 7.1
}

 

 

 

4、Type 和 Value 相互转换

  • 由于 Type 中只有类型信息,所以无法直接通过 Type 获取实例对象的 Value,但是可以通过 New() 这个方法得到一个指向 type 类型的指针,值是零值。MakeMap() 方法和 New() 方法类似,只不过是创建了一个 Map。
  • 需要特殊说明的一个方法是 Zero(),这个方法返回指定类型的零值。这个零值与 Value 结构的 zero value 不同,它根本不代表任何值。例如,Zero(TypeOf(42)) 返回带有 Kind Int 且值为 0 的值。返回的值既不可寻址,也不可改变。
  • 由于反射对象 Value 中本来就存有 Tpye 的信息,所以 Value 向 Type 转换比较简单。
func (v Value) Type() Type {
	f := v.flag
	if f == 0 {
		panic(&ValueError{"reflect.Value.Type", Invalid})
	}
	if f&flagMethod == 0 {
		// Easy case
		return v.typ
	}

	// Method value.
	// v.typ describes the receiver, not the method type.
	i := int(v.flag) >> flagMethodShift
	if v.typ.Kind() == Interface {
		// Method on interface.
		tt := (*interfaceType)(unsafe.Pointer(v.typ))
		if uint(i) >= uint(len(tt.methods)) {
			panic("reflect: internal error: invalid method index")
		}
		m := &tt.methods[i]
		return v.typ.typeOff(m.typ)
	}
	// Method on concrete type.
	ms := v.typ.exportedMethods()
	if uint(i) >= uint(len(ms)) {
		panic("reflect: internal error: invalid method index")
	}
	m := ms[i]
	return v.typ.typeOff(m.mtyp)
}

 

5. Value 指针转换成值

  • 把指针的 Value 转换成值 Value 有 2 个方法 Indirect() 和 Elem()。
  • 将值 Value 转换成指针的 Value 只有 Addr() 这一个方法。
  •  

 

posted @ 2020-08-27 22:50  codestacklinuxer  阅读(278)  评论(0编辑  收藏  举报