天天看點

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 *rtype

word unsafe.Pointer

就可以了。

例如

w := os.Stdout

,該變量的接口值在記憶體裡是這樣的:

golang擷取變量資料類型

那麼對于第三種,類型斷言是怎麼判斷是不是某個接口呢?回到最初,在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
}      

轉載于:https://www.cnblogs.com/xbblogs/p/9489489.html