Go 反射

获取类型和值

  • 之前讲过接口nil不一定等于空接口,因为一个 interface 底层 由 type + value 构成,只有 typevalue 都匹配,才能 ==
  • reflect.VlaueOf 就是用来获取具体的 reflect.Value
  • reflect.TypeOf 用来获取具体的 reflect.Type
func main() {
	var (
		a *A
		b interface{}
	)
	fmt.Println(a)
	if b == nil {
		fmt.Println("b is nil")
	}
	fmt.Println(reflect.TypeOf(b), reflect.ValueOf(b))
	fmt.Println(reflect.TypeOf(a), reflect.ValueOf(a))
	b = a
	if b == nil {
		fmt.Println("b is nil")
	} else {
		fmt.Printf("current b is %v \n", b)
		fmt.Println("b not eq nil")
	}
	fmt.Println(reflect.TypeOf(b), reflect.ValueOf(b))
}

image.png
上面的代码说明了,刚开始的空接口 == nil,后来的接口为啥不等于 nil,因为 type变了,虽然value 还是 nil

获取属性的类型和值

  • 通过 reflect.Value 或者 reflect.TypeNumField 获取属性数量
  • 通过 reflect.TypeField 方法 获取属性相关信息
  • 通过 reflect.ValueField 方法 获取值相关信息
package main

import (
	"fmt"
	"reflect"
)

type A struct {
	Name string
	Age  int
}

func main() {
	var a A
	getType := reflect.TypeOf(a)
	getValue := reflect.ValueOf(a)
	fmt.Println("field num", getType.NumField())
	for i := 0; i < getType.NumField(); i++ {
		field := getType.Field(i)
		value := getValue.Field(i)
		fmt.Println("field name is", field.Name, "field value is", value.Interface())
	}
}

image.png

通过反射修改值

  • 通过获取value的反射对象即可,reflect.ValueOf 传入的必须是指针类型,只有原始反射对象可以进行修改,可以通过 reflect.ValueElem 方法取得
  • 通过 reflect.ValueCanset 方法来判断是否可以设置
  • 通过 Set... 系列方法来设置具体类型的值
package main

import (
	"fmt"
	"reflect"
)

type A struct {
	Name string
	Age  int
}

func main() {
	a := A{
		Name: "old name",
	}

	valueOfA := reflect.ValueOf(&a).Elem()

	nameField := valueOfA.Field(0)

	if nameField.CanSet() {
		nameField.SetString("new name")
	} else {
		fmt.Println("don't set")
	}
	
	fmt.Println("new value", a.Name)
}

  • 因为调用 set... 设置值,需要知道类型,可以通过 reflect.Typekind 方法获取原始类型
    • 再通过 switch 去匹配类型来调用具体的 set... 方法
package main

import (
	"fmt"
	"reflect"
)

type A struct {
	Name string
	Age  int
}

func main() {
	a := A{
		Name: "old name",
	}

	fmt.Println("old value", a.Name)
	valueOfA := reflect.ValueOf(&a).Elem()
	getType := reflect.TypeOf(a)
	field := getType.Field(0)

	nameField := valueOfA.Field(0)

	if nameField.CanSet() {
		switch field.Type.Kind() {
		case reflect.String:
			fmt.Println("string")
			nameField.SetString("new value")
		}
	} else {
		fmt.Println("don't set")
	}

	fmt.Println("new value", a.Name)
}

image.png

获取方法的名称和类型

  • 先通过 reflect.TypeNumMethod 方法获取方法数量
  • 在通过 reflect.TypeMethod 方法获取到具体的方法信息 reflect.Method
package main

import (
	"fmt"
	"reflect"
)

type A struct {
	Name string
	Age  int
}

func (receiver *A) SetName(name string) {
	receiver.Name = name
}

func (receiver *A) SetAge(age int) {
	receiver.Age = age
}

