GoLang 反射

Golang Series

Hello GoLang: https://blog.yexca.net/archives/154
GoLang (var and const) 变量与常量: https://blog.yexca.net/archives/155
GoLang (func) 函数: https://blog.yexca.net/archives/156
GoLang (slice and map) 切片: https://blog.yexca.net/archives/160
GoLang (OOP) 面向对象: https://blog.yexca.net/archives/162
GoLang (reflect) 反射: This Page
GoLang (struct tag) 结构体标签: https://blog.yexca.net/archives/205
GoLang (goroutine) go 程: https://blog.yexca.net/archives/206
GoLang (channel) 通道: https://blog.yexca.net/archives/207


反射指一类应用,它们能够自描述和自控制

pair

Go 语言变量包括 type 和 value 部分,组成 pair

image

static type 是在编码时程序员看见的类型,concrete type 是在 runtime 系统看见的类型

类型断言能否成功,取决于变量的 concrete type,而不是 static type。因此一个 read 变量如果 concrete type 也实现了 write 方法的话,也可以被类型断言为 write

反射建立于类型之上,静态类型以及固定,因此反射主要与 interface 类型相关 (它是 concrete type)

package main

import "fmt"

type ReadBook interface {
	Read()
}
type WriteBook interface {
	Write()
}

type Book struct {
}

func (this *Book) Read() {
	fmt.Println("read Book")
}
func (this *Book) Write() {
	fmt.Println("write Book")
}

func main() {
	// b: pair<type:Book, value:book{}地址>
	b := &Book{}

	// r: pair<type:, value:>
	var r ReadBook
	// r: pair<type:Book, value:book{}地址>
	r = b
	r.Read()

	var w WriteBook
	w = r.(WriteBook) // 因为r的type是Book,所以
	w.Write()
}

TypeOf and ValueOf

reflect.TypeOf() 是获取 pair 中的 type,reflect.ValueOf() 获取 pair 中的 value

package main

import (
	"fmt"
	"reflect"
)

func main() {
	a := 3.14
	fmt.Println("type of a:", reflect.TypeOf(a)) // float64
	fmt.Println("value of a:", reflect.ValueOf(a)) // 3.14
}

类型转换

执行 reflect.ValueOf() 后得到类型为 reflect.Value 变量

已知原数据类型

已知原数据类型可以直接强制转换

package main

import (
	"fmt"
	"reflect"
)

func main() {
	a := 3.14
	fmt.Println("type of a:", reflect.TypeOf(a))
	fmt.Println("value of a:", reflect.ValueOf(a))

	value := reflect.ValueOf(a)
	fmt.Printf("type of value:%T\n", value) // reflect.Value
	newA := value.Interface().(float64)
	fmt.Printf("type of newA:%T\n", newA) // float64
}

需要注意类型转换需要完全一致,否则将会 panic,如指针

package main

import (
	"fmt"
	"reflect"
)

func main() {
	a := 3.14
	fmt.Println("type of a:", reflect.TypeOf(a))
	fmt.Println("value of a:", reflect.ValueOf(a))

    // 传输地址
	point := reflect.ValueOf(&a)
	fmt.Printf("type of value:%T\n", point) // reflect.Value
    // 转换类型为指针
	newA := point.Interface().(*float64)
	fmt.Printf("type of newA:%T\n", newA) // *float64
}

也就是说反射可以将 “反射类型对象” 再重新转换为 “接口类型变量”

未知原数据类型

通过遍历探寻 Field 获得

package main

import (
	"fmt"
	"reflect"
)

type Person struct {
	// 一行为一个 field
	Name string
	Age  int
	Rank float64
}

// 注意方法为公有,若私有则无法访问
func (person Person) Sleep() {
	fmt.Println("person sleep")
}

func main() {
	p := Person{"zhangSan", 18, 5.2}
	getFieldAndMethod(p)
}

func getFieldAndMethod(input interface{}) {
	getType := reflect.TypeOf(input)
	fmt.Println("type:", getType.Name())

	getValue := reflect.ValueOf(input)
	fmt.Println("value/AllField:", getValue)

	// 获取属性
	numField := getValue.NumField()
	for i := 0; i < numField; i++ {
		fieldType := getType.Field(i)
		fieldValue := getValue.Field(i).Interface()
		fmt.Printf("%s: %v = %v\n", fieldType.Name, fieldType.Type, fieldValue)
	}

	// 获取方法
	numMethod := getType.NumMethod()
	for i := 0; i < numMethod; i++ {
		method := getType.Method(i)
		fmt.Printf("%s: %v\n", method.Name, method.Type)
	}
}

通过 reflec.Value 赋值

package main

import (
	"fmt"
	"reflect"
)

func main() {
	a := 3.14

    // 只有是指针时才可赋值
	pointer := reflect.ValueOf(&a)
	// 设置指向该地址,获取原始值
	newValue := pointer.Elem()
    
	// 判断是否可以设置值
	fmt.Println("value canSet:", newValue.CanSet())
	if newValue.CanSet() {
		// 如果可以设置
		newValue.SetFloat(9.96)
		// 显示变量值
		fmt.Println("value of a:", a) // 9.96
	} else {
		fmt.Println("error")
	}
}

通过 reflec.Value 调用方法

通过函数名称调用

package main

import (
	"fmt"
	"reflect"
)

type User struct {
	name string
	age  int
}

func (user User) MethodHasArgs(a string, b int) {
	fmt.Println("User MethodHasArgs")
}
func (user User) MethonNotArgs() {
	fmt.Println("User MethodNotArgs")
}

func main() {
	user := User{"zhangSan", 18}
	value := reflect.ValueOf(user)

	// 通过函数名调用
	method1 := value.MethodByName("MethodHasArgs")
	// 构建参数
	args1 := []reflect.Value{reflect.ValueOf("string"), reflect.ValueOf(18)}
	// 调用函数
	method1.Call(args1)

	// 无参调用
	method2 := value.MethodByName("MethonNotArgs")
	args2 := make([]reflect.Value, 0)
	method2.Call(args2)

}

反射的基本原理

image

This post is licensed under CC BY-NC-SA 4.0 by the author.