> 文章列表 > Go语言之反射(反射的简单使用,原理)

Go语言之反射(反射的简单使用,原理)

Go语言之反射(反射的简单使用,原理)

一、反射的基础

1.什么是反射

  • Go语言中,反射的机制就是在运行的时候,可以获取到其变量类型和值,且可以对其类型和值进行检查,对其值进行修改。
  • 即在不知道具体的类型的情况下,可以用反射机制来查看变量类型、更新变量的值。
  • Go中反射主要涉及到两个概念:Type和Value。对所有的接口进行反射时,都可以得到一个包含Type和Value的信息结构,Type是反射的这个变量本身的类型信息,Value是反射的这个变量本身的值信息。

2.反射的使用

(1) 获取到变量类型

  • 方法:reflect.TypeOf(x)
  • 作用:获取变量x的类型信息
    package mainimport ("fmt""reflect"
    )func main(){x := 3.1415y := 3z := "sheena"u := truefmt.Println("x的type为:", reflect.TypeOf(x))fmt.Println("y的type为:", reflect.TypeOf(y))fmt.Println("z的type为:", reflect.TypeOf(z))fmt.Println("u的type为:", reflect.TypeOf(u))
    }
    

    结果:Go语言之反射(反射的简单使用,原理)

(2) 进行变量类型判断

  • 方法:xtype := reflect.TypeOf(x) xkind := xtype.Kind() if xkind == reflect.Type
  • 作用:先调用TypeOf方法获取到x的类型信息,再获取到类型信息中的类型,与类型进行判断
    package mainimport ("fmt""reflect"
    )func main(){x := 3.1415y := 3z := "sheena"u := true//获取变量的类型xtype := reflect.TypeOf(x)ytype := reflect.TypeOf(y)ztype := reflect.TypeOf(z)utype := reflect.TypeOf(u)//Kind()方法的返回结果主要是用来进行类型判断的xkind := xtype.Kind()ykind := ytype.Kind()zkind := ztype.Kind()ukind := utype.Kind()//对x进行类型判断if xkind == reflect.String{fmt.Println("x的type是string")}else if xkind == reflect.Float64{fmt.Println("x的type是float64")}//对y进行类型判断if ykind == reflect.Float64{fmt.Println("y的type是Float64")}else if ykind == reflect.Int{fmt.Println("y的type是int")}//对z进行类型判断if zkind == reflect.Float64{fmt.Println("z的type是Float64")}else if zkind == reflect.String{fmt.Println("z的type是string")}//对u进行类型判断if ukind == reflect.Bool{fmt.Println("u的type是bool")}else if ukind == reflect.String{fmt.Println("u的type是string")}
    }
    

    结果:
    Go语言之反射(反射的简单使用,原理)

(3) 获取到变量值

  • 方法:reflect.ValueOf(x)
  • 作用:获取x变量的值
    package mainimport ("fmt""reflect"
    )func main(){x := 3.1415y := 3z := "sheena"u := truefmt.Println("x的value为:", reflect.ValueOf(x))fmt.Println("y的value为:", reflect.ValueOf(y))fmt.Println("z的value为:", reflect.ValueOf(z))fmt.Println("u的value为:", reflect.ValueOf(u))
    }
    

    结果:
    Go语言之反射(反射的简单使用,原理)

(4) 修改变量值

  • 方法一:使用具体类型的值的设置方法,如SetString()、SetFloat64()等(可直接接收具体要修改的值)
    package mainimport ("fmt""reflect"
    )func main(){x := 3.1415y := 3z := "sheena"u := truefmt.Println("x修改前的value为:", reflect.ValueOf(x))fmt.Println("y修改前的value为:", reflect.ValueOf(y))fmt.Println("z修改前的value为:", reflect.ValueOf(z))fmt.Println("u修改前的value为:", reflect.ValueOf(u))//通过反射传入变量x的地址,并且通过Elerex := reflect.ValueOf(&x).Elem()rey := reflect.ValueOf(&y).Elem()rez := reflect.ValueOf(&z).Elem()reu := reflect.ValueOf(&u).Elem()//判断是否可以修改变量x的值,若可以,则用SetFLoat64()方法进行修改if rex.CanSet(){rex.SetFloat(61.23466)fmt.Println("x修改后的value为:", reflect.ValueOf(x))}else {fmt.Println("该变量不能修改")}if rey.CanSet(){rey.SetInt(10000)fmt.Println("y修改后的value为:", reflect.ValueOf(y))}else {fmt.Println("该变量不能修改")}if rez.CanSet(){rez.SetString("hello world")fmt.Println("z修改后的value为:", reflect.ValueOf(z))}else{fmt.Println("该变量不能修改")}if reu.CanSet(){reu.SetBool(false)fmt.Println("u修改后的value为:", reflect.ValueOf(u))}else {fmt.Println("该变量不能修改")}
    }
    

    结果:
    Go语言之反射(反射的简单使用,原理)

  • 方法二:是用Set方法直接设置值(注意set方法接收的是ValueOf的返回值)
    package mainimport ("fmt""reflect"
    )func main(){x := 3.1415y := 3z := "sheena"u := truefmt.Println("x修改前的value为:", reflect.ValueOf(x))fmt.Println("y修改前的value为:", reflect.ValueOf(y))fmt.Println("z修改前的value为:", reflect.ValueOf(z))fmt.Println("u修改前的value为:", reflect.ValueOf(u))//通过反射传入变量x的地址,并且通过Elerex := reflect.ValueOf(&x).Elem()rey := reflect.ValueOf(&y).Elem()rez := reflect.ValueOf(&z).Elem()reu := reflect.ValueOf(&u).Elem()//判断是否可以修改变量x的值,若可以,则用Set()方法进行修改if rex.CanSet(){ax := reflect.ValueOf(61.23466) // 使用Set方法修改值,Set方法接收的是ValueOf的返回值rex.Set(ax)fmt.Println("x修改后的value为:", reflect.ValueOf(x))}else {fmt.Println("该变量不能修改")}if rey.CanSet(){ay := reflect.ValueOf(10000)// 使用Set方法修改值,Set方法接收的是ValueOf的返回值rey.Set(ay)fmt.Println("y修改后的value为:", reflect.ValueOf(y))}else {fmt.Println("该变量不能修改")}if rez.CanSet(){az := reflect.ValueOf("hello world")// 使用Set方法修改值,Set方法接收的是ValueOf的返回值rez.Set(az)fmt.Println("z修改后的value为:", reflect.ValueOf(z))}else{fmt.Println("该变量不能修改")}if reu.CanSet(){au := reflect.ValueOf(false)// 使用Set方法修改值,Set方法接收的是ValueOf的返回值reu.Set(au)fmt.Println("u修改后的value为:", reflect.ValueOf(u))}else {fmt.Println("该变量不能修改")}
    }
    

    结果:
    Go语言之反射(反射的简单使用,原理)

