Go反射四讲---第一讲:什么是反射,反射常用的API,反射三原则以及注意事项

2024-08-25 10:28

本文主要是介绍Go反射四讲---第一讲:什么是反射,反射常用的API,反射三原则以及注意事项,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

反射-引入

这是我们反射四讲的第一讲,本次主要向大家介绍一下反射的基础概念以及相关的API。

在计算机科学中,反射是指计算机程序能够在运行时可以访问,检测和修改本身状态或者行为的一种能力。

简单的来说,反射就是程序在运行时能够动地查看自己的状态,并允许修改自身的行为。

首先,要明白对于一个类型变量。它有两层含义,一是类型是什么,二是存储的值是什么。类型决定了变量的存储方式,支持的操作和方法等。值无外乎是读与写,在存储是以0,1格式存放,并使用类型加以约束。类型和值不是孤立存在的。

反射的相关API都在 reflect 包中,最核心的就是reflect.Valuereflect.Type
其中,reflect.Value用于操作值,部分值是可以被反射修改的。reflect.Type用于操作类型信息。类型信息是只能读取的。

注意:reflect.Type可以通过reflect.Value得到,但是反过来不行。

reflect.Type

func TypeOf(i any) Type
//type any = interface{}

形参是一个空接口类型,返回值是一个 Type 接口类型。

注意:reflect包有一个很强的的假设,我们必须我们要操作的是什么Kind

Kind是一个枚举值,用来判断操作的对应类型。例如是否是指针,是否是数组,是否是切片等。
如果使用不当就会引起 panic。so,在调用API之前,我们一定要先读注释,确认什么情况下可以使用!!!

接下来,讲解一下 Type 接口提供的主要通用方法。

类型的通用方法

// Name returns the type's name within its package for a defined type.
Name() string// Kind returns the specific kind of this type.
Kind() Kind// Implements reports whether the type implements the interface type u.
Implements(u Type) bool// AssignableTo reports whether a value of the type is assignable to type u.
AssignableTo(u Type) bool// ConvertibleTo reports whether a value of the type is convertible to type u.
ConvertibleTo(u Type) bool// Comparable reports whether values of this type are comparable.
Comparable() bool// NumMethod returns the number of methods accessible using Method.
NumMethod() int// Method returns the i'th method in the type's method set.
Method(int) Method// MethodByName returns the method with that name in the type's
// method set and a boolean indicating if the method was found.
MethodByName(string) (Method, bool)// PkgPath returns a defined type's package path, that is, the import path
PkgPath() string// Size returns the number of bytes needed to store
Size() uintptr

类型的专有方法

type Type interface {
// Elem returns a type's element type.
// It panics if the type's Kind is not Array, Chan, Map, Pointer, or Slice.
Elem() Type// Bits returns the size of the type in bits.
Bits() int// Len returns an array type's length.
// It panics if the type's Kind is not Array.
Len() int// struct 专有的方法// NumField returns a struct type's field count.
// It panics if the type's Kind is not Struct.
NumField() int// Field returns a struct type's i'th field.
// It panics if the type's Kind is not Struct.
// It panics if i is not in the range [0, NumField()).
Field(i int) StructField// FieldByIndex returns the nested field corresponding
// to the index sequence. It is equivalent to calling Field
// successively for each index i.
// It panics if the type's Kind is not Struct.
FieldByIndex(index []int) StructField// FieldByName returns the struct field with the given name
// and a boolean indicating if the field was found.
// If the returned field is promoted from an embedded struct,
// then Offset in the returned StructField is the offset in
// the embedded struct.
FieldByName(name string) (StructField, bool)// FieldByNameFunc returns the struct field with a name
// that satisfies the match function and a boolean indicating if
// the field was found.
FieldByNameFunc(match func(string) bool) (StructField, bool)// func 专有的方法// IsVariadic reports whether a function type's final input parameter
// IsVariadic panics if the type's Kind is not Func.
IsVariadic() bool// In returns the type of a function type's i'th input parameter.
// It panics if the type's Kind is not Func.
// It panics if i is not in the range [0, NumIn()).
In(i int) Type// NumIn returns a function type's input parameter count.
// It panics if the type's Kind is not Func.
NumIn() int// NumOut returns a function type's output parameter count.
// It panics if the type's Kind is not Func.
NumOut() int// map 专有的方法// Key returns a map type's key type.
// It panics if the type's Kind is not Map.
Key() Type
}

