Рефлексия является ключевым моментом всех объектно-ориентированных языков и предоставляет разработчикам гибкие возможности манипулирования. Используя рефлексию, вы можете получать информацию о различных объектах/структурах, формулировать различные стратегии и выполнять сложные операции.
Операция отражения в го и java сильно отличается, я тоже столкнулся с некоторыми трудностями при обучении и записал, чтобы усилить впечатление.
Задача: прочитать файл Excel и сгенерировать соответствующий объект в соответствии с полученной структурой.
подготовка данных
То, что я готовлю, - это информация об ученике, отправленная школой ранее (пожалуйста, не беспокойтесь о том, почему я использую эти данные, это то, что я нашел случайно)
На основе информации о полях в таблице 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В методе нам нужно выполнить три шага:
- Получить информацию о структуре
- Получить информацию таблицы Excel
- Создание объектов с отражением
Сначала вставьте полный код для получения информации о структуре, а затем проанализируйте его:
// 字段类型切片
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(): Получить категорию указанного поля
Получить поля можно тремя способами:
-
Получено по индексному индексу:
Field(intdex int)/FieldByIndex(indexs int[])за
FieldByIndexметод, который используется для вложенного сбора данных, напримерFieldByIndex(1, 2)равноField(1).Field(2) -
Получить по имени поля:
FieldByName(fieldName string) -
Получается регулярным сопоставлением:
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} }
Изображение эффекта после запуска вышеуказанной программы: