go_反射

本文介绍了Go语言中反射的应用,以SQL语句生成器为例,阐述了反射在运行时检查变量类型和值的作用。通过reflect包、reflect.Type和reflect.Value等,可实现通用的SQL插入语句生成。同时提醒反射虽强大,但应谨慎使用,避免影响代码可维护性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

反射是Go的高级用法之一,是程序在运行时检查其变量和值并找到他们的类型的能力

为什么需要反射

任何人在学习反射时都会遇到的第一个问题是,当我们程序中的每个变量都由我们定义并且我们在编译时本身就知道它的类型时,为什么我们甚至需要在运行时检查一个变量并找到它的类型。嗯,这在大多数情况下都是正确的,但并非总是如此。

举一个栗子来引入反射的作用

假设我们要编写一个简单的函数,它将一个结构作为参数,并使用它创建一个 SQL 插入查询:
在这里插入图片描述
我们需要编写一个函数,将上面程序中的结构o作为参数,并返回以下 SQL 插入查询:
insert into order values(1234, 567)

函数如下:

package main

import (  
    "fmt"
)

type order struct {  
    ordId      int
    customerId int
}

func createQuery(o order) string {  
    i := fmt.Sprintf("insert into order values(%d, %d)", o.ordId, o.customerId)
    return i
}

func main() {  
    o := order{
        ordId:      1234,
        customerId: 567,
    }
    fmt.Println(createQuery(o))
}

函数createQuery通过使用结构体o中的ordId和customerId字段创建了SQL插入语句。该程序将输出:

insert into order values(1234, 567)

现在我们来升级这个sql语句生成器。如果我们想让它变得通用,可以适用于任何结构体类型该怎么办呢?

package main

type order struct {  
    ordId      int
    customerId int
}

type employee struct {  
    name string
    id int
    address string
    salary int
    country string
}

func createQuery(q interface{}) string {  
}

func main() {

}

在这里插入图片描述
在这里插入图片描述

由于该createQuery函数应该适用于任何结构,因此它需要一个interface{}作为参数。为简单起见,我们只处理包含 string 和 int 类型字段的结构体,但可以扩展为包含任何类型的字段。

createQuery 函数应该适用于所有的结构体。编写此函数的唯一方法是在运行时检查传递给它的struct参数的类型,找到它的字段然后创建sql语句。这时就需要用到反射了。

reflect包

reflect包在Go中实现了运行时的反射
reflect包有助于识别底层的具体类型和interface{}变量的值
这正是我们所需要的。该createQuery函数接受一个interface{}参数,并且需要根据参数的具体类型和值创建查询interface{}。这正是反射包的作用。

reflect.Type 和 reflect.Value

参数 interface{} 的具体类型由 reflect.Type 表示,而 reflect.Value 表示它的具体值。reflect.TypeOf() 和 reflect.ValueOf() 两个函数可以分别返回 reflect.Type 和 reflect.Value。这两种类型是我们创建sql生成器的基础。我们现在用一个简单的例子来理解这两种类型。
在这里插入图片描述
从输出中,我们可以看到程序打印出接口的具体类型和值。

reflect.Kind

Kind和Type的区别
在这里插入图片描述

Type表示 interface{} 的实际类型,在本例中为 main.Order,Kind表示该类型的具体种类。在这种情况下,它是一个struct。

NumField()和Field() 方法

NumField()方法返回结构中的字段数,Field(i int)方法返回reflect.Value

在这里插入图片描述

Int() 和 String() 方法

Int和String可以帮助我们分别取出reflect.Value为int64和string的值
在这里插入图片描述
在这里插入图片描述

完整的程序:sql语句生成器

package main

import (
	"fmt"
	"reflect"
)

type order struct {
	ordId      int
	customerId int
}

type employee struct {
	name    string
	id      int
	adress  string
	salary  int
	country string
}

func createQuery(q interface{}) {
	if reflect.ValueOf(q).Kind() == reflect.Struct {
		//Name returns the type's name within its package for a defined type.
		//For other (non-defined) types it returns the empty string.
		t := reflect.TypeOf(q).Name()
		query := fmt.Sprintf("insert into %s values(", t)
		v := reflect.ValueOf(q)
		for i := 0; i < v.NumField(); i++ {
			switch v.Field(i).Kind() {
			case reflect.Int:
				if i == 0 {
					//Sprintf formats according to a format specifier and returns the resulting string.
					query = fmt.Sprintf("%s%d", query, v.Field(i).Int())
				} else {
					query = fmt.Sprintf("%s,%d", query, v.Field(i).Int())
				}
			case reflect.String:
				if i == 0 {
					query = fmt.Sprintf("%s\"%s\"", query, v.Field(i).String())
				} else {
					query = fmt.Sprintf("%s,\"%s\"", query, v.Field(i).String())
				}
			default:
				fmt.Println("unsupported type")
				return
			}
		}
		query = fmt.Sprintf("%s)", query)
		fmt.Println(query)
		return
	}
	fmt.Println("unsupported type")
}

func main() {
	o := order{
		ordId:      89,
		customerId: 13,
	}
	createQuery(o)

	p := employee{
		name:    "亚瑟",
		id:      454,
		adress:  "沈阳大街",
		salary:  19999,
		country: "指定有你好果子次中国",
	}
	createQuery(p)

	q := 2345
	createQuery(q)
}

在这里插入图片描述
注意:
反射是 Go 中一个非常强大和先进的概念,应该谨慎使用。使用反射编写清晰且可维护的代码非常困难。应尽可能避免使用,仅在绝对必要时使用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值