Go_反射的使用
创始人
2025-06-01 11:52:21
0

反射可以在运行时动态地检查变量和类型,并且可以调用变量和类型的方法、获取和修改变量的值和类型等。使用反射可以使程序更加灵活,但也需要谨慎使用

  1. 基于反射的代码是极其脆弱的,反射中的类型错误会在真正运行的时候才会引发panic,那很可能是在代码写完的很长时间之后。
  2. 大量使用反射的代码通常难以理解。
  3. 反射的性能低下,基于反射实现的代码通常比正常代码运行速度慢一到两个数量级。

refect:

Go语言的反射主要涉及两个重要的类型:reflect.Typereflect.Value。其中,reflect.Type表示类型信息,包括类型的名称、方法和字段等;reflect.Value表示值信息,包括值的类型和内容等。通过这两个类型,我们可以对变量和类型进行各种反射操作。通过refect包下的reflect.TypeOf()reflect.ValueOf()两个函数来获取对象的ValueType

TypeOf():

reflect.TypeOf():用于获取变量的数据类型

func main() {// 获取x的数据类型,并赋值给tvar x float64 = 3.14fmt.Println(reflect.TypeOf(x)) // float64fmt.Printf("%T", x)     // float64    Printf底层就是使用了反射,所以不管传入什么类型都能获取到
}

在反射中关于类型还划分为两种:

类型(Type):类型指的是变量的数据类型

种类(Kind):种类是语言原生数据类型,用于区分指针、结构体、数组、切片等类型。

type Myint int
type MyStruct struct {Name stringage  int
}var x Myint = 123
var y *Myint = &x
var z MyStruct = MyStruct{"itzhuzhu", 20}
var w *MyStruct = &zfunc main() {fmt.Println(reflect.TypeOf(x)) // main.Myintfmt.Println(reflect.TypeOf(y)) // *main.Myintfmt.Println(reflect.TypeOf(z)) // main.MyStructfmt.Println(reflect.TypeOf(w)) // *main.MyStruct// Kind:fmt.Println(reflect.TypeOf(x).Kind()) // intfmt.Println(reflect.TypeOf(y).Kind()) // ptrfmt.Println(reflect.TypeOf(z).Kind()) // structfmt.Println(reflect.TypeOf(w).Kind()) // ptr
}

Kind源码:

// Kind表示类型所表示的特定类型
type Kind uint
const (Invalid Kind = iota  // 非法类型Bool                 // 布尔型Int                  // 有符号整型Int8                 // 有符号8位整型Int16                // 有符号16位整型Int32                // 有符号32位整型Int64                // 有符号64位整型Uint                 // 无符号整型Uint8                // 无符号8位整型Uint16               // 无符号16位整型Uint32               // 无符号32位整型Uint64               // 无符号64位整型Uintptr              // 指针Float32              // 单精度浮点数Float64              // 双精度浮点数Complex64            // 64位复数类型Complex128           // 128位复数类型Array                // 数组Chan                 // 通道Func                 // 函数Interface            // 接口Map                  // 映射Ptr                  // 指针Slice                // 切片String               // 字符串Struct               // 结构体UnsafePointer        // 底层指针
)

ValueOf():

reflect.ValueOf():用于获取变量的值

func main() {var x float64 = 3.14fmt.Println(reflect.ValueOf(x)) // 3.14
}

通过反射获取变量的值

func reflectValue(x interface{}) {v := reflect.ValueOf(x) // 获取接口x的值k := v.Kind()           // 获取接口x的种类switch k {case reflect.Int64:// v.Int()从反射中获取整型的原始值,然后通过int64()强制类型转换fmt.Printf("数据类型为Int64 值是:%d\n", int64(v.Int()))case reflect.Float32:// v.Float()从反射中获取浮点型的原始值,然后通过float32()强制类型转换fmt.Printf("数据类型为Float32 值是:%f\n", float32(v.Float()))case reflect.Float64:// v.Float()从反射中获取浮点型的原始值,然后通过float64()强制类型转换fmt.Printf("数据类型为Float64 值是:%f\n", float64(v.Float()))}
}
func main() {var a float32 = 3.14var b int64 = 100reflectValue(a) // 数据类型为Float32 值是:3.140000reflectValue(b) // 数据类型为Int64 值是:100/*在这里,reflect.ValueOf(10)的作用是将整数10转换为一个reflect.Value类型的值,该值包含整数10的类型和值等信息。具体来说,它返回一个reflect.Value类型的值c,该值表示整数10,并且可以使用反射获取它的类型和值等信息。通过使用reflect.ValueOf(),我们可以将一个普通的值转换为反射类型的值,从而使用反射对它进行进一步处理。*/c := reflect.ValueOf(10)fmt.Printf("c的数据类型是:%T\n", c) // c的数据类型是:reflect.Value
}

反射修改变量的值:

要修改变量的值,需要使用reflect.Value类型的Elem()方法获取到变量的指针,然后再使用reflect.Value类型的Set()方法修改值。

func main() {var x float64 = 3.14v := reflect.ValueOf(&x).Elem()// 也可以修改为其它数据类型,使用v.SetFloat(3.15)fmt.Println(x) // 3.15
}

isNil、isValid:

isNil():返回v的值是否为nil,v的值的类型必须是通道、函数、接口、映射、指针、切片之一,否则IsNil函数会导致panic。

func (v Value) IsNil() bool

IsValid():返回v是否有值,如果v是零值返回false,此时v除了IsValid、String、Kind之外的方法都会导致panic。

func (v Value) IsValid() bool

演示:

func main() {// *int类型空指针var a *intfmt.Println("a是否为空:", reflect.ValueOf(a).IsNil())// nil值fmt.Println("a是否有值:", reflect.ValueOf(a).IsValid())fmt.Println("测试传入nil值:", reflect.ValueOf(nil).IsValid())// 实例化一个匿名结构体b := struct{}{}// 判断结构体中是否有abc字段fmt.Println("struct是否存在abc字段:", reflect.ValueOf(b).FieldByName("abc").IsValid())// 判断结构体中是否有abc方法fmt.Println("struct是否存在abc方法:", reflect.ValueOf(b).MethodByName("abc").IsValid())// mapc := map[string]int{}// 判断map中的key是否有abcfmt.Println("map中是否有abc这个key:", reflect.ValueOf(c).MapIndex(reflect.ValueOf("abc")).IsValid())
}

输出:

a是否为空: true
a是否有值: true
测试传入nil值: false
struct是否存在abc字段: false
struct是否存在abc方法: false
map中是否有abc这个key: false

反射获取结构体信息:

方法说明
Field(i int) StructField根据索引,返回索引对应的结构体字段的信息
NumField() int返回结构体成员字段数量
NumMethod() int返回结构体方法的数量
Method(int) Method返回该类型的第i个方法
FieldByName(name string) (StructField, bool)根据给定字符串返回字符串对应的结构体字段的信息。
FieldByIndex(index []int) StructField多层成员访问时,根据 []int 提供的每个结构体的字段索引,返回字段的信息。
FieldByNameFunc(match func(string) bool) (StructField,bool)根据传入的匹配函数匹配需要的字段。
MethodByName(string)(Method, bool)根据方法名返回该类型方法集中的方法

StructField类型用来描述结构体中的一个字段的信息。

type StructField struct {Name      string    // Name是字段的名字。PkgPath   string    // PkgPath是非导出字段的包路径,对导出字段该字段为""Type      Type      // 字段的类型Tag       StructTag // 字段的标签Offset    uintptr   // 字段在结构体中的字节偏移量Index     []int     // 用于Type.FieldByIndex时的索引切片Anonymous bool      // 是否匿名字段
}

演示:

type student struct {Name string `json:"name"`Age  int    `json:"age"`
}func (s student) StudentMethod() {fmt.Println("我是student的方法")
}func main() {stu := student{Name: "韩信",Age:  18,}t := reflect.TypeOf(stu)fmt.Println("根据索引返回字段信息:", t.Field(0))fmt.Println("结构体的名称:", t.Name())fmt.Println("结构体的类型:", t.Kind())fmt.Println("结构体的成员数量:", t.NumField())fmt.Println("结构体的方法数量:", t.NumMethod())fmt.Println("返回第i个方法:", t.Method(0))name, b := t.FieldByName("Name")fmt.Println("字段信息为:", name, "字段是否存在:", b)// 也可以使用if去判断,如果ok为true则执行if nameFiled, ok := t.FieldByName("Name"); ok {fmt.Println("字段信息为:", nameFiled, "字段是否存在:", ok)}// 通过for循环遍历结构体的所有字段信息for i := 0; i < t.NumField(); i++ {field := t.Field(i)fmt.Println(field.Name, field.PkgPath, field.Type, field.Tag.Get("json"), field.Index, field.Offset, field.Anonymous)}// 通过字段名获取指定结构体字段信息if field, ok := t.FieldByName("Name"); ok {fmt.Println(field.Name, field.PkgPath, field.Type, field.Tag.Get("json"), field.Index, field.Offset, field.Anonymous)}
}

输出:

根据索引返回字段信息: {Name  string json:"name" 0 [0] false}
结构体的名称: student
结构体的类型: struct
结构体的成员数量: 2
结构体的方法数量: 1
返回第i个方法: {StudentMethod  func(main.student)  0}
字段信息为: {Name  string json:"name" 0 [0] false} 字段是否存在: true
字段信息为: {Name  string json:"name" 0 [0] false} 字段是否存在: true
Name  string name [0] 0 false
Age  int age [1] 16 false
Name  string name [0] 0 false

演示:

