zoukankan      html  css  js  c++  java
  • 第十二章反射

    Go语言提供了一种机制,在编译时不知道类型的情况下,可更新变量、在运行时查看值、调用方法以及直接对它们的布局进行操作,这种机制称为反射(reflection)

    • 为什么使用反射
    • reflect.Type和reflect.Value

    反射功能由reflect包提供,它定义了两个重要的类型:Type和Value。Type表示Go语言的一个类型,它是一个有很多方法的接口,这些方法可以用来识别类型以及透视类型的组成部分,比如一个结构的各个字段或者一个函数的各个参数。reflect.Type接口只有一个实现,即类型描述符,接口值中的动态类型也是类型描述符

    7.5-->接口值:一个接口类型的值有两个部分:一个具体类型和该类型的一个值,二者称为接口的动态类型和动态值。对于像Go这样的静态类型语言,类型仅仅是一个编译时的概念,所以类型不是一个值。在我们的概念模型中,用类型描述符来提供每个类型的具体信息,比如它的名字和方法。对于一个接口值,类型部分就用对应的类型描述符来表述

    reflect.TypeOf函数接受任何的interface{}参数,并且把接口中的动态类型以reflect.Type形式返回

    t := reflect.TypeOf(3)

    fmt.Println(t.String())//"int"

    fmt.Println(t)//"int"

    把一个具体值赋给一个接口类型时会发生一个隐式类型转换,转换会生成一个包含两部分内容的接口值:动态类型部分是操作数的类型(int),动态值部分是操作数的值(3)

    因为reflect.TypeOf返回一个接口值对应的动态类型,所以它返回总是具体类型(而不是接口类型)

    reflect.Type满足fmt.Stringer。因为输出一个接口值的动态类型在调试和日志中很常用,所以fmt.Printf提供了一个简写方式%T,内部实现就使用了reflect.TypeOf

    reflect包的另一个重要类型就是value。reflect.Value可以包含一个任意类型的值

    reflect.ValueOf函数接受任意的interface{}并将接口的动态值以reflect.Value的形式返回。与reflect.TypeOf类似,reflect.ValueOf的返回值也都是具体值,不过reflect.Value也可以包含一个接口值

    reflect.ValueOf的逆操作是reflect.Value.Interface方法,它返回一个interface{}接口,与reflect.Value包含同一个具体值

    reflect.TypeOf返回类型(reflect.Type)

    reflect.ValueOf返回值(reflect.Value)

    可以从reflect.Value获得类型

    通过kind来判断类型

    package format
    
    import (
        "reflect"
        "strconv"
    )
    
    //Any把任何值格式化为一个字符串
    func Any(value interface{}) string {
        return formatAtom(reflect.ValueOf(value))
    }
    
    //formatAtom格式化一个值,且不分析它的内部结构
    func formatAtom(v reflect.Value) string {
        switch v.Kind() {
        case reflect.Invalid:
            return "invalid"
        case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
            return strconv.FormatInt(v.Int(), 10)
        case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,reflect.Uintptr:
            return strconv.FormatUint(v.Uint(), 10)
        case reflect.Bool:
            return strconv.FormatBool(v.Bool())
        case reflect.String:
            return strconv.Quote(v.String())
        case reflect.Chan, reflect.Func, reflect.Ptr, reflect.Slice, reflect.Map:
            return v.Type().String() + "0x" +
                strconv.FormatUint(uint64(v.Pointer()), 16)
        default:
            return v.Type().String() + "value"
        }
    }
    func TestTypeAndValue(t *testing.T) {
        var f int64 = 10
        t.Log(reflect.TypeOf(f), reflect.ValueOf(f))
        t.Log(reflect.ValueOf(f).Type())
    }
    
    func CheckType(v interface{}) {
        t := reflect.TypeOf(v)
        switch t.Kind(){
        case reflect.Float32, reflect.Float64:
            fmt.Println("Float")
        case reflect.Int, reflect.Int32, reflect.Int64:
            fmt.Println("Integer")
        default:
            fmt.Println("Unknown", t)
        }
    }
    
    func TestBasixType(t *testing.T) {
        var f float64 = 12
        CheckType(f)
    }
    • Display:一个递归的值显示器

    Display函数对给定的任意一个复杂值x,输出这个复杂值的完整结构,并对找到的每个元素标上这个元素的路径

    func Dispaly(name string, x interface{}) {
        fmt.Printf("Display %s (%T):
    ", name, x)
        display(name, reflect.ValueOf(x))
    }
    
    在display中,我们使用之前定义的formatAtom函数来输出基础值(基础类型、函数和通道),使用reflect.Value的一些方法来递归展示复杂类型的每个组成部分。当递归深入时,path字符串会增长,以表示如何找到当前值
    
    func display(path string, v reflect.Value) {
        switch v.Kind() {
        case reflect.Invalid:
            fmt.Println("%s = invalid
    ", path)
        case reflect.Slice, reflect.Array:
            for i := 0; i < v.Len(); i++ {
                display(fmt.Sprintf("%s[%d]", path, i), v.Index(i))
            }
        }
    }
    •  利用反射编写灵活的代码

    按名字访问结构的成员  reflect.ValueOf(*e).FieldByName("Name")

    按名字访问结构的方法  reflect.ValueOf(e).MethodByName("UpdateAge").Call([]reflect.Value{reflect.ValueOf(1)})

    type Employee struct {
        EmployeeID     string
        Name           string `format:"normal"`
        Age            int
    }
    
    //结构体tag `format:"normal"`
    func (e
    *Employee) UpdateAge(newVal int) { e.Age = newVal } type Customer struct { cookieID string Name string Age int }

    func TestInvokeByName(t *testing.T) {
    e := &Employee{"1", "Mike", 30}

    //按名字获取成员
    t.Logf("Name: value(%[1]v), Type(%[1]T)", reflect.ValueOf(*e).FieldByName("Name"))
    if nameField, ok := reflect.TypeOf(*e).FieldByName("Name"); !ok {
    t.Error("Failed to get 'Name' field.")
    } else {
    t.Log("Tag: format", nameField.Tag.Get("format"))
    }
    reflect.ValueOf(e).MethodByName("UpdateAge").Call([]reflect.Value{reflect.ValueOf(1)})
    t.Log("Updated Age:", e)
    }
    • 按名字访问结构的成员
    • 万能程序

    DeepEqual  比较切片和map?

    func TestDeepEqual(t *testing.T) {
        a := map[int]string{1: "one", 2: "two", 3: "three"}
        b := map[int]string{1: "one", 2: "two", 4: "three"}
        fmt.Println(reflect.DeepEqual(a, b))
    
        s1 := []int{1, 2, 3}
        s2 := []int{1, 2, 3}
        s3 := []int{2, 3, 1}
        t.Log("s1 == s2?", reflect.DeepEqual(s1, s2))
        t.Log("s1 == s3?", reflect.DeepEqual(s1, s3))
    
    }
    func TestFillNameAndAge(t *testing.T) {
        settings := map[string]interface{}{"Name": "Mike", "Age": 40}
        e := Employee{}
        if err := fillBySettings(&e, settings); err != nil {
            t.Fatal(err)
        }
        t.Log(e)
        c := new(Customer)
        if err := fillBySettings(c, settings); err != nil{
            t.Fatal(err)
        }
        t.Log(*c)
    }
    
    func fillBySettings(st interface{}, settings map[string]interface{}) error {
        //Elem()获得指针指向的结构
        if reflect.TypeOf(st).Elem().Kind() != reflect.Struct{
            return errors.New("the first param should be a pointer to the struct type")
        }
    
        if settings == nil {
            return errors.New("settings is nil")
        }
        
        for k, v := range settings {
            if field, ok = (reflect.ValueOf(st)).Elem().Type().FieldByName(k); !ok {
                continue
            }
            if field.Type == reflect.TypeOf(v) {
                vstr := reflect.ValueOf(st)
                vstr = vstr.Elem()
                vstr.FieldByName(k).Set(reflect.ValueOf(v))
            }
        }
        return nil
    }
    • 不安全编程

    Unsafe

    "不安全"行为的危险性

    i := 10
    f := *(*float64)(unsafe.Poniter(&i))
    • 示例:编码S表达式
    • 使用reflect.Value来设置值

    反射如何改变值?

    一个变量是一个可寻址的存储区域,其中包含了一个值,并且它的值可以通过这个地址来更新

    通过reflect.ValueOf(x)返回的reflect.Value都是不可寻址的

    调用reflect.ValueOf(&x).Elem()可以获得任意变量x可寻址的Value值

    可以通过变量的CanAddr方法来询问reflect.Value变量是否可寻址

    可以通过一个指针来间接获取一个可寻址的reflect.Value,即使这个指针是不可寻址的

    从一个可寻址的reflect.Value()获取变量需要三步

    首先,调用Addr(),返回一个Value,其中包含一个指向变量的指针

    接下来,在这个Value上调用Interface(),会返回一个包含这个指针的interface{}值

    最后,如果知道变量的类型,可以使用类型断言来把接口内容转换为一个普通指针

    之后就可以通过这个指针来更新变量

    还可以直接通过可寻址的reflect.Value来更新变量,不用通过指针,而是直接调用reflect.Value.Set方法

    • 示例:解码S表达式
    • 访问结构体字段标签
    • 显示类型的方法
    • 注意事项
  • 相关阅读:
    zoj 1239 Hanoi Tower Troubles Again!
    zoj 1221 Risk
    uva 10192 Vacation
    uva 10066 The Twin Towers
    uva 531 Compromise
    uva 103 Stacking Boxes
    稳定婚姻模型
    Ants UVA
    Golden Tiger Claw UVA
    关于upper、lower bound 的探讨
  • 原文地址:https://www.cnblogs.com/liushoudong/p/13086844.html
Copyright © 2011-2022 走看看