GO基础进阶篇 (十二)、反射

什么是反射

Go语言中的反射是指在程序运行时检查程序的结构,比如变量的类型、方法的签名等。Go语言的反射包是reflect。通过反射,你可以动态地检查类型信息、获取字段和方法、调用方法等。

反射可以在运行时动态获取变量的各种信息,比如变量的类型、值等
如果是结构体,还可以获取到结构体本身的各种信息,比如结构体的字段、方法
通过反射,还可以修改变量的值、调用方法
使用反射,需要引入一个包:reflect,它定义了内个重要的类型 Type 和 Value 任意接口值在反射中都可以理解为由 reflect.Type 和 reflect.Value两部分组成,并且reflect 包提供了 reflect.Typ!alue和Type.Of和 reflect.ValueOf 两个函数来获取任意对象的 V
(Type)和种类(Kind)的区别。编程中,使用最多的是类型,但在反射中,当需要区分一个品种的类型时在使用反射时,需要首先理解类型.就会用到种类(Kind)。

package main

import (
	"fmt"
	"reflect"
)

func main() {
	var a float64 = 3.14
	fmt.Println(reflect.TypeOf(a))
	fmt.Println(reflect.ValueOf(a))

	v := reflect.ValueOf(a)

	if v.Kind() == reflect.Float64 {
		fmt.Println("v.kind() is float64")
	}

	fmt.Println(v.Type())
	fmt.Println(v.Float())
}

1.反射的应用场景

需要反射的2个常见场景:

  • 1.有时你需要编写一个函数,但是并不知道传给你的参数多型是什么,可能是没约定好;也可能是传入的类型很多,这时反射就会用的上了。
  • 2.有时候需要根据某些条件决定调用哪个函数,比如根据户的输入来决定。这时就需要对函数和函数的参数进行反函数。

但是对于反射,还是有几点不太建议使用反射的理由:

  • 1、与反射相关的代码,经常是难以阅读的。在软件工程中代码可读性也是一个非常重要的指标。
  • 2、Go语言作为一门静态语言,编码过程中,编译器能提发现一些类型错误,但是对于反射代码是无能为力的。所可能会运行很久,才会出错,这时候经常是直接panic,可会造成严重的后果。
  • 3、反射对性能影响还是比较大的,比正常代码运行速度慢一到两个数量级。所以,对于一个项目中处于运行效率关钱位置的代码,尽量避免使用反射特性。

2. 反射获取变量信息

package main

import (
	"fmt"
	"reflect"
)

type User struct {
	Name string
	Age  int
	Sex  string
}

func (user User) Say(msg string) {
	fmt.Println("user说", msg)
}
func main() {
	user := User{Name: "xiaolong", Age: 18, Sex: "男"}
	reflectGetInfo(user)
}

func reflectGetInfo(input interface{}) {

	//获取参数类型
	getType := reflect.TypeOf(input)
	fmt.Println("type is", getType.Name())
	fmt.Println("kind is", getType.Kind())

	//获取值
	getValue := reflect.ValueOf(input)
	fmt.Println("getValue is", getValue)

	
	//通过反射获取反射结果内的字段与方法
	for i := 0; i < getType.NumField(); i++ {
		filed := getType.Field(i)
		value := getValue.Field(i).Interface()
		fmt.Println(filed.Name, filed.Type, value)
	}

	for i := 0; i < getType.NumMethod(); i++ {
		method := getType.Method(i)
		fmt.Println(method.Name, method.Type)
	}
}

3.反射修改对象的值

package main

import (
	"fmt"
	"reflect"
)

func main() {
	var num float64 = 1.23

	point := reflect.ValueOf(&num) //想要通过指针修改值,需要传入指针
	newValue := point.Elem()
	fmt.Println(newValue.Kind())
	fmt.Println(newValue.Type())
	fmt.Println(newValue.CanSet())

	if newValue.CanSet() {
		newValue.SetFloat(3.14)
	}
	fmt.Println(num)
}

修改结构体

package main

import (
	"fmt"
	"reflect"
)

type User struct {
	Name string
	Age  int
	Sex  string
}

func (user User) Say(msg string) {
	fmt.Println("user说", msg)
}
func main() {
	user := User{Name: "xiaolong", Age: 18, Sex: "男"}
	fmt.Println(user)
	newValue := reflect.ValueOf(&user)

	if newValue.Kind() == reflect.Ptr { //首先是指针
		valueTemp := newValue.Elem()
		if valueTemp.CanSet() {
			valueTemp.FieldByName("Name").SetString("dalong")
			valueTemp.FieldByName("Age").SetInt(38)
		}
	}
	fmt.Println(user)
}

4.反射调用方法函数

方法调用

package main

import (
	"fmt"
	"reflect"
)

type User struct {
	Name string
	Age  int
	Sex  string
}

func (user User) Say(msg string) {
	fmt.Println("user说", msg)
}

func (user User) GetInfo() {
	fmt.Println("user 信息", user.Name, user.Age)
}

func main() {
	user := User{Name: "xiaolong", Age: 18, Sex: "男"}
	//reflectGetInfo(user)
	fmt.Println(user)

	//无参数方法调用
	value := reflect.ValueOf(user)
	value.MethodByName("GetInfo").Call(nil)

	//有参数方法调用
	args := make([]reflect.Value, 1)
	args[0] = reflect.ValueOf("反射调用")
	value.MethodByName("Say").Call(args)
}

函数调用

package main

import (
	"fmt"
	"reflect"
)

func main() {
	v1 := reflect.ValueOf(func1)
	v1.Call(nil)

	v2 := reflect.ValueOf(func2)
	args := make([]reflect.Value, 2)
	args[0] = reflect.ValueOf(1)
	args[1] = reflect.ValueOf("gagaga")
	v2.Call(args)

	v3 := reflect.ValueOf(func3)
	args1 := make([]reflect.Value, 2)
	args1[0] = reflect.ValueOf(1)
	args1[1] = reflect.ValueOf("gagaga")
	res := v3.Call(args1)
	fmt.Println(res[0].Interface())
}

func func1() {
	fmt.Println("无参数fun1")
}
func func2(i int, s string) {
	fmt.Println("有参数fun2", i, s)
}
func func3(i int, s string) string {
	fmt.Println("有参数fun3", i, s)
	return s
}

你可能感兴趣的:(GO语言从基础到应用,golang,开发语言,后端)