Skip to content
Bunch of examples for dealing with the reflect package
Branch: master
Clone or download
Pull request Compare This branch is 4 commits ahead of a8m:master.
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
img
README.md

README.md

Golang reflect package examples

这个仓库包含了reflect包的示例 主要用于编码解码和动态调用函数
这些例子大多来自于我过去从事的项目,也有一些来自我目前正在从事的项目 您还将在示例中找到有用的注释,这将帮助您理解代码。 有些是我的,有些是从godoc网站上下载的。

如果你想贡献这个仓库,不要犹豫创建一个PR吧 gopher的logo来自@egonelbre/gophers.

目录

读取struct的tag

package main

import (
	"fmt"
	"reflect"
)

type User struct {
	Email  string `mcl:"email"`
	Name   string `mcl:"name"`
	Age    int    `mcl:"age"`
	Github string `mcl:"github" default:"a8m"`
}

func main() {
	var u interface{} = User{}
	// TypeOf返回代表u的动态类型的反射Type
	t := reflect.TypeOf(u)
	if t.Kind() != reflect.Struct {
		return
	}
	for i := 0; i < t.NumField(); i++ {
		f := t.Field(i)
		fmt.Println(f.Tag.Get("mcl"), f.Tag.Get("default"))
	}
}
//输出  
// email   
// name   
// age   
// github a8m 

调用Kind()返回的是这个列表中定义的值Kind().
Playground

获取和设置结构体字段

package main

import (
	"fmt"
	"reflect"
)

type User struct {
	Email  string `mcl:"email"`
	Name   string `mcl:"name"`
	Age    int    `mcl:"age"`
	Github string `mcl:"github" default:"a8m"`
}

func main() {
	u := &User{Name: "Ariel Mashraki"}
	// Elem返回指向u的指针
	v := reflect.ValueOf(u).Elem()
	f := v.FieldByName("Github")
	// 确保这个字段被定义,并且可以被设置
	if !f.IsValid() || !f.CanSet() {
		return
	}
	if f.Kind() != reflect.String || f.String() != "" {
		return
	}
	// 设置字段的值
	f.SetString("a8m")
	fmt.Printf("Github username was changed to: %q\n",u.Github)
}
//输出  
//Github username was changed to: "a8m"

Playground

使用字符串填充切片

package main

import (
	"fmt"
	"io"
	"reflect"
)

func main() {
	var (
		a []string
		b []interface{}
		c []io.Writer
	)
	fmt.Println(fill(&a), a) // 通过
	fmt.Println(fill(&b), b) // 通过
	fmt.Println(fill(&c), c) // 失败

}

func fill(i interface{}) error {
	v := reflect.ValueOf(i)
	// 判断是否为指针
	if v.Kind() != reflect.Ptr {
		return fmt.Errorf("non-pointer %v", v.Type())
	}
	// 获取指针v指向的值
	v = v.Elem()
	// 判断值的类型是否为slice
	if v.Kind() != reflect.Slice {
		return fmt.Errorf("can not fill non-slice value")
	}
	v.Set(reflect.MakeSlice(v.Type(), 3, 3))
	// 验证切片的类型
	if !canAssign(v.Index(0)) {
		return fmt.Errorf("can not assign string to slice elements")
	}
	for i, w := range []string{"foo", "bar", "baz"} {
		v.Index(i).Set(reflect.ValueOf(w))
	}
	return nil
}

// 接收一个string或一个空的interface
func canAssign(v reflect.Value) bool {
	return v.Kind() == reflect.String || (v.Kind() == reflect.Interface && v.NumMethod() == 0)
}

// 输出
//<nil> [foo bar baz]   
//<nil> [foo bar baz]          
//can not assign string to slice elements [<nil> <nil> <nil>]

Playground

设置一个数字的值

package main

import (
	"fmt"
	"reflect"
)

const n = 255

func main() {
	var (
		a int8
		b int16
		c uint
		d float32
		e string
	)
	fmt.Println(fill(&a), a)
	fmt.Println(fill(&b), b)
	fmt.Println(fill(&c), c)
	fmt.Println(fill(&d), c)
	fmt.Println(fill(&e), e)
}

func fill(i interface{}) error {
	v := reflect.ValueOf(i)
	if v.Kind() != reflect.Ptr {
		return fmt.Errorf("non-pointer %v", v.Type())
	}
	// get the value that the pointer v points to.
	v = v.Elem()
	switch v.Kind() {
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		if v.OverflowInt(n) {
			return fmt.Errorf("can't assign value due to %s-overflow", v.Kind())
		}
		v.SetInt(n)
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
		if v.OverflowUint(n) {
			return fmt.Errorf("can't assign value due to %s-overflow", v.Kind())
		}
		v.SetUint(n)
	case reflect.Float32, reflect.Float64:
		if v.OverflowFloat(n) {
			return fmt.Errorf("can't assign value due to %s-overflow", v.Kind())
		}
		v.SetFloat(n)
	default:
			return fmt.Errorf("can't assign value to a non-number type")
	}
	return nil
}

