您现在的位置是:首页 > 正文

Golang 学习二十(反射)

2024-02-01 04:00:15阅读 2


变量 ---> 一个值,一个类型,值类型
数组 ---> 相同类型,多个值,长度固定,值类型
切片 ---> 相同类型,多个值,长度动态,引用类型,有序
指针 ---> 地址,引用类型
映射 ---> k-v 按名字取值,无序,相同类型,多个值,长度动态
结构体 ---> 多个值,类型多样,字段(属性)
接口 ---> 接受任意类型的定义

反射概念:运行时动态的获取变量的相关信息。

import ("reflect")

1. reflect

  1. reflect.TypeOf获取变量类型,返回 reflect.Type 类型
  2. reflect.ValueOf获取变量的值,返回 reflect.Value 类型
  3. reflect.Value.Kind获取变量的类别,返回一个常量
  4. reflect.Value.Interface()转换成 interface{} 类型

1650296396314

int ---> interface ---> reflect.value ---> string

示例:获取变量类型

package main

import (
    "fmt"
    "reflect"
)

func test(i interface{}) {
    // 反射获取值类型
    t := reflect.TypeOf(i)
    fmt.Println(t)
    // 反射获取值
    v := reflect.ValueOf(i)
    fmt.Println(v)
}

func main() {
    var a int = 10
    test(a)
}

/*
int
10
*/

示例:类型和类别

package main

import (
    "fmt"
    "reflect"
)

type Student struct {
    Name string
    Age int
    Score float32
}

func test(i interface{}) {
    // 反射获取类型
    t := reflect.TypeOf(i)
    fmt.Println("类型:",t)
    // 反射获取类别
    v := reflect.ValueOf(i)
    k := v.Kind()
    fmt.Println("类别:",k)
}

func main() {
    var stu Student = Student {
        Name: "zhangsan",
        Age: 19,
        Score: 88,
    }
    test(stu)
}

/*
类型: main.Student
类别: struct
*/ 

示例:断言处理类型转化

package main

import (
    "fmt"
    "reflect"
)

type Student struct {
    Name string
    Age int
    Score float32
}

func test(i interface{}) {
    // 反射获取类型
    t := reflect.TypeOf(i)
    fmt.Println("类型:",t)
    // 反射获取类别
    v := reflect.ValueOf(i)
    k := v.Kind()
    fmt.Println("类别:",k)
    
    // 转换成接口类型
    iv := v.Interface()
    // 断言
    stu,ok := iv.(Student)
    if ok {
        fmt.Printf("结构:%v 类型:%T\n",stu,stu)
    }
}

func main() {
    var stu Student = Student {
        Name: "zhangsan",
        Age: 19,
        Score: 88,
    }
    test(stu)
}

/*
类型: main.Student
类别: struct
结构:{zhangsan 19 88} 类型:main.Student
*/

2. ValueOf

获取变量值

reflect.ValueOf(x).Float()
reflect.ValueOf(x).Int()
reflect.ValueOf(x).String()
reflect.ValueOf(x).Bool()

示例:类型转换

package main

import (
    "fmt"
    "reflect"
)

func testInt(b interface{}) {
    val := reflect.ValueOf(b)
    fmt.Printf("val 类型:%T\n",val)
    // 转换成 int 类型,其他类型都可以转化成 string
    a := val.Int()
    fmt.Printf("a 类型:%T\n",a)
}

func main() {
    testInt(100)
}

/*
val 类型:reflect.Value
a 类型:int64
*/

3. Value.Set

设置变量值

reflect.Value.SetFloat()	#设置浮点数
reflect.Value.SetInt()		#设置整数
reflect.Value.SetString()	#设置字符串

示例:思考为何会报错

package main

import (
    "fmt"
    "reflect"
)

func testInt(b interface{}) {
    val := reflect.ValueOf(b)
    // 更改值
    val.SetInt(100)
    c := val.Int()
    fmt.Printf("类型:%T,值:%d\n",c,c)
}

func main() {
    var i = 10
    testInt(i)
    fmt.Println(i)
}

/*
panic: reflect: reflect.Value.SetInt using unaddressable value

goroutine 1 [running]:
......
*/

修改后

package main

import (
    "fmt"
    "reflect"
)

func testInt(b interface{}) {
    val := reflect.ValueOf(b)
    // 更改值需要value的地址,否则会崩溃,Elem() 表示 *
    val.Elem().SetInt(100)
    c := val.Elem().Int()
    fmt.Printf("类型:%T,值:%d\n",c,c)
}

func main() {
    var i = 10
    testInt(&i)
    fmt.Println(i)
}

/*
类型:int64,值:100
100
*/

