標籤:reflection
reflection 反射
- 反射可大大提高程式的靈活性,使得interface{}有更大的發揮餘地
- 反射使用TypeOf和ValueOf函數從介面中擷取目標對象資訊
- 反射會將匿名欄位作為獨立欄位(匿名欄位本質)
- 想要利用反射修改對象狀態,前提是interface.data是settable,即pointer-interface
- 通過反射可以"動態"調用方法
舉例1、通過反射來擷取屬性資訊,方法資訊
//反射 練習//擷取欄位的類型資訊,方法資訊, 屬性資訊package mainimport ( "fmt" "reflect")type User struct { Id int Name string Age int}func (user User) Hello(){ fmt.Println("hello world")}func main() { user := User{Id:2, Name:"xiaoqiang",Age:30} info(user)}//定義一個方法//此方法接收的是一個空介面//這樣的話,其實是任何類型都可以接收了func info(o interface{}) { //擷取o的類型 t := reflect.TypeOf(o) fmt.Println("Type:\t", t.Name()) //取出類型後,需要對類型進行校正, //查看是否是規定的類型 if k :=t.Kind(); k != reflect.Struct { //如果不是規定的Struct類型的話,就需要進行 //異常處理 fmt.Println("輸入參數的類型不符!") return } //擷取o的屬性 v := reflect.ValueOf(o) fmt.Println("Fields:\n") //開始遍曆,輸出欄位 for i := 0; i < t.NumField(); i++ { //擷取屬性下標 f := t.Field(i) val := v.Field(i).Interface() //輸出屬性的名稱,類型,對應的值 fmt.Printf("%6s:%v = %v\n", f.Name, f.Type, val) } //開始擷取 方法的基本資料 for i:=0; i<t.NumMethod(); i++ { m := t.Method(i) fmt.Printf("%6s:%v\n", m.Name, m.Type) }}
2、反射是如何 擷取 匿名欄位呢?
//反射 是如何處理 匿名欄位的?package mainimport ( "reflect" "fmt")type Stu struct { Id int Name string Age int}type Man struct { //這裡你要注意一下,你建立的屬性,是有順序的,是有下標的 //如Stu 下標 就是0, title下標就是1 // Stu 就是匿名屬性 Stu title string}func main() { //注意,對匿名欄位進行初始化時的方式,其實本質上跟其他屬性是一樣的 m := Man{Stu:Stu{Id:2,Name:"Jack",Age:19}, title:"Manager"} t := reflect.TypeOf(m) //取匿名欄位的方式 //FieldByIndex 方法,傳入的是一個切片slice類型 //第1個0,表示,匿名欄位在Man中的下標位置 //第2個0,表示,你要取匿名欄位中哪個屬性的下標 fmt.Printf("%#v\n", t.FieldByIndex([]int{0,0})) //取的是id fmt.Printf("%#v\n", t.FieldByIndex([]int{0,1})) //取的是Name fmt.Printf("%#v\n", t.FieldByIndex([]int{0,2})) //取的是Age}
3、通過反射對基本類型進行修改
//如果通過反射,對基本類型進行修改package mainimport ( "reflect" "fmt")func main() { //下面測試,對基本類型的修改 操作 x := 456 //傳遞的參數是 地址 v := reflect.ValueOf(&x) //Elem方法,是取出元素值來,然後通過setint方法,進行重新設定 v.Elem().SetInt(789) fmt.Println(x)}
4、通過反射 對複雜類型進行修改
//如果通過反射,對複雜類型進行修改package mainimport ( "reflect" "fmt")type Teac struct { Id int Name string Age int}func main() { teac := Teac{Id:5,Name:"Ant-man",Age:23} fmt.Println("teac:\t", teac) //傳遞的是 地址哦 Set(&teac) fmt.Println("teac:\t", teac)}func Set(o interface{}) { v := reflect.ValueOf(o) if v.Kind() == reflect.Ptr && !v.Elem().CanSet() { fmt.Printf("xxx") return }else{ v = v.Elem() } // 通過FieldByName 這個方法,直接輸入 名稱,來擷取 f := v.FieldByName("Name") //校正,是否取到Name屬性的值 if !f.IsValid() { fmt.Printf("BAD") return } //然後,再校正,類型是否匹配 if f.Kind() == reflect.String { f.SetString("Iron Man") }}
5、通過反射對方法進行動態調用
//通過反射,進行方法的調用,相當於動態調用了package mainimport ( "fmt" "reflect")type Teacher struct { Id int Name string Age int}//通過receiver將Show方法,跟Teacher類型,進行綁定func (teacher Teacher)Show(name string) { fmt.Println("hello, ", name, ", my name is ", teacher.Name)}//注意======目前沒有發現====如何通過====反射===來擷取=====私人方法func (teacher Teacher)info(){ fmt.Println("=====")}func main() { teacher := Teacher{Id:34, Name:"Thor",Age:34} teacher.Show("Hawkeye") //下面通過反射,調用show方法 v := reflect.ValueOf(teacher) //擷取show方法 m := v.MethodByName("Show") //校正一下,是否擷取到show方法呢 if !m.IsValid() { fmt.Printf("=======沒有擷取到制定的方法====") return } //參數必須是切片類型 //reflect.Value{} 這裡面,可以設定多個參數類型 //目前,我們這裡只有一個string類型的參數 // args := []reflect.Value{reflect.ValueOf("Hulk")} m.Call(args)}
Go語言之reflection