Menu
Coddy logo textTech

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())   // 2

Sie 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: 30

Reflection 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.

challenge icon

Aufgabe

Einfach

Lassen 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 das reflect-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, und reflect.ValueOf(), um auf Feldwerte zuzugreifen. Iterieren Sie durch die Felder mit NumField(), Field(i) auf dem Typ für Metadaten und Field(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 (product oder employee) und seine Feldwerte ein, erstellen Sie dann das entsprechende Struct und inspizieren Sie es.

    Für product: Erstellen Sie ein Product-Struct mit den Feldern Name (string), Price (float64) und InStock (bool). Lesen Sie Name, Preis und Lagerstatus ein.

    Für employee: Erstellen Sie ein Employee-Struct mit den Feldern ID (int), Name (string) und Department (string). Lesen Sie ID, Name und Abteilung ein.

    Übergeben Sie das erstellte Struct an InspectStruct und geben Sie das Ergebnis aus.

Die folgenden Eingaben werden bereitgestellt:

  • Zeile 1: Struct-Typ (product oder employee)
  • Folgende Zeilen: Feldwerte für diesen Struct-Typ

Zum Beispiel, gegeben:

product
Laptop
999.99
true

Ihre Ausgabe sollte sein:

Type: Product
Kind: struct
Fields: 3
- Name (string): Laptop
- Price (float64): 999.99
- InStock (bool): true

Und gegeben:

employee
42
Alice Johnson
Engineering

Ihre Ausgabe sollte sein:

Type: Employee
Kind: struct
Fields: 3
- ID (int): 42
- Name (string): Alice Johnson
- Department (string): Engineering

Und gegeben:

product
Headphones
79.50
false

Ihre Ausgabe sollte sein:

Type: Product
Kind: struct
Fields: 3
- Name (string): Headphones
- Price (float64): 79.5
- InStock (bool): false

Beachten 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())   // 2

Iterieren 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: 30

Reflection 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

	}
}
quiz iconTeste dich selbst

Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.

Alle Lektionen in Objektorientierte Programmierung