何为反射

Reflection in computing is the ability of a program to examine its own structure, particularly through types; it’s a form of metaprogramming. It’s also a great source of confusion.
(在计算机领域,反射是一种让程序—主要是通过类型—理解其自身结构的一种能力。它是元编程的组成之一,同时它也是一大引人困惑的难题。)

维基百科中的定义:
在计算机科学中,反射是指计算机程序在运行时(Run time)可以访问、检测和修改它本身状态或行为的一种能力。用比喻来说,反射就是程序在运行的时候能够“观察”并且修改自己的行为。

不同语言的反射模型不尽相同,有些语言还不支持反射。《Go语言圣经》中是这样定义反射的:Go语言提供了一种机制在运行时更新变量和检查它们的值、调用它们的方法,但是在编译时并不知道这些变量的具体类型,这称为反射机制。

为什么要用反射?

需要反射的2个常见场景∶
1.有时你需要编写一个函数,但是并不知道传给你的参数类型是什么,可能是没约定好﹔也可能是传入的类型很多,这些类型并不能统一表示。这时反射就会用的上了。
2.有时候需要根据某些条件决定调用哪个函数,比如根据用户的输入来决定。这时就需要对函数和函数的参数进行反射,在运行期间动态地执行函数。

但是对于反射,还是有几点不太建议使用反射的理由︰
1.与反射相关的代码,经常是难以阅读的。在软件工程中,代码可读性也是一个非常重要的指标。
2.Go语言作为一门静态语言,编码过程中,编译器能提前发现一些类型错误,但是对于反射代码是无能为力的。所以包含反射相关的代码,很可能会运行很久,才会出错,这时候经常是直接panic,可能会造成严重的后果。
3.反射对性能影响还是比较大的,比正常代码运行速度慢一到两个数量级。所以,对于一个项目中处于运行效率关键位置的代码,尽量避免使用反射特性。

反射基础

go语言中关于反射的操作定义在reflect包中

反射是在接口的基础上实现的,当向接口变量赋予一个实体类型的时候,接口就会存储实体的类型信息,包含了type和value,反射就是通过接口的类型信息实现的,反射建立在类型的基础上。

在golang的实现中, 每个interface变量都有一个对应pair,pair中记录了实际变量的值和类型:(value,type)

value是实际变量值,type是实际变量的类型,也就是说不管一开始定义的接口是什么类型的接口,对这个接口进行赋值后pair中的value和type都是和赋的值有关的。一个interface{}类型的变量包含了两个指针,一个指针指向值的类型【对应concrete type】,另外一个指针指向实际的值【对应value】

//举个例子:创建一个File类型的变量,并将其赋值给Reader类型的接口
file, err := os.OpenFile("d://123.txt", os.O_CREATE|os.O_RDONLY, 0777)
    if err != nil {
    fmt.Println("err")
}
var a io.Reader
a = file	//此处使用实际类型OS.File给Reader接口赋值
			//pair中存储的为:(file,*os.File)
fmt.Println(a)
file.Close()

反射的使用

reflect的基本功能TypeOf和ValueOf

func TypeOf(i interface{})Type{...}
//获取pair中的Type,接口为空返回nil
func ValueOf(i interface{})value{...}
//获取pair中的value,接口为空返回0

使用reflect:

var i int64 = 999
fmt.Println("Type:", reflect.TypeOf(i))   //Type: int64
fmt.Println("Value:", reflect.ValueOf(i)) //Value: 999

x := reflect.ValueOf(i)
fmt.Printf("%T\n", x)//reflect.Value
fmt.Println("is x int64?:", x.Kind() == reflect.Int64) //is x int64?: true
fmt.Println(x.Type())//int64
fmt.Println(x.Int())//999

从reflect.Value对象转换回interface对象

func (v Value)Interface(){...}
//已知原数据类型,获得interface中的信息
//realValue := value.Interface.(已知的类型)
var a float64 = 6.668
value := reflect.ValueOf(a)//接口类型变为反射类型对象
realvalue := value.Interface().(float64)//反射类型变为接口类型对象
fmt.Println("realvalue:",realvalue)//realvalue: 6.668

/*
	可以理解为强制转换,转换的时候类型要完全符合,否则会直接panic
	如以下例子中,*float64和float64是两种类型,弄错了就会panic
*/
point := reflect.ValueOf(&a)
realpoint := point.Interface().(*float64)
fmt.Println("realpoint:", realpoint)
//数据类型未知,获取interface中的信息
type Person struct {
	name string
	age  int
	sex  string
}
func (p Person) Say(msg string) {
	fmt.Println(p.name, ":", msg)
}
func (p Person) PrintInfo() {
	fmt.Printf("name:%s,age:%d,sex:%s\n", p.name, p.age, p.sex)
}

