zoukankan      html  css  js  c++  java
  • golang 反射

    转自:http://golanghome.com/post/546

    自己在用Go写Web框架时,遇到要从接口中返回对象信息的技术问题。网上关于Go中接口反射的资料较少,所以自己学习了一段时间,特将结果与大家分享。

    代码约定

    import (
        "fmt"
        "reflect"
    )
    
    type boy struct {
        Name string  
        age  int
    }
    
    type human interface {
        SayName()
        SayAge()
    }
    
    func (this *boy) SayName() {
        fmt.Println(this.Name)
    }
    
    func (this *boy) SayAge() {
        fmt.Println(this.age)
    }
    
    func main() {
        // 定义接口变量
        var i human
        // 初始化对象,jown持有对象指针。
        jown := &boy{
            Name: "jown",
            age: 15,
        }
        // 因为boy实现了human中的方法,所以它实现了human接口。
        // 这时,i就指向jown对象。
        i = jown 
    
        // 通过反射获取接口i 的类型和所持有的值。
        t := reflect.TypeOf(i)
        v := reflect.ValueOf(i)
    
        // ... 后续操作
    }

    t,v 的打印结果:

    tv 实现了String() string 所对应的接口,所以可以在fmt中打印出来。

    fmt.Println(t)
    fmt.Println(v)
    
    // 打印结果
    *main.boy 
    <*main.boy Value>

    从上面的打印结果可见:

    1. t 表示i接口的类型为指向main包下struct boy的指针类型。它可以用来存储指向boy的指针。
    2. v 表示i接口目前的所存储值为指向main包下struct boy的指针,也可以理解为上面代码中的jown

    得到了这些信息,我们就可以进行后续操作:

    通过接口i查询对象的名字

    reflect.Type类型下有一个方法Name() string,用来返回不包含包名的类型的名字。所以,我们需要知道i所存储的对象的类型。reflect.Elem() Type可以返回类型的成员类型。这样我们就可以用这两个函数返回对象的名字。

        // 获取i所指向的对象的类型
        structType := t.Elem()
        // 获取对象的名字
        structName := structType.Name()
        fmt.Println(structName)
    
        // 打印结果
        boy

    通过接口i查询对象的方法信息

    由于在Go中,func也是一种类型,所以对象的方法存在值和类型的说法。

    通过t获取对象方法的信息。

    利用t的MethodByName() Method方法:

        method, _ := t.MethodByName("SayAge")
        fmt.Println(method)
    
        // 打印结果
        {SayAge  func(*main.boy) <func(*main.boy) Value> 1}

    它返回了一个对象方法的信息集合即Method,关于Method结构定义在reflect/type.go下具体为:

    // Method represents a single method.
    type Method struct {
        // Name is the method name.
        // PkgPath is the package path that qualifies a lower case (unexported)
        // method name.  It is empty for upper case (exported) method names.
        // The combination of PkgPath and Name uniquely identifies a method
        // in a method set.
        // See http://golang.org/ref/spec#Uniqueness_of_identifiers
        Name    string
        PkgPath string
    
        Type  Type  // method type
        Func  Value // func with receiver as first argument
        Index int   // index for Type.Method
    }

    可以看出该信息包含对象方法的名字,包路径(导出的方法,此路径为空),方法的类型,方法的接收者,该方法在对象中的位置从0开始。

    通过v获取对象方法的信息。

    利用v.MethodByName() Value方法:

        method := v.MethodByName("SayAge")
        fmt.Println(method)
    
        // 打印结果
        <*main.boy Value>

    它又返回了一个Value类型。我们通过类型中的方法获取一些对象方法的信息:

        // 返回方法的地址
        fmt.Println(method.Pointer())
        // 返回方法的类型
        fmt.Println(method.Type())
        // 对象方法是否可以更改
        fmt.Println(method.CanSet())
        
        //打印结果
        4527472
            func()
        false

    通过v调用方法。

    注意,调用的方法必须是可导出的。

        // 有输入参数的方法调用。例如, func (this *boy) SayName (name string) {}。
        // 构造输入参数
        args := []reflect.Value{reflect.ValueOf("liming")}
        // 通过v进行调用
        v.MethodByName("SayName").Call(args)
    
        // 无输入参数的方法调用。例如,func (this *boy) SayName(){}。
        // 构造zero value
        args := make([]reflect.Value, 0)
        // 通过v进行调用
        v.MethodByName("SayName").Call(args)

    总结

    通过上面的代码,Go的反射比较方便和强大。在静态类型语言中实现反射是一件挺伟大的事。当然,这也是得益于Go有一层中间件Runtime,reflect包的大部分功能靠它来实现。上面只是列举了通过接口反射对象的方法信息以及调用方法。我们还可以加以变通来反射对象的字段信息,已经动态修改字段内容等等。总的来说,运用好reflect包对写通用型框架还是很有帮助的。在此只是抛砖引玉,还是望大神Carry,指导。

  • 相关阅读:
    【SAS NOTE】OUTPUT
    【SAS NOTES】_NULL_
    【SAS NOTE】sas 9.2 安装
    【SAS NOTE】FREQ
    纯数学教程 Page 203 例XLI (1)
    纯数学教程 Page 203 例XLI (3)
    纯数学教程 Page 203 例XLI (2)
    Prove Cauchy's inequality by induction
    纯数学教程 Page 325 例LXVIII (15) 调和级数发散
    纯数学教程 Page 325 例LXVIII (15) 调和级数发散
  • 原文地址:https://www.cnblogs.com/rojas/p/4389389.html
Copyright © 2011-2022 走看看