Go反射编程

reflect.TypeOf vs. reflect.ValueOf:

  • reflflect.TypeOf 返回类型 (reflflect.Type)
  • reflflect.ValueOf 返回值 (reflflect.Value)
  • 可以从 reflflect.Value 获得类型
  • 通过 kind 的来判断类型
func CheckType(v interface{}) {
    t := reflect.TypeOf(v)
    switch t.Kind() {
    case reflect.Float32, reflect.Float64:
        fmt.Println("Float")
    case reflect.Int, reflect.Int32, reflect.Int64:
        fmt.Println("Integer")
    default:
        fmt.Println("Unknown", t)
    }
}

func TestBasicType(t *testing.T) {
    var f float64 = 12
    CheckType(f)
    /** 运行结果:
    === RUN   TestBasicType
    Float
    --- PASS: TestBasicType (0.00s)
    */
}

利用反射编写灵活的代码:

  • 按名字访问结构的成员

    reflect.ValueOf(*e).FieldByName("Name")

  • 按名字访问结构的方法

    reflect.ValueOf(*e).MethodByName("UpdateAge").Call([]reflect.Value{reflect.ValueOf(1)})

type Employee struct {
    EmployeeId string
    Name       string `format:"normal"`
    Age        int
}

func (e *Employee) UpdateAge(newVal int) {
    e.Age = newVal
}

func TestInvokeByName(t *testing.T) {
    e := &Employee{"1", "Mike", 30}
    // 按名字获取成员
    t.Logf("Name:value(%[1]v),Type(%[1]T)", reflect.ValueOf(*e).FieldByName("Name"))
    if nameField, ok := reflect.TypeOf(*e).FieldByName("Name"); !ok {
        t.Error("Failed to get 'Name' field.")
    } else {
        t.Log("Tag:format", nameField.Tag.Get("format"))
    }
    reflect.ValueOf(e).MethodByName("UpdateAge").Call([]reflect.Value{reflect.ValueOf(1)})
    t.Log("Updated Age:", e)
    /** 运行结果:
    === RUN   TestInvokeByName
        TestInvokeByName: reflect_test.go:28: Name:value(Mike),Type(reflect.Value)
        TestInvokeByName: reflect_test.go:32: Tag:format normal
        TestInvokeByName: reflect_test.go:35: Updated Age: &{1 Mike 1}
    --- PASS: TestInvokeByName (0.00s)
    */
}

Struct Tag:

type BasicInfo struct {
  Name string `json:"name"`
  Age int `json:"age"`
}

访问Struct:

if nameField, ok := reflect.TypeOf(*e).FieldByName("Name"); !ok {
t.Error("Failed to get 'Name' field.")
} else {
t.Log("Tag:format", nameField.Tag.Get("format")) }

Reflect.Type 和 Reflflect.Value 都有 FieldByName ⽅法,注意他们的区别。

DeepEqual:

比较切片和map

type Customer struct {
    CookieID string
    Name     string
    Age      int
}

func TestDeepEqual(t *testing.T) {
    a := map[int]string{1: "one", 2: "two", 3: "three"}
    b := map[int]string{1: "one", 2: "two", 4: "three"}
    fmt.Println(reflect.DeepEqual(a, b))

    s1 := []int{1, 2, 3}
    s2 := []int{1, 2, 3}
    s3 := []int{2, 3, 1}
    t.Log("s1 == s2?", reflect.DeepEqual(s1, s2))
    t.Log("s1 == s3?", reflect.DeepEqual(s1, s3))

    c1 := Customer{"1", "Mike", 40}
    c2 := Customer{"1", "Mike", 40}

    fmt.Println(reflect.DeepEqual(c1, c2))
    /** 运行结果:
    === RUN   TestDeepEqual
    false
        TestDeepEqual: fiexible_reflect_test.go:23: s1 == s2? true
        TestDeepEqual: fiexible_reflect_test.go:24: s1 == s3? false
    true
    --- PASS: TestDeepEqual (0.00s)
    */
}

关于“反射”你应该知道的:

  • 提⾼了程序的灵活性

  • 降低了程序的可读性

  • 降低了程序的性能

