Podstawy refleksji
Część sekcji Programowanie obiektowe ścieżki GO w Coddy. Lekcja 83 z 107.
Refleksja pozwala programowi analizować i modyfikować własną strukturę w czasie wykonywania. Pakiet reflect języka Go zapewnia tę możliwość, umożliwiając sprawdzanie typów, odczytywanie pól struktur i dynamiczne wywoływanie metod.
Dwa podstawowe typy w refleksji to reflect.Type i reflect.Value. Uzyskujesz je za pomocą reflect.TypeOf() i 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()) // 2Możesz iterować po polach struktury, aby sprawdzić ich nazwy, typy i wartości:
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
value := v.Field(i)
fmt.Printf("%s: %v\n", field.Name, value)
}
// Wynik:
// Name: Alice
// Age: 30Refleksja jest potężna, ale wiąże się z kompromisami: omija kontrolę typów podczas kompilacji, działa wolniej niż bezpośredni dostęp i utrudnia zrozumienie kodu. Używaj jej oszczędnie do zadań takich jak serializacja JSON, mapowanie ORM czy tworzenie uniwersalnych narzędzi, gdy typ nie jest znany podczas kompilacji.
Wyzwanie
ŁatwyZbudujmy inspektor struktur, który używa refleksji do analizowania dowolnej struktury i opisywania jej budowy! To praktyczne zastosowanie refleksji: tworzenie narzędzi działających z typami nieznanymi w czasie kompilacji.
Podzielisz kod na dwa pliki:
inspector.go: Utwórz narzędzie inspekcji oparte na refleksji.Zbuduj funkcję
InspectStruct(v any) string, która używa pakietureflectdo analizowania przekazanej jej dowolnej struktury. Twoja funkcja powinna zwracać sformatowany raport zawierający:- Nazwę typu struktury
- Rodzaj struktury (powinien mieć wartość "struct")
- Liczbę pól
- Dla każdego pola: nazwę pola, jego typ i bieżącą wartość
Format wyjściowy powinien wyglądać tak:
Type: [TypeName] Kind: struct Fields: [count] - [FieldName] ([FieldType]): [Value] - [FieldName] ([FieldType]): [Value] ...Użyj
reflect.TypeOf(), aby uzyskać informacje o typie, ireflect.ValueOf(), aby uzyskać dostęp do wartości pól. Przejdź przez pola, używającNumField(),Field(i)dla typu, aby uzyskać metadane, orazField(i)dla wartości, aby uzyskać rzeczywiste wartości.main.go: Zdefiniuj przykładowe struktury i użyj swojego inspektora.Wczytaj typ struktury (
productlubemployee) oraz wartości jej pól, a następnie utwórz odpowiednią strukturę i ją przeanalizuj.Dla
product: Utwórz strukturęProductz polamiName(string),Price(float64) iInStock(bool). Wczytaj nazwę, cenę i stan magazynowy.Dla
employee: Utwórz strukturęEmployeez polamiID(int),Name(string) iDepartment(string). Wczytaj identyfikator, imię i dział.Przekaż utworzoną strukturę do
InspectStructi wypisz wynik.
Dane wejściowe będą miały następującą postać:
- Wiersz 1: Typ struktury (
productlubemployee) - Kolejne wiersze: Wartości pól tej struktury
Na przykład dla danych:
product
Laptop
999.99
trueWynik powinien wyglądać tak:
Type: Product
Kind: struct
Fields: 3
- Name (string): Laptop
- Price (float64): 999.99
- InStock (bool): trueA dla danych:
employee
42
Alice Johnson
EngineeringWynik powinien wyglądać tak:
Type: Employee
Kind: struct
Fields: 3
- ID (int): 42
- Name (string): Alice Johnson
- Department (string): EngineeringA dla danych:
product
Headphones
79.50
falseWynik powinien wyglądać tak:
Type: Product
Kind: struct
Fields: 3
- Name (string): Headphones
- Price (float64): 79.5
- InStock (bool): falseZwróć uwagę, że Twój inspektor obsługuje różne typy struktur, nie znając wcześniej ich budowy. Na tym polega potęga refleksji! Ta sama funkcja InspectStruct dynamicznie obsługuje zarówno struktury Product, jak i Employee.
Spróbuj swoich sił
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
// Struktura Product z polami Name, Price i InStock
type Product struct {
Name string
Price float64
InStock bool
}
// Struktura Employee z polami ID, Name i Department
type Employee struct {
ID int
Name string
Department string
}
func main() {
reader := bufio.NewReader(os.Stdin)
// Odczytaj typ struktury
structType, _ := reader.ReadString('\n')
structType = strings.TrimSpace(structType)
if structType == "product" {
// Odczytaj pola produktu
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: Utwórz strukturę Product z odczytanymi wartościami
// TODO: Wywołaj InspectStruct dla produktu i wypisz wynik
} else if structType == "employee" {
// Odczytaj pola pracownika
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: Utwórz strukturę Employee z odczytanymi wartościami
// TODO: Wywołaj InspectStruct dla pracownika i wypisz wynik
}
}
Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.
Wszystkie lekcje w sekcji Programowanie obiektowe
1Podstawy OOP w Go
Pliki zewnętrzneObszar roboczy i moduły GoPakiety i importyNazwy eksportowane i nieeksportowaneWprowadzenie do OOP w GoStruktury jako klasyDefiniowanie metod dla strukturOdbiorniki wskaźnikowe i wartościoweInicjalizacja strukturFunkcje konstruktoraPodsumowanie – prosty kalkulator4Interfejsy
Wprowadzenie do interfejsówImplementacja niejawnaInterfejs jako kontraktPusty interfejs (any)Asercja typuPrzełącznik typówKompozycja interfejsówInterfejsy Stringer i ErrorPowtórka — kalkulator figur7Hermetyzacja
Pola eksportowane i nieeksportowaneHermetyzacja na poziomie pakietuMetody pobierające i ustawiająceUkrywanie informacji w GoPowtórka – dane studentów10Typy generyczne (Go 1.18+)
Wprowadzenie do typów generycznychParametry typówOgraniczenia typówStruktury generyczneObejście ograniczeń metod generycznychPodsumowanie — generyczna kolekcja2Typy i struktury w praktyce
Typy proste i złożoneDefiniowanie własnych typówTagi pól strukturStruktury anonimoweStruktury zagnieżdżoneWartości zerowe i domyślnePodsumowanie – książka kontaktów5Kompozycja zamiast dziedziczenia
Dlaczego Go nie ma dziedziczeniaPodstawy osadzania strukturPromowanie metodOsadzanie wielu strukturOsadzanie a agregacjaPrzesłanianie osadzonych metodPodsumowanie — hierarchia pracowników8Obsługa błędów i programowanie obiektowe
Interfejs błęduWłasne typy błędówOpakowywanie błędów (fmt.Errorf)Błędy wartowniczeerrors.Is() i errors.As()Panic, Defer i RecoverPodsumowanie — parser plików3Wskaźniki i pamięć
Podstawy wskaźników w GoWskaźniki do strukturPrzekazywanie przez wartość a przez referencjęFunkcja new()Odśmiecanie pamięci w GoPowtórka — kreator listy wiązanej6Polimorfizm w Go
Polimorfizm za pomocą interfejsówDuck typing w GoZasady spełniania interfejsówKolekcje polimorficzneWstrzykiwanie zależnościPowtórka — procesor płatności9Współbieżność i OOP
Podstawy goroutinesKanały i komunikacjaKanały buforowane i niebuforowaneInstrukcja selectsync.Mutex i sync.RWMutexsync.WaitGroupProjektowanie struktur bezpiecznych wątkowoPowtórka – pula workerów12Zaawansowane zagadnienia programowania obiektowego
Wzorzec opcji funkcyjnychWzorzec Builder w GoŁańcuchowe wywoływanie metodAliasy typów a definicje typówPodstawy refleksjiPrzegląd generowania koduPoćwicz samodzielnie: Kompilator Go online