go语言中的反射

Go语言--反射(reflect) 0 反射的概念 反射是指计算机程序在运行时(runtime)可以访问、检测和修改本身状态或行为的一种能力。通俗地将,反射就是程序能够在运行时动态地查看自己的状态,并且允许修改自身的行为。 ... 阅读详情

Go 语言反射的三大法则:

  • 从 interface{} 变量可以反射出反射对象;
  • 从反射对象可以获取 interface{} 变量;
  • 要修改反射对象,其值必须可设置;

从反射对象到接口值的过程就是从接口值到反射对象的镜面过程,两个过程都需要经历两次转换:

  • 从接口值到反射对象:

    • 从基本类型到接口类型的类型转换;
    • 从接口类型到反射对象的转换;
  • 从反射对象到接口值:

    • 反射对象转换成接口类型;
    • 通过显式类型转换变成原始类型;

reflect.Type和reflect.Value

我们通过reflect.TypeOf、reflect.ValueOf可以将一个普通的变量转换成『反射』包中提供的Type和Value。

类型 Type 是反射包定义的一个接口,可以使用 reflect.TypeOf 函数获取任意变量的的类型,Type 接口中定义了一些有趣的方法,MethodByName 可以获取当前类型对应方法的引用、Implements 可以判断当前类型是否实现了某个接口:

type Type interface {
    // 变量的内存对齐,返回 rtype.align
    Align() int

    // struct 字段的内存对齐,返回 rtype.fieldAlign
    FieldAlign() int

    // 根据传入的 i,返回方法实例,表示类型的第 i 个方法
    Method(int) Method

    // 根据名字返回方法实例,这个比较常用
    MethodByName(string) (Method, bool)

    // 返回类型方法集中可导出的方法的数量
    NumMethod() int

    // 只返回类型名,不含包名
    Name() string

    // 返回导入路径,即 import 路径
    PkgPath() string

    // 返回 rtype.size 即类型大小,单位是字节数
    Size() uintptr

    // 返回类型名字,实际就是 PkgPath() + Name()
    String() string

    // 返回 rtype.kind,描述一种基础类型
    Kind() Kind

    // 检查当前类型有没有实现接口 u
    Implements(u Type) bool

    // 检查当前类型能不能赋值给接口 u
    AssignableTo(u Type) bool

    // 检查当前类型能不能转换成接口 u 类型
    ConvertibleTo(u Type) bool

    // 检查当前类型能不能做比较运算,其实就是看这个类型底层有没有绑定 typeAlg 的 equal 方法。
    // 打住!不要去搜 typeAlg 是什么,不然你会陷进去的!先把本文看完。
    Comparable() bool

    // 返回类型的位大小,但不是所有类型都能调这个方法,不能调的会 panic
    Bits() int

    // 返回 channel 类型的方向,如果不是 channel,会 panic
    ChanDir() ChanDir

    // 返回函数类型的最后一个参数是不是可变数量的,"..." 就这样的,同样,如果不是函数类型,会 panic
    IsVariadic() bool

    // 返回所包含元素的类型,只有 Array, Chan, Map, Ptr, Slice 这些才能调,其他类型会 panic。
    // 这不是废话吗。。其他类型也没有包含元素一说。
    Elem() Type

    // 返回 struct 类型的第 i 个字段,不是 struct 会 panic,i 越界也会 panic
    Field(i int) StructField

    // 跟上边一样,不过是嵌套调用的,比如 [1, 2] 就是说返回当前 struct 的第1个struct 的第2个字段,适用于 struct 本身嵌套的类型
    FieldByIndex(index []int) StructField

    // 按名字找 struct 字段,第二个返回值 ok 表示有没有
    FieldByName(name string) (StructField, bool)

    // 按函数名找 struct 字段,因为 struct 里也可能有类型是 func 的嘛
    FieldByNameFunc(match func(string) bool) (StructField, bool)
    
    // 返回函数第 i 个参数的类型,不是 func 会 panic
    In(i int) Type

    // 返回 map 的 key 的类型,不是 map 会 panic
    Key() Type

    // 返回 array 的长度,不是 array 会 panic
    Len() int

    // 返回 struct 字段数量,不是 struct 会 panic
    NumField() int

    // 返回函数的参数数量,不是 func 会 panic
    NumIn() int

    // 返回函数的返回值数量,不是 func 会 panic
    NumOut() int

    // 返回函数第 i 个返回值的类型,不是 func 会 panic
    Out(i int) Type
}

