亚洲视频二区_亚洲欧洲日本天天堂在线观看_日韩一区二区在线观看_中文字幕不卡一区

公告:魔扣目錄網(wǎng)為廣大站長提供免費收錄網(wǎng)站服務(wù),提交前請做好本站友鏈:【 網(wǎng)站目錄:http://www.430618.com 】, 免友鏈快審服務(wù)(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網(wǎng)站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

 

1. 引言

反射是現(xiàn)代編程語言中非常重要的一個特性,尤其是在面向?qū)ο缶幊陶Z言中

此前的文章中,我們看到 golang 如何實現(xiàn)面向?qū)ο蟮姆庋b: 通過 GoLang 實現(xiàn)面向?qū)ο笏枷?/p>

如果能夠熟練運用反射特性,可以在很多情況下寫出通用性極強的代碼,達到事半功倍的效果,那么,究竟什么是反射,在 golang 中反射又是如何實現(xiàn)的,本文我們就來詳細(xì)解讀。

2. 反射

什么是反射?為什么要使用反射?這是本文開始前必須要解決的兩個問題。

2.1. 什么是反射

反射機制是現(xiàn)代編程語言中一個比較高級的特性。 在編譯時不知道類型的情況下,通過反射機制可以獲取對象的類型、值、方法甚至動態(tài)改變對象的成員,這就是反射機制。

2.2. 為什么使用反射

在很多時候,我們都希望代碼具備良好的通用性。 最基本的,對于 fmt.Print 函數(shù),他可以接受任意類型的對象作為參數(shù),我們無法羅列出究竟有多少種入?yún)⒌目赡埽藭r,主動獲取入?yún)⒌念愋途涂梢源蟠蠛喕a的編寫。 更進一步,對于依賴注入與面向切面等設(shè)計模式來說,我們需要為被注入對象動態(tài)添加成員,動態(tài)調(diào)用對象的不同成員。 顯然,反射的存在極大地增加了代碼的靈活度與通用性。

3. golang 與反射

之前的文章中,我們講了 golang 的接口: golang 中的接口

golang 的接口作為 golang 語言中運行時類型抽象的主要工具,它的實現(xiàn)與反射機制的實現(xiàn)有著非常密切的關(guān)聯(lián),他們都涉及 golang 中是如何管理類型的。 golang 中有兩種類型:

  1. static type — 靜態(tài)類型,創(chuàng)建變量時指定的類型,如 var str string,str 的類型 string 就是他的靜態(tài)類型
  2. concrete type — 運行時類型,如一個變量實現(xiàn)了接口中全部方法,那么這個變量的 concrete type 就是該接口類型

所以,golang 中,反射是必須與接口類型結(jié)合使用的。

4. golang 中的接口

golang 中的接口使用下面的幾個結(jié)構(gòu)實現(xiàn)的:

type emptyInterface struct {
    typ  *rtype
    word unsafe.Pointer
}

// nonEmptyInterface is the header for an interface value with methods.
type nonEmptyInterface struct {
    // see ../runtime/iface.go:/Itab
    itab *struct {
        ityp *rtype // static interface type
        typ  *rtype // dynamic concrete type
        hash uint32 // copy of typ.hash
        _    [4]byte
        fun  [100000]unsafe.Pointer // method table
    }
    word unsafe.Pointer
}

type rtype struct {
    size       uintptr
    ptrdata    uintptr  // number of bytes in the type that can contain pointers
    hash       uint32   // hash of type; avoids computation in hash tables
    tflag      tflag    // extra type information flags
    align      uint8    // alignment of variable with this type
    fieldAlign uint8    // alignment of struct field with this type
    kind       uint8    // enumeration for C
    alg        *typeAlg // algorithm table
    gcdata     *byte    // garbage collection data
    str        nameOff  // string form
    ptrToThis  typeOff  // type for pointer to this type, may be zero
}

// An InterfaceType node represents an interface type.
InterfaceType struct {
    Interface  token.Pos  // position of "interface" keyword
    Methods    *FieldList // list of methods
    Incomplete bool       // true if (source) methods are missing in the Methods list
}

復(fù)制

因為 golang 中指針類型與指向區(qū)域的數(shù)據(jù)類型必須一致且不能變更,這為抽象功能的實現(xiàn)帶來了太大的局限,于是 golang 中提供了 unsafe 包,提供了對指針的增強功能,unsafe.Pointer類似于C中的void*,任何類型的指針都可以轉(zhuǎn)換為unsafe.Pointer 類型,unsafe.Pointer 類型也可以轉(zhuǎn)換為任何指針類型。 從上面的代碼中,我們看到,在 golang 中,不具有方法的接口類型與具有方法的接口類型是分別通過 eface 與 iface 兩種類型實現(xiàn)的。

 

