Golang 反射学习

首先,先引用一下很多文章中讲到的golang类型系统。

类型和接口

由于反射是基于类型系统(type system)的,所以先简单了解一下类型系统。

首先Golang是一种静态类型的语言,在编译时每一个变量都有一个类型对应,例如:int,floate32,[]byte,*MyType等等。如果我们这样声明:

type MyInt int

var i int
var j MyInt

上面的i是int类型的,j是MyInt类型的。i和j是不同的静态类型,尽管他们都有相同的相关类型(这里就是int),他们不能互相赋值除非通过强制转换。

一种非常重要的类型分类是接口类型,接口代表中方法的集合。只要一个值实现了接口定义的方法,那么这个值就可以存储这个具体的值。一个著名的例子就是io包中的Reader和Writer。

// Reader is the interface that wraps the basic Read method
type Reader interface {
  Read(p []byte) (n int,err error)
}

// Writer is the interface that wraps the basic Write method
type Writer interface {
  Write(p []byte) (n int,err error)
}

任何是实现了Read(或Write)方法的签名的类型就是实现了io.Reader(或者io.Writer)。也就是说一个io.Reader的变量可以持有任何实现了Read方法的值。

var r io.Reader
  r = os.Stdin
  r = bufio.NewReader(r)
  r = new(bytes.Buffer)
  // and so on

我们要非常清楚的知道不管r持有了哪种具体的值,r的类型永远都是io.Reader。

一个非常重要的的例子就是一个空的接口:

interface{}

这个代表一个空的方法集合并且满足任何值,只要这个值有零个或者多个方法

有人中golang中的interface是动态类型的,这个一个误导。一个interface类型的变量拥有相同的静态类型,尽管运行时这个变量的值会发生改变,但是都是满足一直都是满足这个interface的。

##interface的表示

Russ Cox曾经写个一篇博文详细讨论了golang中的interface的值。 简单类说,一个interface的值存储了一个赋给变量的具体值和这个值类型的描述。

var r io.Reader
  tty,err := os.OpenFile("/dev/tty",os.O_RDWR,0)
  if err != nil {
    return nil,err
  }
  r = tty

这个具体的例子中,r包含了一个(value,type)对,具体的就是(tty,*os.File)。*os.File实现了Read等很多方法,但是io.Reader的接口之允许访问Read方法,所以我们还可以这样做:

var w io.Writer
  w = r.(io.Writer)

通过类型断言(type assertion),因为r照样实现了io.Writer,所以我们可以将r赋值给w.

reflect.Type和reflect.Value

反射是由 reflect 包提供支持. 它定义了两个重要的类型,Type 和 Value.一个Type表示一个Go类型. 它是一个接口,提供了许多函数来区分类型和检查它们的组件。关于reflect.Type的详细描述可以查看源代码.

在程序中通过reflect.TypeOf函数返回reflect.Type. reflect.TypeOf具有识别接口实际类型的能力.

reflect.ValueOf 函数返回reflect.Value.

详细的功能请看下面的示例代码

// reflect_test project main.go
package main

import (
	"fmt"
	"reflect"
	"strconv"
	"strings"
)

type Routera struct {
	f1 int
	f2 string
	f3 []int
}

func (this *Routera) Func_1(name string,a *Routera) {
	fmt.Println("exec Func_1!!!!" + name + strconv.Itoa(a.f1))
}

func (this *Routera) Func_2() {
	fmt.Println("exec Func_2!!!!")
}

func main() {
	router := &Routera{f1: 1009}
	v := reflect.ValueOf(router)
	t := v.Type()
	fmt.Println(v)
	fmt.Println(t)
	fmt.Println(reflect.TypeOf(router))
	fmt.Println(v.NumMethod())

	for i := 0; i < v.NumMethod(); i += 1 {
		methType := v.Method(i).Type()
		//fmt.Printf("fun (%s) %s\n",t,t.Method(i).Name)
		fmt.Println(methType)
		fmt.Printf("fun (%s) %s%s\n",t.Method(i).Name,strings.TrimPrefix(methType.String(),"func"))
	}
	v.Method(0).Call([]reflect.Value{reflect.ValueOf("huangxin"),reflect.ValueOf(router)})
	v.Method(1).Call(nil)
}

该程序的运行结果为:

相关文章

什么是Go的接口? 接口可以说是一种类型,可以粗略的理解为他...
1、Golang指针 在介绍Golang指针隐式间接引用前,先简单说下...
1、概述 1.1&#160;Protocol buffers定义 Protocol buffe...
判断文件是否存在,需要用到"os"包中的两个函数: os.Stat(...
1、编译环境 OS :Loongnix-Server Linux release 8.3 CPU指...
1、概述 Golang是一种强类型语言,虽然在代码中经常看到i:=1...