reflect.Value

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

func ValueOf(i any) Value
//type Value struct {
//	typ_ *abi.Type  //值的指针类型typ
//	ptr unsafe.Pointer // 指向值的指针ptr
//	flag //标记字段
//}

注意:这里一定要搞清与Type的区别,Type 直接返回接口,Value 返回的是结构体,而在结构体上绑定了一些方法。

下面我们介绍几个常用的在 Value 结构体上绑定的方法。

// CanAddr reports whether the value's address can be obtained with [Value.Addr].
func (v Value) CanAddr() bool {}// Addr returns a pointer value representing the address of v.
func (v Value) Addr() Value {}// CanSet reports whether the value of v can be changed.
func (v Value) CanSet() bool {}// Elem returns the value that the interface v contains
// or that the pointer v points to.
func (v Value) Elem() Value {}// Field returns the i'th field of the struct v.
func (v Value) Field(i int) Value {}// Bool returns v's underlying value.
func (v Value) Bool() bool {}// Bytes returns v's underlying value.
func (v Value) Bytes() []byte {} // Index returns v's i'th element.
func (v Value) Index(i int) Value {}

这里方法我们没有讲解全面,如果想了解源码内容,可以去 reflect 包下的 value.go 下查看。

反射的原则

下图展示了实例,Type与Value三者之间的关系。

API使用总结:

  1. 实例到 Value。使用实例获取对象的 Value,直接使用 reflect.ValueOf() 函数。

  2. 实例到 Type。使用实例获取对象的 Type, 直接使用 reflect.TypeOf()函数。

  3. TypeValue。无法直接从一个 Type 接口变量获取实例的 Value。一般先使用 Type 构建一个新的实例Value

    第一种,使用func New(typ Type) Value创建一个ValueValueType是一个指定的typ的指针类型。

    第二种,使用func Zero(typ Type) Value创建一个Value,注意此时的Value不能寻址,意味着值不可以变。

    第三种,如果知道一个值的底层存放地址,可以使用func NewAt(typ Type, p unsafe.Pointer) Value来恢复Value。此处的typ是实例类型,p是实例的地址。

  4. ValueType。直接可以调用Value结构体上绑定的func (v Value) Type() Type {}方法。

  5. Value 到实例。可以使用通用方法func (v Value) Interface() (i any) {}直接返回实例。

  6. Value指针到值。使用func (v Value) Elem() Value {}返回值。注意:如果类型是接口,则 Elem()返回接口绑定的实例的 Value ,如果类型是指针,则返回指针值的 Value ,否则引起 panic

  7. Type指针到Type值。使用Elem() Type返回子元素的Type

  8. Type值到Type指针。使用func PtrTo(t Type) Type {},返回指向t的指针类型Type

  9. 值的可修改性。可以使用func (v Value) CanSet() bool {}去判断一个值是否可以修改,如果可以修改返回true。接着可以使用func (v Value) Set(x Value)去设置一个值。

总结

本次,带领大家了解反射的基本概念,如何使用等。待熟悉相关的API之后,下一次,我们将展示给您如何在实战中使用reflect包。

参考

  • 《Go语言核心编程》-- 李文塔
  • https://pkg.go.dev/reflect

这篇关于Go反射四讲---第一讲:什么是反射,反射常用的API,反射三原则以及注意事项的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1105302

相关文章

Go语言结构体标签(Tag)的使用小结

