开发中会频繁的使用各种对象,在Java中称为Javabean,在Go中用结构体。使用ORM框架时,经常会用实体类来映射数据表,但实际上很少会直接使用映射数据表的实体类对象在各层传输,更多的会使用其他对象(如DTO,VO等),对读出的实体类对象的属性进行过滤或增加。

copy()copy()

不过Go自带反射包,利用反射,我们可以手动实现一个任意类型属性拷贝的函数或方法。

实现起来也很简单,喜欢琢磨的朋友可以直接阅读反射的文档自己实现。
golang.org/pkg/reflect…

Overview

以下摘自文档

Package reflect implements run-time reflection, allowing a program to manipulate objects with arbitrary types. The typical use is to take a value with static type interface{} and extract its dynamic type information by calling TypeOf, which returns a Type.

A call to ValueOf returns a Value representing the run-time data. Zero takes a Type and returns a Value representing a zero value for that type.

TypeOfTypeValueOfValue
reflect

Kind

type Kind uint
iotaKindKindStrcutKindPtrSliceMapArrrayChan
ValueTypeTypeMapOf()MapTypeMappanicpanic()Kind

Type

Typereflect.TypeOf(i interface{})
Name()Kind()Kind
KindStructNumField()Field(i int) StructFieldFieldByName(name string) StructFieldStructField
KindArrayChanMapPtrSliceElem()Type
panic()
Type

StructField

用来描述结构体中单个属性,定义如下

type StructField struct {
    Name string
    PkgPath string
    Type      Type      
    Tag       StructTag 
    Offset    uintptr   
    Index     []int     
    Anonymous bool     
}
复制代码
NamePkgPathTypeTag

Value

TypeValuereflect.ValueOf(i interface{})TypeValueValueTypeNumField()Field(i)FieldByName(name string)Elem()
Value
TypeValue
panic()

实践

上文简单了解了一下反射的基础。相信很多人都知道怎么实现了。

Field(i int)StuctFieldNameNameFieldByName(name string)Set(v Value)

coding

func SimpleCopyProperties(dst, src interface{}) (err error) {
	// 防止意外panic
	defer func() {
		if e := recover(); e != nil {
			err = errors.New(fmt.Sprintf("%v", e))
		}
	}()

	dstType, dstValue := reflect.TypeOf(dst), reflect.ValueOf(dst)
	srcType, srcValue := reflect.TypeOf(src), reflect.ValueOf(src)

	// dst必须结构体指针类型
	if dstType.Kind() != reflect.Ptr || dstType.Elem().Kind() != reflect.Struct {
		return errors.New("dst type should be a struct pointer")
	}

	// src必须为结构体或者结构体指针
	if srcType.Kind() == reflect.Ptr {
		srcType, srcValue = srcType.Elem(), srcValue.Elem()
	}
	if srcType.Kind() != reflect.Struct {
		return errors.New("src type should be a struct or a struct pointer")
	}

	// 取具体内容
	dstType, dstValue = dstType.Elem(), dstValue.Elem()

	// 属性个数
	propertyNums := dstType.NumField()

	for i := 0; i < propertyNums; i++ {
		// 属性
		property := dstType.Field(i)
		// 待填充属性值
		propertyValue := srcValue.FieldByName(property.Name)

		// 无效,说明src没有这个属性 || 属性同名但类型不同
		if !propertyValue.IsValid() || property.Type != propertyValue.Type() {
			continue
		}

		if dstValue.Field(i).CanSet() {
			dstValue.Field(i).Set(propertyValue)
		}
	}

	return nil
}
复制代码

小结

reflectpanicrecoverpanicSliceMap

反射包中还有很多有意思的东西,感兴趣的朋友可以参考文档。
golang.org/pkg/reflect…