反射包中 Value 的类型与 Type 不同,它被声明成了结构体。这个结构体没有对外暴露的字段,但是提供了获取或者写入数据的方法:

type Value struct {
    // 反射出来此值的类型,rtype 是啥往上看,但可别弄错了,这 typ 是未导出的,从外部调不到 Type 接口的方法
    typ *rtype

    // 数据形式的指针值
    ptr unsafe.Pointer

    // 保存元数据
    flag
}
// 前提 v 是一个 func,然后调用 v,并传入 in 参数,第一个参数是 in[0],第二个是 in[1],以此类推
func (v Value) Call(in []Value) []Value

// 返回 v 的接口值或者指针
func (v Value) Elem() Value

// 前提 v 是一个 struct,返回第 i 个字段,这个主要用于遍历
func (v Value) Field(i int) Value

// 前提 v 是一个 struct,根据字段名直接定位返回
func (v Value) FieldByName(name string) Value

// 前提 v 是 Array, Slice, String 之一,返回第 i 个元素,主要也是用于遍历,注意不能越界
func (v Value) Index(i int) Value

// 判断 v 是不是 nil,只有 chan, func, interface, map, pointer, slice 可以用,其他类型会 panic
func (v Value) IsNil() bool

// 判断 v 是否合法,如果返回 false,那么除了 String() 以外的其他方法调用都会 panic,事前检查是必要的
func (v Value) IsValid() bool

// 前提 v 是个 map,返回对应 value
func (v Value) MapIndex(key Value)

// 前提 v 是个 map,返回所有 key 组成的一个 slice
func (v Value) MapKeys() []Value

// 前提 v 是个 struct,返回字段个数
func (v Value) NumField() int

// 赋值
func (v Value) Set(x Value)

// 类型
func (v Value) Type() Type

实践

遍历一个结构体的字段以及对应的值

package main

  import (
      "fmt"
      "reflect"
  )

  type Person struct {
      Name     string
      Sex      string
      Age      int
      PhoneNum string
      School   string
      City     string
  }

func main() {
      p1 := Person{
          Name:     "tom",
          Sex:      "male",
          Age:      10,
          PhoneNum: "1000000",
          School:   "spb-kindergarden",
          City:     "cq",
      }

      rv := reflect.ValueOf(p1)
      rt := reflect.TypeOf(p1)
      if rv.Kind() == reflect.Struct {
          for i := 0; i < rt.NumField(); i++ {
              //按顺序遍历
              fmt.Printf("field:%+v,value:%+v\n", rt.Field(i).Name, rv.Field(i))
          }
      }
  }

若知道字段名,直接去取该字段:

    rv := reflect.ValueOf(p1)
    rt := reflect.TypeOf(p1)
    //可以直接取想要的字段
    //reflect的type interface,FieldByName方法会返回字段信息以及是否有该字段;
      if f, ok := rt.FieldByName("Age"); ok {
          fmt.Printf("field:%+v,value:%+v\n", f.Name, rv.FieldByName("Age"))
      }

判断一个变量的类型:

      rv := reflect.ValueOf(p1)
      rt := reflect.TypeOf(p1)
      fmt.Printf("kind is %+v\n", rt.Kind())
      fmt.Printf("kind is %+v\n", rv.Kind())

type和value的Kind()方法都可以返回该变量的类型,不过若取得value后发现其是一个零值,那么会返回Kind为Invalidreflect的Kind一共有27种类型,基本揽括了所有golang中的类型

