Golang反射

反射的结构体

//reflect/type.go
type Type interface {
  // 该类型内存分配大小(内存对齐单位子节)
	Align() int

	// 该类型作为结构体字段时内存分配大小(内存对齐单位子节)
	FieldAlign() int

  // 根据index in [0, NumMethod())获取方法   按lexicographic排序
	Method(int) Method

 //  根据方法名获取方法
	MethodByName(string) (Method, bool)

	// 获取所有可用方法数量
	// 接口类型 获取包含未导出方法
	NumMethod() int

	// 返回类型名,未定义则为空
	Name() string

  // 返回类型所在包名,未定义则为空
	PkgPath() string

	// 错误类型所需子节数 unsafe.Sizeof.
	Size() uintptr

	// 返回类型名称字符串
	String() string

	// 返回此类型的kind类型
	Kind() Kind

	// 返回是否实现了u接口
	Implements(u Type) bool

	// 返回类型的值是否可分配给类型u
	AssignableTo(u Type) bool

	// 返回类型的值是否可以转换为u类型
	ConvertibleTo(u Type) bool

	// 返回类型的值是否可对比
	Comparable() bool

	// Methods applicable only to some types, depending on Kind.
	// The methods allowed for each kind are:
	//
	//	Int*, Uint*, Float*, Complex*: Bits
	//	Array: Elem, Len
	//	Chan: ChanDir, Elem
	//	Func: In, NumIn, Out, NumOut, IsVariadic.
	//	Map: Key, Elem
	//	Ptr: Elem
	//	Slice: Elem
	//	Struct: Field, FieldByIndex, FieldByName, FieldByNameFunc, NumField

	// Bits returns the size of the type in bits.
	// It panics if the type's Kind is not one of the
	// sized or unsized Int, Uint, Float, or Complex kinds.
	Bits() int

    
	// Elem返回类型的元素类型
	// 不是 Array, Chan, Map, Ptr, or Slice. panic
	Elem() Type

  // ---------------   struct  ------------------------
	// 字段返回结构类型的第i个字段。
	// 不是struct 或者下表越界 painc
	Field(i int) StructField

	// 字段返回嵌套结构类型的第i个字段。
	// 不是struct 或者下表越界 painc
	FieldByIndex(index []int) StructField

	// 按名称返回字段
	FieldByName(name string) (StructField, bool)

 // 按过滤方法返回匹配字段
	FieldByNameFunc(match func(string) bool) (StructField, bool)
  
	// 返回结构体字段总数
	// 不是 Struct panic.
	NumField() int
  // ---------------   struct  ------------------------

  // ---------------   func  --------------------
 //返回函数类型第i的输入参数
  // 不是Func painc
	In(i int) Type
  
  	// 返回方法输入参数总数
	// 不是Func painc
	NumIn() int

	// 返回函数输出参数总数
	// 不是Func painc
	NumOut() int

  // 返回函数类型第i的输出参数
  // 不是Func painc
	Out(i int) Type
  
  // 返回函数是否包含可变参数
  // 不是Func painc
	IsVariadic() bool
  // ---------------   func  --------------------
  
  
  // ---------------   Map  --------------------
	// 返回map的key类型
	// 不是Map panic
	Key() Type
  // ---------------   Map  --------------------
  
  
  // ---------------   Array  --------------------
	// 返回数组类型的长度 
	// 不是Array panic
	Len() int
  // ---------------   Array  --------------------

  // ---------------   chan  --------------------
	// 返回chan类型的方向,不是chan会panic
	ChanDir() ChanDir
  
  // ---------------   chan  --------------------

	common() *rtype
	uncommon() *uncommonType
}

反射三定律

反射包里有两个接口类型要先了解一下.

reflect.Type 提供一组接口处理interface的类型,即(value, type)中的type
reflect.Value提供一组接口处理interface的值,即(value, type)中的value
下面会提到反射对象,所谓反射对象即反射包里提供的两种类型的对象。

reflect.Type 类型对象
reflect.Value类型对象

反射第一定律:反射可以将interface类型变量转换成反射对象

package main

import (
    "fmt"
    "reflect"
)

func main() {
    var x float64 = 3.4
    t := reflect.TypeOf(x)  //t is reflect.Type
    fmt.Println("type:", t)

    v := reflect.ValueOf(x) //v is reflect.Value
    fmt.Println("value:", v)
}

程序输出如下:

type: float64
value: 3.4
注意:反射是针对interface类型变量的,其中TypeOf()和ValueOf()接受的参数都是interface{}类型的,也即x值是被转成了interface传入的。

除了reflect.TypeOf()和reflect.ValueOf(),还有其他很多方法可以操作,本文先不过多介绍,否则一不小心会会引起困惑。

反射第二定律:反射可以将反射对象还原成interface对象

package main

import (
   "fmt"
   "reflect"
)

func main() {
   var x float64 = 3.4

   v := reflect.ValueOf(x) //v is reflect.Value

   var y float64 = v.Interface().(float64)
   fmt.Println("value:", y)
}
对象x转换成反射对象v,v又通过Interface()接口转换成interface对象,interface对象通过.(float64)类型断言获取float64类型的值。

反射第三定律:反射对象可修改,value值必须是可设置的

通过反射可以将interface类型变量转换成反射对象,可以使用该反射对象设置其持有的值。在介绍何谓反射对象可修改前,先看一下失败的例子:

package main

import (
    "reflect"
)

func main() {
    var x float64 = 3.4
    v := reflect.ValueOf(x)
    v.SetFloat(7.1) // Error: will panic.
}

如下代码,通过反射对象v设置新值,会出现panic。报错如下:

panic: reflect: reflect.Value.SetFloat using unaddressable value

错误原因即是v是不可修改的。

反射对象是否可修改取决于其所存储的值,回想一下函数传参时是传值还是传址就不难理解上例中为何失败了。

上例中,传入reflect.ValueOf()函数的其实是x的值,而非x本身。即通过v修改其值是无法影响x的,也即是无效的修改,所以golang会报错。

想到此处,即可明白,如果构建v时使用x的地址就可实现修改了,但此时v代表的是指针地址,我们要设置的是指针所指向的内容,也即我们想要修改的是*v。 那怎么通过v修改x的值呢?

reflect.Value提供了Elem()方法,可以获得指针向指向的value。看如下代码:

package main

import (
"reflect"
    "fmt"
)

func main() {
    var x float64 = 3.4
    v := reflect.ValueOf(&x)
    v.Elem().SetFloat(7.1)
    fmt.Println("x :", v.Elem().Interface())
}

输出为:

x : 7.1

如何通过反射来进行结构体方法的调用?

package main

import (
   "fmt"
   "reflect"
)

type Person struct {
   Name string
   Age int
   Sex string
}

func (p Person)Say(msg string)  {
   fmt.Println("hello,",msg)
}
func (p Person)PrintInfo()  {
   fmt.Printf("姓名:%s,年龄:%d,性别:%s\n",p.Name,p.Age,p.Sex)
}

func (p Person) Test(i,j int,s string){
   fmt.Println(i,j,s)
}

// 如何通过反射来进行方法的调用?
// 本来可以用结构体对象.方法名称()直接调用的,
// 但是如果要通过反射,
// 那么首先要将方法注册,也就是MethodByName,然后通过反射调动mv.Call

func main() {
   p2 := Person{"张三",20,"男"}
   // 1. 要通过反射来调用起对应的方法,必须要先通过reflect.ValueOf(interface)来获取到reflect.Value,
   // 得到“反射类型对象”后才能做下一步处理
   getValue := reflect.ValueOf(p2)

   // 2.一定要指定参数为正确的方法名
   // 先看看没有参数的调用方法

   methodValue1 := getValue.MethodByName("PrintInfo")
   fmt.Printf("Kind : %s, Type : %s\n",methodValue1.Kind(),methodValue1.Type())
   methodValue1.Call(nil) //没有参数,直接写nil

   args1 := make([]reflect.Value, 0) //或者创建一个空的切片也可以
   methodValue1.Call(args1)

   // 有参数的方法调用
   methodValue2 := getValue.MethodByName("Say")
   fmt.Printf("Kind : %s, Type : %s\n",methodValue2.Kind(),methodValue2.Type())
   args2 := []reflect.Value{reflect.ValueOf("反射机制")}
   methodValue2.Call(args2)

   methodValue3 := getValue.MethodByName("Test")
   fmt.Printf("Kind : %s, Type : %s\n",methodValue3.Kind(),methodValue3.Type())
   args3 := []reflect.Value{reflect.ValueOf(5), reflect.ValueOf(2),reflect.ValueOf("you")}

   methodValue3.Call(args3)
}
Kind : func, Type : func()
姓名:张三,年龄:20,性别:男
姓名:张三,年龄:20,性别:男
Kind : func, Type : func(string)
hello, 反射机制
Kind : func, Type : func(int, int, string)
5 2 you

如何通过反射来进行方法的调用?

package main
 
import (
   "fmt"
   "reflect"
)
 
func main() {
   //函数的反射
   f1 := fun1
   value := reflect.ValueOf(f1)
   fmt.Printf("Kind : %s , Type : %s\n",value.Kind(),value.Type()) //Kind : func , Type : func()
 
   value2 := reflect.ValueOf(fun2)
   fmt.Printf("Kind : %s , Type : %s\n",value2.Kind(),value2.Type()) //Kind : func , Type : func(int, string)
 
   //通过反射调用函数
   value.Call(nil)
 
   value2.Call([]reflect.Value{reflect.ValueOf("hello"),reflect.ValueOf(61)})
 
}
 
func fun1(){
   fmt.Println("我是函数fun1(),无参的。")
}
 
func fun2(s string, i int){
   fmt.Println("我是函数fun2(),有参数:",s,i)
}
Kind : func , Type : func()
Kind : func , Type : func(string, int)
我是函数fun1(),无参的。
我是函数fun2(),有参数: hello 61

反射未导出的字段

package main
import (
	"fmt"
	"reflect"
)
type Student struct {
   
	name string
	Age  int
}

func main() {
    var TestStudent = Student{name: "jack",Age:  12,}
	fmt.Printf("testStudent.name: %v\n", getUnExportedField(&TestStudent, "name"))
}

func getUnExportedField(ptr interface{}, fieldName string) reflect.Value {
	v := reflect.ValueOf(ptr).Elem().FieldByName(fieldName)
	return v
}
//输出 testStudent.name: jack

总结--综合

package main

import (
    "fmt"
    "reflect"
)

type People struct {
    Age   int
    Name string
}

type TestInterface interface {
    GetName() string
}

func (p *People) UpdateName(newName string) {
    p.Name = newName
}
func (p *People) GetName() string {
    return p.Name
}

