zoukankan      html  css  js  c++  java
  • [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
    }
    

    利用反射可以查看结构体或其他类型的内存分配情况,如内存大小,字节对齐的大小等....

    http://www.fly63.com/article/detial/7405

    Size():关于内存

    为什么要字节对齐?

    加快访问速度,减少读取次数。如果不对齐,那么一个数据的地址可能会分散到用两个字节,那么CPU就可能需要分两次读取。

    img

    我们假设CPU以4字节为单位读取内存。

    如果变量在内存中的布局按4字节对齐,那么读取a变量只需要读取一次内存,即word1;读取b变量也只需要读取一次内存,即word2。

    而如果变量不做内存对齐,那么读取a变量也只需要读取一次内存,即word1;但是读取b变量时,由于b变量跨越了2个word,所以需要读取两次内存,分别读取word1和word2的值,然后将word1偏移取后3个字节,word2偏移取前1个字节,最后将它们做或操作,拼接得到b变量的值。

    显然,内存对齐在某些情况下可以减少读取内存的次数以及一些运算,性能更高。

    另外,由于内存对齐保证了读取b变量是单次操作,在多核环境下,原子性更容易保证。

    但是内存对齐提升性能的同时,也需要付出相应的代价。由于变量与变量之间增加了填充,并没有存储真实有效的数据,所以占用的内存会更大。这也是一个典型的空间换时间的应用场景。

    • 增加CPU吞吐量,减少读取次数
    • 保证原子操作
    • 空间换时间,如果没有安排好内存的话,中间会多出很多空白

    part1

    img

    part2

    img

    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
    
    
  • 相关阅读:
    Java栈、堆、方法区
    Java冒泡算法
    JDK8 API下载
    java 对两个整数变量的值进行互换。三种方法
    Java中double相减精度的问题,和解决方法
    window.onload问题
    ES6中class详解
    YOLO V2
    YOLO V1
    ORB-SLAM2源码解析
  • 原文地址:https://www.cnblogs.com/biningooginind/p/12769505.html
Copyright © 2011-2022 走看看