func main() {
	var a A
	//有方法是依赖指针的所以需要传指针
	getType := reflect.TypeOf(&a)
	num := getType.NumMethod()
	for i := 0; i < num; i++ {
		method := getType.Method(i)
		fmt.Println("method name:", method.Name, "method type:", method.Type)
	}
}

image.png

调用方法

  • 通过 reflect.MethodCall 方法即可调用反射对象的方法
    • Call 中 接收的参数为 reflect.Value 的切片
    • 如果反射对象的方法不需要参数,传一个 reflect.Value 的空切片即可
    • 如果反射对象需参数,那么需要由反射对像参数的 reflect.Value 组成切片,传入 Call 完成调用
package main

import (
	"fmt"
	"reflect"
)

type A struct {
	Name string
	Age  int
}

type Body struct {
	Like string
	Desc string
}

func (a A) Pr() {
	fmt.Println("A pr")
}

func (a A) Talk(b Body) {
	fmt.Printf("Like:%s,Desc:%s", b.Like, b.Desc)
}

func main() {
	var a A
	getType := reflect.ValueOf(a)
	pr := getType.Method(0)
	//不需要参数
	pr.Call([]reflect.Value{})
	b := Body{
		Like: "i'm like",
		Desc: "i'm desc",
	}
	talk := getType.Method(1)
	//Talk 需要传入 Body struct, 所以反射调用,需要传入 由 Body的 reflect.Value 组成切片参数
	talk.Call([]reflect.Value{
		reflect.ValueOf(b),
	})
}

image.png

反射的缺点

  • 反射慢
    • 不管什么编程语言,反射都慢
    • 反射实现里有对 reflect.kind 大量的枚举 + 类型转换 等操作
    • reflect.Value 不能复用,每次都是返回一个新的值,其中 typ 还是指针类型,涉及对指针的频繁分配,GC

image.png
image.png
image.png

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在 Go 语言中,反射是一种机制,可以在程序运行时动态获取变量的类型信息和值信息,并可以对其进行修改。反射可以使代码更加灵活,但也会带来一定的性能损失。 Go 语言中的反射主要通过 `reflect` 包来实现。`reflect` 包提供了两个重要的类型:`Type` 和 `Value`,分别表示类型信息和值信息。通过 `Type` 和 `Value`,我们可以获取变量的类型信息和值信息,并可以对其进行修改。 下面是一个使用反射获取变量类型和值的示例代码: ```go package main import ( "fmt" "reflect" ) func main() { var x float64 = 3.14 fmt.Println("type:", reflect.TypeOf(x)) fmt.Println("value:", reflect.ValueOf(x)) } ``` 在上面的示例代码中,我们定义了一个 `float64` 类型的变量 `x`,然后使用 `reflect.TypeOf()` 和 `reflect.ValueOf()` 函数分别获取变量的类型信息和值信息,并打印出来。由于 `reflect.TypeOf(x)` 返回的是一个 `reflect.Type` 类型的值,因此我们需要使用 `fmt.Println()` 函数来打印其名称。而 `reflect.ValueOf(x)` 返回的是一个 `reflect.Value` 类型的值,因此我们可以直接使用 `%v` 格式化符号来打印其值。 除了获取变量的类型信息和值信息之外,反射还可以用于动态调用函数和修改变量的值。例如,通过 `Value` 类型的 `Elem()` 方法可以获取指向变量的指针,并使用 `Set()` 方法来修改变量的值。 下面是一个使用反射动态修改变量值的示例代码: ```go package main import ( "fmt" "reflect" ) func main() { var x float64 = 3.14 v := reflect.ValueOf(&x).Elem() v.SetFloat(6.28) fmt.Println(x) } ``` 在上面的示例代码中,我们首先定义了一个 `float64` 类型的变量 `x`,然后使用 `reflect.ValueOf(&x).Elem()` 获取指向变量的指针,并使用 `SetFloat()` 方法将其值修改为 `6.28`。最后,我们打印出变量 `x` 的值,可以看到其值已经被修改为 `6.28`。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值