type student struct {Name    stringAge     intAddress Address
}type Address struct {City    stringCountry string
}func (p *student) SayHello() {fmt.Printf("你好,我是野王 %s\n", p.Name)
}func main() {p := student{Name: "韩信",Age:  30,Address: Address{City:    "野区",Country: "王者荣耀",},}// 使用 FieldByIndex 获取结构体中嵌套的字段值,返回第3个字段的第0个信息cityField := reflect.ValueOf(p).FieldByIndex([]int{2, 0})fmt.Println(cityField.Interface()) // 野区// 使用 FieldByNameFunc 根据字段名获取字段值ageFieldVal := reflect.ValueOf(p).FieldByNameFunc(func(name string) bool {return name == "Age"})if ageFieldVal.IsValid() {ageField := ageFieldVal.Interface().(int)fmt.Println(ageField) // 30}// 使用 MethodByName 调用结构体中的方法method := reflect.ValueOf(&p).MethodByName("SayHello")method.Call(nil) // 你好,我是野王 韩信
}

相关内容

热门资讯

《题破山寺后禅院》阅读答案及... 《题破山寺后禅院》阅读答案及翻译赏析  题破山寺后禅院  常建  清晨入古寺,初日照高林。  竹径通...
但称“甘如饴” 勿谓是《诗经... 但称“甘如饴” 勿谓是《诗经》  “谁谓荼苦,其甘如饴”。这句话是不是诗经里的名句呢,下面一起来看看...
大同文言文翻译赏析 大同文言文翻译赏析  《大同》文中"大道"就是指治理社会的最高准则,其治理的效果就是国泰民安的理想状...
红楼梦中王熙凤的诗词 红楼梦中王熙凤的诗词  红楼梦中王熙凤的诗词  凡鸟偏从末世来,都知爱慕此生才。  一从二令三人木,...
《望岳》全文赏析 《望岳》全文赏析  望岳,是唐代著名诗人杜甫的名篇,该诗通过描绘泰山雄伟磅礴的景象,热情赞美了泰山高...
《弟子规》全文及释义 《弟子规》全文及释义  《弟子规》弘扬了中华民族传统美德,在阅读同时提升道德修养。下面是小编为大家整...
终不知车文言文道理 终不知车文言文道理  导语:为了向别人炫耀,把破车运回家乡当做好车炫耀,害的乡人模仿破车;面对质疑,...
与太史公书文言文 与太史公书文言文  太史公:  伯夷、叔齐,善人者,积仁洁行,而饿死于首阳山;颜渊好学,糟糠不厌,而...
师说中韩愈择师的标准 师说中韩愈择师的标准  《师说》是唐代文学家韩愈创作的一篇议论文。文章阐说从师求学的道理,讽刺耻于相...
纳兰性德《浣溪沙·败叶填溪水... 纳兰性德《浣溪沙·败叶填溪水已冰》鉴赏及译文  《浣溪沙·败叶填溪水已冰》  清代:纳兰性德  败叶...
黄帝内经十二经络养生法 黄帝内经十二经络养生法  现在大成之道国学研究院张成院长,张成黄帝内经十二经络养生法导师。就帮助各位...
《题弟侄书堂》原文及译文 《题弟侄书堂》原文及译文  在日常学习、工作或生活中,大家都看到过许多经典的古诗吧,古诗准确地来说应...
桃夭诗经赏析 桃夭诗经赏析  即便只读过很少几篇《诗经》的人,一般也都知道“桃之夭夭,灼灼其华”,那么大家会怎么赏...
曼斯菲尔德《苍蝇》阅读练习及... 曼斯菲尔德《苍蝇》阅读练习及答案  苍蝇  【新西兰】曼斯菲尔德  “你这儿可真舒服。”伍迪菲尔德坐...
李白诗将进酒书法作品 李白诗将进酒书法作品  在《将进酒》这首诗中,小编感受到的是李白一方面对自己充满自信,孤高自傲;另一...
庄子秋水原文以及译文 庄子秋水原文以及译文  秋水选自《庄子·外篇》,《秋水》篇。庄子,姓庄,名周,字子休(亦说子沐),宋...
《上冢宰许公书》原文和译文解... 《上冢宰许公书》原文和译文解析  上冢宰许公①书  何景明  中书舍人②何某顿首,上书冢宰许公下执事...
《小石潭记》文言文精细阅读 高中文言文《游褒禅山记》优秀教案推荐度:《昆虫记》的阅读题附答案推荐度:丑石教案设计推荐度:黄山奇石...
登高丘而望远唐诗翻译及赏析 登高丘而望远唐诗翻译及赏析  登高丘而望远海,六鳌骨已霜,三山流安在?  扶桑半摧折,白日沉光彩。 ...
古籍《海经·海外西经》原文 古籍《海经·海外西经》原文  热爱文学的人,不妨来读古籍吧,下面小编为大家带来了古籍《海经·海外西经...