Goalng下的反射模块reflect学习使用

2018-04-27  本文已影响410人  9c46ece5b7bd

反射reflection

反射

常用的类型、函数和方法

//返回动态类型i的类型,如果i是一个空结构体类型,TypeOf将返回nil
func TypeOf(i interface{}) Type

//Type 接口类型
type Type interface {
    Align() int
    FieldAlign() int
    //指定结构体中方法的下标,返回某个方法的对象,需要注意的是返回的Method是一个独立的结构体
    Method(int) Method
    /*
    type Method struct {
        Name string
        PkgPath string
        Type Type
        Func Value
        Index int
    }
    */
    
    
    MethodByName(string) (Method, bool)
    
    //返回该结构体类型的方法下标
    NumMethod() int
    //返回类型的名称,即动态类型i的名称
    Name() string
    PkgPath() string
    Size() uintptr
    String() string
    Kind() Kind
    Implements(u Type) bool
    AssignableTo(u Type) bool
    ConvertibleTo(u Type) bool
    Comparable() bool
    Bits() int
    ChanDir() ChanDir
    IsVariadic() bool
    Elem() Type
    //返回结构体类型第i个字段
    Field(i int) StructField
    //StructField结构体
    //type StructField struct {
    // Name string
    // PkgPath string
    // Type Type
    // Tag  StructTag
    // Offset uintptr
    // Index []int
    // Anonymous bool
    
    //根据结构体字段索引获取嵌入字段的结构体信息
    FieldByIndex(index []int) StructField
    
    FieldByName(name string) (StructField, bool)
    FieldByNameFunc(match func(string) bool) (StructField, bool)
    In(i int) Type
    Key() Type
    Len() int
    //返回动态类型i(结构体字段)的字段总数
    NumField() int
    NumIn() int
    NumOut() int
    Out(i int) Type
}

//返回接口i的一个初始化的新值.ValueOf(nil)返回一个零值
func ValueOf(i interface{}) Value

// Value结构体
type Value struct {
    
}
// Value结构体的一些方法
// 返回结构体v中的第i个字段。如果v的类型不是结构体或者i超出了结构体的范围,则会出现panic
func (v Value) Field(i int) Value

