Основы рефлексии
Часть раздела Объектно-ориентированное программирование путешествия по GO на Coddy. Урок 83 из 107.
Reflection позволяет программе исследовать и изменять собственную структуру во время выполнения. Пакет Go reflect предоставляет эту возможность, позволяя исследовать типы, читать поля структур и динамически вызывать методы.
Два основных типа в reflection — это reflect.Type и reflect.Value. Их получают с помощью reflect.TypeOf() и reflect.ValueOf():
import "reflect"
type Person struct {
Name string
Age int
}
p := Person{Name: "Alice", Age: 30}
t := reflect.TypeOf(p)
v := reflect.ValueOf(p)
fmt.Println(t.Name()) // Person
fmt.Println(t.Kind()) // struct
fmt.Println(v.NumField()) // 2Вы можете перебирать поля структуры, чтобы изучить их имена, типы и значения:
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
value := v.Field(i)
fmt.Printf("%s: %v\n", field.Name, value)
}
// Вывод:
// Name: Alice
// Age: 30reflection — мощный инструмент, но у него есть компромиссы: он обходит проверку типов во время компиляции, работает медленнее прямого доступа и усложняет понимание кода. Используй его умеренно для таких задач, как маршалинг JSON, сопоставление ORM или создание универсальных утилит, когда тип неизвестен во время компиляции.
Задание
ЛегкоДавайте создадим инспектор структур, который использует reflection для анализа любой структуры и составления отчёта о её структуре! Это практический пример применения reflection: создание утилит, работающих с типами, неизвестными во время компиляции.
Вы организуете код в двух файлах:
inspector.go: создайте утилиту для проверки на основе reflection.Создайте функцию
InspectStruct(v any) string, которая использует пакетreflectдля анализа любой переданной ей структуры. Функция должна возвращать отформатированный отчёт, содержащий:- имя типа структуры
- вид структуры (должен быть "struct")
- количество полей
- для каждого поля: имя поля, его тип и текущее значение
Формат вывода должен быть следующим:
Type: [TypeName] Kind: struct Fields: [count] - [FieldName] ([FieldType]): [Value] - [FieldName] ([FieldType]): [Value] ...Используйте
reflect.TypeOf(), чтобы получить информацию о типе, иreflect.ValueOf(), чтобы получить доступ к значениям полей. Перебирайте поля с помощьюNumField(),Field(i)у типа для получения metadata иField(i)у значения для получения фактических значений.main.go: определите примеры структур и используйте свой инспектор.Прочитайте тип структуры (
productилиemployee) и значения её полей, затем создайте соответствующую структуру и проанализируйте её.Для
product: создайте структуруProductс полямиName(string),Price(float64) иInStock(bool). Прочитайте название, цену и статус наличия на складе.Для
employee: создайте структуруEmployeeс полямиID(int),Name(string) иDepartment(string). Прочитайте ID, имя и department.Передайте созданную структуру в
InspectStructи выведите результат.
Будут предоставлены следующие входные данные:
- Строка 1: тип структуры (
productилиemployee) - Следующие строки: значения полей для этого типа структуры
Например, если введено:
product
Laptop
999.99
trueВаш вывод должен быть таким:
Type: Product
Kind: struct
Fields: 3
- Name (string): Laptop
- Price (float64): 999.99
- InStock (bool): trueА если введено:
employee
42
Alice Johnson
EngineeringВаш вывод должен быть таким:
Type: Employee
Kind: struct
Fields: 3
- ID (int): 42
- Name (string): Alice Johnson
- Department (string): EngineeringА если введено:
product
Headphones
79.50
falseВаш вывод должен быть таким:
Type: Product
Kind: struct
Fields: 3
- Name (string): Headphones
- Price (float64): 79.5
- InStock (bool): falseОбратите внимание, как ваш инспектор работает с разными типами структур, не зная их структуру заранее. В этом и заключается сила reflection! Одна и та же функция InspectStruct динамически обрабатывает обе структуры — Product и Employee.
Попробуйте сами
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
// Структура Product с полями Name, Price и InStock
type Product struct {
Name string
Price float64
InStock bool
}
// Структура Employee с полями ID, Name и Department
type Employee struct {
ID int
Name string
Department string
}
func main() {
reader := bufio.NewReader(os.Stdin)
// Читаем тип структуры
structType, _ := reader.ReadString('\n')
structType = strings.TrimSpace(structType)
if structType == "product" {
// Читаем поля продукта
name, _ := reader.ReadString('\n')
name = strings.TrimSpace(name)
priceStr, _ := reader.ReadString('\n')
priceStr = strings.TrimSpace(priceStr)
price, _ := strconv.ParseFloat(priceStr, 64)
inStockStr, _ := reader.ReadString('\n')
inStockStr = strings.TrimSpace(inStockStr)
inStock, _ := strconv.ParseBool(inStockStr)
// TODO: Создайте структуру Product с прочитанными значениями
// TODO: Вызовите InspectStruct с продуктом и выведите результат
} else if structType == "employee" {
// Читаем поля сотрудника
idStr, _ := reader.ReadString('\n')
idStr = strings.TrimSpace(idStr)
id, _ := strconv.Atoi(idStr)
name, _ := reader.ReadString('\n')
name = strings.TrimSpace(name)
department, _ := reader.ReadString('\n')
department = strings.TrimSpace(department)
// TODO: Создайте структуру Employee с прочитанными значениями
// TODO: Вызовите InspectStruct с сотрудником и выведите результат
}
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы ООП в Go
Внешние файлыРабочее пространство и модули GoПакеты и импортыЭкспортируемые и неэкспортируемые именаВведение в ООП в GoСтруктуры как классыОпределение методов структурПолучатели-указатели и получатели-значенияИнициализация структурФункции-конструкторыИтоги — Простой калькулятор4Интерфейсы
Введение в интерфейсыНеявная реализацияИнтерфейс как контрактПустой интерфейс (any)Утверждение типаПереключатель типовКомпозиция интерфейсовИнтерфейсы Stringer и ErrorПовторение: Калькулятор фигур7Инкапсуляция
Экспортируемые и неэкспортируемые поляИнкапсуляция на уровне пакетовГеттеры и сеттерыСокрытие информации в GoИтоги — Записи о студентах10Обобщения (Generics) (Go 1.18+)
Введение в GenericsПараметры типовОграничения типовОбобщенные структурыОбходной путь для обобщенных методовИтоги — Обобщенная коллекция13Паттерны проектирования. Часть 1
Введение в паттерны проектированияПаттерн SingletonПаттерн FactoryПаттерн Abstract FactoryПаттерн ObserverПаттерн Strategy2Глубокое погружение в типы и структуры
Базовые и составные типыОпределение пользовательских типовТеги структурАнонимные структурыВложенные структурыНулевые значения и значения по умолчаниюПовторение — Контактная книга5Композиция вместо наследования
Почему в Go нет наследованияОсновы встраивания структурПродвижение методовВстраивание нескольких структурВстраивание против агрегацииЗатенение встроенных методовИтоги — Иерархия сотрудников8Обработка ошибок и ООП
Интерфейс errorПользовательские типы ошибокОбертывание ошибок (fmt.Errorf)Sentinel-ошибкиerrors.Is() и errors.As()Panic, Defer и RecoverИтоги — Парсер файлов11Стандартная библиотека и ООП
io.Reader и io.Writersort.InterfaceИнтерфейс fmt.Stringerencoding/json со структурамиИнтерфейс http.HandlerПовторение: модели REST API14Паттерны проектирования. Часть 2
Паттерн КомандаПаттерн АдаптерПаттерн ДекораторПаттерн Шаблонный методПаттерн СостояниеПаттерн КомпоновщикMiddleware как Декоратор3Указатели и память
Основы указателей в GoУказатели на структурыПередача по значению и по ссылкеФункция new()Сборка мусора в GoПовторение: Конструктор связного списка6Полиморфизм в Go
Полиморфизм через интерфейсыУтиная типизация в GoПравила реализации интерфейсовПолиморфные коллекцииВнедрение зависимостейИтоги — Обработчик платежей9Конкурентность и ООП
Основы горутинКаналы и взаимодействиеБуферизованные и небуферизованные каналыОператор selectsync.Mutex и sync.RWMutexsync.WaitGroupПроектирование потокобезопасных структурПовторение — Worker Pool12Продвинутые концепции ООП
Паттерн функциональных опцийПаттерн Builder в GoЦепочки вызовов методовПсевдонимы и определения типовОсновы рефлексииОбзор генерации кодаПотренируйтесь самостоятельно: Онлайн-компилятор Go