---
title: 学习下Golang 的反射
date: 2019-01-07
updated: 2019-01-08
---
# Golang 的Reflection
Go 的反射有三个基础概念: Types Types, Kinds, and Values. 

## 获取变量反射类型 TypeOf
你可以通过`refType := reflect.TypeOf(var)`获取变量反射类型(`reflect.Type`), 它支持下列方法:
1. `refType.Name()`返回 TypeName: `int`,`string`、`Foo`、`Bar`等, 注意 slice 和 pointer  TypeName 是空的
1. `refType.Kind()`返回 Kind: `int`,`string`、`struct`、`ptr`等

对于某些Kind 类(pointer, map, slice, channel, or array)来说:
1. `refType.Elem()`返回包含元素的`refType`: 

对于struct 来说:
1. `refType.Elem()`返回包含元素的`refType`: 仅限 pointer, map, slice, channel, or array
1. `refType.NumField()` 返回field 数量
1. `refType.Field(i)` 返回`reflect.StructField` 类型元素`f=field[i]` :
    1. `f.Name` 返回元素变量名
    2. `f.Type` 返回元素的反射类型`reflection.Type`: 你可以访问到`f.Type.Name()`,`f.Type.Kind()`
    3. `f.Tag` 返回元素的Tag


来看下例子(by Jon Bodner@medium)

    type Foo struct {
        A int `tag1:"First Tag" tag2:"Second Tag"`
        B string
    }

    func main() {
        sl := []int{1, 2, 3}
        greeting := "hello"
        greetingPtr := &greeting
        f := Foo{A: 10, B: "Salutations"}
        fp := &f

        slType := reflect.TypeOf(sl)
        gType := reflect.TypeOf(greeting)
        grpType := reflect.TypeOf(greetingPtr)
        fType := reflect.TypeOf(f)
        fpType := reflect.TypeOf(fp)

        examiner(slType, 0)
        examiner(gType, 0)
        examiner(grpType, 0)
        examiner(fType, 0)
        examiner(fpType, 0)
    }

    func examiner(t reflect.Type, depth int) {
        fmt.Println(strings.Repeat("\t", depth), "Type is", t.Name(), "and kind is", t.Kind())
        switch t.Kind() {
        case reflect.Array, reflect.Chan, reflect.Map, reflect.Ptr, reflect.Slice:
            fmt.Println(strings.Repeat("\t", depth+1), "Contained type:")
            examiner(t.Elem(), depth+1)
        case reflect.Struct:
            for i := 0; i < t.NumField(); i++ {
                f := t.Field(i)
                fmt.Println(strings.Repeat("\t", depth+1), "Field", i+1, "name is", f.Name, "type is", f.Type.Name(), "and kind is", f.Type.Kind())
                if f.Tag != "" {
                    fmt.Println(strings.Repeat("\t", depth+2), "Tag is", f.Tag)
                    fmt.Println(strings.Repeat("\t", depth+2), "tag1 is", f.Tag.Get("tag1"), "tag2 is", f.Tag.Get("tag2"))
                }
            }
        }
    }

输出结果:https://play.golang.org/p/lZ97yAUHxX

    Type is  and kind is slice
        Contained type:
        Type is int and kind is int
    Type is string and kind is string
    Type is  and kind is ptr
        Contained type:
        Type is string and kind is string
    Type is Foo and kind is struct
        Field 1 name is A type is int and kind is int
            Tag is tag1:"First Tag" tag2:"Second Tag"
            tag1 is First Tag tag2 is Second Tag
        Field 2 name is B type is string and kind is string
    Type is  and kind is ptr
        Contained type:
        Type is Foo and kind is struct
            Field 1 name is A type is int and kind is int
                Tag is tag1:"First Tag" tag2:"Second Tag"
                tag1 is First Tag tag2 is Second Tag
            Field 2 name is B type is string and kind is string

## 获取变量反射值
获取变量反射值的方式有几个:
1. 通过`refVal := reflect.ValueOf(var)`获取变量反射值,不可以修改值
1. 通过`refPtrVal := reflect.ValueOf(&var)`获取指针反射值,可以修改值

refVal/refPtrVal 提供的method 有:
1. refVal.Type()    返回reflect.Type 类型

注意下只有`refPtrVal`才可以修改值

    refPtrVal.Elem().Set(newRefVal)
    refPtrVal.Elem().SetString("string")
    refPtrVal.Elem().Field(0).setInt(1)

### 获取Field字段

    rv := reflect.ValueOf(struct{Id int}{1})
    f := reflect.Indirect(rv).FieldByName('Id')
    f.Int()
    f.Interface().{struct{Id int}}

## 创建实例

## 创建模板实例
怎么创建呢?
1. 先给New(rt reflect.Type)
2. 由于go 不支持泛型(generics), 你需要用 interface.(FOO) 返回正常的变量

比如:

    newPtrVal := reflect.New(refType)
    newPtrVal.Elem().Field(0).SetInt(20)
    newPtrVal.Elem().Interface().(Foo)