type Employee struct {
    EmployeeID string
    Name       string `format:"normal"`
    Age        int
}

func (e *Employee) UpdateAge(newVal int) {
    e.Age = newVal
}

type Customer struct {
    CookieID string
    Name     string
    Age      int
}

func fillBySettings(st interface{}, settings map[string]interface{}) error {

    // func (v Value) Elem() Value
    // Elem returns the value that the interface v contains or that the pointer v points to.
    // It panics if v's Kind is not Interface or Ptr.
    // It returns the zero Value if v is nil.

    if reflect.TypeOf(st).Kind() != reflect.Ptr {
        return errors.New("the first param should be a pointer to the struct type.")
    }
    // Elem() 获取指针指向的值
    if reflect.TypeOf(st).Elem().Kind() != reflect.Struct {
        return errors.New("the first param should be a pointer to the struct type.")
    }

    if settings == nil {
        return errors.New("settings is nil.")
    }

    var (
        field reflect.StructField
        ok    bool
    )

    for k, v := range settings {
        if field, ok = (reflect.ValueOf(st)).Elem().Type().FieldByName(k); !ok {
            continue
        }
        if field.Type == reflect.TypeOf(v) {
            vstr := reflect.ValueOf(st)
            vstr = vstr.Elem()
            vstr.FieldByName(k).Set(reflect.ValueOf(v))
        }

    }
    return nil
}

func TestFillNameAndAge(t *testing.T) {
    settings := map[string]interface{}{"Name": "Mike", "Age": 30}
    e := Employee{}
    if err := fillBySettings(&e, settings); err != nil {
        t.Fatal(err)
    }
    t.Log(e)
    c := new(Customer)
    if err := fillBySettings(c, settings); err != nil {
        t.Fatal(err)
    }
    t.Log(*c)
    /** 运行结果:
    === RUN   TestFillNameAndAge
        TestFillNameAndAge: fiexible_reflect_test.go:69: { Mike 30}
        TestFillNameAndAge: fiexible_reflect_test.go:74: { Mike 30}
    --- PASS: TestFillNameAndAge (0.00s)
    */
}

”不安全“行为的危险性:

func TestUnsafe(t *testing.T) {
    i := 10
    f := *(*float64)(unsafe.Pointer(&i))
    t.Log(unsafe.Pointer(&i))
    t.Log(f)
    /** 运行结果:
    === RUN   TestUnsafe
        TestUnsafe: unsafe_test.go:11: 0xc000016268
        TestUnsafe: unsafe_test.go:12: 5e-323
    --- PASS: TestUnsafe (0.00s)
    */
}

// The cases is suitable for unsafe
type MyInt int

// 合理的类型转换
func TestConvert(t *testing.T) {
    a := []int{1, 2, 3, 4}
    b := *(*[]MyInt)(unsafe.Pointer(&a))
    t.Log(b)
    /** 运行结果:
    === RUN   TestConvert
        TestConvert: unsafe_test.go:26: [1 2 3 4]
    --- PASS: TestConvert (0.00s)
    */
}

// 原子类型操作
func TestAtomic(t *testing.T) {
    var shareBuffer unsafe.Pointer
    writeDataFn := func() {
        data := []int{}
        for i := 0; i < 100; i++ {
            data = append(data, i)
        }
        atomic.StorePointer(&shareBuffer, unsafe.Pointer(&data))
    }
    readDataFn := func() {
        data := atomic.LoadPointer(&shareBuffer)
        fmt.Println(data, *(*[]int)(data))
    }
    var wg sync.WaitGroup
    writeDataFn()
    for i := 0; i < 10; i++ {
        wg.Add(1)
        go func() {
            for i := 0; i < 10; i++ {
                writeDataFn()
                time.Sleep(time.Microsecond * 100)
            }
            wg.Done()
        }()
        wg.Add(1)
        go func() {
            for i := 0; i < 10; i++ {
                readDataFn()
                time.Sleep(time.Microsecond * 100)
            }
            wg.Done()
        }()
    }
    wg.Wait()
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269