开发者学堂课程【Go语言核心编程 - 面向对象、文件、单元测试、反射、TCP编程:反射的最佳实践(2)】学习笔记,与课程紧密联系,让用户快速学习知识。
课程地址:https://developer.aliyun.com/learning/course/626/detail/9774
反射的最佳实践(2)
内容介绍
一、使用反射来获取 tag 标签
二、定义的适配器
三、使用反射操作任意的结构体类型
四、使用反射创建并操作结构体
一、使用反射来获取 tag 标签
使用反射的方式来获取结构体的 tag 标签,遍历字段的值,修改字段值,调用结构体方法。唯一的变化就是在 TestStruct 这里传了一个&a。然后再加 a 是原来的“黄狮子”就变成了“白象精”,又在原来的基础上加了一个Elem,就是希望在反射里面把它的字段改变过来。代码如下:
Package main
import(
“
encoding/json
“fmt”
“reflect”
)
type Monster struct{
Name string‘json:“Monster”'
Age int
Score float32
Sex string
}
func(s monster) Print() {
fmt. PrintIn(〝---start---〞)
fmt.PrintIn(s)
fmt.PrintIn(---end---)
func TestStruct(a interface {}) {
typ:=reflect.Typeof(a)
val:=reflect.Valueof(a)
Kd:=val.Kind()
If kd!=reflect.struct {
fmt PrintIn(〞
e
xpect
s
truct〝)
return
}
num:val.Elem().NumField()
val.Elem().Field(0).SetString(“白象精”)
For i:=0;i
fmt Printf(〝%d%v\n,i,val.Elem().Field(i).Kind())
}
fmt Printf(“struct has %d fields\n”,num)
tag:tye.Elem().Field(
0
)
.Tag.Get(“json”)
fmt Printf(“
tag=
%
s
\n
,tag
)
numOfMethod:=val.Elem().NumMethod()
fmt Printf(“struct has %d methods\n”,numOfMethod)
val.Elem().Method(0).Call(nil)
}
Func main() }
var a Monster=Monster{
Name:“黄狮子”
Age:408,
Score:92.8,
//先说明一下,Marshal 就是通过反射获取到 struct 的 tag 值
result,_:json.Marshal(a)
fmt Println(“json result:”,string(result))
TestStruct(&a)
fmt Println(a)
二、定义的适配器
第二个应用也是一个非常经典的反射最佳实践,是适配器的写法。适配器的写法的代码在这里。这是一个测试用例,就要像测试用例那样去命名。注意文件命名时必须是 XXX test 这样的形式。前面的部分随便改,后面必须以 test 结尾。
1、定义了两个函数 test1和 test2
test1:=func(v1 int,v2 int){
t.Log(v1,v2)
}
test2=func(v1 lnt,v2 lnt,s string){
t.Log(v1,v2,s)
2、定义一个适配器函数用作统一处理接口,其大致接口如下:
bridge:=func(call interface{},args…interface{})
//内容
}
//实现调用 test1对应的函数
bridge(test1,1,2)
//实现调用 test2对应的函数
bridge(test2,1,2,“test2”)
3、要求使用反射机制来完成
4、代码如下:
Package test
import(
“testing”
“reflect”
)
//文件命名xxx_test.go
func TestReflectFunc(t*testing.T){
call1:=func(v1 lnt,v2 lnt){
t.Log(v1,v2)
}
call2:=func(v1 lnt,v2 lnt,s string){
t.Log(v1,v2,s)
}
var(
function reflect.Value
inValue [] reflect.Value
n int
)
bridge:=func(call interface{},args…interface{})
n=len(args)
inValue=make([]reflect.Value,n)
For i=0;i<n;i++{
inValue[i]=reflect.ValueOf(args[i])
}
function=reflect.ValueOf(call)
Function.Call(inValue)
)
bridge(call1,1,2)
bridge(call2,1,2,“test2”)
三、使用反射操作任意的结构体类型
这里也写着测试用例,它可以去操作任何的结构体。先创建了一个 var,里面是一个 model,它是一个指针,是指向 user 的一个指针类型。然后 sv 是 reflect.Value 的一个类型。用 model 去获写 sv,这个地方是相当于一个指针类型的,然后写一个日志把它的类型打出来了。sv.Elem 就相当于取到了 reflect.Value。然后通过 FieldByName 找到了 UserId 并且设值。这个地方其实就是在提醒怎样去修改字段了。sv 原来是指针,取到它对应的真实的 reflect.Value,再调用 FieldByName 并把值设置进去。这就相当于可以操作任意的一种结构体。这个结构体是什么都可以帮助操作,在底层帮助操作。代码如下:
type user struct{
Userld string
Name string
}
Func TestReflectStruct(t*testing.T)
var(
model *user
sv reflect.Value
)
model=&user{}
sv=reflect.ValueOf(model)
t.LOG(“reflect.ValueOf”,sv.Kind().String())
t.LOG(“reflect.ValueOf.Elem”,sv.Kind().String())
sv.FieldByName(“Userld”).SetString(“12345678”)
sv.FieldByName(“Name”).SetString(“nickname”)
t.Log(“model”,model)
四、使用反射创建并操作结构体
就连结构体的创建都可以用反射来完成。
1、代码如下:
package test
import{
“
testing
”
“
reflect
”
)
type user struct{
Userld string
Name string
}
func TestReflectStructPtr(*Testing.T)
var
model *user
st reflect.Type
elem reflect.Value
)
st=fellect.Typeof(model)//获取类型*user
t.Log(〞reflect .Typeof,stKind().String())//.ptr
st=st.Elem()//st 指向的类型
t.Log(〞reflect .Typeof.Elem,stKind().String())//.struct
Elem=reflect.New(st)//New 返回一个 value 类型值,该值持有一个指向类型为 type 的新申请的零值的指针
t.Log(“reflect.New”,elem.Kind{}.String())//ptr
t.Log(“reflect.New.Elem”,elem.Elem().Kind().String{})//struct
//model 就是创建的 user 结构体变量(实例)
model=elem.interface().(*user)
//model 是 *user 它的指向和 elem 是一样的
elem=elem.Elem()//取得 elem 指向的值
elem.FieldByName(“Userld”).SetString(“12345678”)//赋值…
elem.FieldByName(“Name”).SetString(“nickname”)
T. Log=(“model model.Name”,model,model.Name)
首先有一个 var,里面有 user 的指针,有一个 type 类型,有一个 value 类型。然后注释写得非常详细,先获取 user,这个 st 就应该是一个指针。St.Kind 打印出来就是 ptr,st.Elem 让 st 指向真正的类型。reflect(New) 就类似于在 java 里面的那种在底层的创建对象。把这个指针传进去最后返回 elem。这个 elem 还不是一个实例,它是 new 了一个 value 类型的值,该值持有一个指向类型为 typ 的新申请的零值的指针。它里面还没有数据,但已经指向了一个 user 的地址。这个时候 model 和 elem 其实都指向了一个 user 的地址空间。这就意味着如果改变了其中之一值都会发生变化。通过 elem 取到了它真正指向的值。这个指针指向了一个真正的空间,这个空间就是 user 的一个空间。model 还是通过地址指向数据空间的。当把值设完以后,model 里面的值也会有了。首先 elem 是一个指针,它相当于先指向了一个地址,这个地址在指向了一个真正的数据空间。这个空间就是 user 的一个空间。紧接着做了一个 model 也指向了这个地址空间,elem.Elem 就当是 elem 换了一个地址。因为只有这样做才能往里面赋值。