1. 程式人生 > >【go原始碼分析】go原始碼之反射reflect原始碼分析

【go原始碼分析】go原始碼之反射reflect原始碼分析

1. 反射描述

      反射是指能夠自描述和自控制,通過採用某種機制來實現對自己行為的描述。Golang語言實現了反射,反射機制就是在執行時動態的呼叫物件的方法和屬性,官方自帶的reflect包就是反射相關的

      Golang設計的原則中變數包括(type, value)兩部分,反射主要與Golang的interface型別相關,只有interface型別才有反射機制,實現路徑為src/reflect檔案中

 

  1.1 靜態型別

        靜態型別語言。每個變數的型別在編譯時都是確定的:int,float32, *AutoType, []byte,

  1.2 動態 型別

       動態型別:執行時給這個變數複製(如果值為nil的時候沒有動態型別)。一個變數的動態型別在執行時可能改變,這主要依賴於它的賦值(前提是這個變數時介面型別)

 

2. 介面

  2.1 Type介面

  • Name: 返回型別的名稱。 沒有型別名稱返回空字串
  • Kind: 返回type型別,如2.2所示
  • Elem: 如果不是Array, Chan, Map, Ptr, or Slice將產生panic
  • Field: 返回第i個欄位,只能使用在struct型別,其他型別或者躍界將panic
  • Key: 返回map型別的key型別,非map型別將panic
  • Len: 返回陣列的長度,其他型別將panic
type Type interface {
        // Methods applicable to all types.
        
        // Align returns the alignment in bytes of a value of
        // this type when allocated in memory.
        Align() int
        
        // FieldAlign returns the alignment in bytes of a value of
        // this type when used as a field in a struct.
        FieldAlign() int
        Name() string
        Kind() Kind
        Elem() Type
        
        // Field returns a struct type's i'th field.
        // It panics if the type's Kind is not Struct.
        // It panics if i is not in the range [0, NumField()).
        Field(i int) StructField

        // Key returns a map type's key type.
        // It panics if the type's Kind is not Map.
        Key() Type

        // Len returns an array type's length.
        // It panics if the type's Kind is not Array.
        Len() int

  2.2 Kind型別

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
) 

  2.3 emptyInterface結構體

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

 

3. Type

  3.1 TypeOf函式

    動態獲取輸入引數介面中的值的型別,如果介面為空則返回nil

    轉換為emptyInterface

// 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)
}

  3.2 rtype結構體,實現了Type介面

  • size: 儲存這個型別的一個值所需要的位元組數(值佔用的位元組數)
  • algin: 這個型別的一個變數在記憶體中的對齊後的所用的位元組數 (變數佔的位元組數)
  • FieldAlign: 這種型別的變數如果是struct中的欄位,那麼它對齊後所用的位元組數
// rtype is the common implementation of most values.
// It is embedded in other struct types.
//              
// rtype must be kept in sync with ../runtime/type.go:/^type._type.
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
} 

 

4. Value

    Value描述物件的值資訊,並不是所有的方法對任何的型別都有意義,特定的方法只適用於特定的型別

type Value struct {
        // typ holds the type of the value represented by a Value.
        typ *rtype
       
        // Pointer-valued data or, if flagIndir is set, pointer to data.
        // Valid when either flagIndir is set or typ.pointers() is true.
        ptr unsafe.Pointer
       
        // flag holds metadata about the value.
        // The lowest bits are flag bits:
        //      - flagStickyRO: obtained via unexported not embedded field, so read-only
        //      - flagEmbedRO: obtained via unexported embedded field, so read-only
        //      - flagIndir: val holds a pointer to the data
        //      - flagAddr: v.CanAddr is true (implies flagIndir)
        //      - flagMethod: v is a method value.
        // The next five bits give the Kind of the value.
        // This repeats typ.Kind() except for method values.
        // The remaining 23+ bits give a method number for method values.
        // If flag.kind() != Func, code can assume that flagMethod is unset.
        // If ifaceIndir(typ), code can assume that flagIndir is set.
        flag
       
        // A method value represents a curried method invocation
        // like r.Read for some receiver r. The typ+val+flag bits describe
        // the receiver r, but the flag's Kind bits say Func (methods are
        // functions), and the top bits of the flag give the method number
        // in r's type's method table.
}

 

總結

   Golang reflect慢主要有兩個原因

  • 涉及到記憶體分配以及後續的GC
  • reflect實現裡面有大量的列舉,也就是for迴圈,比如型別之類的

 

示例

package main

import (
	"fmt"
	"reflect"
)

type Bird struct {
	name     string
	age      int
	location string
}

func (b Bird) Sing() string {
	return "sing func"
}

func (b *Bird) Fly() string {
	return "fly func"
}

func (b *Bird) Eat() {
}

func main() {
	bird := Bird{
		name:     "pig",
		age:      100,
		location: "Beijing",
	}

	t := reflect.TypeOf(bird)

	fmt.Printf("type t = %#v\n", t)

	fmt.Printf("type name = %#v\n", t.Name())
	fmt.Printf("type string = %#v\n", t.String())
	fmt.Printf("type pkg path = %#v\n", t.PkgPath())
	fmt.Printf("type kind = %#v\n", t.Kind())
	fmt.Printf("type size = %#v\n", t.Size())

	fieldType, _ := t.FieldByName("age")
	fmt.Printf("type field: %v", fieldType)

	v := reflect.ValueOf(bird)

	fmt.Printf("value of: %#v\n", v)
	fmt.Printf("value of Type: %#v\n", v.Type())
	fmt.Printf("value of Type name: %#v\n", v.Type().Name())
	fmt.Printf("value of Type size: %#v\n", v.Type().Size())
	fmt.Printf("value of Type kind: %#v\n", v.Type().Kind())

	fieldValue := v.FieldByName("age")

	fmt.Printf("value field age  = %#v", fieldValue)
}