什么是反射
反射可以再运行时动态的获取变量的信息,比如变量的类型、值等
如果是结构体,还可以获取到结构体本身的各种信息,比如结构体的字段和方法
通过反射,还可以修改变量的值、调用方法
使用反射,需要引入一个包: reflect,它定义了两个重要的类型Type和Value任意接口值在反射中都可以理解为由 reflect.Type和reflect.Value两部分组成,并且reflect包提供了reflect.TypeOf 和reflect.ValueOf两个函数来获取任意对象的Value和Type。
在使用反射时,需要首先理解类型(Type) 和种类(Kind)的区别。编程中,使用最多的是类型,但在反射中,当需要区分一个品种的类型时,就会用到种类(Kind) .
静态类型&动态类型
在反射的概念中,编译时就知道变量的是静态类型,运行时才知道一个变量类型叫做动态类型
静态类型:就是变量声明时赋予的类型
var name string //string就是静态类型
var age int //int就是静态类型
动态类型:在运行是可能改变,这主要依赖于它的赋值
var A interface // 静态类型为 interface{}
A=10//静态类型为interface{} 动态类型为int
A="string"//静态类型为interface{} 动态类型string
为什么要用反射
需要反射的2个常见场景:
1.有时你需要编写一个函数,但是并不知道传给你的数类型是什么,可能是没约定好;也可能是传入的类型很多,这些类型并不能统一表示。这时反射就会用的上了。
⒉.有时候需要根据某些条件决定调用哪个函数,比如根据用户的输入来决定。这时就需要对函数和函数的参数进行反射,在运行期间动态地执行函数。
但是对于反射,还是有几点不太建议使用反射的理由:
1、与反射相关的代码,经常是难以阅读的。在软件工程中,代码可读性也是一个非常重要的指标。
2、Go语言作为一门静态语言,编码过程中,编译器能提前发现一些类型错误,但是对于反射代码是无能为力的。所以包含反射相关的代码,很可能会运行很久,才会出错,这时候经常是直接panic,可能会造成严重的后果。
3、反射对性能影响还是比较大的,比正常代码运行速度慢一到两个数量级。所以,对于一个项目中处于运行效率关键位置的代码,尽量避免使用反射特性。
反射获取变量的信息
/*
获取字段
1、先获取type对象,reflect.type
Numfild() 获取有几个字段
filed(index) 得到字段的值
2、通过Filed获取没一个Filed字段
3、interface(),得到对象的value
*/
package main
import (
"fmt"
"reflect"
)
type User struct {
Name string
Age int
Sex string
}
func (use User) Say(msg string) {
fmt.Println("User说:", msg)
}
func (user User) PrintInfo() {
fmt.Printf("姓名:%s,年龄:%d,性别:%s\n", user.Name)
}
func main() {
user := User{"kuangshen", 18, "男"}
reflectinfo(user)
}
func reflectinfo(inter interface{}) {
//获取参数类型
gettype := reflect.TypeOf(inter)
fmt.Println("get type is:", gettype.Name())
fmt.Println("get kind is:", gettype.Kind())
// 获取值
getvalue := reflect.ValueOf(inter)
fmt.Println("get value is:", getvalue)
/*
获取字段
1、先获取type对象,reflect.type
Numfild() 获取有几个字段
filed(index) 得到字段的值
2、通过Filed获取没一个Filed字段
3、interface(),得到对象的value
*/
for i := 0; i < gettype.NumField(); i++ {
filed := gettype.Field(i)
value := getvalue.Field(i).Interface()
fmt.Printf("字段名:%s,字段类型:%s,字段值%v\n", filed.Name, filed.Type, value)
}
for i := 0; i < gettype.NumMethod(); i++ {
method := gettype.Method(i)
fmt.Printf("方法名:%s,方法类型%v\n", method.Name, method.Type)
}
}
反射设置变量的值
package main
import (
"fmt"
"reflect"
)
type User2 struct {
Name string
Age int
Sex string
}
func main() {
use2 := User2{"kuangshen", 18, "男"}
fmt.Println(use2)
value := reflect.ValueOf(&use2)
if value.Kind() == reflect.Ptr { //判断种类是否是指针
newvalue := value.Elem()
if newvalue.CanSet() { //判断是否能够被修改
newvalue.FieldByName("Name").SetString("秦疆")
newvalue.FieldByName("Age").SetInt(26)
}
}
fmt.Println(use2)
}
反射调用的方法
package main
import (
"fmt"
"reflect"
)
type User3 struct {
Name string
Age int
Sex string
}
func (user3 User3) Say3(msg string) {
fmt.Println("User说:", msg)
}
func (user3 User3) PrintInfo3() {
fmt.Printf("姓名:%s,年龄:%d,性别:%s\n", user3.Name, user3.Age, user3.Sex)
}
func main() {
user3 := User3{"kuanshsen", 18, "男"}
fmt.Println(user3)
// 无参方法调用,可以传nil
value := reflect.ValueOf(user3)
value.MethodByName("printInfo").Call(nil)
//有参方法调用,要先准备参数[]reflect.Value 切片
args := make([]reflect.Value, 1)
args[0] = reflect.ValueOf("反射来调用实现的")
value.MethodByName("Say").Call(args)
}
标签:反射,string,fmt,value,reflect,类型,Go,狂神
From: https://www.cnblogs.com/DuPengBG/p/17023358.html