什么是反射?
反射就是程序能够在运行时检查变量和值,求出它们的类型。
为何需要检查变量,确定变量的类型?
举个例子来说明需要在运行时求得变量类型的情况:我们需要编写一个简单的函数,它接收结构体作为参数,并用它来创建一个SQL插入查询。
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() {
o := order{1234, 567}
// insert into order values(1234, 567)
fmt.Println(createQuery(o))
e := employee{"Naveen", 565, "Science Park Road", 90000, "Singapore"}
// insert into employee values("Naveen", 565, "Science Park Road", 90000, "Singapore")
fmt.Println(createQuery(e))
}
如上代码,我们的目标就是完成createQuery
函数,我们想让它变得通用,可以接收任何结构体作为参数,根据结构体的字段创建插入查询。如main
函数所示,不同的结构体输出对应的SQL语句。
createQuery
函数应该适用于所有的结构体。因此,要编写这个函数,就必须在运行时检查传递过来的结构体参数的类型,找到结构体字段,接着创建查询。这时就需要用到反射了。
reflect包
reflect
实现了运行时反射。reflect
包会帮助识别interface{}
变量的底层具体类型和具体值。
reflect
包中的几种类型和方法:
reflect.Type
表示interface{}
的具体类型,而reflect.Value
表示它的具体值。reflect.TypeOf()
和reflect.ValueOf()
两个函数可以分别返回reflect.Type
和reflect.Value
。
type order struct {
ordId int
customerId int
}
func main() {
o := order{1234, 567}
fmt.Println(reflect.TypeOf(o)) // main.order
fmt.Println(reflect.ValueOf(o)) // {1234 567}
}
- 如下所示,
reflect.Type
表示interface{}
的实际类型(这里是main.order
),而reflect.Kind
表示该类型的特定类别(这里是struct
)。
type order struct {
ordId int
customerId int
}
func main() {
o := order{1234, 567}
fmt.Println(reflect.TypeOf(o)) // main.order
fmt.Println(reflect.TypeOf(o).Kind()) // struct
fmt.Println(reflect.ValueOf(o)) // {1234 567}
fmt.Println(reflect.ValueOf(o).Kind()) // struct
}
NumField()
方法返回结构体中字段的数量,而Field(i int)
方法返回字段i
的reflect.Value
。
type order struct {
ordId int
customerId int
}
func main() {
o := order{1234, 567}
// NumField方法只能在结构体上使用,所以先检查o的类别是struct
if reflect.ValueOf(o).Kind() == reflect.Struct {
v := reflect.ValueOf(o)
fmt.Println("Number of fields:", v.NumField())
for i := 0; i < v.NumField(); i++ {
fmt.Println("Field:", i,
"Type:", reflect.TypeOf(o).Field(i).Name,
"Value:", v.Field(i))
}
}
/*
Number of fields: 2
Field: 0 Type: ordId Value: 1234
Field: 1 Type: customerId Value: 567
*/
}
Int()
和String()
可以帮助我们分别取出reflect.Value
作为int64
和string
。
完整的程序
package main
import (
"fmt"
"reflect"
)
type order struct {
ordId int
customerId int
}
type employee struct {
name string
id int
address string
salary int
country string
}
func createQuery(q interface{}) {
if reflect.ValueOf(q).Kind() == reflect.Struct {
t := reflect.TypeOf(q).Name() // order
query := fmt.Sprintf("insert into %s values(", t) // "insert into order values("
v := reflect.ValueOf(q) // {1234 567}
for i := 0; i < v.NumField(); i++ {
switch v.Field(i).Kind() {
case reflect.Int:
if i == 0 {
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{456, 56}
createQuery(o)
e := employee{"Naveen", 565, "Coimbatore", 90000, "India"}
createQuery(e)
i := 90
createQuery(i)
/*
insert into order values(456, 56)
insert into employee values("Naveen", 565, "Coimbatore", 90000, "India")
unsupported type
*/
}
应该使用反射吗?
反射是Go语言中非常强大和高级的概念,我们应该小心谨慎地使用它。使用反射编写清晰和可维护的代码是十分困难的。我们应该尽可能避免使用它,只在必须用到它时,才使用反射。
reference: