Je suis confronté à un problème de débutants Golang et je ne sais pas comment le résoudre correctement. Pourriez-vous s'il vous plaît m'aider?
Info: Même si cela va à l'encontre du concept de Go (n'essayant pas d'être un langage POO), j'aimerais encore discuter de certaines solutions.
J'aimerais connaître le nom de la structure externe / parent dans le receveur / enfant. Veuillez consulter le code suivant (terrain de jeu: https://play.golang.org/p/h6dARJQwidS )
package main
import (
"fmt"
"reflect"
)
type Parent struct {
Id uint32
}
func (p *Parent) GetStructName() string {
return reflect.TypeOf(p).Elem().Name()
}
type Child struct {
Parent
}
func main() {
myChild := Child{}
fmt.Println(myChild.GetStructName()) // Gives "Parent" instead of "Child". How to get "Child"?
}
Il affiche "Parent", bien que la structure soit un "Child". Quelqu'un peut-il me dire comment obtenir le nom de structure correct? J'ai vu une `` solution '' dans un autre sujet de stackoverflow qui fonctionne `` correctement '' ( Go - get parent struct ), mais je ne pense pas que ce soit une bonne solution.
3 Réponses :
GetStructName est une méthode du type Parent pas Child , également Golang n'a pas d'héritage, à la place il y a struct embedding (également là est l'intégration d'interface), qui est un peu comme l'héritage, mais avec une différence clé:
Lorsque nous intégrons un type, les méthodes de ce type deviennent des méthodes du type externe, mais lorsqu'elles sont appelées, le récepteur de la méthode est le type interne, pas le type externe.
Cela signifie essentiellement que lorsque vous appelez GetStructName , le récepteur de la méthode est Parent (le type interne ou intégré), et non Child code >.
Ceci est fondamentalement différent de l'héritage de classe typique, et cela explique le comportement que vous voyez.
C'est bien documenté ici .
D'accord merci. Je pense que je veux aller avec la tête à travers le mur lorsque j'essaie d'utiliser la POO dans Go. Mais il semble toujours "juste" de le faire :-)
Bien que la réponse de Daniel ci-dessus réponde au "pourquoi" de celui-ci, vous pouvez toujours "sorta" obtenir le comportement que vous recherchez probablement via (un peu moche):
package main
import (
"fmt"
"reflect"
)
type NamedReturningType interface {
GetStructName() string
}
type Parent struct {
Id uint32
}
func (p *Parent) GetStructName() string {
return reflect.TypeOf(p).Elem().Name()
}
type Child struct {
Parent
}
func (c *Child) GetStructName() string {
return reflect.TypeOf(c).Elem().Name()
}
func main() {
myChild := Child{}
fmt.Println(myChild.GetStructName())
myParent := Parent{}
fmt.Println(myParent.GetStructName())
}
(terrain de jeu: https://play.golang.org/p/qEtoEulFSPy )
EDIT: Ajout d'une interface que ces types peuvent implémenter afin de rendre le code plus générique.
C'était aussi une de mes «solutions», mais celle-ci impliquait d'écrire la même méthode à chaque enfant encore et encore. Je devrai peut-être mieux comprendre le concept de compostion et voir si je peux retravailler le code vers le concept go.
J'ai ajouté une interface au code ci-dessus pour le rendre un peu générique afin que vous puissiez maintenant utiliser l'interface dans les fonctions qui doivent implémenter cette fonction de retour de nom. Néanmoins, comme vous l'avez dit, la composition serait la voie à suivre.
Pour être complet, je voulais partager ma solution (Playground: https: //play.golang. org / p / tUhlz_o8Z7V ).
Comme décrit dans ma question initiale, l'idée vient de Go - get parent struct .
C'est également lié à une requête Go2 que j'ai vue ici: https://github.com/golang/go/issues/28254
package main
import (
"fmt"
"log"
"reflect"
)
// we need an interface so methods are being embedded automatically
type IParent interface {
Init(IParent) IParent
}
// internal private fields, non-visible from the outside
type Parent struct {
_IsInitialized bool
_Self IParent
}
// init the struct, set "_Self" to it's caller
func (p *Parent) Init(o IParent) IParent {
p._Self = o
p._IsInitialized = true
return o
}
// This method uses "_Self" to determine what it actually is
func (p *Parent) GetStructName() string {
if !p._IsInitialized {
log.Fatal("Struct not initialized. You may call 'myVar.Init(&myVar)' to initialize it.")
}
return reflect.TypeOf(p._Self).Elem().Name()
}
// Below childs have "Init()" from Parent, so they implement IParent automatically
// No need to duplicate any methods here anymore
type Child1 struct {
Parent
}
type Child2 struct {
Parent
}
type Child3 struct {
Parent
}
type Child4 struct {
Parent
}
func main() {
myChild1 := Child1{}
myChild1.Init(&myChild1) // Init object (set _Self on struct)
fmt.Println(myChild1.GetStructName()) // Gives "Child1"
myChild2 := Child2{}
myChild2.Init(&myChild2) // Init object (set _Self on struct)
fmt.Println(myChild2.GetStructName()) // Gives "Child2"
myChild3 := Child3{}
myChild3.Init(&myChild3) // Init object (set _Self on struct)
fmt.Println(myChild3.GetStructName()) // Gives "Child3"
myChild4 := Child4{}
fmt.Println(myChild4.GetStructName()) // Fatal error
}
// Footnotes:
//---
//
// This attempt tries to solve a go 'inheritance' problem although go is *NOT* meant to be an OOP language. It was a funny experiment still :-)
// License: open domain, no attribution
// https://www.xsigndll.com
//
//---
Go n'a pas d'héritage. Essayer de faire semblant de le faire ne fera que rendre les choses difficiles. Retravaillez votre solution pour utiliser à la place les méthodes de composition disponibles.
@JimB Ok. Je vais creuser dedans. De votre point de vue: la composition peut-elle résoudre ce problème ou dois-je repenser complètement la structure?
Vous essayez de résoudre votre problème d’héritage. Je suis sûr qu'il existe d'autres moyens de résoudre votre problème (il existe de nombreux langages non-POO), mais vous ne décrivez que la solution que vous proposez, plutôt qu'une déclaration de problème réelle.