《Go语言结构体标签(Tag)的使用小结》结构体标签Tag是Go语言中附加在结构体字段后的元数据字符串,用于提供额外的属性信息,这些信息可以通过反射在运行时读取和解析,下面就来详细的介绍一下Tag的使... 目录什么是结构体标签?基本语法常见的标签用途1.jsON 序列化/反序列化(最常用)2.数据库操作(

SpringBoo WebFlux+MongoDB实现非阻塞API过程

《SpringBooWebFlux+MongoDB实现非阻塞API过程》本文介绍了如何使用SpringBootWebFlux和MongoDB实现非阻塞API,通过响应式编程提高系统的吞吐量和响应性能... 目录一、引言二、响应式编程基础2.1 响应式编程概念2.2 响应式编程的优势2.3 响应式编程相关技术

java中4种API参数传递方式统一说明

《java中4种API参数传递方式统一说明》在Java中,我们可以使用不同的方式来传递参数给方法或函数,:本文主要介绍java中4种API参数传递方式的相关资料,文中通过代码介绍的非常详细,需要的... 目录1. 概述2. 参数传递方式分类2.1 Query Parameters(查询参数)2.2 Path

Java调用DeepSeek API的8个高频坑与解决方法

《Java调用DeepSeekAPI的8个高频坑与解决方法》现在大模型开发特别火,DeepSeek因为中文理解好、反应快、还便宜,不少Java开发者都用它,本文整理了最常踩的8个坑,希望对... 目录引言一、坑 1:Token 过期未处理,鉴权异常引发服务中断问题本质典型错误代码解决方案:实现 Token

Java实现字符串大小写转换的常用方法

《Java实现字符串大小写转换的常用方法》在Java中,字符串大小写转换是文本处理的核心操作之一,Java提供了多种灵活的方式来实现大小写转换,适用于不同场景和需求,本文将全面解析大小写转换的各种方法... 目录前言核心转换方法1.String类的基础方法2. 考虑区域设置的转换3. 字符级别的转换高级转换

Python使用Matplotlib和Seaborn绘制常用图表的技巧

《Python使用Matplotlib和Seaborn绘制常用图表的技巧》Python作为数据科学领域的明星语言,拥有强大且丰富的可视化库,其中最著名的莫过于Matplotlib和Seaborn,本篇... 目录1. 引言:数据可视化的力量2. 前置知识与环境准备2.1. 必备知识2.2. 安装所需库2.3

Go异常处理、泛型和文件操作实例代码

《Go异常处理、泛型和文件操作实例代码》Go语言的异常处理机制与传统的面向对象语言(如Java、C#)所使用的try-catch结构有所不同,它采用了自己独特的设计理念和方法,:本文主要介绍Go异... 目录一:异常处理常见的异常处理向上抛中断程序恢复程序二:泛型泛型函数泛型结构体泛型切片泛型 map三:文

MyBatis配置文件中最常用的设置

《MyBatis配置文件中最常用的设置》文章主要介绍了MyBatis配置的优化方法,包括引用外部的properties配置文件、配置外置以实现环境解耦、配置文件中最常用的6个核心设置以及三种常用的Ma... 目录MyBATis配置优化mybatis的配置中引用外部的propertis配置文件⚠️ 注意事项X

C++,C#,Rust,Go,Java,Python,JavaScript的性能对比全面讲解

《C++,C#,Rust,Go,Java,Python,JavaScript的性能对比全面讲解》:本文主要介绍C++,C#,Rust,Go,Java,Python,JavaScript性能对比全面... 目录编程语言性能对比、核心优势与最佳使用场景性能对比表格C++C#RustGoJavapythonjav

Go语言实现桥接模式

《Go语言实现桥接模式》桥接模式是一种结构型设计模式,它将抽象部分与实现部分分离,使它们可以独立地变化,本文就来介绍一下了Go语言实现桥接模式,感兴趣的可以了解一下... 目录简介核心概念为什么使用桥接模式?应用场景案例分析步骤一:定义实现接口步骤二:创建具体实现类步骤三:定义抽象类步骤四:创建扩展抽象类步