(5) 获取变量的指针所指向的对象

  • 方法:Elem()方法
  • 作用:获取变量x的指针所指向的对象
    package mainimport ("fmt""reflect"
    )func main(){x := 3.1415y := 3z := "sheena"u := true//传入变量地址px := reflect.ValueOf(&x)py := reflect.ValueOf(&y)pz := reflect.ValueOf(&z)pu := reflect.ValueOf(&u)fmt.Println("x的地址是", px)fmt.Println("y的地址是", py)fmt.Println("z的地址是", pz)fmt.Println("u的地址是", pu)//通过变量地址获取到变量的值xe := px.Elem()ye := py.Elem()ze := pz.Elem()ue := pu.Elem()fmt.Println("x的值是", xe)fmt.Println("y的值是", ye)fmt.Println("z的值是", ze)fmt.Println("u的值是", ue)
    }
    

    结果:
    Go语言之反射(反射的简单使用,原理)

(6) 获取结构体变量的类型和值

package mainimport ("fmt""reflect"
)type Stu struct{Name stringAge intSex stringIsCan bool
}func main(){s1 := Stu{Name: "王一", Age: 18, Sex: "男", IsCan: false}s2 := Stu{Name: "王二", Age: 19, Sex: "女", IsCan: true}s3 := Stu{Name: "张三", Age: 20, Sex: "男", IsCan: false}//反射获取结构体的类型和值fmt.Println("s1的类型", reflect.TypeOf(s1))fmt.Println("s1的值", reflect.ValueOf(s1))fmt.Println("s2的类型", reflect.TypeOf(s2))fmt.Println("s2的值", reflect.ValueOf(s2))fmt.Println("s3的类型", reflect.TypeOf(s3))fmt.Println("s3的值", reflect.ValueOf(s3))fmt.Println("TypeOf()和Kind()方法输出的区别")fmt.Println("TypeOf(s1):", reflect.TypeOf(s1))s1tp := reflect.TypeOf(s1)fmt.Println("Kind(s1):", s1tp.Kind())
}

结果:
Go语言之反射(反射的简单使用,原理)

二、反射的原理

1.反射如何获取类型信息

  • reflect包中提供TypeOf接口用于获取一个变量的类型信息。它接收一个空接口类型的参数,并返回一个reflect.Type类型的返回值
  • TypeOf接口:
    func TypeOf(i interface{}) Type{eface := *(*emptyInterface)(unsafe.Pointer(&i))return toType(eface.type)
    }//Type接口提供了一系列方法
    type Type interface{Align()  int                      //对齐边界FieldAlign()  intMethod(int) MethodMethodByName(string)  (Method, bool)       //方法NumMethod()  int         //类型名称Name()  stringPkgPath()  string         //包路径Slize()  uintptrString()  stringKind()  KindImplements(u Type)  bool         //是否实现指定接口AssginableTo(u Type)  boolConvertibleTo(u Type)  boolComparable()  bool                //是否可比较
    }
    

    Go语言之反射(反射的简单使用,原理)

2.反射如何修改变量值

  • 用反射修改变量值,需要用到reflect.Value类型了,如下所示结构体,有三个元素,type字段存储反射变量的类型元数据指针;ptr字段存储数据地址;flag字段是一个位标识符,存储反射变量的描述信息,例如是否为指针,是否为方法或是否只读等等。
    type Value struct {type *rtypeptr unsafe.Pointerflag
    }func ValueOf(i interface{}) Value{if i == nill{return Value()}escapes(i)return unpackEface(i)
    }
    
  • 通常会有reflect.ValueOf方法来拿到反射类型的值,注意这里的参数i和TypeOf函数一样是个空接口类型,所以处理方式一样。(编译阶段会增加一个临时变量作为拷贝)
    Go语言之反射(反射的简单使用,原理)