当前位置: 首页 > news >正文

服装网站建设基本流程wordpress用什么系统

服装网站建设基本流程,wordpress用什么系统,良品铺子vi设计手册,聊城做网站的公司市场在go的源码包及一些开源组件中#xff0c;经常可以看到reflect反射包的使用#xff0c;本文就与大家一起探讨go反射机制的原理、学习其实现源码 首先#xff0c;了解一下反射的定义#xff1a; 反射是指计算机程序能够在运行时#xff0c;能够描述其自身状态或行为、调整…在go的源码包及一些开源组件中经常可以看到reflect反射包的使用本文就与大家一起探讨go反射机制的原理、学习其实现源码 首先了解一下反射的定义 反射是指计算机程序能够在运行时能够描述其自身状态或行为、调整或修改其状态或行为的能力。 具体到go的反射机制对应为 go提供了在运行时检查变量的值、更新变量的值和调用它们的方法的机制而在编译时并不知道这些变量的具体类型 接口 反射与Interface息息相关反射是通过Interface的类型信息实现的为更方便学习go反射机制先复习一下Interface相关知识 看一下空接口Interface的结构定义 //runtime/runtime2.go type eface struct {_type *_typedata unsafe.Pointer }data表示指向数据的指针_type是所有类型的公共描述信息 _type类型结构的具体定义为 //runtime/type.go type _type struct {size uintptrptrdata uintptr // size of memory prefix holding all pointershash uint32 tflag tflagalign 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 nameOffptrToThis typeOff }其中size是类型的大小hash是类型的哈希值tflag是类型的tag信息与反射相关align和fieldalign与内存对齐相关kind是类型编号具体定义位于runtime/typekind.go中gcdata是gc相关信息 通过空接口的定义可以看出变量在转换为Interface时接口值保存了变量的类型及指针两种信息通过类型信息的对应实现接口与变量之间的互相转换。 go反射三定律 go反射机制在使用时可以总结为以下三定律 反射可以将“接口类型变量”转换为“反射类型对象”反射可以将“反射类型对象”转换为“接口类型变量”如果要修改“反射类型对象”其值必须是“可写的”settable 接下来就结合这三个定律来学习reflect的源码 定律一 反射可以将“接口类型变量”转换为“反射类型对象” 反射类型对象有两种分别是Type和ValueType类型对应一个变量的类型信息而Value类型则对应一个变量的值信息 Type Type为一个接口类型接口包含了所有可对类型信息执行的操作。其中可导出的方法偶尔会在一些源码包里看到而未导出的方法只在内部使用common方法返回类型基本信息uncommon方法返回保留类型实现方法的uncommonType类型。 type Type interface {Align() intFieldAlign() intMethod(int) MethodMethodByName(string) (Method, bool)NumMethod() intName() stringString() stringField(i int) StructFieldFieldByName(name string) (StructField, bool)//....common() *rtypeuncommon() *uncommonType }反射包通过reflect.Typeof()方法返回Type类型下面是方法的实现 // TypeOf returns the reflection Type that represents the dynamic type of i. // If i is a nil interface value, TypeOf returns 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类型第二步toType取出其中typ字段。这个函数的关键在于第一步的类型转换前面复习了空接口eface的定义再来看一下emptyInterface的定义以及其中typ字段的定义 // emptyInterface is the header for an interface{} value. type emptyInterface struct {typ *rtypeword unsafe.Pointer }type rtype struct {size uintptrptrdata uintptr // number of bytes in the type that can contain pointershash uint32 // hash of type; avoids computation in hash tablestflag tflag // extra type information flagsalign uint8 // alignment of variable with this typefieldAlign uint8 // alignment of struct field with this typekind uint8 // enumeration for Calg *typeAlg // algorithm tablegcdata *byte // garbage collection datastr nameOff // string formptrToThis typeOff // type for pointer to this type, may be zero }很清晰发现这两个结构完全相同使用unsafe.Poniter做类型转换也就取出了变量的类型信息及地址 reflect.Typeof方法最终返回了包含变量类型信息的rtype类型的指针也就是表示rtype实现了Type接口的所有方法这些方法分为通用方法和类型特有方法两类 // 通用方法 func (t *rtype) String() string // 获取 t 类型的字符串描述不要通过 String 来判断两种类型是否一致。func (t *rtype) Name() string // 获取 t 类型在其包中定义的名称未命名类型则返回空字符串。func (t *rtype) PkgPath() string // 获取 t 类型所在包的名称未命名类型则返回空字符串。func (t *rtype) Kind() reflect.Kind // 获取 t 类型的类别。func (t *rtype) Size() uintptr // 获取 t 类型的值在分配内存时的大小功能和 unsafe.SizeOf 一样。func (t *rtype) Align() int // 获取 t 类型的值在分配内存时的字节对齐值。func (t *rtype) FieldAlign() int // 获取 t 类型的值作为结构体字段时的字节对齐值。func (t *rtype) NumMethod() int // 获取 t 类型的方法数量。func (t *rtype) Method() reflect.Method // 根据索引获取 t 类型的方法如果方法不存在则 panic。 // 如果 t 是一个实际的类型则返回值的 Type 和 Func 字段会列出接收者。 // 如果 t 只是一个接口则返回值的 Type 不列出接收者Func 为空值。func (t *rtype) MethodByName(string) (reflect.Method, bool) // 根据名称获取 t 类型的方法。func (t *rtype) Implements(u reflect.Type) bool // 判断 t 类型是否实现了 u 接口。func (t *rtype) ConvertibleTo(u reflect.Type) bool // 判断 t 类型的值可否转换为 u 类型。func (t *rtype) AssignableTo(u reflect.Type) bool // 判断 t 类型的值可否赋值给 u 类型。func (t *rtype) Comparable() bool // 判断 t 类型的值可否进行比较操作 // 注意对于数组、切片、映射、通道、指针、接口 func (t *rtype) Elem() reflect.Type // 获取元素类型、获取指针所指对象类型获取接口的动态类型// 数组 func (t *rtype) Len() int // 获取数组的元素个数// Map func (t *rtype) Key() reflect.Type // 获取映射的键类型// 通道 func (t *rtype) ChanDir() reflect.ChanDir // 获取通道的方向// 结构体 func (t *rtype) NumField() int // 获取字段数量func (t *rtype) Field(int) reflect.StructField // 根据索引获取字段func (t *rtype) FieldByName(string) (reflect.StructField, bool) // 根据名称获取字段func (t *rtype) FieldByNameFunc(match func(string) bool) (reflect.StructField, bool) // 根据指定的匹配函数 math 获取字段func (t *rtype) FieldByIndex(index []int) reflect.StructField // 根据索引链获取嵌套字段// 函数 func (t *rtype) NumIn() int // 获取函数的参数数量func (t *rtype) In(int) reflect.Type // 根据索引获取函数的参数信息func (t *rtype) NumOut() int // 获取函数的返回值数量func (t *rtype) Out(int) reflect.Type // 根据索引获取函数的返回值信息func (t *rtype) IsVariadic() bool // 判断函数是否具有可变参数。 // 如果有可变参数则 t.In(t.NumIn()-1) 将返回一个切片。在这些方法中以String()、Field()、NumMethod()分别为例探究一下源码的实现 String()方法返回类型名称当fmt输出时以%T格式输出变量类型名称取的也是本方法的结果 rtype的str字段为类型名字偏移量nameoff方法根据str偏移量转为name类型最后调用name类型的name方法返回[]byte类型 func (t *rtype) String() string {s : t.nameOff(t.str).name()if t.tflagtflagExtraStar ! 0 {return s[1:]}return s } func (t *rtype) nameOff(off nameOff) name {return name{(*byte)(resolveNameOff(unsafe.Pointer(t), int32(off)))} } //resolveNameOff在/runtime/type.go中实现 func resolveNameOff(ptrInModule unsafe.Pointer, off nameOff) name {if off 0 {return name{}}base : uintptr(ptrInModule)for md : firstmoduledata; md ! nil; md md.next {if base md.types base md.etypes {res : md.types uintptr(off)if res md.etypes {println(runtime: nameOff, hex(off), out of range, hex(md.types), -, hex(md.etypes))throw(runtime: name offset out of range)}return name{(*byte)(unsafe.Pointer(res))}}}// No module found. see if it is a run time name.reflectOffsLock()res, found : reflectOffs.m[int32(off)]reflectOffsUnlock()if !found {println(runtime: nameOff, hex(off), base, hex(base), not in ranges:)for next : firstmoduledata; next ! nil; next next.next {println(\ttypes, hex(next.types), etypes, hex(next.etypes))}throw(runtime: name offset base pointer out of range)}return name{(*byte)(res)} }nameoff方法根据rtype类型的name偏移量str字段得到名称*byte的全局地址再根据name结构的方法解析格式得出name、tag、pkgPath等信息。 // name is an encoded type name with optional extra data. // The first byte is a bit field containing: // // 10 the name is exported // 11 tag data follows the name // 12 pkgPath nameOff follows the name and tag // // The next two bytes are the data length: // // l : uint16(data[1])8 | uint16(data[2]) // // Bytes [3:3l] are the string data. // // If tag data follows then bytes 3l and 3l1 are the tag length, // with the data following. type name struct {bytes *byte } //name结构方法与runtime/type.go中一致 func (n name) nameLen() int func (n name) tagLen() int func (n name) name() (s string) func (n name) tag() (s string) func (n name) pkgPath() string回过头来看runtime中resolveNameOff方法firstmoduledata是一个全局的变量保存了编译过程中所有类型的名称、类型等信息与nameoff类型的str字段相同rtype中typeOff类型的ptrToThis为类型信息偏移量查找方法逻辑同resolveNameOff 下面通过一小段代码来验证类型和名称信息是全局唯一的采用的方法同反射中以emptyInterface结构做类型转换一样 由于TypeOf返回为接口类型不便于操作我们以Value类型为例Value类型包含一个rtype类型字段 type TestType struct {Name stringAge int } type value struct {typ *ltypeptr unsafe.Pointerflag uintptr } //ltype字段定义同rtype字段 type ltype struct {size uintptrptrdata uintptrhash uint32tflag uint8align uint8fieldAlign uint8kind uint8alg *intgcdata *bytestr int32ptrToThis int32 } func main() {var a TestTypevar b TestTypevalueA : reflect.ValueOf(a)valueB : reflect.ValueOf(b)usA : (*value)(unsafe.Pointer(valueA))usB : (*value)(unsafe.Pointer(valueB))fmt.Printf(A.str:%X, B.str:%X\n, usA.typ.str, usB.typ.str)fmt.Printf(A.ptrToThis:%X, B.ptrToThis:%X\n, usA.typ.ptrToThis, usB.typ.ptrToThis)fmt.Printf(a.typ:%p, b.typ:%p\n, usA.typ, usB.typ) }通过value类型转换分别取出a、b两个变量类型信息再分别打印出a和b的name偏移量、类型偏移量类型结构指向地址 来看一下结果验证是否为全局信息 A.str:6206, B.str:6206 A.ptrToThis:B320, B.ptrToThis:B320 a.typ:0x4a5360, b.typ:0x4a5360所以编码过程中用到的所有变量在编译构建过程中已经将所有的类型信息保存在全局空间内生成反射对象、接口类型转化等过程都是在取这一些类型信息 继续研究下一个Field方法 Field方法是struct类型的特有方法方法首先就判断了类型类型不匹配直接panic func (t *rtype) Field(i int) StructField {if t.Kind() ! Struct {panic(reflect: Field of non-struct type)}tt : (*structType)(unsafe.Pointer(t))return tt.Field(i) } //与runtime/type.go中定义同步 type structType struct {rtypepkgPath namefields []structField // sorted by offset } // Field方法返回第i个Field func (t *structType) Field(i int) (f StructField) {if i 0 || i len(t.fields) {panic(reflect: Field index out of bounds)}p : t.fields[i]f.Type toType(p.typ)f.Name p.name.name()f.Anonymous p.embedded()if !p.name.isExported() {f.PkgPath t.pkgPath.name()}if tag : p.name.tag(); tag ! {f.Tag StructTag(tag)}f.Offset p.offset()f.Index []int{i}return }方法关键的第一步将rtype指针转为structType类型指针structType类型第一个字段rtype为类型的基本信息后面两个字段为类型特有信息最终通过sturctType类型的Field方法返回StructField类型 再来看一下其他类型结构其第一个字段均为rtype类型且与runtime/type.go中定义同步 type sliceType struct {rtypeelem *rtype // slice element type } type ptrType struct { rtypeelem *rtype // pointer element (pointed at) type } type arrayType struct {rtypeelem *rtype // array element typeslice *rtype // slice typelen uintptr } type chanType struct {rtypeelem *rtype // channel element typedir uintptr // channel direction (ChanDir) }对于特殊类型通用类型信息指针*rtype其实是执行其完整类型信息只是再TypeOf时只取出了第一个字段 还是用一小段代码验证一下structType类型的转换 type TestType struct {Name stringAge int } type name struct {bytes *byte } type structType struct {ltypepkgPath namefields []structField } type structField struct {name nametyp *ltypeoffsetEmbed uintptr } func main() {var a TestTypevalueA : reflect.ValueOf(a)usA : (*value)(unsafe.Pointer(valueA))stA : (*structType)(unsafe.Pointer(usA.typ))fmt.Printf(field length:%d\n, len(stA.fields))fmt.Printf(field 0 kind:%b\n, stA.fields[0].typ.kind)fmt.Printf(field 1 kind:%b\n, stA.fields[1].typ.kind) }再来验证一下结果与TestType类型是否一致字段数量2第一个字段类型2值为24类型为string第二个字段类型值为2类型为int完全一致 field length:2 field 0 kind:11000 field 1 kind:10000010最后是第三个方法NumMethod Type接口的NumMethod方法返回方法数量首先判断了是否是Interface类型是调用Interface类型的NumMethod方法与Field类似 func (t *rtype) NumMethod() int { if t.Kind() Interface {tt : (*interfaceType)(unsafe.Pointer(t))return tt.NumMethod()}return len(t.exportedMethods()) } func (t *rtype) exportedMethods() []method {ut : t.uncommon()if ut nil {return nil } return ut.exportedMethods() } type uncommonType struct {pkgPath nameOff // import path; empty for built-in types like int, stringmcount uint16 // number of methodsxcount uint16 // number of exported methodsmoff uint32 // offset from this uncommontype to [mcount]method_ uint32 // unused } func (t *uncommonType) exportedMethods() []method {if t.xcount 0 {return nil} return (*[1 16]method)(add(unsafe.Pointer(t), uintptr(t.moff), t.xcount 0))[:t.xcount:t.xcount] }其他类型先根据uncommon方法得到uncommonType类型位置调用exportedMethods方法取出method信息 这个方法的关键在于uncommon看一下uncommon方法的源码 func (t *rtype) uncommon() *uncommonType {if t.tflagtflagUncommon 0 {return nil}switch t.Kind() {case Struct:return (*structTypeUncommon)(unsafe.Pointer(t)).ucase Ptr:type u struct {ptrTypeu uncommonType}return (*u)(unsafe.Pointer(t)).ucase Func:type u struct {funcTypeu uncommonType}return (*u)(unsafe.Pointer(t)).ucase Slice:type u struct {sliceTypeu uncommonType}return (*u)(unsafe.Pointer(t)).ucase Array:type u struct {arrayTypeu uncommonType}return (*u)(unsafe.Pointer(t)).ucase Chan:type u struct {chanTypeu uncommonType}return (*u)(unsafe.Pointer(t)).ucase Map:type u struct {mapTypeu uncommonType}return (*u)(unsafe.Pointer(t)).ucase Interface:type u struct {interfaceTypeu uncommonType}return (*u)(unsafe.Pointer(t)).udefault:type u struct {rtypeu uncommonType}return (*u)(unsafe.Pointer(t)).u} }每种类型的处理方法都是一样的定义一个临时类型第一个字段为类型信息第二个字段为uncommonType类型。说明所有实现了方法的类型其类型信息的地址后都紧跟着一个uncommonType类型这个类型里维护相关方法信息 同样的一段代码验证一下 func (t TestType) Func1(in string) error {return nil } func (t TestType) Func2() {return } type u struct {structTypeu uncommonType } type uncommonType struct {pkgPath int32mcount uint16xcount uint16moff uint32_ uint32 } func main() {var a TestTypevalueA : reflect.ValueOf(a)usA : (*value)(unsafe.Pointer(valueA))ucA : (*u)(unsafe.Pointer(usA.typ))fmt.Printf(uncommonType mcount:%d\n, ucA.u.mcount)fmt.Printf(uncommonType moff:%d\n, ucA.u.moff) }对照一下验证结果 uncommonType mcount:2 uncommonType moff:64mcount表示方法类型moff方法信息偏移量是64而不是16所以可推论方法信息不是紧接着uncommonType排列的 简单来做第一个总结所有类型的类型信息均在编译时确定并保存在全局变量中这些类型信息的排列如图 可以通过通用类型指针取到其类型特有信息类型方法信息等 Value Value是一个类型通过reflect方法ValueOf方法返回Value是存放类型值信息的结构先来看一下定义 type Value struct {typ *rtype// Pointer-valued data or, if flagIndir is set, pointer to data.// Valid when either flagIndir is set or typ.pointers() is true.ptr unsafe.Pointer// flag holds metadata about the value.// The lowest bits are flag bits:// - flagStickyRO: obtained via unexported not embedded field, so read-only// - flagEmbedRO: obtained via unexported embedded field, so read-only// - flagIndir: val holds a pointer to the data// - flagAddr: v.CanAddr is true (implies flagIndir)// - flagMethod: v is a method value.// The next five bits give the Kind of the value.// This repeats typ.Kind() except for method values.// The remaining 23 bits give a method number for method values.// If flag.kind() ! Func, code can assume that flagMethod is unset.// If ifaceIndir(typ), code can assume that flagIndir is set.flag相比Type接口的底层rtype不同Value类型多了两个字段分别是ptr指向数据的指针flag标识位指针指向变量数据。而相比空接口类型多了这个flag字段flag是Value内部方法中的一个比较关键的数据用在很多判断条件中 func ValueOf(i interface{}) Value {if i nil {return Value{}}escapes(i)return unpackEface(i) } func unpackEface(i interface{}) Value {e : (*emptyInterface)(unsafe.Pointer(i))// NOTE: dont read e.word until we know whether it is really a pointer or not.t : e.typif t nil {return Value{}}f : flag(t.Kind())if ifaceIndir(t) {f | flagIndir}return Value{t, e.word, f} } //逃逸方法 func escapes(x interface{}) {if dummy.b {dummy.x x} } var dummy struct {b boolx interface{} } ValueOf方法还是通过接口类型转为包内的emptyInterface类型取出变量类型信息和变量地址 Value提供以下方法可分为取值操作、设置值操作、类型特有方法三类 //用于获取值方法 func (v Value) Int() int64 // 获取int类型值如果 v 值不是有符号整型则 panic。func (v Value) Uint() uint64 // 获取unit类型的值如果 v 值不是无符号整型包括 uintptr则 panic。func (v Value) Float() float64 // 获取float类型的值如果 v 值不是浮点型则 panic。func (v Value) Complex() complex128 // 获取复数类型的值如果 v 值不是复数型则 panic。func (v Value) Bool() bool // 获取布尔类型的值如果 v 值不是布尔型则 panic。func (v Value) Len() int // 获取 v 值的长度v 值必须是字符串、数组、切片、映射、通道。func (v Value) Cap() int // 获取 v 值的容量v 值必须是数值、切片、通道。func (v Value) Index(i int) reflect.Value // 获取 v 值的第 i 个元素v 值必须是字符串、数组、切片i 不能超出范围。func (v Value) Bytes() []byte // 获取字节类型的值如果 v 值不是字节切片则 panic。func (v Value) Slice(i, j int) reflect.Value // 获取 v 值的切片切片长度 j - i切片容量 v.Cap() - i。 // v 必须是字符串、数值、切片如果是数组则必须可寻址。i 不能超出范围。func (v Value) Slice3(i, j, k int) reflect.Value // 获取 v 值的切片切片长度 j - i切片容量 k - i。 // i、j、k 不能超出 v 的容量。i j k。 // v 必须是字符串、数值、切片如果是数组则必须可寻址。i 不能超出范围。func (v Value) MapIndex(key Value) reflect.Value // 根据 key 键获取 v 值的内容v 值必须是映射。 // 如果指定的元素不存在或 v 值是未初始化的映射则返回零值reflect.ValueOf(nil)func (v Value) MapKeys() []reflect.Value // 获取 v 值的所有键的无序列表v 值必须是映射。 // 如果 v 值是未初始化的映射则返回空列表。func (v Value) OverflowInt(x int64) bool // 判断 x 是否超出 v 值的取值范围v 值必须是有符号整型。func (v Value) OverflowUint(x uint64) bool // 判断 x 是否超出 v 值的取值范围v 值必须是无符号整型。func (v Value) OverflowFloat(x float64) bool // 判断 x 是否超出 v 值的取值范围v 值必须是浮点型。func (v Value) OverflowComplex(x complex128) bool // 判断 x 是否超出 v 值的取值范围v 值必须是复数型。//设置值方法 func (v Value) SetInt(x int64) //设置int类型的值func (v Value) SetUint(x uint64) // 设置无符号整型的值func (v Value) SetFloat(x float64) // 设置浮点类型的值func (v Value) SetComplex(x complex128) //设置复数类型的值func (v Value) SetBool(x bool) //设置布尔类型的值func (v Value) SetString(x string) //设置字符串类型的值func (v Value) SetLen(n int) // 设置切片的长度n 不能超出范围不能为负数。func (v Value) SetCap(n int) //设置切片的容量func (v Value) SetBytes(x []byte) //设置字节类型的值func (v Value) SetMapIndex(key, val reflect.Value) //设置map的key和value前提必须是初始化以后存在覆盖、不存在添加//其他方法 //结构体相关 func (v Value) NumField() int // 获取结构体字段成员数量func (v Value) Field(i int) reflect.Value //根据索引获取结构体字段func (v Value) FieldByIndex(index []int) reflect.Value // 根据索引链获取结构体嵌套字段func (v Value) FieldByName(string) reflect.Value // 根据名称获取结构体的字段不存在返回reflect.ValueOf(nil)func (v Value) FieldByNameFunc(match func(string) bool) Value // 根据匹配函数 match 获取字段,如果没有匹配的字段则返回零值reflect.ValueOf(nil)//chan相关 func (v Value) Send(x reflect.Value)// 发送数据会阻塞v 值必须是可写通道。func (v Value) Recv() (x reflect.Value, ok bool) // 接收数据会阻塞v 值必须是可读通道。func (v Value) TrySend(x reflect.Value) bool // 尝试发送数据不会阻塞v 值必须是可写通道。func (v Value) TryRecv() (x reflect.Value, ok bool) // 尝试接收数据不会阻塞v 值必须是可读通道。func (v Value) Close() // 关闭通道//函数相关 func (v Value) Call(in []Value) (r []Value) // 通过参数列表 in 调用 v 值所代表的函数或方法。函数的返回值存入 r 中返回。 // 要传入多少参数就在 in 中存入多少元素。 // Call 即可以调用定参函数参数数量固定也可以调用变参函数参数数量可变。func (v Value) CallSlice(in []Value) []Value // 调用变参函数定律二 反射可以将“反射类型对象”转换为“接口类型变量” Value类型可以转换为原空接口类型方法如下 func (v Value) Interface() (i interface{}) {return valueInterface(v, true) } func valueInterface(v Value, safe bool) interface{} {if v.flag 0 {panic(ValueError{reflect.Value.Interface, 0})}if safe v.flagflagRO ! 0 {panic(reflect.Value.Interface: cannot return value obtained from unexported field or method)}if v.flagflagMethod ! 0 {v makeMethodValue(Interface, v)}if v.kind() Interface {if v.NumMethod() 0 {return *(*interface{})(v.ptr)}return *(*interface {M()})(v.ptr)}// TODO: pass safe to packEface so we dont need to copy if safetrue?return packEface(v) } func packEface(v Value) interface{} {t : v.typvar i interface{}e : (*emptyInterface)(unsafe.Pointer(i))// First, fill in the data portion of the interface.switch {case ifaceIndir(t):if v.flagflagIndir 0 {panic(bad indir)}// Value is indirect, and so is the interface were making.ptr : v.ptrif v.flagflagAddr ! 0 {// TODO: pass safe boolean from valueInterface so// we dont need to copy if safetrue?c : unsafe_New(t)typedmemmove(t, c, ptr)ptr c}e.word ptrcase v.flagflagIndir ! 0:// Value is indirect, but interface is direct. We need// to load the data at v.ptr into the interface data word.e.word *(*unsafe.Pointer)(v.ptr)default:// Value is direct, and so is the interface.e.word v.ptr}e.typ treturn i }packEface对应之前的unpackEface完成空接口类型与反射变量类型的转换 方法比较容易理解其中有个flagIndir的判断需要再深入理解下先详细看一下Value的flag字段含义 // - flagStickyRO: obtained via unexported not embedded field, so read-only// - flagEmbedRO: obtained via unexported embedded field, so read-only// - flagIndir: val holds a pointer to the data// - flagAddr: v.CanAddr is true (implies flagIndir)// - flagMethod: v is a method value.flagIndir是标识当前指针即通过空接口传进来的指针是实际数据还是指向数据的指针标识是在ValueOf中设置的判断依据也是ifaceIndir这个方法 func ValueOf(i interface{}) Value {//...if ifaceIndir(t) {f | flagIndir}//... } func ifaceIndir(t *rtype) bool {return t.kindkindDirectIface 0 } //flagIndir flagIndir flag 1 7rtype里kind字段为变量类型变量类型小于1 5 所以这个flagIndir是哪里定义是什么逻辑 其定义位置如下 //compile/internal/gc/reflect.go:890 if isdirectiface(t) {i | objabi.KindDirectIface }func isdirectiface(t *types.Type) bool {if t.Broke() {return false}switch t.Etype {case TPTR,TCHAN,TMAP,TFUNC,TUNSAFEPTR:return truecase TARRAY:// Array of 1 direct iface type can be direct.return t.NumElem() 1 isdirectiface(t.Elem())case TSTRUCT:// Struct with 1 field of direct iface type can be direct.return t.NumFields() 1 isdirectiface(t.Field(0).Type)}return false }根据这个函数可以看出所有指针类型、只有一个元素且元素为指针类型的数组和struct均为direct类型除此之外ValueOf的返回值Value中flag的flagIndir位均置为1 具体看一下packEface这个方法按照三种case来分别举例说明 第一种Indirect类型首先以int为例验证说明 //case ifaceIndir(t) type value struct {typ *ltypeptr unsafe.Pointerflag uintptr } type eface struct {*ltypedata unsafe.Pointer } func main() {a : 100aValue : reflect.ValueOf(a)ua : (*value)(unsafe.Pointer(aValue))fmt.Printf(flag:%b\n, ua.flag)fmt.Printf(kind:%b\n, ua.typ.kind)fmt.Printf(before ptr:%p\n, a)fmt.Printf(value ptr:%p\n, ua.ptr)aInterface : aValue.Interface()ia : (*eface)(unsafe.Pointer(aInterface))fmt.Printf(after ptr:%p\n, ia.data) }输出结果为 flag:10000010 kind:10000010 before ptr:0xc000018060 before ptr:0xc000018068 after ptr:0xc000018068由flag和kind判断其类型为intflagIndir位为1进入第一个case。原变量地址为0x8060转为空接口时进行了变量复制到了Value类型内地址变为0xx8068同时flagAddr为为0返回interface内指针指向Value内复制变量地址 下面例子验证flagAddr为1 type Test struct {Name *stringAge int } func main() {a : testc : 100b : Test{a, c}bValue : reflect.ValueOf(b)nAge : bValue.Elem().Field(1)un : (*value)(unsafe.Pointer(nAge))fmt.Printf(flag:%b\n, un.flag)fmt.Printf(kind:%b\n, un.typ.kind)fmt.Printf(before ptr age:%p\n, b.Age)fmt.Printf(before ptr test:%p\n, b)fmt.Printf(value ptr:%p\n, un.ptr)nInterface : nAge.Interface()in : (*eface)(unsafe.Pointer(nInterface))fmt.Printf(after ptr:%p\n, in.data) }输出结果为 flag:110000010 kind:10000010 before ptr age:0xc0000101f8 before ptr test:0xc0000101f0 value ptr:0xc0000101f8 after ptr:0xc000018080由flag看出其flagIndir与flagAddr均置为1进入flagAddr条件判断内返回interface底层数据指向新创建的类型地址其内容一致。由于第一步传入为Test指针故test地址为0x01f0其Age字段为0x01f0看到value内的地址为0x01f8最终进行变量复制返回复制新地址为0x8080 第二种Value为Indirect类型返回interface为direct类型以下例说明 func main() {a : testc : 100b : Test{a, c}bValue : reflect.ValueOf(b)nName : bValue.Field(0)un : (*value)(unsafe.Pointer(nName))fmt.Printf(flag:%b\n, un.flag)fmt.Printf(kind:%b\n, un.typ.kind)fmt.Printf(before ptr a:%p\n, a)fmt.Printf(before ptr test:%p\n, b)fmt.Printf(value ptr:%p\n, un.ptr)nInterface : nName.Interface()in : (*eface)(unsafe.Pointer(nInterface))fmt.Printf(after ptr:%p\n, in.data) }输出结果为 flag:10010110 kind:110110 before ptr a:0xc0000101e0 before ptr test:0xc0000101f0 value ptr:0xc000010200 after ptr:0xc0000101e0由kind看出flagIndir为0且flag中flagIndir为1进入第二个case。原变量a指针地址为0x01e0test结构地址为0x01f0转为Value后test结构地址变为0x0200最终返回interface内指针指向地址为0x0200地址内指针数据指向原变量a地址 最终可以总结出Value类型内数据指针根据传入类型是否Indirect可能是原数据或者是原数据复制变量的指针最终调用Interface方法返回带数据的空接口类型时其指针一定会转化为指向一个复制变量中避免与通过反射更新原数据互相影响 接着做出第二个总结关于reflect.Type、reflect.Value、interface{}三者的转化关系如下图 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EcCeApBu-1678360708936)(/Users/zsh/Desktop/111.png)] 定律三 如果要修改“反射类型对象”其值必须是“可写的”settable 反射对象是否可写同样是由flag来决定的其判断位有两个flagAddr和flagRO 可以通过CanSet方法进行判断其值是否是settable func (v Value) CanSet() bool {return v.flag(flagAddr|flagRO) flagAddr }由代码可以看出只有flagAddr设置而flagRO未设置时value可设置 接下来再来看一下这两个字段值以及如何设置 const (flagStickyRO flag 1 5flagEmbedRO flag 1 6flagAddr flag 1 8flagRO flag flagStickyRO | flagEmbedRO )flagRO包含两种只读flagStickyRO和flagEmbedRO均是未导出只读类型 先来看flagAddr的设置 func (v Value) Elem() Value {k : v.kind()switch k {case Interface:var eface interface{}if v.typ.NumMethod() 0 {eface *(*interface{})(v.ptr)} else {eface (interface{})(*(*interface {M()})(v.ptr))}x : unpackEface(eface)if x.flag ! 0 {}return xcase Ptr:ptr : v.ptrif v.flagflagIndir ! 0 {ptr *(*unsafe.Pointer)(ptr)}if ptr nil {return Value{}}tt : (*ptrType)(unsafe.Pointer(v.typ))typ : tt.elem//设置flagAddrfl : v.flagflagRO | flagIndir | flagAddrfl | flag(typ.Kind())return Value{typ, ptr, fl}}panic(ValueError{reflect.Value.Elem, v.kind()}) } flagAddr在Elem()方法时set即只有对指针类型调用Elem方法后才可以进行赋值操作 一段代码进行验证 func main() {i : 1v : reflect.ValueOf(i)vPtr : reflect.ValueOf(i)elem : vPtr.Elem()fmt.Printf(value of i:%v\n, v.CanSet())fmt.Printf(value of i:%v\n, vPtr.CanSet())fmt.Printf(value of i elem:%v\n, elem.CanSet())elem.SetInt(10) fmt.Println(i) }输出结果为 value of i:false value of i:false value of i elem:true 10Elem方法代表对指针变量进行解引用即对应*arg这种操作设置值操作可以理解为以下代码 func main() {i : 1v : i*v 10 }flagRO针对结构体内field其设置也是在Field方法内参考代码 func (v Value) Field(i int) Value {if v.kind() ! Struct {panic(ValueError{reflect.Value.Field, v.kind()})}tt : (*structType)(unsafe.Pointer(v.typ))if uint(i) uint(len(tt.fields)) {panic(reflect: Field index out of range)}field : tt.fields[i]typ : field.typ// Inherit permission bits from v, but clear flagEmbedRO.fl : v.flag(flagStickyRO|flagIndir|flagAddr) | flag(typ.Kind())// Using an unexported field forces flagRO.if !field.name.isExported() {if field.embedded() {fl | flagEmbedRO} else {fl | flagStickyRO}}ptr : add(v.ptr, field.offset(), same as non-reflect v.field)return Value{typ, ptr, fl} }通过代码来验证下flag设置 type TestStruct struct {stringName stringage intCity *stringprovince string } func main() {var test TestStructtest.age 100test.Name testtemp : guangzhoutest.City temptest.province guangdongelem : reflect.ValueOf(test).Elem()for i : 0; i elem.NumField(); i {field : elem.Field(i)un : (*value)(unsafe.Pointer(field))fmt.Printf(field index:%d, value:%v, flag:%b, canset:%v\n, i, field, un.flag, field.CanSet())} }输出结果为 field index:0, value:, flag:111011000, canset:false field index:1, value:test, flag:110011000, canset:true field index:2, value:100, flag:110100010, canset:false field index:3, value:0xc0000101e0, flag:110010110, canset:true field index:4, value:guangdong, flag:110111000, canset:falseflagAddr为18flagRO为15和16对照上面输出结果CanSet返回结果与flag对应正确 调用Set、SetInt、SetFloat等方法进行值的更新在set之前会进行settable的验证如果不可set会产生panic。所有一定在完全确认Value是settable之后再调用方法 总结 反射对象类型有Type和Value两种TypeOf和ValueOf方法入参均为interface{}类型依赖空接口类型中类型信息及变量指针实现所有反射功能类型信息维护在全局变量中由编译时确定类型信息包括基本类型信息、特定类型信息、类型方法信息三部分连续排列Type、Value和interface{}之间可以进行互相转换利用反射类型进行更新操作需先调用Elem()方法并确保Value是settable 思考 反射功能强大为什么不推荐使用反射 性能new对象多且堆上分配。如Value类型Elem()、Field()方法返回新的Value类型均会创建新的Value可控性一部分反射方法基于固定类型实现传入类型不匹配就会panic对新手不友好
http://www.hkea.cn/news/14302235/

