1.方法和类型的反射
反射可以在运行时检查类型和变量,例如它的大小、方法和 动态 的调用这些方法。这对于没有源代码的包尤其有用。这是一个强大的工具,除非真得有必要,否则应当避免使用或小心使用。
reflect.TypeOf 和 reflect.ValueOf,返回被检查对象的类型和值
Interface() 方法可以得到还原(接口)值
Kind 总是返回底层类型
package mainimport ("fmt""reflect")func main() {var x float64 = 3.4fmt.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: float64value: 3.4type: float64kind: float64value: 3.43.4value is 3.40e+003.4**/
2 通过反射修改(设置)值
可以使用 CanSet() 方法测试是否可设置
要想让其可设置我们需要使用 Elem() 函数,这间接的使用指针:v = v.Elem()
package mainimport ("fmt""reflect")func main() {var x float64 = 3.14v := reflect.ValueOf(x)//setting a value//v.SetFloat(3.1415) // Error: will panic: reflect.Value.SetFloat using unaddressable valuefmt.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: falsetype of v *float64settability of v: falsethe elem of v is: 3.14settability of v: true3.14153.1415**/
3.反射结构
有些时候需要反射一个结构类型。NumField() 方法返回结构内的字段数量;通过一个 for 循环用索引取得每个字段的值 Field(i)。
我们同样能够调用签名在结构上的方法,例如,使用索引 n 来调用:Method(n).Call(nil)。
package mainimport ("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 structfor 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 mainimport ("fmt""reflect")type T struct {A intB 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 = 231: B string = skidoot is now {77 test}**/
4.printf和反射
fmt 包中的 Printf(以及其他格式化输出函数)都会使用反射来分析它的 … 参数。
package mainimport ("os""strconv")type Stringer interface {String() string}type Celsius float64func (c Celsius) String() string {return strconv.FormatFloat(float64(c), 'f', 1, 64) + " ℃"}type Day intvar 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))}
