Bases de la Réflexion
Fait partie de la section Programmation Orientée Objet du Journey GO de Coddy — leçon 83 sur 107.
La réflexion permet à un programme d'examiner et de manipuler sa propre structure au moment de l'exécution. Le package reflect de Go offre cette capacité, vous permettant d'inspecter des types, de lire des champs de structures et d'appeler des méthodes de manière dynamique.
Les deux types fondamentaux de la réflexion sont reflect.Type et reflect.Value. Vous les obtenez en utilisant reflect.TypeOf() et 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()) // 2Vous pouvez itérer sur les champs d'une structure pour examiner leurs noms, types et valeurs :
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
value := v.Field(i)
fmt.Printf("%s: %v\n", field.Name, value)
}
// Sortie :
// Name: Alice
// Age: 30La réflexion est puissante mais comporte des compromis : elle contourne la vérification des types à la compilation, s'exécute plus lentement que l'accès direct et rend le code plus difficile à comprendre. Utilisez-la avec parcimonie pour des tâches telles que le marshaling JSON, le mapping ORM ou la création d'utilitaires génériques où le type n'est pas connu au moment de la compilation.
Défi
FacileConstruisons un inspecteur de structures qui utilise la réflexion pour examiner n'importe quelle structure et rapporter sa composition ! C'est un cas d'utilisation pratique de la réflexion : créer des utilitaires qui fonctionnent avec des types inconnus au moment de la compilation.
Vous organiserez votre code sur deux fichiers :
inspector.go: Créez votre utilitaire d'inspection basé sur la réflexion.Créez une fonction
InspectStruct(v any) stringqui utilise le packagereflectpour examiner n'importe quelle structure qui lui est passée. Votre fonction doit retourner un rapport formaté contenant :- Le nom du type de la structure
- Le genre (kind) de la structure (devrait être "struct")
- Le nombre de champs
- Pour chaque champ : le nom du champ, son type et sa valeur actuelle
Le format de sortie doit être :
Type: [TypeName] Kind: struct Fields: [count] - [FieldName] ([FieldType]): [Value] - [FieldName] ([FieldType]): [Value] ...Utilisez
reflect.TypeOf()pour obtenir les informations de type etreflect.ValueOf()pour accéder aux valeurs des champs. Parcourez les champs en utilisantNumField(),Field(i)sur le type pour les métadonnées, etField(i)sur la valeur pour les valeurs réelles.main.go: Définissez des structures d'exemple et utilisez votre inspecteur.Lisez un type de structure (
productouemployee) et les valeurs de ses champs, puis créez la structure appropriée et inspectez-la.Pour
product: Créez une structureProductavec les champsName(string),Price(float64) etInStock(bool). Lisez le nom, le prix et le statut du stock.Pour
employee: Créez une structureEmployeeavec les champsID(int),Name(string) etDepartment(string). Lisez l'ID, le nom et le département.Passez la structure créée à
InspectStructet affichez le résultat.
Les entrées suivantes seront fournies :
- Ligne 1 : Type de structure (
productouemployee) - Lignes suivantes : Valeurs des champs pour ce type de structure
Par exemple, avec :
product
Laptop
999.99
trueVotre sortie devrait être :
Type: Product
Kind: struct
Fields: 3
- Name (string): Laptop
- Price (float64): 999.99
- InStock (bool): trueEt avec :
employee
42
Alice Johnson
EngineeringVotre sortie devrait être :
Type: Employee
Kind: struct
Fields: 3
- ID (int): 42
- Name (string): Alice Johnson
- Department (string): EngineeringEt avec :
product
Headphones
79.50
falseVotre sortie devrait être :
Type: Product
Kind: struct
Fields: 3
- Name (string): Headphones
- Price (float64): 79.5
- InStock (bool): falseRemarquez comment votre inspecteur fonctionne avec différents types de structures sans connaître leur structure à l'avance — c'est toute la puissance de la réflexion ! La même fonction InspectStruct gère dynamiquement les structures Product et Employee.
Aide-mémoire
Le package reflect permet aux programmes d'examiner et de manipuler leur propre structure au moment de l'exécution.
Les deux types fondamentaux sont reflect.Type et reflect.Value, obtenus à l'aide de reflect.TypeOf() et 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()) // 2Parcourez les champs de la structure pour examiner leurs noms, types et valeurs :
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
value := v.Field(i)
fmt.Printf("%s: %v\n", field.Name, value)
}
// Sortie :
// Name: Alice
// Age: 30La réflexion contourne la vérification des types à la compilation, s'exécute plus lentement que l'accès direct et rend le code plus difficile à comprendre. Utilisez-la pour des tâches telles que le marshaling JSON, le mapping ORM ou la création d'utilitaires génériques où le type n'est pas connu au moment de la compilation.
Essayez vous-même
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
// Structure Product avec les champs Name, Price, et InStock
type Product struct {
Name string
Price float64
InStock bool
}
// Structure Employee avec les champs ID, Name, et Department
type Employee struct {
ID int
Name string
Department string
}
func main() {
reader := bufio.NewReader(os.Stdin)
// Lire le type de structure
structType, _ := reader.ReadString('\n')
structType = strings.TrimSpace(structType)
if structType == "product" {
// Lire les champs du produit
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: Créer une structure Product avec les valeurs lues
// TODO: Appeler InspectStruct avec le produit et afficher le résultat
} else if structType == "employee" {
// Lire les champs de l'employé
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: Créer une structure Employee avec les valeurs lues
// TODO: Appeler InspectStruct avec l'employé et afficher le résultat
}
}
Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.
Toutes les leçons de Programmation Orientée Objet
1Fondamentaux de la POO en Go
Fichiers externesEspace de travail et Modules GoPackages et ImportsNoms exportés vs non exportésIntroduction à la POO en GoLes Structs comme ClassesDéfinir des méthodes sur les StructsReceveurs par pointeur vs par valeurInitialisation de StructsFonctions constructeursRécapitulatif - Calculatrice simple4Interfaces
Introduction aux interfacesImplémentation impliciteL'interface comme contratInterface vide (any)Assertion de typeType SwitchComposition d'interfacesInterfaces Stringer & ErrorRécapitulatif - Calculateur de formes7Encapsulation
Champs exportés vs non exportésEncapsulation au niveau du packageMéthodes Getter & SetterMasquage d'informations en GoRécapitulatif - Dossiers étudiants10Génériques (Go 1.18+)
Introduction aux génériquesParamètres de typeContraintes de typeStructures génériquesContournement pour les méthodes génériquesRécapitulatif - Collections génériques13Patrons de conception, Partie 1
Introduction aux Design PatternsPatron SingletonPatron FactoryPatron Abstract FactoryPatron ObserverPatron Strategy2Exploration approfondie des Types et Structs
Types de base et compositesDéfinitions de types personnalisésTags de StructStructs anonymesStructs imbriquéesValeurs par défaut et Zero ValuesRécapitulatif - Carnet de contacts5Composition plutôt qu'héritage
Pourquoi Go n'a pas d'héritageLes bases de l'imbrication de structsPromotion de méthodesImbriquer plusieurs structsImbrication vs AgrégationMasquage de méthodes imbriquéesRécapitulatif - Hiérarchie des employés8Gestion des erreurs & POO
L'interface errorTypes d'erreurs personnalisésError Wrapping (fmt.Errorf)Erreurs sentinelleserrors.Is() et errors.As()Panic, Defer et RecoverRécapitulatif - File Parser11Bibliothèque standard et POO
io.Reader & io.WriterL'interface sort.InterfaceL'interface fmt.Stringerencoding/json avec les StructsL'interface http.HandlerRécapitulatif - Modèles d'API REST14Patrons de conception, Partie 2
Patron CommandePatron AdaptateurPatron DécorateurPatron Template MethodPatron ÉtatPatron CompositeMiddleware en tant que Décorateur3Pointeurs et Mémoire
Bases des pointeurs en GoPointeurs vers des structsPassage par valeur vs référenceLa fonction new()Garbage Collection en GoRécapitulatif - Constructeur de liste chaînée6Le polymorphisme en Go
Le polymorphisme via les interfacesLe Duck Typing en GoRègles de satisfaction des interfacesCollections polymorphesInjection de dépendancesRécapitulatif - Processeur de paiement9Concurrence et POO
Bases des GoroutinesChannels et CommunicationChannels avec buffer vs sans bufferL'instruction Selectsync.Mutex et sync.RWMutexsync.WaitGroupConception de structures Thread-SafeRécapitulatif - Worker Pool12Concepts POO Avancés
Pattern Functional OptionsPattern Builder en GoChaînage de méthodesAlias de types vs DéfinitionsBases de la RéflexionAperçu de la génération de code