Go语言如何利用反射机制 动态调用结构体中的方法和属性

2024-05-14 19:19:43 浏览数 (1)

相信做个PHP的同学,在很多时候都使用过如下的方式去调用一个类中的方法,或者某个属性。

代码语言:php复制
$method = "show";
$object = new A();
$object->$method();

在PHP中使用这种写法是完全没有问题的,但是做Go这种静态语言中,在编译阶段就会发生错误,因此在Go语言中不能使用该方式调用,而需要反射机制来实现。

在实际的项目开发中,很多时候我们要实现某种功能,可能需要对接不同的平台,每个平台的接口肯定是不同的。但为了方便系统的维护、扩展。都会把不同平台的实现方式封装成一个扩展,然后在调用时通过一个工厂类去处理调用具体的扩展,只要保证每一个扩展中的返回参数格式一致就可以了。至于每一个扩展具体是怎么实现的,调用方根本不用关心,只需要关心入参和出参即可

为了保持每一个扩展中的返回参数方法,格式都保持一致,后期易于扩展。一般我们会封装一个接口,几口定义好提供给外部的方法,方法的接收参数和返回参数。如下示例代码:

代码语言:php复制
interface A
{
    public function show(): string;
    public function run(): array;
}

class B implements A
{
    public function show(): string
    {
        return __CLASS__ . __METHOD__ . __LINE__;
    }

    public function run(): array
    {
        return [
            "class" => __CLASS__,
            "method" => __METHOD__,
            "line" => __LINE__,
        ];
    }
}


class C implements A
{
    public function show(): string
    {
        return __CLASS__ . __METHOD__ . __LINE__;
    }

    public function run(): array
    {
        return [
            "class" => __CLASS__,
            "method" => __METHOD__,
            "line" => __LINE__,
        ];
    }
}


class D
{
    public function print(A $a, string $method)
    {
        print_r($a->$method());
    }
}

$d = new D;
// 动态设置调用的类以及类下的方法
$d->print(new B, "run");

看完上面的代码,在回过头去考虑在Go语言是否可以这样实现呢?

在Go语言中,要实现这样的操作,可以采用这样的思路,但是在调用的地方就不能这么写。因为Go语言属于编译型语言,发现找不到对应的方法,就会编译不通过。

因此,Go语言提供了一种机制在运行时更新变量和检查他们的值,调用它们的方法,但是在编译时并不知道这些变量的具体类型,这称为反射机制。

使用上面PHP的代码,我们用Go语言进行实现一次。

代码语言:go复制
package main

import (
	"fmt"
	"reflect"
)

// 使用interface限定参数类型,动态调用struct中的方法、方法

type A1 interface {
	Show1(name string)
}

type B1 struct {
	Name1   string
	Age1    uint
	Sex1    string
	IsOver1 bool
}

func (b1 B1) Show1(name string) {
	fmt.Println(reflect.TypeOf(b1), name)
}

func (b1 B1) Run1() {
	fmt.Print("b1->Run1()")
}

type C1 struct {
}

func (c1 C1) Show1(name string) {
	fmt.Println(reflect.TypeOf(c1))
}

type D1 struct {
}

func (d1 D1) Run1(name string) {
	fmt.Println(reflect.TypeOf(d1))
}

func printShow(a A1, menthod string, args ...interface{}) {
	inputs := make([]reflect.Value, len(args))
	for i, _ := range args {
		inputs[i] = reflect.ValueOf(args[i])
	}
	// 动态调用struct中的方法
	reflect.ValueOf(a).MethodByName(menthod).Call(inputs)

	// 动态调用struct中的属性
	fmt.Println("所有属性值", reflect.ValueOf(a).Elem())
	fmt.Println("指定属性值", reflect.ValueOf(a).Elem().FieldByName("IsOver1"))
}

func main() {
	// 使用此方式直接调用,
	printShow(&B1{}, "Show1")
}
go

0 人点赞