设为首页 加入收藏

TOP

深度解密Go语言之反射(五)
2019-05-23 14:33:19 】 浏览:379
Tags:深度 解密 语言 反射
for i := 0; i < st.NumField(); i++ { f := st.Field(i) tag := f.Tag.Get("qson") if (tag == "Name" || f.Name == "Name") && f.Type.Kind() == reflect.String { foundName = true break } } if !foundName { return } if niceField, foundHandsome := st.FieldByName("Handsome"); foundHandsome == false || niceField.Type.Kind() != reflect.Bool { return } // 设置名字为 "qcrao" 的对象的 "Handsome" 字段为 true for i := 0; i < v.Len(); i++ { e := v.Index(i) handsome := e.FieldByName("Handsome") // 寻找字段名为 Name 或者 tag 的值为 Name 的字段 var name reflect.Value for j := 0; j < st.NumField(); j++ { f := st.Field(j) tag := f.Tag.Get("qson") if tag == "Name" || f.Name == "Name" { name = v.Index(i).Field(j) } } if name.String() == "qcrao" { handsome.SetBool(true) } } } func main() { children := []Child{ {Name: "Ava", Grade: 3, Handsome: true}, {Name: "qcrao", Grade: 6, Handsome: false}, } adults := []Adult{ {ID: "Steve", Occupation: "Clerk", Handsome: true}, {ID: "qcrao", Occupation: "Go Programmer", Handsome: false}, } fmt.Printf("adults before handsome: %v\n", adults) handsome(adults) fmt.Printf("adults after handsome: %v\n", adults) fmt.Println("-------------") fmt.Printf("children before handsome: %v\n", children) handsome(children) fmt.Printf("children after handsome: %v\n", children) }

代码运行结果:

adults before handsome: [{Steve Clerk true} {qcrao Go Programmer false}]
adults after handsome: [{Steve Clerk true} {qcrao Go Programmer true}]
-------------
children before handsome: [{Ava 3 true} {qcrao 6 false}]
children after handsome: [{Ava 3 true} {qcrao 6 true}]

代码主要做的事情是:找出传入的参数为 Slice,并且 Slice 的元素为结构体,如果其中有一个字段名是 Name 或者是 标签名称为 Name,并且还有一个字段名是 Handsome 的情形。如果找到,并且字段名称为 Name 的实际值是 qcrao 的话,就把另一个字段 Handsome 的值置为 true。

程序并不关心传入的结构体到底是什么,只要它的字段名包含 NameHandsome,都是 handsome 函数要工作的对象。

注意一点,Adult 结构体的标签 qson:"Name",中间是没有空格的,否则 Tag.Get("qson") 识别不出来。

未导出成员

利用反射机制,对于结构体中未导出成员,可以读取,但不能修改其值。

注意,正常情况下,代码是不能读取结构体未导出成员的,但通过反射可以越过这层限制。另外,通过反射,结构体中可以被修改的成员只有是导出成员,也就是字段名的首字母是大写的。

一个可取地址的 reflect.Value 变量会记录一个结构体成员是否是未导出成员,如果是的话则拒绝修改操作。
CanAddr 不能说明一个变量是否可以被修改。
CanSet 则可以检查对应的 reflect.Value 是否可取地址并可被修改。

package main

import (
    "reflect"
    "fmt"
)

type Child struct {
    Name     string
    handsome bool
}

func main() {
    qcrao := Child{Name: "qcrao", handsome: true}

    v := reflect.ValueOf(&qcrao)

    f := v.Elem().FieldByName("Name")
    fmt.Println(f.String())

    f.SetString("stefno")
    fmt.Println(f.String())

    f = v.Elem().FieldByName("handsome")
    
    // 这一句会导致 panic,因为 handsome 字段未导出
    //f.SetBool(true)
    fmt.Println(f.Bool())
}

执行结果:

qcrao
stefno
true

上面的例子中,handsome 字段未导出,可以读取,但不能调用相关 set 方法,否则会 panic。反射用起来一定要小心,调用类型不匹配的方法,会导致各种 panic。

反射的实际应用

反射的实际应用非常广:IDE 中的代码自动补全功能、对象序列化(json 函数库)、fmt 相关函数的实现、ORM(全称是:Object Relational Mapping,对象关系映射)……

这里举 2 个例子:json 序列化和 DeepEqual 函数。

json 序列化

开发过 web 服务的同学,一定用过 json 数据格式。json 是一种独立于语言的数据格式。最早用于浏览器和服务器之间的实时无状态的数据交换,并由此发展起来。

Go 语言中,主要提供 2 个函数用于序列化和反序列化:

func Marshal(v interface{}) ([]byte, error)
func Unmarshal(data [
首页 上一页 2 3 4 5 6 7 下一页 尾页 5/7/7
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇simple go web application & 二.. 下一篇20190312_浅谈go&java差异(二)

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目