//以接口类型返回v的当前值
func (v Value) Interface() (i interface{})
//等价于.
var i interface{} = (v's underlying value)


//通过反射方式修改结构体对象的一些方法

//返回接口v包含或者指针v包含的值
func (v Value) Elem() Value
//判断该接口v是否可以被set修改
func (v Value) CanSet() bool

//使用另外一个反射接口去修改反射值
func (v Value) Set(x Value)
//其他不同类型的Set
func (v Value) SetBool(x bool)
func (v Value) SetBytes(x []byte)
func (v Value) SetFloat(x float64)
func (v Value) SetInt(x int64)
//设置结构体对象v的长度为n
func (v Value) SetLen(n int)
func (v Value) SetString(x string)


//一些辅助方法
//返回反射结构体的Value的类型.如果v为零值,IsValid将返回false
func (v Value) Kind() Kind
//判断value是否为有效值,通常用在判断某个字段是否在反射体的Value中
func (v Value) IsValid() bool

//Kind常量
type Kind uint
const (
        Invalid Kind = iota
        Bool
        Int
        Int8
        Int16
        Int32
        Int64
        Uint
        Uint8
        Uint16
        Uint32
        Uint64
        Uintptr
        Float32
        Float64
        Complex64
        Complex128
        Array
        Chan
        Func
        Interface
        Map
        Ptr
        Slice
        String
        Struct
        UnsafePointer
)






反射的基本操作

通过反射来获取结构体字段的名称以及其他相关信息。

$ cat test-reflect.go
package main
import (
    "fmt"
    "reflect"
)

//定义结构体
type User struct{
    Id    int
    Name  string
    Age   int
    }
//定义结构体方法
func (u User) Hello(){
    fmt.Println("Hello xuxuebiao")
    }

func main() {
   u := User{1,"bgops",25}
   Info(u)
   u.Hello()
}

//定义一个反射函数,参数为任意类型
func Info(o interface{}) {
    //使用反射类型获取o的Type,一个包含多个方法的interface
    t := reflect.TypeOf(o)
    //打印类型o的名称
    fmt.Println("type:",t.Name())

    //使用反射类型获取o的Value,一个空的结构体
    v := reflect.ValueOf(o)
    fmt.Println("Fields:")

    //t.NumField()打印结构体o的字段个数(Id,Name,Age共三个)
    for i :=0;i< t.NumField();i++{
        //根据结构体的下标i来获取结构体某个字段,并返回一个新的结构体
        /**
        type StructField struct {
            Name string
            PkgPath string
            Type      Type
            Tag       StructTag
            Offset    uintptr
            Index     []int
            Anonymous bool
        }
        **/
        f := t.Field(i)

        //使用结构体方法v.Field(i)根据下标i获取字段Value(Id,Name,Age)
        //在根据Value的Interface()方法获取当前的value的值(interface类型)
        val := v.Field(i).Interface()
        fmt.Printf("%6s:%v = %v\n",f.Name,f.Type,val)
        }

    //使用t.NumMethod()获取所有结构体类型的方法个数(只有Hello()一个方法)
    //接口Type的方法NumMethod() int
    for i := 0;i < t.NumMethod();i++ {
        //使用t.Method(i)指定方法下标获取方法对象。返回一个Method结构体
        //Method(int) Method
        m := t.Method(i)
        //打印Method结构体的相关属性
        /*
        type Method struct {
              Name    string
              PkgPath string
              Type    Type
              Func    Value
              Index   int
        }
        */
        fmt.Printf("%6s:%v\n",m.Name,m.Type)
        }
    }
    
$ go run test-reflect.go
type: User
Fields:
    Id:int = 1
  Name:string = bgops
   Age:int = 25
 Hello:func(main.User)
Hello xuxuebiao

注意:我们上面的示例是使用值类型进行进行反射构造的。如果是指针类型的话,我们需要使用reflect.Struct字段进行判断接口类型的Kind()方法

if k := t.Kind();k != reflect.Struct {
    fmt.Println("非值类型的反射")
    return
}

匿名字段的反射以及嵌入字段

注意:反射会将匿名字段当做独立的字段去处理,需要通过类型索引方式使用FieldByIndex方法去逐个判断

//根据指定索引返回对应的嵌套字段
FieldByIndex(index []int) StructField

type StructField struct {
    Name    string
    PkgPath string
    Type    Type
    Tag     StructTag
    Offset  uintptr
    Index   []int
    Anonymous   bool //是否为匿名字段
}

示例:

$ cat anonymous-reflect.go
package main
import (
    "fmt"
    "reflect"
)

type User struct {
    Id    int
    Name  string
    Age   int
}

type Manager struct {
    User
    title string
}

func main() {
    //注意匿名字段的初始化操作
    m :=  Manager{User: User{1,"biaoge",24},title:"hello biao"}
    t := reflect.TypeOf(m)

    fmt.Printf("%#v\n",t.FieldByIndex([]int{0}))
    fmt.Printf("%#v\n",t.FieldByIndex([]int{1}))
    fmt.Printf("%#v\n",t.FieldByIndex([]int{0,0}))
    fmt.Printf("%#v\n",t.FieldByIndex([]int{0,1}))

}

$ go run anonymous-reflect.go
reflect.StructField{Name:"User", PkgPath:"", Type:(*reflect.rtype)(0x97260), Tag:"", Offset:0x0, Index:[]int{0}, Anonymous:true}
reflect.StructField{Name:"title", PkgPath:"main", Type:(*reflect.rtype)(0x8bda0), Tag:"", Offset:0x20, Index:[]int{1}, Anonymous:false}
reflect.StructField{Name:"Id", PkgPath:"", Type:(*reflect.rtype)(0x8b8a0), Tag:"", Offset:0x0, Index:[]int{0}, Anonymous:false}
reflect.StructField{Name:"Name", PkgPath:"", Type:(*reflect.rtype)(0x8bda0), Tag:"", Offset:0x8, Index:[]int{1}, Anonymous:false}

通过反射修改目标对象

通过反射的方式去修改对象的某个值。需要注意的亮点是,首先,需要找到对象相关的名称,其次需要找到合适的方法去修改相应的值。

$ cat mod-value-reflect.go
package main
import (
    "fmt"
    "reflect"
)

func main() {
    x := 123
    v := reflect.ValueOf(&x)
    v.Elem().SetInt(5256)
    fmt.Println(x)
}

$ go run mod-value-reflect.go
5256

修改I的时候需要找到对象字段的名称;并且判断类型,使用相对正确的类型修改值

v.FieldByName("Name");f.Kind() == reflect.String {
    f.SetString("test string")
}

判断是否找到正确的字段名称:

f := v.FieldByName("Name1")
//判断反射对象Value中是否找到Name1字段
if !f.IsValid() {
    fmt.Println("bad field")
    return
}

示例:

$ cat mod-value-reflect.go
package main
import (
    "fmt"
    "reflect"
)

type User struct {
    Id  int
    Name string
    Age int
}

//使用反射方式对结构体对象的修改有两个条件
//1.通过指针
//2.必须是可set的方法
func main() {
    num := 123
    numv := reflect.ValueOf(&num)
    //通过Value的Elem()和SetX()方法可直接对相关的对象进行修改
    numv.Elem().SetInt(666)
    fmt.Println(num)

    u := User{1,"biao",24}
    uu := reflect.ValueOf(&u)
    //Set()后面的必须是值类型
    //func (v Value) Set(x Value)
    test := User{2,"bgops",2}
    testv := reflect.ValueOf(test)
    uu.Elem().Set(testv)
    fmt.Println("Change the test to u with Set(x Value)",uu)

    //此时的U已经被上面那个uu通过指针的方式修改了
    Set(&u)
    fmt.Println(u)
}

func Set(o interface{}) {
    v := reflect.ValueOf(o)
    //判断反射体值v是否是Ptr类型并且不能进行Set操作
    if v.Kind() == reflect.Ptr && ! v.Elem().CanSet() {
        fmt.Println("xxx")
        return
        //初始化对象修改后的返回值(可接受v或v的指针)
        } else {
            v = v.Elem()
        }
    //按照结构体对象的名称进行查找filed,并判断类型是否为string,然后进行Set
    if f := v.FieldByName("Name"); f.Kind() == reflect.String {
          f.SetString("BYBY")
        }
    }
    
$ go run mod-value-reflect.go
666
Change the test to u with Set(x Value) &{2 bgops 2}
{2 BYBY 2}

通过反射进行动态方法的调用

使用反射的相关知识进行方法的动态调用

$ cat method-reflect.go
package main
import (
    "fmt"
    "reflect"
)

type User struct {
    Id  int
    Name string
    Age int
}

func (u User) Hello(name string,id int) {
    fmt.Printf("Hello %s,my name is %s and my id is %d\n",name,u.Name,id)
}

func main() {
    u := User{1,"biaoge",24}
    fmt.Println("方法调用:")
    u.Hello("xuxuebiao",121)

    //获取结构体类型u的Value
    v := reflect.ValueOf(u)
    //根据方法名称获取Value中的方法对象
    mv := v.MethodByName("Hello")

    //构造一个[]Value类型的变量,使用Value的Call(in []Value)方法进行动态调用method
    //这里其实相当于有一个Value类型的Slice,仅一个字段
    args := []reflect.Value{reflect.ValueOf("xuxuebiao"),reflect.ValueOf(5256)}
    fmt.Println("通过反射动态调用方法:")
    //使用Value的Call(in []Value)方法进行方法的动态调用
    //func (v Value) Call(in []Value) []Value
    //需要注意的是当v的类型不是Func的化,将会panic;同时每个输入的参数args都必须对应到Hello()方法中的每一个形参上
    mv.Call(args)

}

$ go run method-reflect.go
方法调用:
Hello xuxuebiao,my name is biaoge and my id is 121
通过反射动态调用方法:
Hello xuxuebiao,my name is biaoge and my id is 5256
上一篇下一篇

猜你喜欢

热点阅读