Рефлексия является ключевым моментом всех объектно-ориентированных языков и предоставляет разработчикам гибкие возможности манипулирования. Используя рефлексию, вы можете получать информацию о различных объектах/структурах, формулировать различные стратегии и выполнять сложные операции.
Операция отражения в го и 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} }
Изображение эффекта после запуска вышеуказанной программы: