【问题标题】:Get outer/parent struct name获取外部/父结构名称
【发布时间】:2019-01-20 15:06:15
【问题描述】:

我遇到了一个 Golang 初学者问题,我不知道如何正确解决它。你能帮帮我吗?

信息:尽管这违背了 Go 的概念(不是试图成为一种 OOP 语言),但我还是想讨论一些解决方案。

我想知道接收器/子结构中的外部/父结构名称。请看下面的代码(操场: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"?
}

虽然结构是“子”,但它显示“父”。谁能告诉我如何获得正确的结构名称?我在另一个 stackoverflow 主题中看到了一个“正确”工作的“解决方案”(Go - get parent struct),但我认为这不是一个好的解决方案。

【问题讨论】:

  • Go 没有继承。试图假装它确实只会让事情变得困难。改写您的解决方案,改用可用的组合方法。
  • @JimB 好的。我会深入研究它。从你的角度来看:组合可以解决这个问题还是我需要完全重新考虑结构?
  • 您正在尝试解决继承问题。我确信有其他方法可以解决您的问题(有很多非 OOP 语言),但您只是在描述您提出的解决方案,而不是实际的问题陈述。

标签: go


【解决方案1】:

GetStructName是Parent而不是Child类型的方法,Golang也没有继承,而是有struct embedding(也有interface embedding),有点像继承,但有一个主要区别:

当我们嵌入一个类型时,该类型的方法成为外部类型的方法,但是当它们被调用时,方法的接收者是内部类型,而不是外部类型。

这基本上意味着当你调用GetStructName时,方法的接收者是Parent(内部或嵌入类型),而不是Child。

这与典型的类继承根本不同,它解释了您所看到的行为。

这是有据可查的here。

【讨论】:

  • 好的 - 谢谢。我想在 Go 中尝试使用 OOP 时,我想用头穿过墙。但是这样做仍然感觉“正确”:-)
【解决方案2】:

为了完整起见,我想分享我的解决方案(Playground:https://play.golang.org/p/tUhlz_o8Z7V)。

正如我最初的问题所述,这个想法来自Go - get parent struct。

这也与我在这里看到的 Go2 请求有关: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
//
//---

【讨论】:

    【解决方案3】:

    虽然丹尼尔上面的回答回答了它的“为什么”,但您仍然可以通过(有点难看)“排序”获得您可能正在寻找的行为:

    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())
    }
    

    (游乐场:https://play.golang.org/p/qEtoEulFSPy)

    编辑:添加了这些类型可以实现的接口,以使代码更通用。

    【讨论】:

    • 这也是我的“解决方案”之一,但这意味着一遍又一遍地为每个孩子编写相同的方法。我可能需要更好地理解 compostion 的概念,看看我是否可以将代码改写为 go-concept。
    • 我在上面的代码中添加了一个接口,使它有点通用,以便您现在可以在必须实现此名称返回函数的函数中使用该接口。尽管如此,就像你说的那样,组合将是要走的路。
    猜你喜欢
    • 1970-01-01
    • 2022-01-12
    • 1970-01-01
    • 2020-08-26
    • 2015-12-05
    • 1970-01-01
    • 1970-01-01
    • 2015-07-24
    • 1970-01-01
    相关资源
    最近更新 更多