const (
      Invalid Kind = iota
      Bool
      Int
      Int8
      Int16
      Int32
      Int64
      Uint
      Uint8
      Uint16
      Uint32
      Uint64
      Uintptr
      Float32
      Float64
      Complex64
      Complex128
      Array
      Chan
      Func
      Interface
      Map
      Ptr
      Slice
      String
      Struct
      UnsafePointer
  )

*T有方法Add

  type T struct{}

  func (t *T) Add(a, b int) {
      fmt.Printf("a + b is %+v\n", a+b)
  }

动态调用

     funcName := "Add"
     typeT := &T{}
     a := reflect.ValueOf(1)
     b := reflect.ValueOf(2)
     in := []reflect.Value{a, b}
     reflect.ValueOf(typeT).MethodByName(funcName).Call(in)

动态调用含返回值的方法

 func (t *T) AddRetErr(a, b int) (int, error) {
      if a+b < 10 {
          return a + b, errors.New("total lt 10")
      }
      return a + b, nil
  }

调用

      funcName = "AddRetErr"
      ret := reflect.ValueOf(typeT).MethodByName(funcName).Call(in)
      fmt.Printf("ret is %+v\n", ret)
      for i := 0; i < len(ret); i++ {
          fmt.Printf("ret index:%+v, type:%+v, value:%+v\n", i, ret[i].Kind(), ret[i].Interface())
      }

这里的ret[i].Kind(),若非基础类型,会得到interface,如果err不是nil,

    if v, ok := ret[1].Interface().(error); ok {
          fmt.Printf("v is %+v\n", v)
      }

类型断言会成功,可以用这种方式去判断返回的error是否为空

通过反射修改值
不是所有的反射值都可以修改。对于一个反射值是否可以修改,可以通过CanSet()进行检查。

要修改值,必须满足:

  • 可以寻址
  • 可寻址的类型:
    • 指针指向的具体元素
    • slice的元素
    • 结构体指针的字段
    • 数组指针的元素

1.指针指向的具体元素
需要三步:
取地址:v := reflect.ValueOf(&x)
判断v.Elem()是否可以设值
给v.Elem()设置具体值

      ta := 10
      vta := reflect.ValueOf(&ta)
      if vta.Elem().CanSet() {
          vta.Elem().Set(reflect.ValueOf(11))
      }
      fmt.Println("cant set")
      fmt.Printf("vta is :%+v\n", vta.Elem())

2.slice中的元素

    ts := []int{1, 2, 3}
      tsV := reflect.ValueOf(ts)
      if tsV.Index(0).CanSet() {
          tsV.Index(0).Set(reflect.ValueOf(10))
      }
      fmt.Printf("ts is %+v\n", ts)
      //输出:ts is [10 2 3]

3.结构体指针的字段

      t1 := TagTest{}
      tV := reflect.ValueOf(t)
      
      //结构体指针
      t1V := reflect.ValueOf(&t1)
      
      fmt.Printf("tV:%+v\n", tV)
      for i := 0; i < tV.NumField(); i++ {
          val := tV.Field(i)
          t1V.Elem().Field(i).Set(val)
      }
      fmt.Printf("t1 is %+v\n", t1)

4.数组指针的元素

      tsA := [3]int{1, 2, 3}
      tsAv := reflect.ValueOf(&tsA)
      if tsAv.Elem().Index(0).CanSet() {
          tsAv.Elem().Index(0).Set(reflect.ValueOf(10))
      }
      fmt.Printf("tsA is %+v\n", tsA)
【Golang】Go语言中的反射原理解析与应用实战 反射(Reflection)是计算机科学中的一个重要概念,它允许程序在运行时检查变量和值,获取它们的类型信息,并且能够修改它们。 Go语言通过内置的reflect包提供了反射功能,使得开发者可以编写灵活的代码,处理各种不同类型的值,而不必在编译时就知道这些值的具体类型。 本文将结合实际案例,详细介绍Go语言中反射的基本概念、关键函数以及使用场景。 阅读详情