Here’s some code to demonstrate these concepts:
 https://play.golang.org/p/PFcEYfZqZ8

    type Foo struct {
        A int `tag1:"First Tag" tag2:"Second Tag"`
        B string
    }

    func main() {
        greeting := "hello"
        f := Foo{A: 10, B: "Salutations"}

        gVal := reflect.ValueOf(greeting)
        // not a pointer so all we can do is read it
        fmt.Println(gVal.Interface())

        gpVal := reflect.ValueOf(&greeting)
        // it’s a pointer, so we can change it, and it changes the underlying variable
        gpVal.Elem().SetString("goodbye")
        fmt.Println(greeting)

        fType := reflect.TypeOf(f)
        fVal := reflect.New(fType)
        fVal.Elem().Field(0).SetInt(20)
        fVal.Elem().Field(1).SetString("Greetings")
        f2 := fVal.Elem().Interface().(Foo)
        fmt.Printf("%+v, %d, %s\n", f2, f2.A, f2.B)
    }

### 用反射创建slice, map
下面的例子,展示了如何用反射:`reflect.MakeSlice, reflect.MakeMap, and reflect.MakeChan` 创建实例`slice,map,channel`.

    func main() {
        // declaring these vars, so I can make a reflect.Type
        intSlice := make([]int, 0)
        mapStringInt := make(map[string]int)

        // here are the reflect.Types
        sliceType := reflect.TypeOf(intSlice)
        mapType := reflect.TypeOf(mapStringInt)

        // and here are the new values that we are making
        intSliceReflect := reflect.MakeSlice(sliceType, 0, 0)
        mapReflect := reflect.MakeMap(mapType)

        // and here we are using them
        v := 10
        rv := reflect.ValueOf(v)
        intSliceReflect = reflect.Append(intSliceReflect, rv)
        intSlice2 := intSliceReflect.Interface().([]int)
        fmt.Println(intSlice2)

        k := "hello"
        rk := reflect.ValueOf(k)
        mapReflect.SetMapIndex(rk, rv)
        mapStringInt2 := mapReflect.Interface().(map[string]int)
        fmt.Println(mapStringInt2)
    }

### 用反射创建function
 reflect.MakeFunc(rf) 用于创建function 实例

    func MakeTimedFunction(f interface{}) interface{} {
        rf := reflect.TypeOf(f)
        if rf.Kind() != reflect.Func {
            panic("expects a function")
        }
        vf := reflect.ValueOf(f)
        wrapperF := reflect.MakeFunc(rf, func(in []reflect.Value) []reflect.Value {
            start := time.Now()
            out := vf.Call(in)
            end := time.Now()
            fmt.Printf("calling %s took %v\n", runtime.FuncForPC(vf.Pointer()).Name(), end.Sub(start))
            return out
        })
        return wrapperF.Interface()
    }

    func timeMe() {
        fmt.Println("starting")
        time.Sleep(1 * time.Second)
        fmt.Println("ending")
    }

    func timeMeToo(a int) int {
        fmt.Println("starting")
        time.Sleep(time.Duration(a) * time.Second)
        result := a * 2
        fmt.Println("ending")
        return result
    }

    func main() {
        timed := MakeTimedFunction(timeMe).(func())
        timed()
        timedToo := MakeTimedFunction(timeMeToo).(func(int) int)
        fmt.Println(timedToo(2))
    }

### 用反射创建新struct

    func MakeStruct(vals ...interface{}) interface{} {
        var sfs []reflect.StructField
        for k, v := range vals {
            t := reflect.TypeOf(v)
            sf := reflect.StructField{
                Name: fmt.Sprintf("F%d", (k + 1)),
                Type: t,
            }
            sfs = append(sfs, sf)
        }
        st := reflect.StructOf(sfs)
        so := reflect.New(st)
        return so.Interface()
    }

    func main() {
        s := MakeStruct(0, "", []int{})
        sr := reflect.ValueOf(s)

        // getting and setting the int field
        fmt.Println(sr.Elem().Field(0).Interface())
        sr.Elem().Field(0).SetInt(20)
        fmt.Println(sr.Elem().Field(0).Interface())

        // getting and setting the string field
        fmt.Println(sr.Elem().Field(1).Interface())
        sr.Elem().Field(1).SetString("reflect me")
        fmt.Println(sr.Elem().Field(1).Interface())

        // getting and setting the []int field
        fmt.Println(sr.Elem().Field(2).Interface())
        v := []int{1, 2, 3}
        rv := reflect.ValueOf(v)
        sr.Elem().Field(2).Set(rv)
        fmt.Println(sr.Elem().Field(2).Interface())
    }

## Embedded Fields
Golang 有一个匿名struct field 委托行为(Embeded Fields),
比如下面的Bar 看起来是继承了Foo.Double() 方法,实际上它是编译期间,内嵌到Bar的。
 https://play.golang.org/p/aeroNQ7bEI

    package main
    type Foo struct {
        A int
    }

    func (f Foo) Double() int {
        return f.A * 2
    }

    type Bar struct {
        Foo
        B int
    }

    func main() {
        b := Bar{Foo{5}, 100}
        print(b.Double())
    }

如果使用反射来构建带有嵌入字段的结构,并尝试访问这些字段上的方法,可能得到一些奇怪的行为。
1. Github 中有一个issue,用于修复此https://github.com/golang/go/issues/15924
2. https://github.com/golang/go/issues/16522 

这两个issue 都有一段时间没有任何进展了, 最好的办法就是不要使用Embeded Fields。

# 参考
- [go reflection]

[go reflection]: https://medium.com/capital-one-tech/learning-to-use-go-reflection-822a0aed74b7
  1. 笔记