Reflection-Grundlagen
Teil des Abschnitts Objektorientierte Programmierung der GO-Journey von Coddy — Lektion 83 von 107.
Reflection ermöglicht es einem Programm, seine eigene Struktur zur Laufzeit zu untersuchen und zu manipulieren. Das reflect-Paket von Go bietet diese Funktionalität und ermöglicht es Ihnen, Typen zu inspizieren, Strukturfelder zu lesen und Methoden dynamisch aufzurufen.
Die beiden Kerntypen in der Reflexion sind reflect.Type und reflect.Value. Sie erhalten diese mit reflect.TypeOf() und 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()) // 2Sie können über Struct-Felder iterieren, um deren Namen, Typen und Werte zu untersuchen:
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
value := v.Field(i)
fmt.Printf("%s: %v\n", field.Name, value)
}
// Ausgabe:
// Name: Alice
// Age: 30Reflection ist leistungsstark, bringt aber Kompromisse mit sich: Sie umgeht die Typprüfung zur Kompilierzeit, läuft langsamer als direkter Zugriff und macht den Code schwerer verständlich. Verwenden Sie sie sparsam für Aufgaben wie JSON-Marshaling, ORM-Mapping oder das Erstellen generischer Dienstprogramme, bei denen der Typ zur Kompilierzeit nicht bekannt ist.
Aufgabe
EinfachLassen Sie uns einen Struct-Inspektor bauen, der Reflection verwendet, um ein beliebiges Struct zu untersuchen und dessen Struktur zu melden! Dies ist ein praktischer Anwendungsfall für Reflection – das Erstellen von Dienstprogrammen, die mit Typen arbeiten, die zur Kompilierzeit unbekannt sind.
Sie werden Ihren Code auf zwei Dateien verteilen:
inspector.go: Erstellen Sie Ihr auf Reflection basierendes Inspektions-Utility.Erstellen Sie eine Funktion
InspectStruct(v any) string, die dasreflect-Paket verwendet, um jedes ihr übergebene Struct zu untersuchen. Ihre Funktion sollte einen formatierten Bericht zurückgeben, der Folgendes enthält:- Den Typnamen des Structs
- Den Kind des Structs (sollte "struct" sein)
- Die Anzahl der Felder
- Für jedes Feld: den Feldnamen, seinen Typ und seinen aktuellen Wert
Das Ausgabeformat sollte wie folgt aussehen:
Type: [TypeName] Kind: struct Fields: [count] - [FieldName] ([FieldType]): [Value] - [FieldName] ([FieldType]): [Value] ...Verwenden Sie
reflect.TypeOf(), um Typinformationen zu erhalten, undreflect.ValueOf(), um auf Feldwerte zuzugreifen. Iterieren Sie durch die Felder mitNumField(),Field(i)auf dem Typ für Metadaten undField(i)auf dem Wert für die tatsächlichen Werte.main.go: Definieren Sie Beispiel-Structs und verwenden Sie Ihren Inspektor.Lesen Sie einen Struct-Typ (
productoderemployee) und seine Feldwerte ein, erstellen Sie dann das entsprechende Struct und inspizieren Sie es.Für
product: Erstellen Sie einProduct-Struct mit den FeldernName(string),Price(float64) undInStock(bool). Lesen Sie Name, Preis und Lagerstatus ein.Für
employee: Erstellen Sie einEmployee-Struct mit den FeldernID(int),Name(string) undDepartment(string). Lesen Sie ID, Name und Abteilung ein.Übergeben Sie das erstellte Struct an
InspectStructund geben Sie das Ergebnis aus.
Die folgenden Eingaben werden bereitgestellt:
- Zeile 1: Struct-Typ (
productoderemployee) - Folgende Zeilen: Feldwerte für diesen Struct-Typ
Zum Beispiel, gegeben:
product
Laptop
999.99
trueIhre Ausgabe sollte sein:
Type: Product
Kind: struct
Fields: 3
- Name (string): Laptop
- Price (float64): 999.99
- InStock (bool): trueUnd gegeben:
employee
42
Alice Johnson
EngineeringIhre Ausgabe sollte sein:
Type: Employee
Kind: struct
Fields: 3
- ID (int): 42
- Name (string): Alice Johnson
- Department (string): EngineeringUnd gegeben:
product
Headphones
79.50
falseIhre Ausgabe sollte sein:
Type: Product
Kind: struct
Fields: 3
- Name (string): Headphones
- Price (float64): 79.5
- InStock (bool): falseBeachten Sie, wie Ihr Inspektor mit verschiedenen Struct-Typen arbeitet, ohne deren Struktur im Voraus zu kennen – das ist die Stärke von Reflection! Dieselbe InspectStruct-Funktion verarbeitet sowohl Product- als auch Employee-Structs dynamisch.
Spickzettel
Das reflect-Paket ermöglicht es Programmen, ihre eigene Struktur zur Laufzeit zu untersuchen und zu manipulieren.
Die beiden Kerntypen sind reflect.Type und reflect.Value, die mit reflect.TypeOf() und reflect.ValueOf() ermittelt werden:
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()) // 2Iterieren Sie über Strukturfelder, um deren Namen, Typen und Werte zu untersuchen:
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
value := v.Field(i)
fmt.Printf("%s: %v\n", field.Name, value)
}
// Output:
// Name: Alice
// Age: 30Reflection umgeht die Typprüfung zur Kompilierzeit, läuft langsamer als der direkte Zugriff und macht den Code schwerer verständlich. Verwenden Sie es für Aufgaben wie JSON-Marshaling, ORM-Mapping oder das Erstellen generischer Hilfsprogramme, bei denen der Typ zur Kompilierzeit nicht bekannt ist.
Probier es selbst
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
// Product struct mit den Feldern Name, Price und InStock
type Product struct {
Name string
Price float64
InStock bool
}
// Employee struct mit den Feldern ID, Name und Department
type Employee struct {
ID int
Name string
Department string
}
func main() {
reader := bufio.NewReader(os.Stdin)
// Den struct-Typ einlesen
structType, _ := reader.ReadString('\n')
structType = strings.TrimSpace(structType)
if structType == "product" {
// Die product-Felder einlesen
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: Ein Product struct mit den gelesenen Werten erstellen
// TODO: InspectStruct mit dem product aufrufen und das Ergebnis ausgeben
} else if structType == "employee" {
// Die employee-Felder einlesen
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: Ein Employee struct mit den gelesenen Werten erstellen
// TODO: InspectStruct mit dem employee aufrufen und das Ergebnis ausgeben
}
}
Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.
Alle Lektionen in Objektorientierte Programmierung
1Grundlagen der Go OOP
Externe DateienGo Workspace & ModulePackages & ImportsExportierte vs. nicht-exportierte NamenEinführung in OOP in GoStructs als KlassenMethoden für Structs definierenPointer- vs. Value-ReceiverStruct-InitialisierungKonstruktor-FunktionenZusammenfassung – Einfacher Taschenrechner4Interfaces
Einführung in InterfacesImplizite ImplementierungInterface als VertragLeeres Interface (any)Type AssertionType SwitchInterface-KompositionStringer & Error InterfacesRückblick - Formen-Rechner7Kapselung
Exportierte vs. nicht exportierte FelderKapselung auf Paket-EbeneGetter- & Setter-MethodenInformation Hiding in GoZusammenfassung - Studierendendatensätze10Generics (Go 1.18+)
Einführung in GenericsTypparameterType ConstraintsGenerische StructsWorkaround für generische MethodenZusammenfassung – Generische Collection2Vertiefung: Typen & Structs
Basis- & zusammengesetzte TypenBenutzerdefinierte TypdefinitionenStruct TagsAnonyme StructsVerschachtelte StructsZero Values & StandardwerteZusammenfassung - Kontaktbuch5Komposition statt Vererbung
Warum Go keine Vererbung hatGrundlagen des Struct EmbeddingMethod PromotionEinbetten mehrerer StructsEmbedding vs. AggregationShadowing eingebetteter MethodenZusammenfassung – Mitarbeiter-Hierarchie8Fehlerbehandlung & OOP
Das error-InterfaceEigene FehlertypenError Wrapping (fmt.Errorf)Sentinel-Fehlererrors.Is() und errors.As()Panic, Defer und RecoverZusammenfassung – Datei-Parser3Zeiger & Speicher
Zeiger-Grundlagen in GoZeiger auf StructsWertübergabe vs. ReferenzübergabeDie new() FunktionGarbage Collection in GoWiederholung – Linked List Builder6Polymorphismus in Go
Polymorphismus über InterfacesDuck Typing in GoInterface-ErfüllungsregelnPolymorphe CollectionsDependency InjectionZusammenfassung – Payment Processor9Nebenläufigkeit & OOP
Goroutines GrundlagenChannels & KommunikationGepufferte vs. ungepufferte ChannelsSelect-Anweisungsync.Mutex & sync.RWMutexsync.WaitGroupThread-sicheres Struct-DesignZusammenfassung - Worker Pool12Fortgeschrittene OOP-Konzepte
Functional Options PatternBuilder-Pattern in GoMethodenverkettungTyp-Aliase vs. DefinitionenReflection-GrundlagenÜberblick zur Code-Generierung