golang獲取變量數據類型


如果某個函數的入參是interface{},有下面幾種方式可以獲取入參的方法:

1 fmt:

import "fmt"
func main() {
    v := "hello world"
    fmt.Println(typeof(v))
}
func typeof(v interface{}) string {
    return fmt.Sprintf("%T", v)
}

2 反射:

import (
    "reflect"
    "fmt"
)
func main() {
    v := "hello world"
    fmt.Println(typeof(v))
}
func typeof(v interface{}) string {
    return reflect.TypeOf(v).String()
}

 

3 類型斷言:

func main() {
    v := "hello world"
    fmt.Println(typeof(v))
}
func typeof(v interface{}) string {
    switch t := v.(type) {
    case int:
        return "int"
    case float64:
        return "float64"
    //... etc
    default:
        _ = t
        return "unknown"
    }
}

 

其實前兩個都是用了反射,fmt.Printf(“%T”)里最終調用的還是reflect.TypeOf()

func (p *pp) printArg(arg interface{}, verb rune) {
    ...
    // Special processing considerations.
    // %T (the value's type) and %p (its address) are special; we always do them first.
    switch verb {
    case 'T':
        p.fmt.fmt_s(reflect.TypeOf(arg).String())
        return
    case 'p':
        p.fmtPointer(reflect.ValueOf(arg), 'p')
        return
    }

reflect.TypeOf()的參數是v interface{},golang的反射是怎么做到的呢?

在golang中,interface也是一個結構體,記錄了2個指針:

  • 指針1,指向該變量的類型
  • 指針2,指向該變量的value

如下,空接口的結構體就是上述2個指針,第一個指針的類型是type rtype struct;非空接口由於需要攜帶的信息更多(例如該接口實現了哪些方法),所以第一個指針的類型是itab,在itab中記錄了該變量的動態類型: typ *rtype

// emptyInterface is the header for an interface{} value.
type emptyInterface struct {
    typ  *rtype
    word unsafe.Pointer
}

// nonEmptyInterface is the header for a interface value with methods.
type nonEmptyInterface struct {
    // see ../runtime/iface.go:/Itab
    itab *struct {
        ityp   *rtype // static interface type
        typ    *rtype // dynamic concrete type
        link   unsafe.Pointer
        bad    int32
        unused int32
        fun    [100000]unsafe.Pointer // method table
    }
    word unsafe.Pointer
}

 

我們來看看reflect.TypeOf():

// TypeOf returns the reflection Type that represents the dynamic type of i.
// If i is a nil interface value, TypeOf returns nil.
func TypeOf(i interface{}) Type {
    eface := *(*emptyInterface)(unsafe.Pointer(&i))
    return toType(eface.typ)
}

 

TypeOf看到的是空接口interface{},它將變量的地址轉換為空接口,然后將將得到的rtype轉為Type接口返回。需要注意,當調用reflect.TypeOf的之前,已經發生了一次隱式的類型轉換,即將具體類型的向空接口轉換。這個過程比較簡單,只要拷貝typ *rtypeword unsafe.Pointer就可以了。

例如w := os.Stdout,該變量的接口值在內存里是這樣的:

A *os.File interface value

那么對於第三種,類型斷言是怎么判斷是不是某個接口呢?回到最初,在golang中,接口是一個松耦合的概念,一個類型是不是實現了某個接口,就是看該類型是否實現了該接口要求的所有函數,所以,類型斷言判斷的方法就是檢查該類型是否實現了接口要求的所有函數。

走讀k8s代碼的時候,可以看到比較多的類型斷言的用法:

func LeastRequestedPriorityMap(pod *api.Pod, meta interface{}, nodeInfo *schedulercache.NodeInfo) (schedulerapi.HostPriority, error) {
    var nonZeroRequest *schedulercache.Resource
    if priorityMeta, ok := meta.(*priorityMetadata); ok {
        nonZeroRequest = priorityMeta.nonZeroRequest
    } else {
        // We couldn't parse metadata - fallback to computing it.
        nonZeroRequest = getNonZeroRequests(pod)
    }
    return calculateUnusedPriority(pod, nonZeroRequest, nodeInfo)
}

 

類型斷言的實現在src/runtime/iface.go里(?),不過這塊代碼沒看懂,等以后再更新吧。

func assertI2I2(inter *interfacetype, i iface) (r iface, b bool) {
    tab := i.tab
    if tab == nil {
        return
    }
    if tab.inter != inter {
        tab = getitab(inter, tab._type, true)
        if tab == nil {
            return
        }
    }
    r.tab = tab
    r.data = i.data
    b = true
    return
}

func assertE2I2(inter *interfacetype, e eface) (r iface, b bool) {
    t := e._type
    if t == nil {
        return
    }
    tab := getitab(inter, t, true)
    if tab == nil {
        return
    }
    r.tab = tab
    r.data = e.data
    b = true
    return
}

 

 查看原文地址


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM