Type and Value

文章详细介绍了Go语言中的反射库reflect的核心类型reflect.Type和reflect.Value。reflect.Type用于获取变量的类型信息,包括数据结构、通用方法以及特定类型的专有方法。reflect.Value则关注变量的值,提供了访问和修改值的API。文章通过示例代码展示了如何使用这两个类型进行类型检查、类型转换和值操作。
摘要由CSDN通过智能技术生成

前言

reflect.Type和reflect.Value是go 反射的两大基本类型,一个管变量的类型方面,一个管变量的值方面。

一、reflect.Type

1.1 数据结构

reflect.Type是一个接口,定义了获取变量类型的相关信息的方法,rtype是其实现的结构体,通用的描述公共类型的结构。

type rtype struct {
	size       uintptr
	ptrdata    uintptr // number of bytes in the type that can contain pointers
	hash       uint32  // hash of type; avoids computation in hash tables
	tflag      tflag   // extra type information flags
	align      uint8   // alignment of variable with this type
	fieldAlign uint8   // alignment of struct field with this type
	kind       uint8   // enumeration for C
	// function for comparing objects of this type
	// (ptr to object A, ptr to object B) -> ==?
	equal     func(unsafe.Pointer, unsafe.Pointer) bool
	gcdata    *byte   // garbage collection data
	str       nameOff // string form
	ptrToThis typeOff // type for pointer to this type, may be zero
}

可以通过func TypeOf(i interface{}) Type 来获取一个Type类型的接口变量。

为什么反射接口返回的是一个 Type 接口类型,而不是直接返回 Type ?

  • 因为类型信息是一个只读的信息,不可能动态地修改类型的相关信息,那太不安全了;
  • 因为不同的类型,类型定义也不一样,使用接口这一抽象数据结构能够进行统一的抽象。 所以refelct 包通过 reflect. TypeOf() 函数返回一个 Type 的接口变量,通过接口抽象出来的方法访问具体类型的信息。

1.2 方法

1.2.1 所有类型通用方法

// 所有类型通用的方法
type Type interface {
	// 返回包含包名的类型名字,对于未命名类型返回的是空
	Name() string
	// Kind 返回该类型的底层基础类型
	Kind() Kind
	// 确定当前类型是否实现了 u 接口类型
	// 注意这里的 u 必须是接口类型的 Type
	Implements(u Type) bool
	// 判断当前类型的实例是否能赋位给 type 为 u 的类型交量
	AssignableTo(u Type) bool
	// 判断当前类型的实例是否能强制类型转换为 u 类型交量
	ConvertibleTo(u Type) bool
	// 判断当前类型是否支持比较(等 于或不等于)
	// 支持等于的类型可以作为 map 的 key
	Comparable() bool
	// 返回一个类型的方法的个数
	NumMethod() int
	// 通过索引位访问方法,索引值必须属于[ 0, NumMethod()],否则引发 panic
	Method(int) Method
	// 通过方法名获取 Method
	MethodByName(string) (Method, bool)
	// 返回类型的包路径,如采类型是预声明类型或未命名类型,则返回空字符串
	PkgPath() string
	// 返回存放该类型的实例需要多大的字节空间
	Size() uintptr
}

1.2.2 不同基础类型的专有方法

这些方法是某种类型特有的 , 如果不是某种特定类型却调用了 该类型的方法, 则会引发panic 。所 以为了避免 panic , 在调用特定类型的专有方法前 ,要清楚地知道该类型是什么 ,如果不确定类型,则要先调用 kind() 方法确定类型后再调用类型的专有方法。

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

// 返回类型的元素类型,该方法只适合 Array 、 Chan, Map, Ptr, Slice 类型
Elem() Type
// 返回数值型类型内存占用的位数
Bits() int
// struct 类型专用的方法
// 通过整数索 引获取 struct 字段
Field(i int) StructField
// /获取嵌入字段获取 struct 字段
FieldByIndex(index []int) StructField
// 通过名字查找获取 struct 字段
FieldByName(name string) (StructField, bool)
// func专用字段
// 返回第 i 个输入参数类型
In(i int) Type
// 返回第 i 个返回值类型
Out(i int) Type
// 输入参数个数
NumIn() int
// 返回值个数
NumOut() int
// map 类型专用的方法
// 返回map key 的 type
Key() Type

示例1:

package main

import (
	"fmt"
	"reflect"
)

type Student struct {
	Name string "学生姓名" // tag
	Age  int    `a:"1" b:"2"`
}
type Stu struct {
	a *Stu
	b int
}

func main() {
	var stu Stu
	fmt.Println(stu.a == nil, stu.b == 0)
	rt := reflect.TypeOf(stu)
	// FieldByName;Tag
	if Name, ok := rt.FieldByName("Name"); ok {
		fmt.Println(Name.Tag)
	}
	if Age, ok := rt.FieldByName("Age"); ok {
		fmt.Println(Age.Tag.Get("a"))
		fmt.Println(Age.Tag.Get("b"))
	}
	// rt本身的元信息
	fmt.Println(rt.Name())
	fmt.Println(rt.NumField())
	fmt.Println(rt.PkgPath())
	fmt.Println(rt.String())
	// kind
	fmt.Println(rt.Kind())
	// 换种方式获取所有字段名称
	for i := 0; i < rt.NumField(); i++ {
		fmt.Printf("type.Field[%d].Name:%v \n", i, rt.Field(i).Name)
	}
	// slice
	sc := make([]int, 10)
	sc = append(sc, 1, 2, 3)
	srt := reflect.TypeOf(sc)
	// 元素的type
	ert := srt.Elem()
	fmt.Println(ert.Kind())
	fmt.Printf("%d", ert.Kind())
	fmt.Println(ert.NumMethod())
	fmt.Println(ert.PkgPath())
}

在这里插入图片描述

示例2:

package main

import (
	"fmt"
	"reflect"
)

func main() {
	// int 和 他名int 的当前类型和底层类型
	var a Int = 1
	var b int = 1

	rta := reflect.TypeOf(a)
	rtb := reflect.TypeOf(b)

	fmt.Println(rta == rtb)
	fmt.Println(rta.Kind() == rtb.Kind())
	// 具体类型和接口类型的type
	iA := new(A)
	var sB A = B{}
	var sC C
	rtiA := reflect.TypeOf(iA)
	rtsB := reflect.TypeOf(sB)
	rtsC := reflect.TypeOf(sC)
	//fmt.Println(reflect.TypeOf(rtiA.Field(1)))
	fmt.Println(rtsB.Name())
	fmt.Println(rtsC.Name())
	fmt.Println(rtsB.Kind() == rtsC.Kind())
	fmt.Println(rtiA.Kind())
}

type Int int

type A interface {
	String() string
}
type B struct {
}

func (b B) String() string {
	return "b"
}

type C struct {
}

在这里插入图片描述

二、reflect.Value

reflect.Value 表示实例的值信息, reflect.Value 是一个 struct ,并提供了一系列的 method 给使用者 。

type flag uintptr
type Value struct {
	
	typ *rtype
	
	ptr unsafe.Pointer

	flag
}

refelct. Value 总共有三个字段, 一个是值的类型指针 typ ,另 一个是指向值的指针 ptr , 最后一个是标记字段 flag 。
通过func ValueOf(i interface{}) Value 来获取变量Value的相关信息。

Value结构体提供了丰富的API给用户,简单的示例如下,

package main

import (
	"fmt"
	"reflect"
	"unsafe"
)

type E interface {
	e()
}
type F struct {
	age int
}

func (f *F) e() {

}

func main() {
	user := User{1, 18, "lls"}
	u2 := user
	u2.Id = 1
	fmt.Println(&user == &u2)
	uv := reflect.ValueOf(user)
	uv2 := reflect.ValueOf(&user)
	uv2.Elem().Field(0).Set(reflect.ValueOf(100))
	fmt.Println(uv.CanSet(), uv.FieldByName("Id").CanSet())
	uv2.Elem().Field(0).Set(reflect.ValueOf(100))
	fmt.Println(uv2.CanSet(), uv2.Elem().Field(0).CanSet())
	fmt.Println(uv2.Elem().Field(0), user.Id)
	fmt.Println()
	ut := reflect.TypeOf(user)
	//reflect.PtrTo()
	//a := reflect.New(ut)
	a := reflect.NewAt(ut, unsafe.Pointer(&user))
	fmt.Println(a, reflect.ValueOf(a))
	fmt.Println(uv.Field(0))
	for i := 0; i < uv.NumField(); i++ {
		field := uv.Type().Field(i)
		fieldValue := uv.Field(i).Interface()
		switch val := fieldValue.(type) {
		case int:
			fmt.Println(field.Name, val, field.Type)
		case string:
			fmt.Println(field.Name, val, field.Type)
		}
	}
}

type User struct {
	Id   int
	Age  int
	Name string
}

在这里插入图片描述

总结

1)reflect.Type是封装变量的类型相关信息,reflect.Value是封装变量的值信息。

参考资料

[1] Go 语言核心编程

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值