1.方法和类型的反射
反射可以在运行时检查类型和变量,例如它的大小、方法和 动态 的调用这些方法。这对于没有源代码的包尤其有用。这是一个强大的工具,除非真得有必要,否则应当避免使用或小心使用。
reflect.TypeOf 和 reflect.ValueOf,返回被检查对象的类型和值
Interface() 方法可以得到还原(接口)值
Kind 总是返回底层类型
package main
import (
"fmt"
"reflect"
)
func main() {
var x float64 = 3.4
fmt.Println("type:", reflect.TypeOf(x))
v := reflect.ValueOf(x)
fmt.Println("value:", v)
fmt.Println("type:", v.Type())
fmt.Println("kind:", v.Kind())
fmt.Println("value:", v.Float())
fmt.Println(v.Interface())
fmt.Printf("value is %5.2e\n", v.Interface())
y := v.Interface().(float64)
fmt.Println(y)
}
/**
type: float64
value: 3.4
type: float64
kind: float64
value: 3.4
3.4
value is 3.40e+00
3.4
**/
2 通过反射修改(设置)值
可以使用 CanSet() 方法测试是否可设置
要想让其可设置我们需要使用 Elem() 函数,这间接的使用指针:v = v.Elem()
package main
import (
"fmt"
"reflect"
)
func main() {
var x float64 = 3.14
v := reflect.ValueOf(x)
//setting a value
//v.SetFloat(3.1415) // Error: will panic: reflect.Value.SetFloat using unaddressable value
fmt.Println("settability of v:", v.CanSet())
v = reflect.ValueOf(&x)
fmt.Println("type of v", v.Type())
fmt.Println("settability of v:", v.CanSet())
v = v.Elem()
fmt.Println("the elem of v is:", v)
fmt.Println("settability of v:", v.CanSet())
v.SetFloat(3.1415)
fmt.Println(v.Interface())
fmt.Println(v)
}
/**
settability of v: false
type of v *float64
settability of v: false
the elem of v is: 3.14
settability of v: true
3.1415
3.1415
**/
3.反射结构
有些时候需要反射一个结构类型。NumField() 方法返回结构内的字段数量;通过一个 for 循环用索引取得每个字段的值 Field(i)。
我们同样能够调用签名在结构上的方法,例如,使用索引 n 来调用:Method(n).Call(nil)。
package main
import (
"fmt"
"reflect"
)
type NotknownType struct {
s1, s2, s3 string
}
func (n NotknownType) String() string {
return n.s1 + " - " + n.s2 + " - " + n.s3
}
var secret interface{} = NotknownType{"ada", "go", "object"}
func main() {
value := reflect.ValueOf(secret)
typ := reflect.TypeOf(secret)
//alternative
//typ := value.Type()
fmt.Println(value)
fmt.Println(typ)
knd := value.Kind()
fmt.Println(knd)
//iterate through the fields of struct
for i := 0; i < value.NumField(); i++ {
fmt.Printf("Field %d: %v\n", i, value.Field(i))
//value.Field(i).SetString("C#") //error: panic: reflect.Value.SetString using value obtained using unexported field
}
//call the first method, which is String():
results := value.Method(0).Call(nil)
fmt.Println(results)
}
但是如果尝试更改一个值,会得到一个错误,因为结构中只有被导出字段(首字母大写)才是可设置的
package main
import (
"fmt"
"reflect"
)
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("test")
fmt.Println("t is now", t)
}
/**
0: A int = 23
1: B string = skidoo
t is now {77 test}
**/
4.printf和反射
fmt 包中的 Printf(以及其他格式化输出函数)都会使用反射来分析它的 … 参数。
package main
import (
"os"
"strconv"
)
type Stringer interface {
String() string
}
type Celsius float64
func (c Celsius) String() string {
return strconv.FormatFloat(float64(c), 'f', 1, 64) + " ℃"
}
type Day int
var dayName = []string{"Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"}
func (day Day) String() string {
return dayName[day]
}
func print(args ...interface{}) {
for i, args := range args {
if i > 0 {
os.Stdout.WriteString(" ")
}
switch a := args.(type) {
case Stringer:
os.Stdout.WriteString(a.String())
case int:
os.Stdout.WriteString(strconv.Itoa(a))
case string:
os.Stdout.WriteString(a)
default:
os.Stdout.WriteString("???")
}
}
}
func main() {
print(Day(1), "was", Celsius(18.36))
}