中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Go語言之反射

發布時間:2020-07-07 06:31:13 來源:網絡 閱讀:1260 作者:baby神 欄目:編程語言

和Java語言一樣,Go也實現運行時反射,這為我們提供一種可以在運行時操作任意類型對象的能力。比如我們可以查看一個接口變量的具體類型,看看一個結構體有多少字段,如何修改某個字段的值等。


TypeOf和ValueOf


在Go的反射定義中,任何接口都會由兩部分組成的,一個是接口的具體類型,一個是具體類型對應的值。比如var i int = 3 ,因為interface{}可以表示任何類型,所以變量i可以轉為interface{},所以可以把變量i當成一個接口,那么這個變量在Go反射中的表示就是<Value,Type>,其中Value為變量的值3,Type變量的為類型int


在Go反射中,標準庫為我們提供兩種類型來分別表示他們reflect.Valuereflect.Type,并且提供了兩個函數來獲取任意對象的ValueType


func main() {
    u:= User{"張三",20}
    t:=reflect.TypeOf(u)
    fmt.Println(t)
}
type User struct{    Name string    Age int
}


reflect.TypeOf可以獲取任意對象的具體類型,這里通過打印輸出可以看到是main.User這個結構體型。reflect.TypeOf函數接受一個空接口interface{}作為參數,所以這個方法可以接受任何類型的對象。


接著上面的例子,我們看下如何反射獲取一個對象的Value


 v:=reflect.ValueOf(u)
    fmt.Println(v)


TypeOf函數一樣,也可以接受任意對象,可以看到打印輸出為{張三 20}。對于以上這兩種輸出,Go語言還通過fmt.Printf函數為我們提供了簡便的方法。


    fmt.Printf("%T\n",u)
    fmt.Printf("%v\n",u)


這個例子和以上的例子中的輸出一樣。


reflect.Value轉原始類型


上面的例子我們可以通過reflect.ValueOf函數把任意類型的對象轉為一個reflect.Value,那我們如果我們想逆向轉過回來呢,其實也是可以的,reflect.Value為我們提供了Inteface方法來幫我們做這個事情。繼續接上面的例子:


    u1:=v.Interface().(User)
    fmt.Println(u1)


這樣我們就又還原為原來的User對象了,通過打印的輸出就可以驗證。這里可以還原的原因是因為在Go的反射中,把任意一個對象分為reflect.Valuereflect.Type,而reflect.Value又同時持有一個對象的reflect.Valuereflect.Type,所以我們可以通過reflect.ValueInterface方法實現還原。現在我們看看如何從一個reflect.Value獲取對應的reflect.Type



  t1:=v.Type()
    fmt.Println(t1)


如上例中,通過reflect.ValueType方法就可以獲得對應的reflect.Type


獲取類型底層類型


底層的類型是什么意思呢?其實對應的主要是基礎類型,接口、結構體、指針這些,因為我們可以通過type關鍵字聲明很多新的類型,比如上面的例子,對象u的實際類型是User,但是對應的底層類型是struct這個結構體類型,我們來驗證下。


fmt.Println(t.Kind())


通過Kind方法即可獲取,非常簡單,當然我們也可以使用Value對象的Kind方法,他們是等價的。


Go語言提供了以下這些最底層的類型,可以看到,都是最基本的。


const (
    Invalid Kind = iota    
   Bool    Int    Int8    Int16    Int32    Int64    Uint    Uint8    Uint16    Uint32    Uint64    Uintptr    Float32    Float64    Complex64    Complex128    Array    Chan    Func    Interface    Map    Ptr    Slice    String    Struct    UnsafePointer
)


遍歷字段和方法


通過反射,我們可以獲取一個結構體類型的字段,也可以獲取一個類型的導出方法,這樣我們就可以在運行時了解一個類型的結構,這是一個非常強大的功能。



for i:=0;i<t.NumField();i++ {
        fmt.Println(t.Field(i).Name)
    }    
    for i:=0;i<t.NumMethod() ;i++  {
        fmt.Println(t.Method(i).Name)
    }


這個例子打印出結構體的所有字段名以及該結構體的方法。NumField方法獲取結構體有多少個字段,然后通過Field方法傳遞索引的方式,循環獲取每一個字段,然后打印出他們的名字。


同樣的對于方法也類似,這里不再贅述。


修改字段的值


假如我們想在運行中動態的修改某個字段的值有什么辦法呢?一種就是我們常規的有提供的方法或者導出的字段可以供我們修改,還有一種是使用反射,這里主要介紹反射。


func main() {
    x:=2
    v:=reflect.ValueOf(&x)
    v.Elem().SetInt(100)
    fmt.Println(x)
}


以上就是通過反射修改一個變量的例子。


因為reflect.ValueOf函數返回的是一份值的拷貝,所以前提是我們是傳入要修改變量的地址。


其次需要我們調用
Elem方法找到這個指針指向的值。


最后我們就可以使用
SetInt方法修改值了。


以上有幾個重點,才可以保證值可以被修改,Value為我們提供了CanSet方法可以幫助我們判斷是否可以修改該對象。


我們現在可以更新變量的值了,那么如何修改結構體字段的值呢?大家自己試試。


動態調用方法


結構體的方法我們不光可以正常的調用,還可以使用反射進行調用。要想反射調用,我們先要獲取到需要調用的方法,然后進行傳參調用,如下示例:


func main() {
    u:=User{"張三",20}
    v:=reflect.ValueOf(u)

    mPrint:=v.MethodByName("Print")
    args:=[]reflect.Value{reflect.ValueOf("前綴")}
    fmt.Println(mPrint.Call(args))
}
type User struct{    Name string    Age int
}
func (u User) Print(prfix string){    fmt.Printf("%s:Name is %s,Age is %d",prfix,u.Name,u.Age)
}


MethodByName方法可以讓我們根據一個方法名獲取一個方法對象,然后我們構建好該方法需要的參數,最后調用Call就達到了動態調用方法的目的。


獲取到的方法我們可以使用IsValid 來判斷是否可用(存在)。


這里的參數是一個Value類型的數組,所以需要的參數,我們必須要通過ValueOf函數進行轉換。


向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

专栏| 淮安市| 南木林县| 太仆寺旗| 海晏县| 饶阳县| 文安县| 合肥市| 镇远县| 金华市| 桐梓县| 东城区| 集贤县| 海伦市| 延安市| 高邑县| 城市| 新乐市| 武平县| 阿拉善右旗| 洛宁县| 内丘县| 辰溪县| 永清县| 邵阳市| 新丰县| 临武县| 荣昌县| 莒南县| 华宁县| 通化县| 河源市| 福建省| 扎囊县| 工布江达县| 高淳县| 黔东| 宜良县| 湘乡市| 凤城市| 滦南县|