<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
反射是通過實體物件獲取反射物件(Value、Type),然後可以操作相應的方法。在某些情況下,我們可能並不知道變數的具體型別,這時候就可以用反射來獲取這個變數的型別或者方法。
其實反射的操作步驟非常的簡單,就是通過實體物件獲取反射物件(Value、Type),然後操作相應的方法即可。
下圖描述了範例、Value、Type 三者之間的轉換關係:
反射 API 的分類總結如下:
通過範例獲取 Value 物件,直接使用 reflect.ValueOf() 函數。例如:
func ValueOf(i interface {}) Value
通過範例獲取反射物件的 Type,直接使用 reflect.TypeOf() 函數。例如:
func TypeOf(i interface{}) Type
Type 裡面只有型別資訊,所以直接從一個 Type 介面變數裡面是無法獲得範例的 Value 的,但可以通過該 Type 構建一個新範例的 Value。reflect 包提供了兩種方法,範例如下:
//New 返回的是一個 Value,該 Value 的 type 為 PtrTo(typ),即 Value 的 Type 是指定 typ 的指標型別 func New(typ Type) Value //Zero 返回的是一個 typ 型別的零佳,注意返回的 Value 不能定址,位不可改變 func Zero(typ Type) Value
如果知道一個型別值的底層存放地址,則還有一個函數是可以依據 type 和該地址值恢復出 Value 的。例如:
func NewAt(typ Type, p unsafe.Pointer) Value
從反射物件 Value 到 Type 可以直接呼叫 Value 的方法,因為 Value 內部存放著到 Type 型別的指標。例如:
func (v Value) Type() Type
Value 本身就包含型別和值資訊,reflect 提供了豐富的方法來實現從 Value 到範例的轉換。例如:
//該方法最通用,用來將 Value 轉換為空介面,該空介面內部存放具體型別範例 //可以使用介面型別查詢去還原為具體的型別 func (v Value) Interface() (i interface{}) //Value 自身也提供豐富的方法,直接將 Value 轉換為簡單型別範例,如果型別不匹配,則直接引起 panic func (v Value) Bool () bool func (v Value) Float() float64 func (v Value) Int() int64 func (v Value) Uint() uint64
從一個指標型別的 Value 獲得值型別 Value 有兩種方法,範例如下。
//如果 v 型別是介面,則 Elem() 返回介面繫結的範例的 Value,如採 v 型別是指標,則返回指標值的 Value,否則引起 panic func (v Value) Elem() Value //如果 v 是指標,則返回指標值的 Value,否則返回 v 自身,該函數不會引起 panic func Indirect(v Value) Value
指標型別 Type 到值型別 Type。例如:
//t 必須是 Array、Chan、Map、Ptr、Slice,否則會引起 panic //Elem 返回的是其內部元素的 Type t.Elem() Type
值型別 Type 到指標型別 Type。例如:
//PtrTo 返回的是指向 t 的指標型 Type func PtrTo(t Type) Type
Value 值的修改涉及如下兩個方法:
//通過 CanSet 判斷是否能修改 func (v Value ) CanSet() bool //通過 Set 進行修改 func (v Value ) Set(x Value)
Value 值在什麼情況下可以修改?我們知道範例物件傳遞給介面的是一個完全的值拷貝,如果呼叫反射的方法 reflect.ValueOf() 傳進去的是一個值型別變數, 則獲得的 Value 實際上是原物件的一個副本,這個 Value 是無論如何也不能被修改的。
第一條是最基本的:反射可以從介面值得到反射物件。
反射是一種檢測儲存在 interface中的型別和值機制。這可以通過 TypeOf函數和 ValueOf函數得到。
第二條實際上和第一條是相反的機制,反射可以從反射物件獲得介面值。
它將 ValueOf的返回值通過 Interface()函數反向轉變成 interface變數。
前兩條就是說 介面型變數和 反射型別物件可以相互轉化,反射型別物件實際上就是指的前面說的 reflect.Type和 reflect.Value。
第三條不太好懂:如果需要操作一個反射變數,則其值必須可以修改。
反射變數可設定的本質是它儲存了原變數本身,這樣對反射變數的操作,就會反映到原變數本身;反之,如果反射變數不能代表原變數,那麼操作了反射變數,不會對原變數產生任何影響,這會給使用者帶來疑惑。所以第二種情況在語言層面是不被允許的。
從relfect.Value中獲取介面interface的資訊
當執行reflect.ValueOf(interface)之後,就得到了一個型別為”relfect.Value”變數,可以通過它本身的Interface()方法獲得介面變數的真實內容,然後可以通過型別判斷進行轉換,轉換為原有真實型別。不過,我們可能是已知原有型別,也有可能是未知原有型別,因此,下面分兩種情況進行說明。
已知型別後轉換為其對應的型別的做法如下,直接通過Interface方法然後強制轉換,如下:
realValue := value.Interface().(已知的型別)
範例程式碼:
package main import ( "fmt" "reflect" ) func main() { var num float64 = 3.1415926 pointer := reflect.ValueOf(&num) value := reflect.ValueOf(num) // 可以理解為「強制轉換」,但是需要注意的時候,轉換的時候,如果轉換的型別不完全符合,則直接panic // Golang 對型別要求非常嚴格,型別一定要完全符合 // 如下兩個,一個是*float64,一個是float64,如果弄混,則會panic convertPointer := pointer.Interface().(*float64) convertValue := value.Interface().(float64) fmt.Println(convertPointer) fmt.Println(convertValue) }
執行結果:
0xc000018080
3.1415926
說明
很多情況下,我們可能並不知道其具體型別,那麼這個時候,該如何做呢?需要我們進行遍歷探測其Filed來得知,範例如下:
package main import ( "fmt" "reflect" ) type Person struct { Name string Age int Sex string } func (p Person)Say(msg string) { fmt.Println("hello,",msg) } func (p Person)PrintInfo() { fmt.Printf("姓名:%s,年齡:%d,性別:%sn",p.Name,p.Age,p.Sex) } func main() { p1 := Person{"王富貴",20,"男"} DoFiledAndMethod(p1) } // 通過介面來獲取任意引數 func DoFiledAndMethod(input interface{}) { getType := reflect.TypeOf(input) //先獲取input的型別 fmt.Println("get Type is :", getType.Name()) // Person fmt.Println("get Kind is : ", getType.Kind()) // struct getValue := reflect.ValueOf(input) fmt.Println("get all Fields is:", getValue) //{王富貴 20 男} // 獲取方法欄位 // 1. 先獲取interface的reflect.Type,然後通過NumField進行遍歷 // 2. 再通過reflect.Type的Field獲取其Field // 3. 最後通過Field的Interface()得到對應的value for i := 0; i < getType.NumField(); i++ { field := getType.Field(i) value := getValue.Field(i).Interface() //獲取第i個值 fmt.Printf("欄位名稱:%s, 欄位型別:%s, 欄位數值:%v n", field.Name, field.Type, value) } // 通過反射,操作方法 // 1. 先獲取interface的reflect.Type,然後通過.NumMethod進行遍歷 // 2. 再公國reflect.Type的Method獲取其Method for i := 0; i < getType.NumMethod(); i++ { method := getType.Method(i) fmt.Printf("方法名稱:%s, 方法型別:%v n", method.Name, method.Type) } }
執行結果:
get Type is : Person
get Kind is : struct
get all Fields is: {王富貴 20 男}
欄位名稱:Name, 欄位型別:string, 欄位數值:王富貴
欄位名稱:Age, 欄位型別:int, 欄位數值:20
欄位名稱:Sex, 欄位型別:string, 欄位數值:男
方法名稱:PrintInfo, 方法型別:func(main.Person)
方法名稱:Say, 方法型別:func(main.Person, string)
獲取未知型別的interface的具體變數及其型別的步驟為:
獲取未知型別的interface的所屬方法(函數)的步驟為:
以上就是Go利用反射reflect實現獲取介面變數資訊的詳細內容,更多關於Go reflect獲取介面資訊的資料請關注it145.com其它相關文章!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45