eface 與 iface 兩者都同樣是由兩個指針來實現(xiàn)的,分別指向接口本身的類型描述結(jié)構(gòu)與接口實現(xiàn)的內(nèi)存空間。

4.1. 接口類型斷言的實現(xiàn)

此前介紹接口的文章中,我們有介紹到接口的類型斷言,其實現(xiàn)原理就是通過將斷言類型的 _type 與 data 指針指向的數(shù)據(jù)空間中的 type 進行比較實現(xiàn)的。 因此,即使斷言類型與數(shù)據(jù)類型在內(nèi)存中是一模一樣的,也無法通過斷言實現(xiàn)其類型的轉(zhuǎn)換:

package main

import "fmt"

type temprature int

func main() {
    var temprature interface{} = temprature(5)
    fmt.Printf("temprature is %d", temprature.(int))
}

復(fù)制

雖然在內(nèi)存中,我們定義的 temprature 與 int 是相同的,但其 _type 值是不同的,因此上述代碼拋出了 panic:

panic: interface conversion: interface {} is main.temprature, not int

5. 反射的實現(xiàn) — reflect 包

在 golang 中,reflect 包實現(xiàn)了反射機制,它定義了兩個重要的類型:Type 和 Value,分別用來獲取接口類型變量的實際類型與值。 獲取他們的方法就是 TypeOf 和 ValueOf 方法。 我們修改一下上面的示例代碼:

package main

import (
    "fmt"
    "reflect"
)

type temprature int

func main() {
    var temp interface{} = temprature(5)
    fmt.Printf("temprature is %dn", temp.(temprature))
    itype := reflect.TypeOf(temp)
    ivalue := reflect.ValueOf(temp)
    fmt.Printf("%v: %v", itype, ivalue)
}

復(fù)制

打印出了:

temprature is 5 main.temprature: 5

可以看到,通過 TypeOf 與 ValueOf 方法,我們已經(jīng)取到了接口變量的類型和實際的值。

6. golang 反射的實現(xiàn)原理

讓我們來看一下 TypeOf 與 ValueOf 的實現(xiàn)。

6.1. TypeOf 源碼

func TypeOf(i interface{}) Type {
    eface := *(*emptyInterface)(unsafe.Pointer(&i))
    return toType(eface.typ)
}

func toType(t *rtype) Type {
    if t == nil {
        return nil
    }
    return t
}

復(fù)制

TypeOf 的實現(xiàn)通過 unsafe.Pointer 進行指針類型的強制轉(zhuǎn)換,從而通過返回的實例中獲取到內(nèi)存中數(shù)據(jù)的實際類型字段。

6.2. ValueOf 源碼

func ValueOf(i interface{}) Value {
    if i == nil {
        return Value{}
    }
    escapes(i)

    return unpackEface(i)
}

func escapes(x interface{}) {
    if dummy.b {
        dummy.x = x
    }
}

func unpackEface(i interface{}) Value {
    e := (*emptyInterface)(unsafe.Pointer(&i))
    // NOTE: don't read e.word until we know whether it is really a pointer or not.
    t := e.typ
    if t == nil {
        return Value{}
    }
    f := flag(t.Kind())
    if ifaceIndir(t) {
        f |= flagIndir
    }
    return Value{t, e.word, f}
}

復(fù)制

ValueOf 的實現(xiàn)相較于 TypeOf 的實現(xiàn)顯得略為復(fù)雜一些。 在 unpackEface 函數(shù)中,同樣通過 unsafe.Pointer 將傳入?yún)?shù)轉(zhuǎn)換為了 emptyInterface 類型,從而可以獲取到傳入?yún)?shù)的類型字段與指向?qū)嶋H數(shù)據(jù)的指針,最終封裝為 Value 類型值返回:

type flag uintptr

type Value struct {
    typ *rtype
    ptr unsafe.Pointer
    flag
}

復(fù)制

這個結(jié)構(gòu)正是 golang 保存任何一個類型變量的存儲結(jié)構(gòu),因此,ValueOf 的返回值可以直接作為變量值來使用。

7. 后記

那么,在實際的使用場景中,反射能夠為我們帶來哪些便捷的功能呢?敬請期待下一篇文章 — golang 中反射的使用。

分享到:
標(biāo)簽:golang
用戶無頭像

網(wǎng)友整理

注冊時間:

網(wǎng)站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網(wǎng)站吧!
最新入駐小程序

數(shù)獨大挑戰(zhàn)2018-06-03

數(shù)獨一種數(shù)學(xué)游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學(xué)四六

運動步數(shù)有氧達人2018-06-03

記錄運動步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓(xùn)練成績評定2018-06-03

通用課目體育訓(xùn)練成績評定