当前位置: 首页 > news >正文

Go语言入门-反射4(动态构建类型)

12.4 动态构建类型

反射技术不仅可以获取类型信息、执行类型成员,还可以动态创建类型。目前不支持动态创建的类型有:数组、切片、函数、结构体(仅包含字段,目前不支持生成方法成员)、通道(chan)、映射。

12.4.1 New 函数

New 函数创建指定类型的新实例,并以 Value 类型返回。不过,被创建的新实例实际是 *T 类型,假设调用 New 函数时指定的类型是 T,那么新实例的类型为 *T

若要获取指针所指向的对象,可以调用 Indirect 函数。该函数的源代码如下:

func Indirect(v Value) Value {
    if v.Kind() == Ptr {
        return v.Elem()
    }
    return v
}

从上述源代码中可以看出,Indirect函数实际上是调用了 Value类型的Elem 方法。

下面是一个使用 New 函数的简单示例:

var v1 = reflect.New(reflect.TypeOf(1))
fmt.Printf("v1 的类型: %v\n", v1.Type())

var v2 = reflect.New(reflect.TypeOf(struct {
    F1 uint
    F2 string
}{}))
fmt.Printf("v2 的类型: %v\n", v2.Type())

var v3 = reflect.New(reflect.TypeOf(func(string) {}))
fmt.Printf("v3 的类型: %v\n", v3.Type())

以下是示例的输出内容:

v1 的类型: *int
v2 的类型: *struct { F1 uint; F2 string }
v3 的类型: *func(string)

不管调用 New 函数时指定的是什么类型,它返回的实例都是指针类型。

12.4.2 创建数组类型

在运行阶段动态创建数组类型,可以调用 ArrayOf 函数。它的签名如下:

func ArrayOf(count int, elem Type) Type

count 参数用来指定数组的长度(即元素个数),elem 参数指定数组元素的类型。返回的 Type 对象表示新的数组类型。

例如,可以使用下面的代码生成元素类型为 string 的数组,数组长度为 5。

//元素类型
var elemtp = reflect.TypeOf("xxx")
//构建数组类型
var arrType = reflect.ArrayOf(5, elemtp)

然后可以调用 New 函数创建数组实例,接着用类型化方法给数组的元素赋值。

var arrVal = reflect.New(arrtype).Elem()

由于New函数创建的实例是指针类型,所以要调用Elem方法获取指针所指向的真实实例。
最后,可以给数组的元素赋值。

// 给数组的元素赋值
arrVal.Index(0).SetString("Item_1")
arrVal.Index(1).SetString("Item_2")
arrVal.Index(2).SetString("Item_3")
arrVal.Index(3).SetString("Item_4")
arrVal.Index(4).SetString("Item_5")

调用 Interface 方法获取真实的数组实例,接着用类型断言将其转换为[5]string类型,并且枚举它的元素列表。

theArr := arrVal.Interface().([5]string)
if ok{
    for i, item := range theArr {
        fmt.Printf("%d - %s\n", i, item) 
    }
}

枚举出来的元素如下:

0 - Item_1
1 - Item_2
2 - Item_3
3 - Item_4
4 - Item_5

12.4.3 创建结构体类型

使用 StructOf 函数动态创建新的结构体类型,在调用该函数时候,需要提供一个StructField 列表。此列表包含了结构体的所有字段信息。在目前的Go语言版本中,只能为新的结构体类型添加字段,尚不支持生成方法列表。

StructOf 函数将忽略 StructField 对象的 OffsetIndex 字段的值,也就是说,结构体中的字段的位置和字节偏移量都由编译器决定。因此,在使用 StructField 对象的时候,不需要设置 OffsetIndex 字段的值,一般指定字段名称(Name)和字段类型(Type)即可。

在下面的示例中,动态创建了结构体类型,它包含四个字段,然后将其实例化,并为每个字段赋值。

步骤 1:准备需要的字段信息(四个字段)。

var fds = []reflect.StructField{
    reflect.StructField{
        Name: "Header",
        Type: reflect.TypeOf("zzz"),// string
    },
    reflect.StructField{
        Name: "Size",
        Type: reflect.TypeOf(0), // int
    },
    reflect.StructField{
        Name: "Data",
        Type: reflect.TypeOf([]byte{}),
    },
    reflect.StructField{
        Name: "Position",
        Type: reflect.PtrTo(reflect.TypeOf(uint(0))), // *uint
    },
}

步骤 2:创建新结构体类型。

newStruct := reflect.StructOf(fds)

步骤 3:创建新结构体类型的实例。

structVal := reflect.New(newStruct).Elem()

New函数创建的实例是指针类型,所以要调用Elem方法获取指针所指向的真实实例。
步骤 4:设置结构体实例的字段值。

structVal.Field(0).SetString("test") // Header
structVal.Field(1).SetInt(6)        // Size
structVal.Field(2).SetBytes([]byte("c2a5de")) // Data
var v uint = 60
structVal.Field(3).Set(reflect.ValueOf(&v)) // Position

步骤 5:本示例的运行结果如下:

动态创建的结构体类型:
struct { Header string; Size int; Data []uint8; Position *uint }

结构体实例的值:
{Header:test Size:6 Data:[99 97 100 101 122] Position:0xc0000a00d0}

12.4.4 动态创建和调用函数

要动态创建新的函数类型,可以调用 FuncOf 函数。

func FuncOf(in []Type, out []Type, variadic bool) Type

其中,in 表示函数的输入参数列表,out 表示的是函数的返回值(输出)列表。Variadic 参数是个布尔值,若为 true,则表示此函数所有输入参数的最后一个是可变参数;若为 false,则表示此函数没有可变参数。

FuncOf 函数只是创建函数类型(用Type表示),不包括函数体部分。要想创建函数实例,还需要调用 MakeFunc 函数。

func MakeFunc(typ Type, fn func(args []Value) (results []Value)) Value

typ 指定函数的类型,fn 是一个匿名函数,用于构建函数体逻辑。当目标函数被调用时,目标函数的输入参数传递给 fn,而目标函数的返回将通过 fn 返回。

MakeFunc 函数调用成功后,返回引用函数实例(包含函数体)的 Value 对象,再使用 Value 对象的 Call 方法来调用目标函数。

在下面的示例中,首先动态创建一个函数类型:两个 float32 类型的输入参数,返回值为 string 类型。接着使用 MakeFunc 函数构建函数体,执行逻辑是将两个输入参数相减,再把结果以字符串形式返回。

步骤 1:本示例的运行结果如下:

var (
    p1 = reflect.TypeOf(float32(0.01))
    p2 = reflect.TypeOf(float32(0.01))
    pins = []reflect.Type{p1, p2}
)

步骤 2:准备函数的返回值列表。

var ret = reflect.TypeOf("")
var pouts = []reflect.Type{ret}

步骤 3:调用 FuncOf 函数,创建新的函数类型。

myFunc := reflect.FuncOf(pins, pouts, false)

步骤 4:调用 MakeFunc 函数,构建函数体逻辑。

funcInst := reflect.MakeFunc(myFunc, func(in []reflect.Value) []reflect.Value {
    // 取出两个参数的值
    f1 := in[0].Float()
    f2 := in[1].Float()
    // 将两个值相减
    fr := f1 - f2
    // 将结果转换为字符串
    str := fmt.Sprintf("%.2f", fr)
    // 封装返回值
    retv := reflect.ValueOf(str)
    return []reflect.Value{retv}
})

步骤 5:尝试调用函数。

var input1, input2 float32 = 0.9, 0.7
cr := funcInst.Call([]reflect.Value{
    reflect.ValueOf(input1),
    reflect.ValueOf(input2),
})

步骤 6:向屏幕输出调用结果。

var resval = cr[0].String()
fmt.Printf("输入参数: %.2f, %.2f\n", input1, input2)
fmt.Printf("函数调用结果: %s\n", resval)

步骤 7:运行代码,输出结果如下:

刚创建的函数类型:
func(float32, float32) string
输入参数: 0.90, 0.70
函数调用结果: 0.20

在调用 FuncOf 函数时,如果 variadic 参数设置为 true,那么输入参数列表中的最后一个元素必须是切片类型。例如:

// 第一个参数,类型:int
p1 := reflect.TypeOf(0)
// 第二个参数,类型:bool
p2 := reflect.TypeOf(true)
// 第三个参数
// 此参数个数可变,类型:[]string
p3 := reflect.TypeOf([]string{})

// 第一个返回值,类型:int
rt1 := reflect.TypeOf(0)
// 第二个返回值,类型:bool
rt2 := reflect.TypeOf(true)

// 构造输入/输出参数列表
pin := []reflect.Type{p1, p2, p3}
pout := []reflect.Type{rt1, rt2}

// 创建函数类型
t := reflect.FuncOf(pin, pout, true)

调用 FuncOf 函数后,产生的新函数类型为:

func(int, bool,...string) (int, bool)

12.4.5 生成通用函数体

MakeFunc 函数生成的函数体逻辑实质上是一个函数实例,可以赋值给类型符合的函数变量。以下示例演示了使用 MakeFunc 函数生成这样的函数逻辑:函数接收两个参数,然后返回两个参数之和。

为了简化代码,本示例仅支持的参数/返回值类型为 int32int64float32float64,即

func (int32, int32) int32
func (int64, int64) int64
func (float32, float32) float32
func (float64, float64) float64

首先,定义 initFunc 函数,该函数通过 iFn 参数接收函数变量的指针。接着使用 MakeFunc 函数生成函数实例。最后,将生成的函数实例赋值给 iFn 参数所指向的变量,即让函数变量引用新创建的函数实例。

