[go]反射
一、reflect.Typeof()
如果傳入的是一個介面,如果是動態綁定了一個實現結構體的,則顯示具體結構體的Type,否則是介面的名字。這個方法返回的是一個Type介面,其實就是返回了 綁定類型的rtype,這個指針在Value這個結構體有
a:=A{}
a.age=19
a.name="PB"
i:=1
log.Println(reflect.TypeOf(a).Name()) //類型的名字 A
log.Println(reflect.TypeOf(i).Name()) //int
//底層基礎類型
log.Println(reflect.TypeOf(a).Kind(),reflect.TypeOf(i).Kind()) //struct int
log.Println(reflect.TypeOf(a).Kind().String()=="struct") //true
//返回的是Kind類型 需要通過string轉化 否則是一個uint類型
Typeof返回的是一個Type介面下面看看Type介面有哪些實現
type Student struct {
name string `pb:"名字"`
age int `json:"年齡"`
}
type Part1 struct {
a bool
b int32 //4 byte
c int8
d int64
e byte
}
type Part2 struct {
e byte
c int8
a bool
b int32
d int64
}
利用反射可以查看結構體或其他類型的記憶體分配情況,如記憶體大小,位元組對齊的大小等....
//www.fly63.com/article/detial/7405
Size():關於記憶體
為什麼要位元組對齊?
加快訪問速度,減少讀取次數。如果不對齊,那麼一個數據的地址可能會分散到用兩個位元組,那麼CPU就可能需要分兩次讀取。
我們假設CPU以4位元組為單位讀取記憶體。
如果變數在記憶體中的布局按4位元組對齊,那麼讀取a變數只需要讀取一次記憶體,即word1;讀取b變數也只需要讀取一次記憶體,即word2。
而如果變數不做記憶體對齊,那麼讀取a變數也只需要讀取一次記憶體,即word1;但是讀取b變數時,由於b變數跨越了2個word,所以需要讀取兩次記憶體,分別讀取word1和word2的值,然後將word1偏移取後3個位元組,word2偏移取前1個位元組,最後將它們做或操作,拼接得到b變數的值。
顯然,記憶體對齊在某些情況下可以減少讀取記憶體的次數以及一些運算,性能更高。
另外,由於記憶體對齊保證了讀取b變數是單次操作,在多核環境下,原子性更容易保證。
但是記憶體對齊提升性能的同時,也需要付出相應的代價。由於變數與變數之間增加了填充,並沒有存儲真實有效的數據,所以佔用的記憶體會更大。這也是一個典型的空間換時間的應用場景。
- 增加CPU吞吐量,減少讀取次數
- 保證原子操作
- 空間換時間,如果沒有安排好記憶體的話,中間會多出很多空白
part1
part2
func main(){
s1 := Student{"pb", 12}
type_s1 := reflect.TypeOf(s1)
type_part1 := reflect.TypeOf(Part1{})
type_part2 := reflect.TypeOf(Part2{})
//關於記憶體
log.Println(type_s1.Align(), type_part1.Align()) //8 8
log.Println(type_s1.FieldAlign(), type_part1.FieldAlign()) //8 8
log.Println(type_part1.Size(), type_part2.Size()) //32 16 why?上圖
}
Key() Elem()
type MyMap map[int]string
log.Println(reflect.TypeOf(MyMap{}).Key()) //返回map類型 key的類型 int
log.Println(reflect.TypeOf(MyMap{}).Elem()) //返回 容器類型中元素的類型 string
//It panics if the type's Kind is not Array, Chan, Map, Ptr, or Slice.
Method() MethodByName()
反射方法。第一個傳入0 1 2來表示方法 後面那個傳入字元串
FieldByXXXX()
反射屬性,Tag屬性可以獲得屬性後面的註解調用Get方法
二、reflect.Valueof()
這是一個結構體,可以操作對象的值,每個方法都返回Value來達到鏈式調用的目的,
Type能實現的功能Value都可以
type Value struct {
typ *rtype //Typeof返回的東西 繼承了Type介面 保存了一個數據的類型和底層指針
ptr unsafe.Pointer
flag
}
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
}
Method()
type Stu struct {
name string "名字"
age int "年齡"
}
func (s *Stu) Say() {
fmt.Println(s.name)
}
func (s Stu) Hello() {
fmt.Println(s.name)
}
func (s Stu) Hello2(ss string,i int) {
fmt.Println(ss,i)
}
s := Stu{"pb", 12}
v := reflect.ValueOf(s)
fmt.Println(v,v.Field(0))
fmt.Println(v.Kind(), v.Type().Name())
fmt.Println(v.FieldByName("name"), v.Field(0))
//操作方法 方法位置按照函數名字進行字典序排序
v.Method(0).Call(nil) //#調用無參函數
v.Method(1).Call([]reflect.Value{reflect.ValueOf("OK"), reflect.ValueOf(1)}) //#調用有參函數 必須是Value類型
Elem()
要修改對象的值,必須指針的Value調用Elem方法才可以修改
只能指針的Value(包括動態綁定的介面,如果這個介面的值是指針類型也可)才可以調用並且修改原來對象的值
s = Stu{Name:"biningo",age:18}
sv:=reflect.ValueOf(s)
log.Println(sv.Field(0))
pv:=reflect.ValueOf(&s)
//// It panics if v's Kind is not Interface or Ptr.
//log.Println(reflect.ValueOf(s).Elem().CanSet())
log.Println(pv.Elem().Field(0),pv.Elem().Field(0).CanSet()) //biningo true
pv.Elem().Field(0).SetString("BININGO")
log.Println(pv.Elem().Field(1).CanSet()) //false 必須要大寫的欄位才可以設置
log.Println(s) //BININGO 18
//對於沒有介面的類型來說 返回的就是一個指針
i:=pv.Interface() //返回空介面
//Valueof返回的是實際動態綁定的類型 這裡是*Stu
log.Println(reflect.ValueOf(i).Elem().CanSet()) //true 如果不加Elem則false
三、Value和Type相互轉換
Type和Value可以相互轉化
通過Type來創建一個Value
t:=sv.Type() //Value->Type
log.Println(t.Name()) //Stu
//Type創建Value
s2:=reflect.New(t)
log.Println(s2.Type(),s2.Elem().CanSet(),s2) //true因為返回的是*Stu 都是默認值
s2.Elem().Field(0).SetString("Biningo2")
//s2.Elem().Field(1).SetInt(19) 私有欄位不可設置
log.Println(s2)
Value可以直接轉化為Type
調用Value的Type方法即可
Value轉化為具體對象
Value-->Interface-->Obj
//Value轉化為具體對象
//Value-->Interface-->Obj
inter:=sv.Interface() //*Stu
s1:=inter.(Stu)
log.Println(s1) // biningo 18