func getTypeMeg(i interface{}) {
	gettype := reflect.TypeOf(i)
	fmt.Println("gettypename:", gettype.Name())
	fmt.Println("gettypekind", gettype.Kind())

	getvalue := reflect.ValueOf(i)
	fmt.Println("getvalue:", getvalue)

	/*
		获取字段信息:
		1.由reflect.Type对象得到字段的总数NumField()和字段信息Field(index) (包含Name和Type两个属性)
		2.有reflect.Value对象的得到字段的值Field(index)
		3.通过Interface()得到字段对应的value
	*/
	for x := 0; x < gettype.NumField(); x++ {
		field := gettype.Field(x)
		value := getvalue.Field(x)
		fmt.Printf("字段名:%s,字段类型:%s,字段值:%v,%T\n", field.Name, field.Type, value, value)
	}
	//获取方法信息:
	for x := 0; x < gettype.NumMethod(); x++ {
		method := gettype.Method(x)
		fmt.Printf("方法名:%s,方法类型:%v\n", method.Name, method.Type)
	}
}

func main() {
	p := Person{"axuezm", 10, "male"}
	getTypeMeg(p)
}
/*输出:
gettypename: Person
gettypekind struct
getvalue: {axuezm 10 male}
字段名:name,字段类型:string,字段值:axuezm,reflect.Value
字段名:age,字段类型:int,字段值:10,reflect.Value
字段名:sex,字段类型:string,字段值:male,reflect.Value
方法名:PrintInfo,方法类型:func(main.Person)
方法名:Say,方法类型:func(main.Person, string)
*/

通过reflect.Value设置实际变量的值

1.在将接口对象转化为Value对象时,传入的要是接口对象的地址,否则不能修改
2.使用reflect.Value.Elem()获取原始值对应的反射对象
3.使用CanSet()方法判断Value对象是否是可以修改的(true可,false不可)
4.使用Set()方法修改值()
func main() {
	var num int = 999
	pointer := reflect.ValueOf(&num)
    /*注意此处一定要传的是指针,传递值的话会在使用Elem方法处会panic*/
	fmt.Println(num)
	fmt.Println("----------------------------")
	fmt.Println(pointer.CanSet())
	fmt.Println("----------------------------")
	value := pointer.Elem()
	fmt.Println(value.CanSet())
	value.SetInt(1024)
	fmt.Println(value)
	fmt.Println(num)
}
/*程序输出:
999
----------------------------
false
----------------------------
true
1024
1024
*/

通过reflect.Value进行方法/函数调用

func (v Value) Call(in []Value) []Value
func (v Value) Method(i int) Value
func (v Value) MethodByName(name string) Value
type Person struct {
	Name string
	Sex  string
	Age  int
}

func (p Person) PrintInfo() {
	fmt.Printf("name:%s,sex:%s,age:%d\n", p.Name, p.Sex, p.Age)
}
func (p Person) Say(msg string) {
	fmt.Println(p.Name, ":", msg)
}
func fun1() {
	fmt.Println("fun1()...")
}
func fun2(num int) {
	fmt.Println("num:", num)
}

func fun3(a, b int, str string) string {
	fmt.Printf("input:a:%d,b:%d,str:%s\n", a, b, str)
	return str
}

func main() {
	fmt.Printf("\n\n")
	fmt.Println("--------------begin--------------")
	p1 := Person{"axuezm", "male", 18}
	value := reflect.ValueOf(p1)

	//调用方法:先通过Value对象获取其方法对象,通过方法对象调用Call方法来调用方法
	methodValue1 := value.MethodByName("PrintInfo")
	fmt.Println("kind:", methodValue1.Kind(), ",type:", methodValue1.Type())
	methodValue1.Call(nil)                      //没有参数可以直接传nil
	methodValue1.Call(make([]reflect.Value, 0)) //也可以创建一个空的切片传入

	methodValue2 := value.MethodByName("Say")
	fmt.Println("kind:", methodValue2.Kind(), ",type:", methodValue2.Type())
	sl := []reflect.Value{reflect.ValueOf("hello,nice to meet you")}
	/*有参时创建一个reflect.Value类型的切片存放所有的参数,
	注意参数要使用ValueOf转换成Value类型*/
	methodValue2.Call(sl)
	fmt.Println("------------------------------------")
	//调用函数:函数也可以当做是变量,像使用变量一样将其转化成Value对象,并通过Call调用
	funcValue1 := reflect.ValueOf(fun1)
	fmt.Printf("kind:%s,type:%s\n", funcValue1.Kind(), funcValue1.Type())
	funcValue1.Call(nil)
	funcValue2 := reflect.ValueOf(fun2)
	fmt.Printf("kind:%s,type:%s\n", funcValue2.Kind(), funcValue2.Type())
	args1 := []reflect.Value{reflect.ValueOf(100)}
	funcValue2.Call(args1)
	funcValue3 := reflect.ValueOf(fun3)
	fmt.Printf("kind:%s,type:%s\n", funcValue3.Kind(), funcValue3.Type())
	args2 := []reflect.Value{reflect.ValueOf(100), reflect.ValueOf(500), reflect.ValueOf("hello world")}
	returns := funcValue3.Call(args2)
	fmt.Printf("%T,%d\n", returns, len(returns))
	s1 := returns[0].Interface().(string)
	fmt.Println(s1)
}
/*输出结果:

--------------begin--------------
kind: func ,type: func()
name:axuezm,sex:male,age:18
name:axuezm,sex:male,age:18
kind: func ,type: func(string)
axuezm : hello,nice to meet you
------------------------------------
kind:func,type:func()
fun1()...
kind:func,type:func(int)
num: 100
kind:func,type:func(int, int, string) string
input:a:100,b:500,str:hello world
[]reflect.Value,1
hello world
*/