4. 结构体反射

示例:反射出结构体属性和方法数量

package main

import (
    "fmt"
    "reflect"
)

type Student struct {
    Name string
    Age int
    Score float32
}

func (s Student) Sleep() {
    fmt.Println("正在睡觉")
}

func (s Student) Run(min interface{}) {
    fmt.Printf("跑步%d分钟",min)
}

// 断言结构体
func Test(object interface{}) {
    // 获取值
    v := reflect.ValueOf(object)
    // 获取类别
    c := v.Kind()
    // 判断类别是否为结构体
    if c != reflect.Struct {
        fmt.Println("expect struct")
        return
    }
    // 获取结构体字段数量
    num := v.NumField()
    fmt.Println("字段数量:",num)
    // 获取结构体方法数量
    numOfMethod := v.NumMethod()
    fmt.Println("方法数量:",numOfMethod)
}

func main() {
    var stu Student = Student {
        Name: "zhangsan",
        Age: 18,
        Score: 90,
    }
    Test(stu)
}

/*
字段数量: 3
方法数量: 2
*/

5. 函数反射

示例:Go 中函数可以赋值给变量。

package main

import "fmt"

func Hello() {
    fmt.Println("hello world")
}

func main() {
    // 函数可以赋值给变量
    a := Hello
    a()
}

/*
hello world
*/

示例:既然函数可以像普通的类型变量一样,那么在反射机制中就和不同的变量是一样,在反射中函数和方法的类型 (Type) 都是 reflect.Func,如果要调用函数,通过 Value 的 Call() 方法

package main

import (
    "fmt"
    "reflect"
)

func Hello() {
    fmt.Println("hello world")
}

func main() {
    // 反射获取 hello
    v := reflect.ValueOf(Hello)
    fmt.Printf("类型:%T\n",v)
    // 判断 hello 的类型
    if v.Kind() == reflect.Func {
        fmt.Println("OK")
    }
    // 反射调用函数
    v.Call(nil)
}

/*
类型:reflect.Value
OK
hello world
*/

Value 的 Call() 方法的参数是一个 Value 的 slice,对应的反射函数类型的参数,返回值也是一个 Value 的 slice,同样对应反射函数类型的返回值。

package main

import (
    "fmt"
    "reflect"
    "strconv"
)

func prints(i int) string {
    fmt.Println("i=",i)
    return strconv.Itoa(i)
}

func main() {
    fv := reflect.ValueOf(prints)
    // 定义参数切片
    params := make([]reflect.Value,1)
    // 参数为 20
    params[0] = reflect.ValueOf(20)
    // 反射函数,获取处理结果
    result := fv.Call(params)
    fmt.Printf("result 类型:%T\n",result)
    fmt.Printf("result 转换后类型:%T,值是:%s\n",result[0].Interface().(string),result[0].Interface().(string))
}

/*
i= 20
result 类型:[]reflect.Value
result 转换后类型:string,值是:20
*/

6. 方法反射

反射中方法的调用。函数和方法可以说其实本质上是相同的,只不过方法与一个对象进行了绑定,方法是对象的一种行为,这种行为是对于这个对象的一系列操作,例如修改对象的某个属性。

package main

import (
    "fmt"
    "reflect"
    "strconv"
)

type Student struct {
    age int
    name string
}

func (s *Student) SetAge(i int) {
    s.age = i
}

func (s *Student) SetName(name string) {
    s.name = name
}

func (s *Student) String() string {
    return fmt.Sprintf("%p",s) + "--name:" + s.name + " age:" + strconv.Itoa(s.age)
}

func main() {
    // 实例化
    stu := &Student{22,"zhangsan"}
    // 反射获取值,指针方式
    stuV := reflect.ValueOf(&stu).Elem()
    // 也可以使用非指针(值类型) stuV := reflect.ValueOf(stu)
    fmt.Println("Before:",stuV.MethodByName("String").Call(nil)[0])
    
    // 修改值
    params := make([]reflect.Value,1)
    params[0] = reflect.ValueOf(18)
    stuV.MethodByName("SetAge").Call(params)
    
    params[0] = reflect.ValueOf("lisi")
    stuV.MethodByName("SetName").Call(params)
    
    fmt.Println("After:",stuV.MethodByName("String").Call(nil)[0])
}

/*
Before: 0xc000004078--name:zhangsan age:22
After: 0xc000004078--name:lisi age:18
*/

示例:使用 Method 方式

package main

import (
    "fmt"
    "reflect"
    "strconv"
)

type Student struct {
    age int
    name string
}

func (s *Student) SetAge(i int) {
    s.age = i
}

func (s *Student) SetName(name string) {
    s.name = name
}

func (s *Student) String() string {
    return fmt.Sprintf("%p",s) + "--name:" + s.name + " age:" + strconv.Itoa(s.age)
}

func main() {
    // 实例化
    stu := &Student{22,"zhangsan"}
    stuV := reflect.ValueOf(&stu).Elem()
    // 方式二
    fmt.Println("Before:",stuV.Method(2).Call(nil)[0])
    // 索引大小取决于方法名称的 ASCII 大小
    params := make([]reflect.Value,1)
    params[0] = reflect.ValueOf(18)
    stuV.Method(0).Call(params)
    params[0] = reflect.ValueOf("lisi")
    stuV.Method(1).Call(params)
    fmt.Println("After:",stuV.Method(2).Call(nil)[0])
}

/*
Before: 0xc000004078--name:zhangsan age:22
After: 0xc000004078--name:lisi age:18
*/


网站文章

  • 【系统性学习】《Mysql基础》【Mysql触发器 新建触发器、修改触发器、删除触发器、举例】 编程入门 学习分享 【公开免费】 热门推荐

    新建触发器: 格式:create trigger 数据库名.触发器名称 触发时刻 insert on 表名 for each........

    2024-02-01 04:00:08
  • Redis 分布式锁如何实现自动续期

    Redis 分布式锁如何实现自动续期

    如果锁当前还是被占用的,那么等待释放锁的消息,具体实现使用了信号量 Semaphore 来阻塞线程,当锁释放并发布释放锁的消息后,信号量的 release() 方法会被调用,此时被信号量阻塞的等待队列...

    2024-02-01 04:00:01
  • C. Palindromic Matrix

    http://codeforces.com/contest/1118/problem/C 题意:给n(0-20),和n*n数组,构造n*n回文数组,行和列调换不变 思路:n为偶时,必然每个数时4的倍数;奇时个数为奇数的一个是最中间,中间行和列两两构造,剩下的四个四个构造; #include<algorithm> #include<set> #include<...

    2024-02-01 03:59:33
  • Windows上利用Python自动切换代理IP的终极方案!

    Windows上利用Python自动切换代理IP的终极方案!

    声明下:不同于网络上千百篇方法,下文是经过各种严格测试都通过的,同时也是一个实验的过程,排除了各种不靠谱的方法。有需要的可以评论来讨论,想要源码和相关参考文献或笔记的,也可以找我。 思路及启发 先说一下我这一路实验的思路吧,这个至关重要。 之前一直在用Python做爬虫抓取数据,发现本机IP的问题不解决,爬虫相当于白费了。然后各种百度,不...

    2024-02-01 03:59:25
  • 3-java安全基础——jdk动态代理

    3-java安全基础——jdk动态代理

    静态代理 什么是静态代理,假设现在有这样一个需求:要求在所有类的addUser方法前后添加打印日志。那么如何在不修改源代码的情况下完成需求? 通常做法是:为每一个目标类创建一个代理类,并让目标类和代理...

    2024-02-01 03:59:17
  • 关于计算机和人物的英语短文,2016年12月英语四级作文范文50例:人脑和电脑

    关于计算机和人物的英语短文,2016年12月英语四级作文范文50例:人脑和电脑

    2016年12月英语四级作文范文50例:人脑和电脑Directions: Write a composition entitled The Brain and the Computer. Yousho...

    2024-02-01 03:58:47
  • 解决vue 路由子组件created和mounted不起作用问题

    解决vue 路由子组件created和mounted不起作用问题判断项目是否启用`keep-alive`启用未启用 判断项目是否启用keep-alive 启用 使用exclude排除组件(我没有成功不...

    2024-02-01 03:58:40
  • hybris笔记

    1.localextensions.xml的extension可以用dir定义也可以name定义,用name定义的话,编译的时候会去找extensioninfo.xml2.更改了items.xml的话,需要重新 ant  build initialize...

    2024-02-01 03:57:59
  • SVN命令使用详解

    标签: it分类: 服务器运维1、检出svn  co  http://路径(目录或文件的全路径) [本地目录全路径]  --username 用户名 --password 密码svn  co  svn://路径(目录或文件的全路径) [本地目录全路径]  --username 用户名 --password 密码svn  checkout 

    2024-02-01 03:57:52
  • WebSocket介绍和Socket的区别

    WebSocket介绍和Socket的区别

    WebSocket介绍与原理 WebSocket protocol 是HTML5一种新的协议。它实现了浏览器与服务器全双工通信(full-duplex)。一开始的握手需要借助HTTP请求完成。 ——百...

    2024-02-01 03:57:48