GO(接口与反射)

194 阅读4分钟

这是我参与2022首次更文挑战的第6天,活动详情查看:2022首次更文挑战

参数传递

分为两种:值传递、引用传递

go选择了值传递,在进行参数传递的时候会进行拷贝操作,包括指针,不过即使指针的值发生了备份(指针自己的地址发生了改变),还是可以通过解引用的方式来进行指针指向值全局修改

当传入的结构体很大的时候,会影响调用性能

接口(interface)

引入中间层,通过中间层的方式解藕上下游依赖

go中不需要显示地声明实现的接口,只要实现了对应接口的方法就视为实现了对应接口,仅在必要时进行检测

interface本身也是一种类型,并不代表无类型

package main 
 
type TestStruct struct{} 
 
func NilOrNot(v interface{}) bool { 
        return v == nil 
} 
 
func main() { 
        var s *TestStruct 
        fmt.Println(s == nil)      // #=> true 
        fmt.Println(NilOrNot(s))   // #=> false 
} 
 

在这进行参数传递的时候,会进行nil转换成interface,interface的内容会变成nil,并且拥有原来结构的类型信息,但是interface本身不是nil

interface也有两种类型:

  • Iface:带有方法的接口
type iface struct { // 16 字节 
        tab  *itab  
        data unsafe.Pointer // 指向数据 
} 
 
  • eface:不带有任何方法的接口
type eface struct { // 16 字节 
        _type *_type // 指向类型 
        data  unsafe.Pointer // 指向数据 
} 

_type:

表示一个结构的类型

type _type struct { 
        size       uintptr // 使用的内存大小 
        ptrdata    uintptr 
        hash       uint32  // 用于判断类型是否相等 
        tflag      tflag 
        align      uint8 
        fieldAlign uint8 
        kind       uint8 
        equal      func(unsafe.Pointer, unsafe.Pointer) bool 
        // 可用于快速判断当前类型的多个对象数据是否相等 
        gcdata     *byte 
        str        nameOff 
        ptrToThis  typeOff 
} 

itab:

更为复杂,可以看作是接口类型与具体类型的结合

type itab struct { // 32 字节 
        inter *interfacetype 
        _type *_type 
        hash  uint32  // _type中hash的拷贝,用于快速判断能否进行类型转换 
        _     [4]byte 
        fun   [1]uintptr //动态派发的虚函数表,内部为函数指针 
} 

interface有定义方法的时候,将结构体转换成interface,会将data进行直接拷贝,将类型指针从run time中获取

两者的转换过程如下

关于结构体与指针

  • 结构体与指针都可以实现方法,指针可以调用结构体实现、但指针没有实现的方法,反之则不行

指针变量可以隐式获取指向的对应结构体,然后再进行函数方法调用

因此当结构体实现函数方法时,指针都可视为实现了该方法

反射

go可以通过反射获取变量的类型和值并且进行修改,类似元编程

  • Typeof:获取类型信息,包装为reflect.Type类型
  • Valueof:获取值信息,包装为reflect.Value类型

元编程
是指某类计算机程序的编写,这类计算机程序编写或者操纵其它程序(或者自身)作为它们的资料,或者在运行时完成部分本应在编译时完成的工作。多数情况下,与手工编写全部代码相比,程序员可以获得更高的工作效率,或者给与程序更大的灵活度去处理新的情形而无需重新编译。

第一法则:

关于获取变量(转换成interface后)的内部信息,对于指针变量可以通过.elem()方法获取指向的值信息,另外reflect.Value本身也是有基本类型信息的

\

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

Value的结构:

type Value struct { 
 typ *rtype // 当前结构数据基本类型信息 
 ptr unsafe.Pointer // 数据指针 
 flag 
} 
 

Type的结构(这里以interface为例):

type interfaceType struct { 
   rtype    // 当前结构基本类型信息 
   pkgPath name      // import path 
 methods []imethod // sorted by hash 
} 

第二法则:

可以从reflect_object转换成interface,然后通过显示声明转换为原来的类型

第三法则:

和其他语言一样,只有通过指针才能够修改原有变量的值,否则只是值拷贝的修改,因此只有获取原有变量的指针,然后通过elem方法获取指针指向的值,然后进行修改(使用.Set()方法)才能更新原来变量的reflect.Value

其他

  • 可以通过reflect.Type.Implements()方法判断是否实现了某些interface中规定的接口
  • ValueOf的对象选择有很多,几乎所有类型都可以使用该方法
  • reflect中还有很多其他的方法,如
    • reflect.Type.Elem()可以获得当前结构的内部子数据(有子数据,如map中的数据)的Type信息
    • reflect.Type.Elem()可以获得当前结构(为指针)指向的数据

Type强调的是类型信息及其相关,如类型内部的函数、域等

Value强调的是值信息及其相关,如值的类型、值的内容等

  • 个人理解,就像c++一样,Type更强调的是类本身的信息,Value强调的是对象的信息