Помните простой бой отражения Го

Go

Рефлексия является ключевым моментом всех объектно-ориентированных языков и предоставляет разработчикам гибкие возможности манипулирования. Используя рефлексию, вы можете получать информацию о различных объектах/структурах, формулировать различные стратегии и выполнять сложные операции.

Операция отражения в го и java сильно отличается, я тоже столкнулся с некоторыми трудностями при обучении и записал, чтобы усилить впечатление.

Задача: прочитать файл Excel и сгенерировать соответствующий объект в соответствии с полученной структурой.

подготовка данных

То, что я готовлю, - это информация об ученике, отправленная школой ранее (пожалуйста, не беспокойтесь о том, почему я использую эти данные, это то, что я нашел случайно)

1595739828556

На основе информации о полях в таблице Excel мы можем создатьStudentСтруктура, которая содержит соответствующие поля:

// 学生结构体
type Student struct {
   Sno int // 学号
   Sname string // 姓名
   Sclass string // 班级
   Sbank string // 卡号
}

Получить информацию о структуре

Операция для функции входа выглядит следующим образом:

func main() {

   var path string = "D:/GoCode/go-datafilter/reources/xxx.xlsx"
   dataType := util.ReadDataFromExcel(path, &Student{})
   for _, data := range dataType.Data {
      fmt.Println(data)
   }
}

Содержание простое, чтобыReadDataFromExcelВ метод передаются два параметра:

  • filePath: путь к файлу Excel для чтения
  • targetStruct: Структура, которую необходимо отразить

существуетReadDataFromExcelВ методе нам нужно выполнить три шага:

  1. Получить информацию о структуре
  2. Получить информацию таблицы Excel
  3. Создание объектов с отражением

Сначала вставьте полный код для получения информации о структуре, а затем проанализируйте его:

// 字段类型切片
var fieldType  = make([]string, 0)
// 字段名称切片
var fieldName = make([]string, 0)

// 读取结构体字段的名字和类型
func getFieldNameAndType(typeof reflect.Type) reflect.Type {

   if typeof.Kind() == reflect.Ptr {
      typeof = typeof.Elem()
   }
   if typeof.Kind() != reflect.Struct {
      panic("bab type")
   }

   fieldsNum := typeof.NumField()
   for i := 0; i < fieldsNum; i++ {
       fieldType = append(fieldType, typeof.Field(i).Type.Name())
      fieldName = append(fieldName, typeof.Field(i).Name)
   }

   datatype.FieldName = fieldName
   datatype.FieldType = fieldType

   return typeof
}

На языке Go он предоставляетreflectПакет используется, чтобы помочь нам с операциями отражения, и пакет инкапсулирует несколько методов.

В Go есть два способа классификации отраженных объектов, один из которых основан на типеType, один основан на типеKind.KindчемTypeболее широкое понятие,TypeОтносится к собственным типам данных системы, таким какint,string,boolи другие типы, и использоватьtypeтип, определенный ключевым словом, в то время какKindЭто порода атрибуции типов. Как показано в следующем примере:

func main() {
   student := Student{}

   typeOf := reflect.TypeOf(student)
   fmt.Println(typeOf.Kind()) // struct:它是一个结构体种类
   // Name():返回表示类型名称的字符串
   fmt.Println(typeOf.Name()) // Student:它是一个Student结构体类型
}

для типов указателейmap,slice,chanЭти ссылочные типы вKindОпределение имеет свою уникальную категорию. Для типов указателейNameВозвращает встроенные типы-примитивы и типы, определенные в пакете, и возвращает значение null для неопределенных типов.KindвстроенныйPtrиспользуется для представления. Вот почему мы видим такое суждение в большей части кода:

if typeof.Kind() == reflect.Ptr {
    typeof = typeof.Elem()
}
if typeof.Kind() != reflect.Struct {
    panic("bad type")
}

Elem()Возвращает тип элемента, на который указывает отраженный указатель, что эквивалентно созданию переменной типа указателя.*работать.

