当前位置:   article > 正文

Go reflect初探_reflect.append

reflect.append

在计算机科学中,反射是指计算机程序在运行时(Run time)可以访问、检测和修改它本
身状态或行为的一种能力。用比喻来说,那种程序能够“观察”并且修改自己的行为。要注意反射和内省(type introspection)的区别。

对应于变量,也就是围绕着它的类型(type)和值(value)进行展开。
Go的空接口概念+反射可以发挥很大的威力>_<

两个重要类型

  • reflect.Type(reflect.Typeof()获得)
  • reflect.Value(reflect.Value()获得)

Type的一些方法

  • String
  • Name 都以字符串的形式返回结构名,只是Name包括包名

  • Field(index) 根据索引获得字段,返回的是StructField(字段也有类型,当然包含一个reflect.Type的字段)

  • NumField 返回的结构的字段数

  • FieldByName 根据字段名获得字段

带Field的方法是获取结构体字段信息的。

type Foo struct {
    X string
    Y int
}
func main() {

    var q = Foo{}
    var f interface{} = q
    t := reflect.TypeOf(f)
    for i := 0; i < t.NumField(); i++ {
        field := t.Field(i)
        fmt.Println(field.Name, field.Type.Name())// output: X Y  string int
    }
    fi, _ := t.FieldByName("Y")
    fmt.Println(fi.Name)                output: Y
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

结构体不光有字段,还有方法,所以Go也提供了Method的一系列函数
- NumMethod
- Method
- FieldByNameFunc

Kind方法 返回对象的基本类型,如果是个结构体就返回struct。
有如下基本返回值

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
)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

大体可以这样使用

func main() {

    var q = []int{1, 2, 3, 4}

    f := reflect.ValueOf(q)

    if f.Kind() == reflect.Slice {
        if fv, ok := f.Interface().([]int); ok {
            fmt.Println(fv)
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

Value的一些方法

Value更关注的是接口值,而不是类型的值

  • Field 返回的仍是一个Value
  • Interface 获得接口值,+ 断言进行类型转换
  • NumField 同上Type
func main() {
    var q = Foo{"1234", 123}
    var f interface{} = q
    t := reflect.ValueOf(f)
    for i := 0; i < t.NumField(); i++ {
        field := t.Field(i)
        fmt.Println(field.Interface())
    }
    field := t.Field(0)

    v, _ := field.Interface().(string)
    fmt.Println(v)
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

可以看到Value和Type有类似的地方
但我们有对改变Value的需求,很少有改变Type的需求。所以Value会有跟多的方法

一系列Set操作

  • Set
  • SetString
  • SetBytes
  • SetInt

  • 但是没有提供SetSlice >_<!
func main() {

    var q = Foo{"1234", 123}
    var f interface{} = &q

    reflect.ValueOf(f).Elem().Field(0).SetString("4321")
    fmt.Println(q)// output:{4321 123}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

如何设置一个结构体? 首先一定要传入的是地址。
其次,使用Elem()方法,才能再去想办法获得字段。
最后要做类型断言和CanSet判断。(不能像我上面一样>_<)


练习:怎么给slice加一个Insert方法呢?而不用丑陋的两次append….

**解答**
func Insert(slice interface{}, pos int, value interface{}) interface{} {

    v := reflect.ValueOf(slice)

    ne := reflect.MakeSlice(reflect.SliceOf(reflect.TypeOf(value)), 1, 1)

    ne.Index(0).Set(reflect.ValueOf(value))
    v = reflect.AppendSlice(v.Slice(0, pos), reflect.AppendSlice(ne, v.Slice(pos, v.Len())))

    return v.Interface()
}
func main() {
    slice := []int{1, 2}

    fmt.Println(Insert(slice, 1, 99))

    slice2 := []string{"a", "c", "d"}

    slice2 = Insert(slice2, 0, "b").([]string)
    fmt.Println(Insert(slice2, 4, "e"))
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

做下性能测试:

func Insert(slice interface{}, pos int, value interface{}) interface{} {

    v := reflect.ValueOf(slice)

    ne := reflect.MakeSlice(reflect.SliceOf(reflect.TypeOf(value)), 1, 1)

    ne.Index(0).Set(reflect.ValueOf(value))
    v = reflect.AppendSlice(v.Slice(0, pos), reflect.AppendSlice(ne, v.Slice(pos, v.Len())))

    return v.Interface()
}
func main() {
    slice := []int{}
    slice2 := []int{}
    t0 := time.Now()
    for i := 1; i < 10000; i++ {
        slice = append(slice[:0], append([]int{i}, slice[0:]...)...)
    }
    t1 := time.Now()
    for i := 1; i < 10000; i++ {
        slice2 = Insert(slice2, 0, i).([]int)
    }
    t2 := time.Now()
    fmt.Println(t2.Sub(t1), t1.Sub(t0))

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

输出:

215.283082ms 138.150966ms
  • 1

连续插入一万个,使用反射和不使用消耗时间差了一倍。但我觉得还是可以接受的。>_<….对于性能要求不高的的地方,用用反射也没什么不可啊!

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小舞很执着/article/detail/907936
推荐阅读
相关标签
  

闽ICP备14008679号