重学Golang-反射

        反射是指一类应用,可以通过采用某种机制来实现对自己行为的描述和检测,并能根据自身行为的状态和结果,调整或修改应用所描述行为的状态和相关的语义。

接口与反射

        变量包括类型和值两部分。类型包括静态类型和具体类型,静态类型是指在编码时就确定的类型,例如int string等,具体类型是指在运行时才能知道的类型。反射就是建立在类型之上,反射主要与Go语言的接口类型相关,只有接口类型才有反射。一个interface{}类型的变量包含了两个指针,一个指向值的类型(具体类型),一个指向实际的值(对应的value)。

反射原理

        Go语言中reflect包提供了两种类型可以访问接口变量的内容,reflect.TypeOf()和reflect.ValueOf()。

示例:

package main

import (
	"fmt"
	"reflect"
)

func main() {
	var money float32 = 88.88
	fmt.Println("type is:", reflect.TypeOf(money))
	fmt.Println("value is:", reflect.ValueOf(money))
}
输出:
type is: float32
value is: 88.88

       未知原有类型

        执行reflect.ValueOf()后,得到一个类型为reflect.Value的变量,可以通过这个变量自身的Interface()方法获得接口变量的真实内容。然后可以通过类型判断进行转换,转换为原有真实变量。但是很多情况下,可能不知道要转换的具体类型,这时候可以通过遍历探测Filed来获取类型,示例:


package main

import (
        "fmt"
        "reflect"
)

type Student struct {
        Name  string
        Age   int
        Score float32
}

func (stu Student) ReflectCall() {
        fmt.Println(stu)
}

func GetReflectType(in interface{}) {
        getType := reflect.TypeOf(in)
        fmt.Println("type:", getType.Name())
        getValue := reflect.ValueOf(in)
        fmt.Println("All filed:", getValue)
        for i := 0; i < getType.NumField(); i++ {
                filed := getType.Field(i)
                value := getValue.Field(i).Interface()
                fmt.Println("filed name:", filed.Name, " filed type:", filed.Type, " filed value:", value)
        }
        for i := 0; i < getType.NumMethod(); i++ {
                method := getType.Method(i)
                fmt.Println("method name:", method.Name, "method type:", method.Type)
        }
}

func main() {
        stu := Student{
                Name:  "bai",
                Age:   18,
                Score: 99.99,
        }
        GetReflectType(stu)

}
输出:
type: Student
All filed: {bai 18 99.99}
filed name: Name  filed type: string  filed value: bai
filed name: Age  filed type: int  filed value: 18
filed name: Score  filed type: float32  filed value: 99.99
method name: ReflectCall method type: func(main.Student)

        在函数GetReflectType中先获取位置类型interface的reflect.Type,然后对NumFiled进行遍历。 再通过reflect.Type的Field获取其Filed。最后通过Filed的Interface()得到对应的value。获取所属方法的步骤类似。

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

        reflect.Value是通过reflect.ValueOf(interface)获得的,只有当参数interface是指针的时候,才可以通过reflect.Value修改实际变量interface的值,即如果要修改反射类型的对象就一定要保证其值是“可寻址的”。示例:

package main

import (
	"fmt"
	"reflect"
)

func main() {
	var money float32 = 33.33
	pointer := reflect.ValueOf(&money)
	newValue := pointer.Elem()
	fmt.Println("new Value type:", newValue.Type())
	fmt.Println("new Value can Set:", newValue.CanSet())
	if newValue.CanSet() {
		newValue.SetFloat(99.99)
	}
	fmt.Println("new value:", money)
}
输出:
new Value type: float32
new Value can Set: true
new value: 99.99

        通过reflect.ValueOf()函数来进行方法的调用

        在工程应用中reflect有一个高级用法,就是通过reflect进行方法的调用。比如在做框架时,需要可以随意扩展方法,或者说用户可以自定义方法,那么通过什么手段来扩展让用户能够自定义,关键点在于用户的自定义方法是未知的,因此可以通过reflect包来实现,示例:

package main

import (
	"fmt"
	"reflect"
)

type Programmer struct {
	Id    int
	Name  string
	Level int
}

func (p Programmer) CallFuncHasArgs(name string, age int) {
	fmt.Println("CallFuncHasArgs name: ", name, ",age:", age, ",original Programmer Name:", p.Name)
}

func (p Programmer) CallFuncNoArgs() {
	fmt.Println("CallFuncNoArgs,original Programmer Name:", p.Name)
}

func main() {
	pro := Programmer{
		Id:    1,
		Name:  "Tom",
		Level: 5,
	}
	getValue := reflect.ValueOf(pro)
	method := getValue.MethodByName("CallFuncHasArgs")
	args := []reflect.Value{reflect.ValueOf("gogogo"), reflect.ValueOf(20)}
	method.Call(args)

	method = getValue.MethodByName("CallFuncNoArgs")
	args = make([]reflect.Value, 0)
	method.Call(args)
}
输出:
CallFuncHasArgs name:  gogogo ,age: 20 ,original Programmer Name: Tom
CallFuncNoArgs,original Programmer Name: Tom

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值