首页 > 其他分享 >Golang反射

Golang反射

时间:2023-11-14 16:35:45浏览次数:22  
标签:反射 Kind int Golang reflect func Type

反射的结构体

//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

标签:反射,Kind,int,Golang,reflect,func,Type
From: https://www.cnblogs.com/qcy-blog/p/17831926.html

相关文章

  • 关于Golang三个内存区域的形象比喻
    当我们使用Go语言编写程序时,可以将这三个内存区域类比为一个大的游乐场。Arena区就像是整个游乐场的主要区域,它是用于分配和管理大对象的地方。在这个区域,我们可以找到各种大型游乐设施,比如大型滑梯、蹦床和攀爬架等。这些设施需要更多的空间和资源来支持,因此它们被分配在Arena区域......
  • Golang内存泄漏的代码示例
    以下是几个可能导致内存泄漏的Go代码示例:资源未关闭:funcreadFile(){ file,err:=os.Open("filename.txt") iferr!=nil{ //错误处理 return } //使用file进行读取操作 //...}在上述代码中,readFile函数打开了一个文件,但没有在使用完后调用file.Close()来关闭文......
  • Golang select语句代码示例
    在Go语言中,select语句用于多路选择(multiplexing),允许在多个通信操作中选择可用的操作进行执行。select语句的语法如下:select{case<-channel1://当channel1有数据可读时执行的代码casedata:=<-channel2://当channel2有数据可读时执行的代码,同时将读取的数据存储在......
  • Golang 无缓冲channel有哪些特点?
    无缓冲channel是Go语言中的一种特殊类型的channel,其主要特点是在发送和接收数据时要求发送方和接收方必须同时准备好,否则会造成阻塞。具体来说,当一个无缓冲channel的发送操作没有准备好时,会阻塞发送方的执行;同样地,当一个无缓冲channel的接收操作没有准备好时,会阻塞接收方的执行。无......
  • Golang中如何使用new函数创建一个结构体对象?
    在Go语言中,可以使用new函数创建一个结构体对象的指针。下面是使用new函数创建结构体对象的示例:typePersonstruct{NamestringAgeint}funcmain(){//使用new函数创建一个Person结构体对象的指针p:=new(Person)//设置结构体对象的字段值......
  • Golang 锁
    写锁需要阻塞写锁:一个协程拥有写锁时,其他协程写锁定需要阻塞写锁需要阻塞读锁:一个协程拥有写锁时,其他协程读锁定需要阻塞读锁需要阻塞写锁:一个协程拥有读锁时,其他协程写锁定需要阻塞读锁不能阻塞读锁:一个协程拥有读锁时,其他协程也可以拥有读锁......
  • Golang 大杀器之trace
    需要下载graphvizpackagemainimport( "log" "os" "runtime/trace")funcmain(){ file,err:=os.Create("trace.out") iferr!=nil{ log.Fatal(err) } deferfile.Close() err=trace.Start(file) iferr!=ni......
  • Golang大杀器之pprof
    需要下载graphvizpackagemainimport( "math/rand" "os" "runtime/pprof" "time")funcgenerate(nint)[]int{ rand.Seed(time.Now().UnixNano()) nums:=make([]int,0) fori:=0;i<n;i++{ nums=append(......
  • Golang 中的 Context 包
    Golang中的Context包原创 Slagga 技术的游戏 2023-11-1212:28 发表于广东收录于合集#Golang89个简介今天,我们将讨论Go编程中非常重要的一个主题:context 包。如果你现在觉得它很令人困惑,不用担心—在本文结束时,你将像专家一样处理context!想象一下,你在一个......
  • golang http rpc
    server端:packagemainimport( "errors" "log" "net" "net/http" "net/rpc")typeArgsstruct{ A,Bint}typeQuotientstruct{ Quo,Remint}//定义typetypeArithintfunc(t*Arith)Multiply......