相关推荐

【机械臂算法】机械臂动力学参数辨识仿真

本文以puma560m机械臂为例子进行动力学参数辨识的讲解,puma560m可以在robotic toolbox中找到,这里以它真实机械臂对他的动力学参数进行辨识。此外这里还有要说的是,机械臂参数辨识其实是一个系统工程,其中和系统的机械、电子、嵌入式都有着很深的联系,并不是仿真这么简单的。

vrijheid的博客 7838

Golang 反射用法介绍

golang 反射内容大致总结,持续修改中...

qq_39122387的博客 1432

从C到汇编:单片机汇编语言核心概念迁移指南

本文详细介绍了从C语言迁移到单片机汇编语言的核心概念与实践指南,特别适合嵌入式开发者。文章深入探讨了汇编语言在性能优化、时序控制等方面的独特优势,并提供了变量存储、函数实现、流程控制等关键概念的对应关系,帮助开发者高效掌握汇编编程技巧。

weixin_31887307的博客 347

Golang的反射reflect深入理解和示例

编程语言中反射的概念 在计算机科学领域,反射是指一类应用,它们能够自描述和自控制。也就是说,这类应用通过采用某种机制来实现对自己行为的描述(self-representation)和监测(examination),并能根据自身行为的状态和结果,调整或修改应用所描述行为的状态和相关的语义。 每种语言的反射模型都不同,并且有些语言根本不支持反射。Golang语言实现了反射,反射机制就是在运行时动态的调用对象的方法和属性,官方自带的reflect包就是反射相关的,只要包含这个包就可以使用。 多插一句,Gol

papaya的博客 1931

从流量到凭证:实战解析SMB协议中的NTLMv2/v1哈希提取与破解

本文详细解析了SMB协议中NTLMv2/v1哈希的提取与破解实战技巧。从流量捕获、哈希格式处理到Hashcat高效破解,提供了一套完整的操作指南。文章特别强调了NTLMv2和NTLMv1哈希的区别及处理要点,并分享了防护建议与检测方法,帮助安全人员提升渗透测试效率。

weixin_30642561的博客 118

BLE天线设计避坑:NRF52832官方50欧姆阻抗匹配的3个常见误区

本文深入解析NRF52832蓝牙SoC天线设计中的常见误区,重点探讨50欧姆阻抗匹配的3个关键问题:PCB板材介电常数差异、匹配元件寄生参数及VSWR测试。通过实测数据和优化方案,帮助工程师规避设计陷阱,提升BLE通信性能。

beer8的博客 216

Go 反射终极指南:从基础到高级全方位解析

反射是一种让程序在运行时自省(introspect)和修改自身结构和行为的机制。虽然这听起来有点像“自我观察”,但实际上,反射在许多现代编程语言中都是一个非常强大和重要的工具。Go语言也不例外。在Go语言中,反射不仅能帮助你更深入地理解语言本身,而且还能极大地增加代码的灵活性和可维护性。反射(Reflection)在编程中通常被定义为在运行时检查程序的能力。这种能力使得一个程序能够操纵像变量、数据结构、方法和类型这样的对象的各种属性和行为。这一机制在Go中主要通过reflect标准库实现。

Lamb的博客 695

【2026OD新机考】【不定滑窗】20260415-API请求日志去重分析【Py/Java/C++/C/JS/Go六种语言OD真题】【欧弟算法】全网注释最详细分类最全的华子OD真题题解

中找到所有的连续的相同元素,同时统计对应的连续相同元素对应的值的平均值。:当右指针为第一个元素(没有前一个元素),或者右指针对应的元素。某微服务系统的日志监控平台需分析API调用记录,日志含大量。相等,说明当前元素仍然属于同一个相同元素窗口,将当前元素值。中的所有元素均相等,并且计算窗口和情况即可。请实现一个函数,对给定的日志数据去重合并处理。位置,表示开启一个新的窗口,此时需要重置窗口和。,为了优化重复的请求记录,存储和后续分析需对。:对应的响应时间数组(毫秒)。重新置为当前右指针的元素值。

weixin_48157259的博客 113

Go语言——反射

文章目录一、reflect-反射-浅析-重要Go语言中的类型反射第一定律:反射可以将“接口类型变量”转换为“反射类型对象”反射第二定律:反射可以将“反射类型对象”转换为“接口类型变量”反射第三定律:如果要修改“反射类型对象”其值必须是“可写的”结构体总结二、reflect-反射类型对象-获取类型信息1. TypeOf()和reflect.Type类型1.1 理解反射的类型(Type)与种类(Kind)1.1.1 反射种类(Kind)的定义1.1.2 从类型对象中获取类型名称和种类的例子1.2. refle

脑袋装水的博客 1566

【AI编程】Trae 环境搭建与配置从使用到项目实战详解

Trae 环境搭建与配置从使用到项目实战详解

congge 5652

搬运机器人系列编程:KUKA KR QUANTEC_(2).KUKA KRL语言编程基础

定义一个函数END!调用函数PRINT "结果: ", result!定义一个过程PRINT msgEND!调用过程printMessage("欢迎使用 KUKA 机器人")!定义搬运零件的起始位置和目标位置!定义焊接路径的各个点!定义喷涂路径的各个点KUKA Robot Language (KRL) 是一种专门为工业机器人设计的编程语言,具有结构化编程、丰富的库函数、实时性以及集成性等特点。

zhubeibei168的博客 1249

Go语言中的反射

序言 计算机中的反射是程序检查它自己的结构的能力,尤其是通过类型;它是元编程的一种形式。它也是一个主要的难点。 在这篇文章中,我们试图通过阐明 Go 语言中反射的工作原理的方式讲清楚反射。每门编程语言的反射模型都不同(而且很多语言根本不支持反射),这篇文章是关于 Go 的,因此在本文的剩余部分中,“反射”一词等价于“Go 中的反射”。 标题类型和接口 因为反射构建在类型系统之上,让我们从复习 Go 中的类型开始。 Go 是静态类型的。每个变量都有静态的类型,也就是说,在编译时每个变量都有已知并且确定的类型:

perror_0的博客 322

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

Golang深入浅出之-Go语言中的反射(reflect):原理与实战应用

反射是Go语言的双刃剑,虽然强大,但需谨慎使用。理解反射的原理,明确其在何时何地能带来价值,以及如何避免潜在问题,是每个Go程序员的必修课。在实际应用中,我们应尽量保持代码的静态类型,只在必要时才使用反射,以保持代码的清晰和高效。

zevjay的博客 627

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

Go 语言中的反射

至此,关于反射就聊到这里,一些关于源码的细节并没有详细说,更多的站在一个使用者的角度去看反射需要注意的点关于反射,大多的人是建议少用,因为是会影响到性能,不过如果不太关注这一点,那么用起来还是非常方便的高级功能自然也是双刃剑,你用不好就会 panic,如果你期望去使用他,那么就去更多的深入了解和一步一步的吃透他吧大道至简,反射三定律,活学活用。

m0_37322399的博客 492

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

go语言中的反射Reflect初探

我们先看看什么是反射,它有什么用。 我们先看卡wiki上关于反射的介绍。 链接为https://en.wikipedia.org/wiki/Reflection_(computer_programming) In computer science, reflection is the ability of a process to examine, introspect, and modify i...

russle的专栏 710

Go语言中的反射:原理与应用

