Nozioni di base sulla reflection
Fa parte della sezione Programmazione orientata agli oggetti del percorso GO di Coddy. Lezione 83 di 107.
La riflessione consente a un programma di esaminare e manipolare la propria struttura durante l'esecuzione. Il pacchetto reflect di Go offre questa funzionalità, permettendoti di ispezionare i tipi, leggere i campi delle struct e chiamare metodi dinamicamente.
I due tipi fondamentali nella reflection sono reflect.Type e reflect.Value. Li ottieni usando reflect.TypeOf() e 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()) // 2Puoi scorrere i campi della struct per esaminarne i nomi, i tipi e i valori:
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: 30La reflection è potente, ma comporta dei compromessi: aggira il controllo dei tipi in fase di compilazione, è più lenta dell’accesso diretto e rende il codice più difficile da comprendere. Usala con moderazione per attività come la serializzazione JSON, la mappatura ORM o la creazione di utilità generiche, quando il tipo non è noto in fase di compilazione.
Sfida
FacileCostruiamo un ispettore di struct che usa la reflection per esaminare qualsiasi struct e descriverne la struttura! Questo è un caso d'uso pratico della reflection: creare utilità che funzionano con tipi sconosciuti al momento della compilazione.
Organizzerai il codice in due file:
inspector.go: crea la tua utilità di ispezione basata sulla reflection.Implementa una funzione
InspectStruct(v any) stringche usa il pacchettoreflectper esaminare qualsiasi struct che le viene passato. La funzione deve restituire un report formattato contenente:- Il nome del tipo della struct
- Il kind della struct (dovrebbe essere "struct")
- Il numero di campi
- Per ogni campo: il nome, il tipo e il valore corrente
Il formato dell'output dovrebbe essere:
Type: [TypeName] Kind: struct Fields: [count] - [FieldName] ([FieldType]): [Value] - [FieldName] ([FieldType]): [Value] ...Usa
reflect.TypeOf()per ottenere le informazioni sul tipo ereflect.ValueOf()per accedere ai valori dei campi. Itera sui campi usandoNumField(),Field(i)sul tipo per i metadati eField(i)sul valore per ottenere i valori effettivi.main.go: definisci struct di esempio e usa il tuo ispettore.Leggi un tipo di struct (
productoemployee) e i valori dei suoi campi, poi crea la struct appropriata e ispezionala.Per
product: crea una structProductcon i campiName(string),Price(float64) eInStock(bool). Leggi il nome, il prezzo e lo stato delle scorte.Per
employee: crea una structEmployeecon i campiID(int),Name(string) eDepartment(string). Leggi l'ID, il nome e il reparto.Passa la struct creata a
InspectStructe stampa il risultato.
Verranno forniti i seguenti input:
- Riga 1: tipo di struct (
productoemployee) - Righe successive: valori dei campi per quel tipo di struct
Per esempio, dato:
product
Laptop
999.99
trueL'output dovrebbe essere:
Type: Product
Kind: struct
Fields: 3
- Name (string): Laptop
- Price (float64): 999.99
- InStock (bool): trueE dato:
employee
42
Alice Johnson
EngineeringL'output dovrebbe essere:
Type: Employee
Kind: struct
Fields: 3
- ID (int): 42
- Name (string): Alice Johnson
- Department (string): EngineeringE dato:
product
Headphones
79.50
falseL'output dovrebbe essere:
Type: Product
Kind: struct
Fields: 3
- Name (string): Headphones
- Price (float64): 79.5
- InStock (bool): falseNota come il tuo ispettore funzioni con diversi tipi di struct senza conoscerne in anticipo la struttura. Questa è la potenza della reflection! La stessa funzione InspectStruct gestisce dinamicamente sia le struct Product sia le struct Employee.
Provalo tu
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
// Struct Product con i campi Name, Price e InStock
type Product struct {
Name string
Price float64
InStock bool
}
// Struct Employee con i campi ID, Name e Department
type Employee struct {
ID int
Name string
Department string
}
func main() {
reader := bufio.NewReader(os.Stdin)
// Leggi il tipo di struct
structType, _ := reader.ReadString('\n')
structType = strings.TrimSpace(structType)
if structType == "product" {
// Leggi i campi del prodotto
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: Crea una struct Product con i valori letti
// TODO: Chiama InspectStruct con il prodotto e stampa il risultato
} else if structType == "employee" {
// Leggi i campi del dipendente
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: Crea una struct Employee con i valori letti
// TODO: Chiama InspectStruct con il dipendente e stampa il risultato
}
}
Questa lezione include un breve quiz. Inizia la lezione per rispondere e tenere traccia dei tuoi progressi.
Tutte le lezioni di Programmazione orientata agli oggetti
1Fondamenti di OOP in Go
File esterniWorkspace e moduli GoPacchetti e importazioniNomi esportati e non esportatiIntroduzione all’OOP in GoGli struct come classiDefinire metodi sugli structReceiver con puntatore vs receiver con valoreInizializzazione degli structFunzioni costruttriciRiepilogo - Calcolatrice semplice4Interfacce
Introduzione alle interfacceImplementazione implicitaL’interfaccia come contrattoInterfaccia vuota (any)Asserzione di tipoSwitch sul tipoComposizione di interfacceInterfacce Stringer ed ErrorRipasso - Calcolatore di forme7Incapsulamento
Campi esportati e non esportatiIncapsulamento a livello di packageMetodi Getter e SetterNascondimento delle informazioni in GoRiepilogo - Registri degli studenti10Tipi generici (Go 1.18+)
Introduzione ai tipi genericiParametri di tipoVincoli di tipoStruct genericheSoluzione alternativa per i metodi genericiRiepilogo - Collezione generica13Modelli di progettazione Parte 1
Introduzione ai modelli di progettazioneModello SingletonModello FactoryModello Abstract FactoryModello ObserverModello Strategy2Approfondimento su tipi e struct
Tipi di base e compostiDefinizioni di tipi personalizzatiTag delle structStruct anonimeStruct annidateValori zero e predefinitiRipasso - Rubrica5Composizione invece dell'ereditarietà
Perché Go non ha l'ereditarietàFondamenti dell'embedding delle structPromozione dei metodiEmbedding di più structEmbedding e aggregazione a confrontoMascheramento dei metodi embeddedRiepilogo: gerarchia dei dipendenti8Gestione degli errori e OOP
L'interfaccia errorTipi di errore personalizzatiWrapping degli errori (fmt.Errorf)Errori sentinellaerrors.Is() ed errors.As()Panic, Defer e RecoverRiepilogo - Parser di file11Libreria standard e OOP
io.Reader e io.Writersort.InterfaceInterfaccia fmt.Stringerencoding/json con le structInterfaccia http.HandlerRipasso - modelli REST API14Modelli di progettazione – Parte 2
Pattern CommandPattern AdapterPattern DecoratorPattern Template MethodPattern StatePattern CompositeMiddleware come Decorator3Puntatori e memoria
Concetti base dei puntatori in GoPuntatori a structPassaggio per valore vs per riferimentoLa funzione new()Garbage collection in GoRiepilogo - Costruttore di liste concatenate6Polimorfismo in Go
Polimorfismo tramite interfacceDuck typing in GoRegole di soddisfacimento delle interfacceCollezioni polimorficheIniezione delle dipendenzeRiepilogo - Elaboratore di pagamenti9Concorrenza e OOP
Basi delle goroutineCanali e comunicazioneCanali bufferizzati vs non bufferizzatiIstruzione selectsync.Mutex e sync.RWMutexsync.WaitGroupProgettazione di struct thread-safeRiepilogo: worker pool12Concetti avanzati di OOP
Pattern delle opzioni funzionaliPattern Builder in GoConcatenamento dei metodiAlias di tipo e definizioni a confrontoNozioni di base sulla reflectionPanoramica sulla generazione del codiceEsercitati da solo: Compilatore Go online