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,指导。

  • 相关阅读:
    Privacy & Logic
    Rules of Evidence
    Court terms & Judicial opinions
    Objections in Court
    US Trial Procedures
    Jeremy Jaynes v. Va.
    C++\CLI编程(一、命名空间)
    C++\CLI编程(一、命名空间)
    C++关于#include 两种 引用方式
    C++关于#include 两种 引用方式
  • 原文地址:https://www.cnblogs.com/rojas/p/4389389.html
Copyright © 2011-2022 走看看