内容简介:在golang中,reflect是一个比较高级的话题,本文将尽可能简单而又清楚的介绍相关内容。本文将从如下几个方面进行阐述:- 什么是reflection ?
一、概述
在golang中,reflect是一个比较高级的话题,本文将尽可能简单而又清楚的介绍相关内容。
本文将从如下几个方面进行阐述:
- 什么是reflection ?
- 如何检查一个变量var 并获得其真实类型
- golang中的reflect包
1.1 reflect.Type 和 reflect.Value
1.2 reflect.Kind
1.3 NumField() 和 Field()
1.4 Int() 和 String()
- 完整实例
- 如何使用reflection
二、什么是reflection
简单的来说反射就是程序在运行时,通过检查其定义的变量以及值,进而找到其对应的真实类型。这样的定义可能很难让人理解,这里不需要担心。
接下来开始让我们根据后面的内容真正的理解reflection(反射)
三、为什么需要通过检查一个变量并获得其真正的类型
在很多语言中都会存在反射或听说过反射,其中有一个问题在学习reflection(反射)过程中必然产生:为什么在程序运行时需要检查一个变量并获得其对应的类型type?产生的疑问的缘由:
在我们的程序中任意一个定义的变量,其类型都是在编译时是可知的。大多数情况下这样的结论是正确的,但不代表全部。
看看下面的一个简单实例:
package main import( "fmt" ) func main(){ i := 10 fmt.Printf("%d %T",i, i) }
运行程序输出结果:
10 int
在上面的实例中i的类型其实在编译时已是可知的,当打印出结果时并没有什么神奇的情况出现。
接下来让我们看看运行时的变量类型,首先定义一个function并用一个struct作为形参传递给fmt.Println。
package main import ( "fmt" ) type order struct{ ordId int customerId int } func main(){ o := order { orderId: 1234, customerId: 567, } fmt.Println(o) }
当运行程序后,输出结果:
{1234 567}
接下来让我们来改造下上面这个例子
package main import ( "fmt" ) type order struct{ ordId int customerId int } func query(o order) string{ sql := fmt.Sprintf("insert into order values(%d, %d)",o.ordId, o.customerId) return sql } func maiin(){ o := order{ ordId: 1234, customerId: 567, } fmt.Println(query(o)) }
运行程序输出结果:
insert into order values(1234, 567)
接着我们继续对实例进行改造,让query参数能够适配任意struct:
package main import ( "fmt" ) type order struct{ ordId int customerId int } type employee struct{ name string id int address string salary int country string } func query(o interface{}) string{ // 省略具体代码 ...... } func maiin(){ // 省略代码 ...... }
在上面的实例发现我们将query的接收参数类型:interface{}为了接收任意类型的struct。
例如:当我们提供一个order的结构体时:
o := order{ ordId: 1234, customerId: 567, }
对应的query返回的输出内容sql:
insert into order values (1234, 567)
那么当我们提供employee的结构体时
e := employee { name: "Naveen", id: 565, address: "Science Park Road, Singapore", salary: 90000, country: "Singapore", }
应该返回的内容sql:
insert into employee values("Naveen", 565, "Science Park Road, Singapore", 90000, "Singapore")
在上面的例子通过提供一个interface{}给query函数能够满足任意struct的处理。(实例也是为了简单起见,提供的struct只包含了string和int类型,不过可以扩展其他类型)
对于query函数来说为了适配任意类型的struct,通过在运行时检查传递的struct的具体类型并根据其包含的字段来生成对应的sql。在这里reflection(反射)是相当有用的。接下来让我们使用golang的reflect包来解决这种情况
四、reflect包
在golang中,reflect包是用来实现运行反射的。通过reflect包能够完成对一个interface{}变量的具体类型以及值的获取。在query函数提供了一个interface{}类型参数并根据interface{}具体的类型以及值生成对应的sql,reflect包对我们来说相对比较需要的并能很好解决该问题。
1、reflect.Type和reflect.Value
interface{}类型变量其具体类型可以使用reflect.Tpye来表示,而其具体值则使用reflect.Value来表示。而reflect.Type和reflect.Value分别提供reflect.TypeOf()和reflect.ValueOf()来获取interface{}的具体类型及具体值。接下来我们结合例子来进行说明
package main import( "fmt" "reflect" ) type order struct{ ordId int customerId int } func query(q interface{}) { t := reflect.TypeOf(q) v := reflect.ValueOf(q) fmt.Println("Type ", t) fmt.Println("Value ", v) } func main(){ o := order{ ordId: 456, customerId: 56, } query(o) }
输出内容:
Type main.order Value {456 56}
从输出的结果可以看出程序输出了interface{}的真实类型以及真实值。
2、reflect.Kind
在reflect还有一个比较重要的类型Kind,也是代表类型,看起来和我们前面提到的reflect.Type很相似,其实两者有着很大的差异:
package main import ( "fmt" "reflect" ) type order struct { ordId int customerId int } func createQuery(q interface{}) { t := reflect.TypeOf(q) k := t.Kind() fmt.Println("Type ", t) fmt.Println("Kind ", k) } func main() { o := order{ ordId: 456, customerId: 56, } createQuery(o) }
输出结果:
Type main.order Kind struct
通过输出结果我们能够很清楚的看出来reflect.Type和reflect.Kind:Type代表interface{}实际类型main.order;而Kind代表具体类型struct。(读起来有点绕口...)
3、NumField() 和Field()
NumField()方法获取一个struct所有的fields,Field(i int)获取指定第i个field的reflect.Value,结合具体实例:
package main import ( "fmt" "reflect" ) type order struct { ordId int customerId int } func createQuery(q interface{}) { if reflect.ValueOf(q).Kind() == reflect.Struct { v := reflect.ValueOf(q) fmt.Println("Number of fields", v.NumField()) for i := 0; i < v.NumField(); i++ { fmt.Printf("Field:%d type:%T value:%v\n", i, v.Field(i), v.Field(i)) } } } func main() { o := order{ ordId: 456, customerId: 56, } createQuery(o) }
输出结果:
Number of fields 2 Field:0 type:reflect.Value value:456 Field:1 type:reflect.Value value:56
4、Int() 和String()
Int()和String()主要用于从reflect.Value提取对应值作为int64和string类型
package main import ( "fmt" "reflect" ) func main() { a := 56 x := reflect.ValueOf(a).Int() fmt.Printf("type:%T value:%v\n", x, x) b := "Naveen" y := reflect.ValueOf(b).String() fmt.Printf("type:%T value:%v\n", y, y) }
输出结果:
type:int64 value:56 type:string value:Naveen
5、完整实例
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 query(q interface{}) { if reflect.ValueOf(q).Kind() == reflect.Struct { t := reflect.TypeOf(q).Name() sql := 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 { sql = fmt.Sprintf("%s%d", query, v.Field(i).Int()) } else { sql = fmt.Sprintf("%s, %d", query, v.Field(i).Int()) } case reflect.String: if i == 0 { sql = fmt.Sprintf("%s\"%s\"", query, v.Field(i).String()) } else { sql = fmt.Sprintf("%s, \"%s\"", query, v.Field(i).String()) } default: fmt.Println("Unsupported type") return } } sql = fmt.Sprintf("%s)", sql) fmt.Println(sql) return } fmt.Println("unsupported type") } func main() { o := order{ ordId: 456, customerId: 56, } query(o) e := employee{ name: "Naveen", id: 565, address: "Coimbatore", salary: 90000, country: "India", } query(e) i := 90 // 这是一个错误的尝试 query(i) }
输出结果:
insert into order values(456, 56) insert into employee values("Naveen", 565, "Coimbatore", 90000, "India") unsupported type
五、如何使用reflection(反射)
前面已经演示了reflection(反射)的使用,那真正的问题:该如何应用反射?
Clear is better than clever. Reflection is never clear.--- Rob Pike
reflection(反射)在golang中比较强大和高级的特性,使用时是需要注意的。因为reflection(反射)很难实现清晰并可维护的代码。遵循一条:尽量避免使用,除非方案必须使用。reflection(反射)的推导是比较复杂的,并不是为了随意使用而设计的。
反射的三大定律:
-
1.interface{}类型的值到反射reflecton对象.
根源上来说, reflection的原理就是检查interface中保存的一对值和类型, 所以在reflect包中,有两个类型我们需要记住, Type和Value两个类型. 通过这两个类型,我们可以访问一个interface变量的内容. 调用reflect.ValueOf和reflect.TypeOf可以检索出一个interface的值和具体类型. 当然通过reflect.Value我们也可以获得reflect.Type。 -
2.反射reflection对象到interface{}类型的值.
通过reflect.Value的Interface方法,我们可以获得一个Interface值。实际上这个方法将一个type和value打包回interface - 3.当修改一个反射reflection时, 其值必须是settable.(留个问题:如何通过reflect.Value来设置一个值,并需要注意哪些事宜)。
更多关于反射
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- Go语言反射之反射调用
- Go语言反射之类型反射
- Go语言反射之值反射
- 模块讲解----反射 (基于web路由的反射)
- 装饰器与元数据反射(4)元数据反射
- .NET/C# 反射的的性能数据,以及高性能开发建议(反射获取 Attribute 和反射调用方法)
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。