下面由golang教學欄位介紹給大家詳解Golang的反射(實例),希望對需要的朋友有幫助!
反射在許多語言中都有其妙用。在電腦科學領域,反射是指一類應用,它們能夠***自描述***和***自控制***。
本文將記錄筆者Golang的反射的筆記。
10s後,以下知識點即將靠近:
1.反射的簡介
2.為什麼要使用反射?
3.反射具體能做什麼
#Golang提供了一種機制,在編譯時不知道類型的情況下,可更新變數、運行時查看值、呼叫方法以及直接對他們的佈局進行操作的機制,稱為反射。
打個比方,有時候我們需要一個函數可以處理各種類型的值。在不知道類型的情況下,你可能會這麼寫:
// 伪代码 switch value := value.(type) { case string: // ...一些操作 case int: // ...一些操作 case cbsStruct: // 自定义的结构体 // ...一些操作 // ... }
有沒發現什麼問題?
這邊存在一個問題:類型很多,這個函數會寫的非常長,而且還可能存在自定的類型,也就是說這個判斷日後可能還要一直改,因為無法知道未知值到底屬於什麼類型。
無法透視一個未知類型的時候,以上程式碼其實不是很合理,這時候就需要有反射來幫忙你處理,反射使用TypeOf和ValueOf函數從介面中取得目標物件的信息,輕鬆完成目的。
1.取得變數內部資訊
reflect提供了兩種類型來進行存取介面變數的內容:
類型 | 作用 |
---|---|
reflect.ValueOf() | 取得輸入參數介面中的資料的值,如果為空則回傳0 <- 注意是0 |
#reflect.TypeOf() | 動態取得輸入參數介面中的值的類型,如果為空則回傳nil <- 注意是nil |
上程式碼
package main import ( "fmt" "reflect" ) func main() { var name string = "咖啡色的羊驼" // TypeOf会返回目标数据的类型,比如int/float/struct/指针等 reflectType := reflect.TypeOf(name) // valueOf返回目标数据的的值,比如上文的"咖啡色的羊驼" reflectValue := reflect.ValueOf(name) fmt.Println("type: ", reflectType) fmt.Println("value: ", reflectValue) }
輸出:
type: string value: 咖啡色的羊驼
更深一層:在上述操作發生的時候,反射將“介面類型的變數”轉為了“反射的介面類型的變數”,例如上文實際上返回的是reflect.Value和reflect.Type的接口對象。 (可以根據ide追蹤相關函數回傳型別便知)
2.struct的反射
package main import ( "fmt" "reflect" ) type Student struct { Id int Name string } func (s Student) Hello(){ fmt.Println("我是一个学生") } func main() { s := Student{Id: 1, Name: "咖啡色的羊驼"} // 获取目标对象 t := reflect.TypeOf(s) // .Name()可以获取去这个类型的名称 fmt.Println("这个类型的名称是:", t.Name()) // 获取目标对象的值类型 v := reflect.ValueOf(s) // .NumField()来获取其包含的字段的总数 for i := 0; i < t.NumField(); i++ { // 从0开始获取Student所包含的key key := t.Field(i) // 通过interface方法来获取key所对应的值 value := v.Field(i).Interface() fmt.Printf("第%d个字段是:%s:%v = %v \n", i+1, key.Name, key.Type, value) } // 通过.NumMethod()来获取Student里头的方法 for i:=0;i<t.NumMethod(); i++ { m := t.Method(i) fmt.Printf("第%d个方法是:%s:%v\n", i+1, m.Name, m.Type) } }
輸出:
这个类型的名称是: Student 第1个字段是:Id:int = 1 第2个字段是:Name:string = 咖啡色的羊驼 第1个方法是:Hello:func(main.Student)
##3 .匿名或嵌入欄位的反射輸出:package main import ( "reflect" "fmt" ) type Student struct { Id int Name string } type People struct { Student // 匿名字段 } func main() { p := People{Student{Id: 1, Name: "咖啡色的羊驼"}} t := reflect.TypeOf(p) // 这里需要加一个#号,可以把struct的详情都给打印出来 // 会发现有Anonymous:true,说明是匿名字段 fmt.Printf("%#v\n", t.Field(0)) // 取出这个学生的名字的详情打印出来 fmt.Printf("%#v\n", t.FieldByIndex([]int{0, 1})) // 获取匿名字段的值的详情 v := reflect.ValueOf(p) fmt.Printf("%#v\n", v.Field(0)) }
reflect.StructField{Name:"Student", PkgPath:"", Type:(*reflect.rtype)(0x10aade0), Tag:"", Offset:0x0, Index:[]int{0}, Anonymous:true} reflect.StructField{Name:"Name", PkgPath:"", Type:(*reflect.rtype)(0x109f4e0), Tag:"", Offset:0x8, Index:[]int{1}, Anonymous:false} main.Student{Id:1, Name:"咖啡色的羊驼"}
4.判斷傳入的型別是否是我們想要的型別##
package main import ( "reflect" "fmt" ) type Student struct { Id int Name string } func main() { s := Student{Id: 1, Name: "咖啡色的羊驼"} t := reflect.TypeOf(s) // 通过.Kind()来判断对比的值是否是struct类型 if k := t.Kind(); k == reflect.Struct { fmt.Println("bingo") } num := 1; numType := reflect.TypeOf(num) if k := numType.Kind(); k == reflect.Int { fmt.Println("bingo") } }輸出:
bingo bingo5.透過反射修改內容
package main import ( "reflect" "fmt" ) type Student struct { Id int Name string } func main() { s := &Student{Id: 1, Name: "咖啡色的羊驼"} v := reflect.ValueOf(s) // 修改值必须是指针类型否则不可行 if v.Kind() != reflect.Ptr { fmt.Println("不是指针类型,没法进行修改操作") return } // 获取指针所指向的元素 v = v.Elem() // 获取目标key的Value的封装 name := v.FieldByName("Name") if name.Kind() == reflect.String { name.SetString("小学生") } fmt.Printf("%#v \n", *s) // 如果是整型的话 test := 888 testV := reflect.ValueOf(&test) testV.Elem().SetInt(666) fmt.Println(test) }輸出:
main.Student{Id:1, Name:"小学生"} 6666.透過反射呼叫方法
package main import ( "fmt" "reflect" ) type Student struct { Id int Name string } func (s Student) EchoName(name string){ fmt.Println("我的名字是:", name) } func main() { s := Student{Id: 1, Name: "咖啡色的羊驼"} v := reflect.ValueOf(s) // 获取方法控制权 // 官方解释:返回v的名为name的方法的已绑定(到v的持有值的)状态的函数形式的Value封装 mv := v.MethodByName("EchoName") // 拼凑参数 args := []reflect.Value{reflect.ValueOf("咖啡色的羊驼")} // 调用函数 mv.Call(args) }輸出:
我的名字是: 咖啡色的羊驼
##4.反射的一些小點
1.使用反射時需要先確定要操作的值是否是期望的類型,是否是可以進行「賦值」操作的,否則reflect包將會毫不留情的產生一個panic。
2.反射主要與Golang的interface類型相關,只有interface型別才有反射一說。如果有興趣可以看看TypeOf和ValueOf,會發現其實傳入參數的時候已經被轉為介面類型了。
// 以下为截取的源代码 func TypeOf(i interface{}) Type { eface := *(*emptyInterface)(unsafe.Pointer(&i)) return toType(eface.typ) } func ValueOf(i interface{}) Value { if i == nil { return Value{} } escapes(i) return unpackEface(i) }
#
以上是詳解Golang的反射(實例)的詳細內容。更多資訊請關注PHP中文網其他相關文章!