func main() {
    u := People{18, "kuteng"}
    //返回指定对象的Kind类型
    fmt.Println(reflect.TypeOf(32).Kind())
    fmt.Println(reflect.ValueOf(32).Kind())

    //根据方法名找方法
    fmt.Println(reflect.TypeOf(&u).MethodByName("UpdateName"))
    fmt.Println(reflect.ValueOf(&u).MethodByName("UpdateName"))

    //返回第i个方法
    fmt.Println(reflect.TypeOf(&u).Method(0))
    fmt.Println(reflect.ValueOf(&u).Method(0))

    //返回拥有的方法总数,包括unexported方法
    fmt.Println(reflect.TypeOf(&u).NumMethod())
    fmt.Println(reflect.ValueOf(&u).NumMethod())

    //取struct结构的第n个field
    fmt.Println(reflect.TypeOf(u).Field(0))
    fmt.Println(reflect.ValueOf(u).Field(1))

    //嵌套的方式取struct的field,比如v.FieldByIndex(1,2,3)等价于 v.field(1).field(2).field(3)
    fmt.Println(reflect.TypeOf(u).FieldByIndex([]int{0}))
    fmt.Println(reflect.ValueOf(u).FieldByIndex([]int{0}))

    //返回名称匹配match函数的field
    fmt.Println(reflect.TypeOf(u).FieldByName("ID"))
    fmt.Println(reflect.ValueOf(u).FieldByName("Name"))

    //返回struct所包含的field数量
    fmt.Println(reflect.TypeOf(u).NumField())
    fmt.Println(reflect.ValueOf(u).NumField())

    //分配内存时的内存对齐字节数
    fmt.Println(reflect.TypeOf(u).Align())
    //作为struct的field时内存对齐字节数
    fmt.Println(reflect.TypeOf(u).FieldAlign())
    //type名 string类型
    fmt.Println(reflect.TypeOf(u).Name())
    //包路径, "encoding/base64", 内置类型返回empty string
    fmt.Println(reflect.TypeOf(u).PkgPath())
    //该类型变量占用字节数
    fmt.Println(reflect.TypeOf(u).Size())
    //type的string表示方式
    fmt.Println(reflect.TypeOf(u).String())
    //判断该类型是否实现了某个接口
    fmt.Println(reflect.TypeOf(u).Implements(reflect.TypeOf((*TestInterface)(nil)).Elem()))
    //判断该类型能否赋值给某个类型
    fmt.Println(reflect.TypeOf(u).AssignableTo(reflect.TypeOf(People{})))
    //判断该类型能否转换为另外一种类型
    fmt.Println(reflect.TypeOf(u).ConvertibleTo(reflect.TypeOf(1)))
    //判断该类型变量是否可以比较
    fmt.Println(reflect.TypeOf(u).Comparable())
    //取该类型的元素,指针指向的结构
    fmt.Println(reflect.TypeOf(&u).Elem())

    //调用函数
    fmt.Println(reflect.ValueOf(&u).MethodByName("GetName").Call([]reflect.Value{}))
    //判断能否取地址
    fmt.Println(reflect.ValueOf(&u).CanAddr())
    //判断Interface方法能否使用
    fmt.Println(reflect.ValueOf(&u).CanInterface())
    //判断值能否改变
    fmt.Println(reflect.ValueOf(&u).CanSet())

    a := []int{0, 1}
    //获取容量 Array/Chan/Slice
    fmt.Println(reflect.ValueOf(a).Cap())
    c := make(chan int)
    //关闭channel
    reflect.ValueOf(c).Close()
    //返回指针实际的值
    fmt.Println(reflect.ValueOf(&u).Elem())
    //索引操作 Array/Slice/String
    fmt.Println(reflect.ValueOf(a).Index(0))
    //修改数组第一个索引的值
    reflect.ValueOf(a).Index(0).Set(reflect.ValueOf(1))
    fmt.Println(a[0])
    //将当前value以interface形式返回
    fmt.Println(reflect.ValueOf(&u).Interface())
    //判断是否为nil,chan, func, interface, map, pointer, or slice valu
    fmt.Println(reflect.ValueOf(&u).IsNil())
    //是否是可操作的Value,返回false表示为zero Value
    fmt.Println(reflect.ValueOf(&u).IsValid())
    //获取长度,适用于Array, Chan, Map, Slice, or String
    fmt.Println(reflect.ValueOf(a).Len())
    m := map[int]string{1: "topgoer.com", 2: "topgoer.cn"}
    //对map类型按key取值
    fmt.Println(reflect.ValueOf(m).MapIndex(reflect.ValueOf(1)))
    //map类型的所有key的列表
    for index, key := range reflect.ValueOf(m).MapKeys() {
        fmt.Println("key=", key)
        fmt.Println("idnex=", index)
    }
    //返回value的Type
    fmt.Println(reflect.ValueOf(1).Type())
}
posted @ 2023-11-14 16:24  朝阳1  阅读(11)  评论(0编辑  收藏  举报