反射是Go语言中一种强大的特性,它允许程序在运行时检查和操作类型、变量和函数。虽然反射操作的性能比直接代码慢,但在很多场景下,它是实现通用功能的唯一方法。通过合理使用反射,我们可以创建更加灵活和通用的代码,例如序列化/反序列化库、ORM框架等。在使用反射时,应该注意性能问题,并遵循最佳实践,确保代码的可读性和可维护性。反射是一把双刃剑,正确使用它可以提高代码的灵活性和通用性,滥用则会导致代码难以理解和维护。因此,在使用反射时,应该权衡利弊,做出明智的选择。

好代码自己会说话,烂代码才需要注释来圆谎 3982

【Go】Go语言中的反射(reflection)

Go语言中的反射(reflection)是指在运行时动态地对程序的类型进行检查、访问和操作的能力。通过反射,我们可以在不知道具体类型的情况下,获取类型的信息,调用其方法,修改其字段值等。方法修改字符串类型的值时,要求传入的参数必须是可寻址的,而不是一个无法获取地址的值。的一部分,而结构体字段默认是不可寻址的。对象的可寻址值,然后再进行字段修改操作。方法来获取字段的可寻址值,然后再调用。要解决这个问题,可以使用。在上述代码中,我们使用。

Fanjufei的博客 402

59.笔记go语言——go语言中的反射

59.笔记go语言——go语言中的反射 每个语言都有反射,Go语言也是。 Go语言是静态类型,所有数据的类型在编译器都是明确的 反射例子一 代码如下: packagemain   import(     "fmt"     "reflect" )   funcmain(){     varxfloat64=3.4     fmt.Println("type:",refl

badman250的专栏 2870

Go 语言反射机制:在框架开发中的灵活应用​​​​

Go 语言的反射机制为框架开发提供了强大而灵活的工具,通过在运行时检查和操作对象类型与值,能够实现通用的配置解析、插件加载、数据验证等功能,极大地提升了框架的通用性、扩展性和动态灵活性。然而,反射机制也带来了性能开销、代码复杂性增加和类型安全等挑战。在实际的框架开发中,我们需要根据具体的需求和场景,合理使用反射机制,权衡其优势与不足。随着 Go 语言生态的不断发展,未来可能会出现更多基于反射机制的优秀框架和工具,同时也可能会有更多优化反射性能、降低使用难度的技术和方法出现。

数字魔方操控师的博客 921

Go语言中的反射与接口:从原理到实践

接口是一种类型,它定义了一组方法签名,任何类型只要实现了这些方法,就被认为实现了该接口。// 定义接口// 实现接口fmt.Println(a.Speak()) // 输出: Woof!fmt.Println(a.Speak()) // 输出: Meow!反射是指程序在运行时检查和操作类型的能力。Go语言的reflect包提供了反射功能。

好代码自己会说话,烂代码才需要注释来圆谎 4272

go在方法中修改结构体的值_Go语言中的反射机制

反射是指在程序执行期间进行代码的访问和修改的1、变量的内在机制var arr [10]int = {1,2,3,5} 见代码的定义和赋值信息,定义一个变量包含两个部分: 类型信息:是静态的元信息,是预先定义好的。 值信息:是程序运行过程中动态改变的。2、Go语言中的反射Go语言总使用reflect包封装了反射相关的方法:获取类型信息:reflect.TypeOf,是静态的。获取值信息:reflec...

weixin_29084231的博客 651

go语言中的反射的使用

<br />今天尝试了一下使用go语言中的反射来将struct类型转换成xml,结果相当纠结。首先去看了一下go的reflect包的实现,根据go的规则,首先应该去看一个NewXXX的方法,结果发现了一个叫NewValue的方法,通过这个方法我们能够得到一个Value接口。另外我们还应该注意到,go的反映实现中将Type和Value分开了,于是还有另外一个接口Type.<br />type Value interface { // Type returns the value's type.

无心云 8923
上一篇: go语言中的接口
下一篇: rust安装 -自定义安装路径和GCC安装
过去日记
过去日记 新星创作者: 后端开发技术领域 新星创作者: 后端开发技术领域
博客等级 码龄3年 5343粉丝 225原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

过去日记

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值