func initFunc(iFn interface{}) {
    var fnVal = reflect.ValueOf(iFn)
    if fnVal.Kind()!= reflect.Ptr {
        fmt.Println("请传递函数变量的内存地址")
        return
    }
    // 取得指针所指向的对象
    fnVal = fnVal.Elem()
    // 函数体逻辑
    var fnBody = func(ip []reflect.Value) (outs []reflect.Value) {
        outs = []reflect.Value{}
        if len(ip)!= 2 {
            fmt.Println("函数应该有两个参数")
            return
        }
        // 验证两个参数的类型是否相同
        if ip[0].Kind()!= ip[1].Kind() {
            fmt.Println("两个参数的类型不一致")
            return
        }
        // 取出两参数的值
        // 然后进行 "+" 运算
        var result interface{}
        switch ip[0].Kind() {
        case reflect.Int32:
            a := int32(ip[0].Int())
            b := int32(ip[1].Int())
            result = a + b
        case reflect.Int64:
            a := int64(ip[0].Int())
            b := int64(ip[1].Int())
            result = a + b
        case reflect.Float32:
            a := float32(ip[0].Float())
            b := float32(ip[1].Float())
            result = a + b
        case reflect.Float64:
            a := float64(ip[0].Float())
            b := float64(ip[1].Float())
            result = a + b
        default:
            result = nil
        }
        // 处理返回值
        resval := reflect.ValueOf(result)
        outs = append(outs, resval)
        return
    }
    // 构建函数实例
    funcInst := reflect.MakeFunc(fnVal.Type(), fnBody)
    // 让传递进来的函数变量引用函数实例
    fnVal.Set(funcInst)
}

再定义四个函数类型的变量。

var (
    addInt32 func(int32, int32) int32
    addInt64 func(int64, int64) int64
    addFloat32 func(float32, float32) float32
    addFloat64 func(float64, float64) float64
)

用前面定义的 initFunc 函数分别给这些函数变量赋值(即初始化)。

initFunc(&addInt32)
initFunc(&addInt64)
initFunc(&addFloat32)
initFunc(&addFloat64)

依次调用这四个函数。

var a1, a2 int32 = 150, 25
r1 := addInt32(a1, a2)
fmt.Printf("%d + %d = %d\n", a1, a2, r1)

var b1, b2 int64 = 98900000, 45231002
r2 := addInt64(b1, b2)
fmt.Printf("%d + %d = %d\n", b1, b2, r2)

var c1, c2 float32 = 0.0021, 1.0099
r3 := addFloat32(c1, c2)
fmt.Printf("%f + %f = %f\n", c1, c2, r3)

var d1, d2 float64 = -770.00000542, 230.90005
r4 := addFloat64(d1, d2)
fmt.Printf("%f + %f = %f\n", d1, d2, r4)

调用结果如下:

150 + 25 = 175
98900000 + 45231002 = 144131002
0.002100 + 1.009900 = 1.012000
-770.000005 + 230.900050 = -539.099955

总结

本文介绍了反射技术动态构建类型的相关内容,主要包括:

  1. New 函数:用于创建指定类型的新实例,返回 *T 类型。
  2. ArrayOf 函数:在运行阶段动态创建数组类型。
  3. StructOf 函数:动态创建新的结构体类型,但目前只能添加字段,不支持生成方法列表。
  4. FuncOf 函数:动态创建新的函数类型,MakeFunc 函数用于构建函数体逻辑。
  5. 生成通用函数体:通过 MakeFunc 函数生成函数实例,并赋值给符合类型的函数变量。

相关文章:

  • PyCharm Community社区版链接WSL虚拟环境
  • 【笔记ing】AI大模型-01大模型基础综述
  • 医学科研工作者的AI助手:高效生成文献结构图和实验流程图
  • U9新开发webapi无授权
  • 使用Docker创建postgres
  • 智慧医院室内导航系统架构拆解:技术选型与性能攻坚指南
  • ssh警告WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!‌的解决方法
  • 2025 年“认证杯”数学中国数学建模网络挑战赛 A题 小行星轨迹预测
  • Mac 关闭浏览器左右滑动切换页面的问题
  • Java常用安全编码的规范整理及工具
  • jQuery UI 小部件方法调用详解
  • 量子计算未来的潜力和挑战
  • DeepSeek提示词实战大全:提示词合集和使用技巧
  • 速学Android 16新功能:带有进度的通知类型
  • mysql-sql查询结构和执行顺序
  • 【计网】一二章习题
  • C++入门一:C++ 编程概述
  • STM32 基础1
  • 2025年4月7日--4月13日(learn openg+dx+ogre+bullet+ue5肉鸽)
  • Google 发布 Sec-Gemini v1:用 AI 重塑网络安全防御格局?
  • 找潍坊做网站的/2021时事政治热点50条
  • 沙发网站建设/百度检索入口
  • 南昌营销型网站建设/交换友链要注意什么
  • 云南建筑培训网/seo外链推广工具
  • 如何做网站滚动屏幕/google官方下载安装
  • 网站备案更换主体/竞价