• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

go的反射

武飞扬头像
白小白的小白
帮助1

运行时反射是程序在运行期间检查其自身结构的一种方式。反射带来的灵活性是一把双刃剑,反射作为一种元编程方式可以减少重复代码2,但是过量的使用反射会使我们的程序逻辑变得难以理解并且运行缓慢。下面说一下go反射的三大法则:

从 interface{} 变量可以反射出反射对象

反射的第一法则是我们能将 Go 语言的 interface{} 变量转换成反射对象。很多人可能会对这一法则产生困惑。
为什么是从 interface{} 变量到反射对象?当我们执行 reflect.ValueOf(1) 时,虽然看起来是获取了基本类型 int 对应的反射类型,但是由于 reflect.TypeOf、reflect.ValueOf 两个方法的入参都是 interface{} 类型,所以在方法执行的过程中发生了类型转换。
因为Go 语言的函数调用都是值传递的,所以变量会在函数调用时进行类型转换。基本类型 int 会转换成 interface{} 类型,这也就是为什么第一条法则是从接口到反射对象
总而言之,使用 reflect.TypeOf 和 reflect.ValueOf 能够获取 Go 语言中的变量对应的反射对象。一旦获取了反射对象,我们就能得到跟当前类型相关数据和操作,并可以使用这些运行时获取的结构执行方法

从反射对象可以获取 interface{} 变量

反射的第二法则是我们可以从反射对象可以获取 interface{} 变量。既然能够将接口类型的变量转换成反射对象,那么一定需要其他方法将反射对象还原成接口类型的变量,reflect 中的 reflect.Value.Interface 就能完成这项工作

v := reflect.ValueOf(5)
i := v.Interface().(int) // 5
fmt.Println("i is ", i)

要修改反射对象,其值必须可设置

Go 语言反射的最后一条法则是与值是否可以被更改有关,如果我们想要更新一个 reflect.Value,那么它持有的值一定是可以被更新的

i := 1
v := reflect.ValueOf(i)
v.SetInt(10)
fmt.Println(i)

当我们执行这段代码是就会报错,但是仔细思考一下就能够发现出错的原因:由于 Go 语言的函数调用都是传值的,所以我们得到的反射对象跟最开始的变量没有任何关系,那么直接修改反射对象无法改变原始变量,程序为了防止错误就会崩溃
由于想修改值只能传递引用:

i := 1
v := reflect.ValueOf(&i)
v.Elem().SetInt(10)
fmt.Println(i)

下面可以看几个例子多理解反射:

package main

import (
	"fmt"
)
type Reader interface {
	ReadBook()
}

type Writer interface {
	WriteBook()
}

// 具体类型
type Book struct {
	
}

func (this *Book) ReadBook(){
	fmt.Println("Read a Book")
}

func (this *Book) WriteBook(){
	fmt.Println("Write a Book")
}



func main() {
	// b: pair<type:Book, value:book{}地址>
	b := &Book{}

	// r: pair<type:,value:>
	var r Reader
	// r: pair<type:Book, value:book{}地址>
	r = b

	r.ReadBook() //Read a Book

	var w Writer
	// w: pair<type:Book, value:book{}地址>
	w = r.(Writer)
	w.WriteBook() // Write a Book

}
学新通

reflect

package main

import (
	"fmt"
	"reflect"
)

func reflectNum(arg interface{}){
	fmt.Println("type: ",reflect.TypeOf(arg)) // float64
	fmt.Println("value: ",reflect.ValueOf(arg)) // 1.23
}



func main() {
	var num float64 = 1.23
	reflectNum(num)

}
学新通

结构体标签tag:

package main

import (
	"fmt"
	"reflect"
	"encoding/json"
)

type resume struct {
	Name string `info:"name" doc:"我的名字"` 
	Sex string `info:"sex" doc:"我的姓别"`
	Age int `info:"age" doc:"我的年龄"`
}

func reflectNum(str interface{}){
	t := reflect.TypeOf(str).Elem()

	// fmt.Println("t is ", t)
	// fmt.Println("numfield is ", t.NumField())

	for i := 0; i < t.NumField(); i  {
		tagstring := t.Field(i).Tag.Get("info")
		fmt.Println("info: ", tagstring)
		d := t.Field(i).Tag.Get("doc")
		fmt.Println("d: ", d)
	}
}

// 结构体中json的用法
type Movie struct {
	Title string `json:"title"`
	Year int `json:"year"`
	Price float64 `json:"price"`
	Actors []string `json:"actors"`
}



func main() {
	var re resume
	reflectNum(&re)

	fmt.Println("====================")

	movie := Movie{"喜剧之王",2000,10.9,[]string{"周星驰","张柏芝"}}

	fmt.Println("movie title is ",movie.Title)

	// 结构体转为 jsonstr marshal
	b, err := json.Marshal(movie)
	if err != nil {
		fmt.Println("json marshal error:", err)
		return
	}

	fmt.Printf("jsonStr = %s\n", b)
	fmt.Printf("jsonStr type = %T\n", b)

	// jsonstr 转为 结构体
	myMovies := Movie{}
	err = json.Unmarshal(b, &myMovies)
	if err != nil {
		fmt.Println("json unmarshal error", err)
		return
	}

	fmt.Printf(" myMovies is %v\n", myMovies)
	fmt.Printf(" myMovies price is %v\n", myMovies.Price)
	fmt.Printf(" myMovies is %T\n", myMovies)


}
学新通

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhgbibcc
系列文章
更多 icon
同类精品
更多 icon
继续加载