相关文章:

  • 重庆网站建设seo公司哪家好陕西最新消息今天
  • 网站备案多长时间来完成网站优化排名易下拉效率
  • 搭建网站需要什么技术建筑贴图素材网站
  • 哪里做百度网站方案设计评分标准
  • 襄城县住房和城市建设局网站外贸网站平台排名
  • 北京 网站定制开发创客贴网页设计网站
  • 电商网站建设与运维需要的软件做网站用的符号
  • 免费网站制作器微信小店
  • 望城区网站建设海外 酒店 网站建设
  • 怎么做原创动漫视频网站ps网站导航怎么做
  • 现在网站建站的主流语言是什么智威汤逊广告公司
  • 优享购物官方网站做网站一般多少
  • 九度互联网站制作效果游戏公司网页设计
  • access数据库网站开发博客个人目录wordpress
  • 手机网站打开自动wap软件培训内容怎么写
  • 2021年建站赚钱外贸soho做网站
  • 谷歌seo 外贸建站网站策划是干嘛的
  • 流量联盟网站源码互联网广告平台有哪些
  • 网站建设好了怎么进行推广深圳网站建设 推荐xtdseo
  • 学校网站管理与建设办法seo优化网站查询
  • 算命网站该怎样做设计说明怎么写模板
  • 官网建设企业关键词排名优化公司外包
  • 招生网站建设策划方案南阳高端网站建设
  • 甘肃省工程建设信息官方网站wordpress框架视频
  • 专门做奢侈品的网站有哪些北京公司网站建设推荐
  • 网站设置多少个关键词wordpress添加购买按钮
  • 亚马逊官方网站怎么做昆明网站建设方案托管
  • 宁波如何建网站建个商城网站需要多少钱
  • 加强网站 网站建设网站开发商问花店老板
  • 人工智能网站开发做响应式网站所用的代码