// 输出
//can not assign value due to int8 overflow 0
//<nil> 255
//<nil> 255
//<nil> 255
//can't assign value to a non-number type 

Playground

将键值对解码为map

package main

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

func main() {
	var (
		m0 = make(map[int]string)
		m1 = make(map[int64]string)
		m2 map[interface{}]string
		m3 map[interface{}]interface{}
		m4 map[bool]string
	)
	s := "1=foo,2=bar,3=baz"

	fmt.Println(decodeMap(s, &m0), m0) // pass
	fmt.Println(decodeMap(s, &m1), m1) // pass
	fmt.Println(decodeMap(s, &m2), m2) // pass
	fmt.Println(decodeMap(s, &m3), m3) // pass
	fmt.Println(decodeMap(s, &m4), m4) // fail
}
func decodeMap(s string, i interface{}) error {
	v := reflect.ValueOf(i)
	if v.Kind() != reflect.Ptr {
		return fmt.Errorf("non-pointer %v", v.Type())
	}
	// 获取指针v指向的值
	v = v.Elem()
	t := v.Type()
	// 如果为nil 分配新的map m2,m3,m4
	if v.IsNil() {
		v.Set(reflect.MakeMap(t))
	}
	for _, kv := range strings.Split(s, ",") {
		s := strings.Split(kv, "=")
		n, err := strconv.Atoi(s[0])
		if err != nil {
			return fmt.Errorf("failed to parse number: %v", err)
		}
		k, e := reflect.ValueOf(n), reflect.ValueOf(s[1])
		// 获取map key的类型 int
		kt := t.Key()

		if !k.Type().ConvertibleTo(kt) {
			return fmt.Errorf("can not convert key to type %v", kt.Kind())
		}
		k = k.Convert(kt)
		// 获取map value的类型
		et := t.Elem()

		if et.Kind() != v.Kind() && !e.Type().ConvertibleTo(et) {
			return fmt.Errorf("can not assign value to type %v", kt.Kind())
		}
		v.SetMapIndex(k, e.Convert(et))

	}
	return nil

}
// 输出
//<nil> map[1:foo 2:bar 3:baz]
//<nil> map[1:foo 2:bar 3:baz]
//<nil> map[1:foo 2:bar 3:baz]
//<nil> map[1:foo 2:bar 3:baz]
// can not convert key to type bool map[]

Playground

解码键值对到struct

package main

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

type User struct {
	Name    string
	Github  string
	private string
}

func main() {
	var (
		v0 User
		v1 *User
		v2 = new(User)
		v3 struct{ Name string }
	)
	s := "Name=Ariel,Github=a8m"

	fmt.Println(decodeStruct(s, &v0), v0) // pass
	fmt.Println(decodeStruct(s, v1), v1)  // fail
	fmt.Println(decodeStruct(s, v2), v2)  // pass
	fmt.Println(decodeStruct(s, v3), v3)  // fail
	fmt.Println(decodeStruct(s, &v3), v3) // pass
}
func decodeStruct(s string, i interface{}) error {
	v := reflect.ValueOf(i)
	// 判断是否是指针或者为nil
	if v.Kind() != reflect.Ptr || v.IsNil() {
		return fmt.Errorf("decode requires non-nil pointer")
	}
	// 获取指针v指向的值
	v = v.Elem()
	for _, kv := range strings.Split(s, ",") {
		s := strings.Split(kv, "=")
		f := v.FieldByName(s[0])
		//确保字段被定义 并且可以被修改
		if !f.IsValid() || !f.CanSet() {
			continue
		}
		f.SetString(s[1])
	}
	return nil
}

// 输出
//<nil> {Ariel a8m }
//decode requires non-nil pointer <nil>
//<nil> &{Ariel a8m }
//decode requires non-nil pointer {}
//<nil> {Ariel}

Playground

将结构体编码为键值对

package main

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

type User struct {
	Email   string `kv:"email,omitempty"`
	Name    string `kv:"name,omitempty"`
	Github  string `kv:"github,omitempty"`
	private string
}

func main() {
	var (
		u = User{Name: "Ariel", Github: "a8m"}
		v = struct {
			A, B, C string
		}{
			"foo",
			"bar",
			"baz",
		}
		w = &User{}
	)
	fmt.Println(encode(u))
	fmt.Println(encode(v))
	fmt.Println(encode(w))
}

// 只支持struct 并且字段类型是string
func encode(i interface{}) (string, error) {
	v := reflect.ValueOf(i)
	t := v.Type()
	if t.Kind() != reflect.Struct {
		return "", fmt.Errorf("type %s is not supported", t.Kind())
	}
	var s []string
	for i := 0; i < t.NumField(); i++ {
		f := t.Field(i)
		//跳过未导出的字段。从godoc:
		// PkgPath是限定小写(未导出)的包路径
		//字段名。它对于大写(导出)字段名是空的。
		if f.PkgPath != "" {
			continue
		}
		fv := v.Field(i)
		key, omit := readTag(f)
		// 如果值为空并且设置了omitempty就跳过
		if omit && fv.String() == "" {
			continue
		}
		s = append(s, fmt.Sprintf("%s=%s", key, fv.String()))

	}
	return strings.Join(s, ","), nil
}

func readTag(f reflect.StructField) (string, bool) {
	val, ok := f.Tag.Lookup("kv")
	if !ok {
		return f.Name, false
	}
	opts := strings.Split(val, ",")
	var omit bool

	if len(opts) == 2 {
		omit = opts[1] == "omitempty"
	}
	return opts[0], omit
}

//输出
//name=Ariel,github=a8m <nil>
//A=foo,B=bar,C=baz <nil>
//type ptr is not supported

Playground

检查底层类型是否实现接口

package main

import (
	"fmt"
	"reflect"
)

type User struct {
	Email   string `kv:"email,omitempty"`
	Name    string `kv:"name,omitempty"`
	Github  string `kv:"github,omitempty"`
	private string
}
type Marshaler interface {
	MarshalKV() (string, error)
}

func (u *User) MarshalKV() (string, error) {
	return fmt.Sprintf("name=%s,email=%s,github=%s", u.Name, u.Email, u.Github), nil
}

func main() {
	
	fmt.Println(encode(User{"boring", "Ariel", "a8m", ""}))	// 未实现接口
	fmt.Println(encode(&User{Github: "posener", Name: "Eyal", Email: "boring"}))
}

var marshalertype = reflect.TypeOf(new(Marshaler)).Elem()

func encode(i interface{}) (string, error) {
	t := reflect.TypeOf(i)
	if !t.Implements(marshalertype) {
		return "", fmt.Errorf("encode only supports structs that implement the Marshaler interface")
	}
	m, _ := reflect.ValueOf(i).Interface().(Marshaler)
	return m.MarshalKV()
}

// 输出
// encode only supports structs that implement the Marshaler interface
//name=Eyal,email=boring,github=posener <nil>

Playground

使用指针包装reflect.Value(T=>*T)

package main

import (
	"fmt"
	"reflect"
)

type User struct {
	Name string
}

func main() {
	// T => *T
	u1 := User{"a8m"}
	p1 := ptr(reflect.ValueOf(u1))
	fmt.Println(u1 == p1.Elem().Interface())

	// *T => **T
	u2 := &User{"a8m"}
	p2 := ptr(reflect.ValueOf(u2))
	fmt.Println(*u2 == p2.Elem().Elem().Interface())
}

// ptr wraps the given value with pointer: V => *V, *V => **V, etc.
func ptr(v reflect.Value) reflect.Value {
	pt := reflect.PtrTo(v.Type()) // create a *T type.
	pv := reflect.New(pt.Elem())  // create a reflect.Value of type *T.
	pv.Elem().Set(v)              // sets pv to point to underlying value of v.
	return pv
}

Playground

函数调用

调用没有参数的方法

package main

import (
	"fmt"
	"reflect"
)

type A struct{}

func (a A) Hello() {
	fmt.Println("hello word")
}
func main() {
	// ValueOf返回一个新值,这是Go的反射接口
	v := reflect.ValueOf(A{})
	m := v.MethodByName("Hello")
	if m.Kind() != reflect.Func {
		return
	}
	m.Call(nil)

}

Playground

调用含有参数的方法

package main

import (
	"fmt"
	"reflect"
)

type A struct{}

func (a A) Hello(word string) {
	fmt.Println("hello", word)
}
func main() {
	// ValueOf返回一个新值,这是Go的反射接口
	v := reflect.ValueOf(A{})
	m := v.MethodByName("Hello")
	if m.Kind() != reflect.Func {
		return
	}
	fmt.Println(m.Type().In(0))
	m.Call([]reflect.Value{reflect.ValueOf("word")})
}
//输出
//string
//hello word
// Hello

Playground

调用带有参数的函数验证返回值

package main

import (
	"fmt"
	"reflect"
)

func Add(a, b int) int { return a + b }
func main() {
	v := reflect.ValueOf(Add)
	if v.Kind() != reflect.Func {
		return
	}

	t := v.Type()
	argv := make([]reflect.Value, t.NumIn())
	for i := range argv {
		// 验证参数i的类型
		if t.In(i).Kind() != reflect.Int {
			return
		}
		argv[i] = reflect.ValueOf(i)
	}
	// 调用函数

	result := v.Call(argv)

	if len(result) != 1 || result[0].Kind() != reflect.Int {
		return
	}
	fmt.Println(result[0].Int())

}
// 输出
// 1

Playground

动态调用一个函数类似于template.text包

package main

import (
	"fmt"
	"html/template"
	"reflect"
	"strconv"
	"strings"
)

func main() {
	funcs := template.FuncMap{
		"trim":    strings.Trim,
		"lower":   strings.ToLower,
		"repeat":  strings.Repeat,
		"replace": strings.Replace,
	}
	fmt.Println(eval(`{{ "hello" 4 | repeat }}`, funcs))
	fmt.Println(eval(`{{ "Hello-World" | lower }}`, funcs))
	fmt.Println(eval(`{{ "foobarfoo" "foo" "bar" -1 | replace }}`, funcs))
	fmt.Println(eval(`{{ "-^-Hello-^-" "-^" | trim }}`, funcs))
}

// evaluate an expression. note that this implemetation is assuming that the
// input is valid, and also very limited. for example, whitespaces are not allowed
// inside a quoted string.
func eval(s string, funcs template.FuncMap) (string, error) {
	args, name := parseArgs(s)
	fn, ok := funcs[name]
	if !ok {
		return "", fmt.Errorf("function %s is not defined", name)
	}
	v := reflect.ValueOf(fn)
	t := v.Type()
	if len(args) != t.NumIn() {
		return "", fmt.Errorf("invalid number of arguments. got: %v, want: %v", len(args), t.NumIn())
	}
	argv := make([]reflect.Value, len(args))
	// go over the arguments, validate and build them.
	// note that we support only int, and string in this simple example.
	for i := range argv {
		var argType reflect.Kind
		// if the argument "i" is string.
		if strings.HasPrefix(args[i], "\"") {
			argType = reflect.String
			argv[i] = reflect.ValueOf(strings.Trim(args[i], "\""))
		} else {
			argType = reflect.Int
			// assume that the input is valid.
			num, _ := strconv.Atoi(args[i])
			argv[i] = reflect.ValueOf(num)
		}
		if t.In(i).Kind() != argType {
			return "", fmt.Errorf("Invalid argument. got: %v, want: %v", argType, t.In(i).Kind())
		}
	}
	result := v.Call(argv)
	// in real-world code, we validate it before executing the function,
	// using the v.NumOut() method. similiar to the text/template package.
	if len(result) != 1 || result[0].Kind() != reflect.String {
		return "", fmt.Errorf("function %s must return a one string value", name)
	}
	return result[0].String(), nil
}

// parseArgs is an auxiliary function, that extract the function and its
// parameter from the given expression.
func parseArgs(s string) ([]string, string) {
	args := strings.Split(strings.Trim(s, "{ }"), "|")
	return strings.Split(strings.Trim(args[0], " "), " "), strings.Trim(args[len(args)-1], " ")
}

调用可变参数的函数

package main

import (
	"fmt"
	"math/rand"
	"reflect"
)

func Sum(x1, x2 int, xs ...int) int {
	sum := x1 + x2
	for _, xi := range xs {
		sum += xi
	}
	return sum
}

func main() {
	v := reflect.ValueOf(Sum)
	if v.Kind() != reflect.Func {
		return
	}
	t := v.Type()
	// 获取参数个数
	argc := t.NumIn()
	
	// 判断是否为可变参数
	if t.IsVariadic() {
		argc += rand.Intn(10)
	}
	
	argv := make([]reflect.Value, argc)
	for i := range argv {
		argv[i] = reflect.ValueOf(i)
	}
	// 调用函数 0 1 2 3 
	result := v.Call(argv)
	fmt.Println(result[0].Int())
}

// 输出
// 6

Playground

运行时创建一个函数

package main

import (
	"fmt"
	"reflect"
)

type Add func(int64, int64) int64

func main() {
	t := reflect.TypeOf(Add(nil))
	mul := reflect.MakeFunc(t, func(args []reflect.Value) []reflect.Value {
		a := args[0].Int()
		b := args[1].Int()
		return []reflect.Value{reflect.ValueOf(a+b)}
	})
	fn, ok := mul.Interface().(Add)
	if !ok {
		return
	}
	fmt.Println(fn(2,3))
}

Playground

You can’t perform that action at this time.