Go-рефлексия реализует копирование свойств любого типа

Go

В разработке часто используются различные объекты, которые называются Javabeans в Java и структуры в Go. При использовании структуры ORM класс сущностей часто используется для сопоставления таблицы данных, но на самом деле объект класса сущностей сопоставленной таблицы данных редко используется непосредственно для передачи на каждом уровне, и больше других объектов (таких как DTO, VO и т. д.) Отфильтруйте или добавьте свойства считываемого объекта класса сущностей.

Друзья, которые используют Java, знают, что есть удобный инструмент под названием BeanUtils, назовите егоcopy()метод для удаления множества операций установки. Go поставляется со многими пакетами, но нет никакого метода копирования, встроенный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.

Грубо говоря, используя отражение, вы можете обрабатывать произвольные типы во время выполнения. пройти черезTypeOfМетод получает информацию о типе, завернутую вTypeсередина. пройти черезValueOfПолучить данные времени выполнения, завернутые вValueсередина.

Представьте нижеreflectНекоторые типы и методы в пакете. Те, кто уже знаком с пакетом отражения, могут сразу перейти к концу.

Kind

определение:type Kind uint

использоватьiotaопределить сериюKindКонстанта, представляющая тип обрабатываемого типа. Звучит запутанно, но на самом деле это очень легко понять.Помимо основных типов, когда мы настраиваем структуру,KindзаStrcut, когда обрабатываемый тип является указателем,KindзаPtrи другие, такие какSlice,Map,Arrray,ChanЖдать.

существуетValueиTypeНекоторые из методов можно использовать только для определенных типов, таких какTypeизMapOf()метод, толькоMapиспользовать, когда используетсяTypeнетMapразpanic. Таких методов гораздо больше, потому что при несовпадении типа будет напрямуюpanic(), чтобы быть в безопасности, вы должны сначала проверитьKindвынести приговор.

Type

ОпределенныйTypeЭто тип интерфейса.После получения экземпляра вы можете получить информацию о типе, вызвав серию методов.reflect.TypeOf(i interface{})Получите экземпляр.

пройти черезName()метод для получения имени типа.Kind()способ получить типKind.

еслиKindявляется структурным типомStruct,пройти черезNumField()Можно получить количество атрибутов структуры, которые можно получить черезField(i int) StructField,FieldByName(name string) StructFieldПолучите определенные свойства, возвращаемое значение является другим определенным типом структурыStructField.

еслиKindзаArray, Chan, Map, Ptr, Slice, доступен черезElem()получить определенный элементType.

Некоторые специальные методы можно использовать только для определенных типов.panic().

дваTypeсопоставимы, вы можете использовать == или != .

StructField

Используется для описания одного свойства в структуре, определяемой следующим образом

type StructField struct {
    Name string
    PkgPath string
    Type      Type      
    Tag       StructTag 
    Offset    uintptr   
    Index     []int     
    Anonymous bool     
}

вNameимя свойства,PkgPathпуть к пакету,Typeинформация о типе атрибута,TagДля метки (обычно используется для решения проблем с кодированием и декодированием, заинтересованные друзья могут посмотреть соответствующие библиотеки и исходный код).

Value

когда используешьTypeКогда мы можем получить только соответствующую информацию о типе.Если нам нужно манипулировать конкретным значением, мы должны использоватьValue,пройти черезreflect.ValueOf(i interface{}). иTypeразные,ValueТип — структура.Valueтакже следоватьTypeподобные методы, такие какNumField(),Field(i),FieldByName(name string),Elem()Ждать.

такжеValueСуществует также ряд методов установки, если значение может быть установлено, то мы можем динамически изменить значение.

иTypeразные,ValueСравнение не может быть выполнено с помощью == или != и должно сравниваться соответствующим методом.

Точно так же некоторые специальные методы можно использовать только для определенных типов.panic().

упражняться

Вышеизложенное кратко понимает основы отражения. Думаю, многие знают, как этого добиться.

Общая идея: поскольку значение необходимо изменить, целевой параметр должен быть передан с указателем структуры, а исходный параметр может быть передан указателем или экземпляром. Пройдите все значения атрибутов копируемого типа, используйтеField(i int)Получите один атрибут, выньтеStuctFieldизName, затем используйтеNameпройти черезFieldByName(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
}

резюме

На данный момент мы завершили копию свойства с тем же именем. потому что использоватьreflectмешок, вездеpanic, поэтому вам нужно использовать функцию задержки в началеrecoverодин разpanic. При передаче параметров решите, использовать ли указатель или экземпляр для второго параметра. Следует отметить, что этот метод копирования является поверхностным копированием, другими словами, если в объекте есть другие ссылочные типы, такие какSlice,MapПодождите, после использования этого метода для завершения копирования содержимое атрибута ссылочного типа в исходном объекте изменилось, а также изменится содержимое соответствующего атрибута объекта.

В пакете отражения есть много интересного, и заинтересованные друзья могут обратиться к документации.
golang.org/pkg/reflect…