先看下面的例子,通过反射一个结构体对象来获取信息。

type Man struct {
	Id   int
	Name string
}

m := Man{1, "a"}

func(v interface{}){
	getValue := reflect.ValueOf(v)
	if getValue.Kind() != reflect.Struct {
		panic("need struct kind")
	}
	getType := reflect.TypeOf(v)
	// 或者 getType := getValue.Type()
	for i := 0; i < getValue.NumField(); i++ {
		fmt.Printf("name: %s, type: %s, value: %v\n", getType.Field(i).Name, getValue.Field(i).Type(), getValue.Field(i).Interface())
	}
}(m)

打印信息

name: Id, type: int, value: 1
name: Name, type: string, value: a
reflect.Valuereflect.Type

根据上面的例子:

  1. 通过 reflect.ValueOf(v) 得到 v 的 Value 对象;
  2. 通过 v 的 Value 对象的 Kind() 方法(或者通过 v 的 Type 对象的 Kind() 方法,后面再讲如何获取 Type 对象)得到类型的种类,此处为 struct ,做一个限制;
  3. Value 对象的 NumField() 方法得到结构体中字段的个数,可以通过遍历得到每个字段;
  4. 结构体中字段是有顺序的,可以通过下标访问到,Value 对象的 Field(i) 方法依然返回 Value 对象,只不过这个对象存储的是第 i 个字段的信息。然后调用 Type() 方法得到字段的 Type 对象。调用 Type 对象的 String() 方法即可得到字段的数据类型,我们知道fmt包中的 Println(), Printf() 等函数会自动去调用对象的 String() 方法的;
  5. 字段的 Value 对象的 Interface() 方法可以得到字段的值;
  6. 而字段的名称是存在于结构体的 Type 对象中的,可以通过 reflect.TypeOf(v) 或者通过 v 的 Value 对象的 Type() 来得到。同样的,使用下标来访问字段,Type 对象的 Field(i) 方法返回 StructField 对象,存储着字段的 Type 信息。
  7. 对象的类型是否可导出不要紧,但是如果要通过反射访问某个字段,那么这个字段应该是可导出的,否则会panic。

看着有点绕,实则是 reflect 包的方法定义的具有一定的迷惑性。

// A StructField describes a single field in a struct.
type StructField struct {
	// Name is the field name.
	Name string
	// PkgPath is the package path that qualifies a lower case (unexported)
	// field name. It is empty for upper case (exported) field names.
	// See https://golang.org/ref/spec#Uniqueness_of_identifiers
	PkgPath string

	Type      Type      // field type
	Tag       StructTag // field tag string
	Offset    uintptr   // offset within struct, in bytes
	Index     []int     // index sequence for Type.FieldByIndex
	Anonymous bool      // is an embedded field
}

type Value struct {
	typ *rtype
	ptr unsafe.Pointer
	flag
}

type Type interface {
	Align() int
	FieldAlign() int
	Method(int) Method
	MethodByName(string) (Method, bool)
	NumMethod() int
	Name() string
	PkgPath() string
	Size() uintptr
	String() string
	Kind() Kind
	Implements(u Type) bool
	AssignableTo(u Type) bool
	ConvertibleTo(u Type) bool
	Comparable() bool
	Bits() int
	ChanDir() ChanDir
	IsVariadic() bool
	Elem() Type
	Field(i int) StructField
	FieldByIndex(index []int) StructField
	FieldByName(name string) (StructField, bool)
	FieldByNameFunc(match func(string) bool) (StructField, bool)
	In(i int) Type
	Key() Type
	Len() int
	NumField() int
	NumIn() int
	NumOut() int
	Out(i int) Type
	common() *rtype
	uncommon() *uncommonType
}