После решения, после того, как мы убедилисьtypeOfПри наличии можно пройтиreflect.Typeметод получения необходимой информации. В приведенном выше коде используются четыре основных метода:

  • NumField(): получить количество полей
  • Field(index int): Получить соответствующий тип поля через индекс
  • Name(): получить имя указанного поля
  • Type(): Получить категорию указанного поля

Получить поля можно тремя способами:

  1. Получено по индексному индексу:Field(intdex int)/FieldByIndex(indexs int[])

    заFieldByIndexметод, который используется для вложенного сбора данных, напримерFieldByIndex(1, 2)равноField(1).Field(2)

  2. Получить по имени поля:FieldByName(fieldName string)

  3. Получается регулярным сопоставлением:FieldByNameFunc(match func(string) bool)

Создание объектов с отражением

Получив соответствующую информацию о структуре, теперь она счастлива.new对象время, общий код выглядит следующим образом:

func ReadDataFromExcel(path string, mytype interface{}) []interface{} {
	//......
    for _, sheet := range file.Sheets{
		for _, row := range sheet.Rows { // 每行读取

			if idx == 0 { // 不读取Excel表的第一行
				idx++
				continue
			}

			newStruct := reflect.New(typeof)

			for idx, cell := range row.Cells { // 每个单元格读取
				text := strings.TrimSpace(cell.String())
				newObject(text, newStruct, idx, idx)
			}
			data = append(data, newStruct)
		}
	}
    //......

}




// 给反射对象赋值
func newObject(value string, newStruct reflect.Value, nameIdx, typeIdx int) {
	switch fieldType[typeIdx] {
	case "int", "int64", "int32":
		num, err := strconv.Atoi(value)
		if err != nil {
			fmt.Println(err)
		}
		newStruct.Elem().FieldByName(fieldName[nameIdx]).SetInt(reflect.ValueOf(num).Int())
	case "string":
		newStruct.Elem().FieldByName(fieldName[nameIdx]).SetString(reflect.ValueOf(value).String())
	case "bool":
		newStruct.Elem().FieldByName(fieldName[nameIdx]).SetBool(reflect.ValueOf(value).Bool())
	case "float32", "float64":
		newStruct.Elem().FieldByName(fieldName[nameIdx]).SetFloat(reflect.ValueOf(value).Float())
	default:
		newStruct.Elem().FieldByName(fieldName[nameIdx]).SetString(reflect.ValueOf(value).String())
	}
}

Сначала используем как в javaClass.getDeclaredConstructor().newInstance()Создайте структуру так же, как вы создаете объект:newStruct := reflect.New(typeof).

Каждая строка эквивалентна структуре, а значение каждой ячейки в каждой строке эквивалентно значению соответствующего поля в структуре.

так вnewObjecВ методе сначала определите, к какому типу относится значение соответствующей ячейки, а затем преобразуйте тип. Поскольку Go — язык со строгой типизацией, этап преобразования немного более громоздкий, но все равно выглядит аккуратно.

Увидев это, мы также можем обнаружить, что нет необходимости создавать отдельное сопоставление [поле --> тип] структуры или срез. непосредственно использовать вышеупомянутыеFieldметод подойдет. Это правда, я делаю это для использования в другом месте, но это не является частью этой статьи.

Value.Eelem()метод сType.Eelem()Методы аналогичны, все они предназначены для получения значения, содержащегося в интерфейсе, или объекта, на который указывает указатель.

Для установки значения объекта отражения мы можем передатьSetXXX()Чтобы установить, режим, который я использую здесь, почтиSetXXX(reflect.ValueOf(value).XXX()).

TypeOfтип для получения значения входного параметра,Valueэто значение для получения данных во входном параметре.

при исполненииreflect.ValueOf(value), вы получите типrelfect.Valueпеременная, доступ к которой можно получить через собственныйInterface()Метод получает реальное содержимое переменной интерфейса, а затем преобразует его в исходный реальный тип посредством оценки типа. Например:

func main() {
	student := Student{
		1,
		"zs",
		"110",
		"001",
	}

	typeOf := reflect.ValueOf(student)
	fmt.Println(typeOf.Interface().(Student))
    // 输出:{1 zs 110 001}
}

Изображение эффекта после запуска вышеуказанной программы:

1595812440634