方法和类型的反射
反射时用程序检查其所拥有的结构,是元编程的一种形式。反射可以在运行时检查类型和变量。除非真的有必要,否则应尽量避免或小心使用。
反射包中的两个简单函数:
- reflect.TypeOf( i interface{ }) :返回被检查对象的类型
- reflect.ValueOf( i interface{ }) :返回被检查对象的数值
var x float64 = 3.14
fmt.Println(reflect.TypeOf(x))
fmt.Println(reflect.ValueOf(x))
输出结果:
float64
3.14
此外,我们可以使用 Kind( ) 方法来识别 reflect.ValueOf( ) 的返回的数据类型。如果x的数值类型为Float64,则reflect.ValueOf(x).Kind( ) == reflect.Float64。
fmt.Printf("%t",reflect.Float64==v.Kind())
输出结果:
true
Kind( ) 方法总是返回底层类型。
type MyInt int
var myint MyInt = 5
fmt.Println(reflect.TypeOf(myint))
fmt.Println(reflect.ValueOf(myint).Kind())
输出结果:
main.MyInt
int
通过输出结果可以看到 reflect.TypeOf( ) 返回的为变量声明时使用的类型,而 Kind( ) 方法返回的为变量声明时使用类型的底层类型。
使用 Interface( )方法可以得到还原值
fmt.Printf("%v,%[1]T\n%v,%[2]T",v,v.Interface())
fmt.Println(v.Interface()==reflect.Float64)
输出结果:
3.14,reflect.Value
3.14,float64
false
Interface( )方法可以将reflect.ValueOf( ) 函数的返回值从Value类型转换为原始类型。使用Float( )、Int( )、String( )、Bool( )等方法同样可以实现相同的功能。
fmt.Printf("%v,%[1]T\n",v.Float())
输出结果:
3.14,float64
通过反射修改值
使用SetFloat( )的方法可以实现修改数值,使用时必须小心,如果该变量不可设置,则会出现报错。可以使用CanSet( )方法来测试是否可设置。CanSet( )方法的返回值为False,可使用Elem( )方法使其成为可设置的状态。使用Elem方法之前需要获取指针。
var Num float64= 3.15
Value := reflect.ValueOf(Num)
if ok := Value.CanSet(); ok {
fmt.Println("Value Can Set")
Value.SetFloat(4.56)
}else{
fmt.Println("Value Cannot Set, Now Elem it")
Value := reflect.ValueOf(&Num)
Value = Value.Elem()
fmt.Println("The CanSet Is ", Value.CanSet())
Value.SetFloat(1.23)
}
fmt.Println(Value)
输出结果:
Value Cannot Set, Now Elem it
The CanSet Is true
3.15
一定注意在使用Elem方法之前重新使用reflect.ValueOf(&Num)
反射结构
有些时候需要反射一个结构类型。NumField( )方法返回结构内的字段数。通过一个 for 循环用索引取得每个字段的值 Field( i )。我们同时能够调用签名在结构上的方法,可以使用索引来调用 Method( n ).Call( nil )。
type ThreeString struct{
s1,s2,s3 string
}
func (s ThreeString)String() string{
return s.s1 + "-" + s.s2 + "-"+s.s3
}
var secret interface{}=ThreeString{"Alpha","Bete","Omega"}
func main(){
Value1 := reflect.ValueOf(secret)
Type1 := reflect.TypeOf(secret)
fmt.Println(Type1)
fmt.Println(Value1.Kind())
for i := 0 ; i < Value1.NumField() ; i++ {
fmt.Printf("Field %d : %v\n",i,Value1.Field(i))
}
fmt.Println(Value1)
results := Value1.Method(0).Call(nil)
fmt.Println(results)
}
输出结果:
main.ThreeString
struct
Field 0 : Alpha
Field 1 : Bete
Field 2 : Omega
Alpha-Bete-Omega
[Alpha-Bete-Omega]
但是如果使用Field( n ).SetString( ) 方法来改变一个值会出现错误:
panic: reflect: reflect.Value.SetString using value obtained using unexported field
因为结构中只有被导出字段(首字母大写)才是可设置的:
type T struct {
A int
B string
}
func main() {
t := T{23,"skidoo"}
s := reflect.ValueOf(&t).Elem()
typeOfT := s.Type()
for i := 0 ; i < s.NumField() ; i++{
f := s.Field(i)
fmt.Printf("%d:%s %s = %v \n", i, typeOfT.Field(i).Name , f.Type(),f.Interface())
}
s.Field(0).SetInt(77)
s.Field(1).SetString("Sunset Strip")
fmt.Println("t is now :",t)
}
输出结果:
0:A int = 23
1:B string = skidoo
t is now : {77 Sunset Strip}
代码下载
以上程序代码均已上传到至github ,有需要可直接进行下载