Menu
Coddy logo textTech

Основы рефлексии

Часть раздела Объектно-ориентированное программирование путешествия по 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: 30

reflection — мощный инструмент, но у него есть компромиссы: он обходит проверку типов во время компиляции, работает медленнее прямого доступа и усложняет понимание кода. Используй его умеренно для таких задач, как маршалинг JSON, сопоставление ORM или создание универсальных утилит, когда тип неизвестен во время компиляции.

challenge icon

Задание

Легко

Давайте создадим инспектор структур, который использует 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 с сотрудником и выведите результат

	}
}
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Объектно-ориентированное программирование

Потренируйтесь самостоятельно: Онлайн-компилятор Go