2

(作为这个问题的后续:嵌套结构初始化文字)。

现在我可以使用易于编写的文字来初始化一个结构,我稍后在我的代码中需要访问父结构的成员,但不知道具体的派生类型。就像这样:

type A struct {
    MemberA string
}

type B struct {
    A
    MemberB string
}

然后我像这样使用它:

b := B {
    A: A { MemberA: "test1" },
    MemberB: "test2",
}
fmt.Printf("%+v\n", b)

var i interface{} = b

// later in the code, I only know that I have something that has a nested A,
// I don't know about B (because A is in a library and B is in the 
// calling code that uses the library).

// so I want to say "give me the A out of i", so I try a type assertion

if a, ok := i.(A); ok {
    fmt.Printf("Yup, A is A: %+v\n", a)
} else {
    fmt.Printf("Aristotle (and John Galt) be damned! A is NOT A\n")
}

// no go

我看到的选项是:

  • 我可以使用反射来查找名为“A”的成员,并假设它是正确的类型,使用它。这将是可行的,但效率较低,而且肯定更“笨拙”。

  • 我可以要求调用者实现一个接口(类似HasA { Aval() A }或类似的接口,它返回一个 A 的实例。到目前为止,这是我能想到的最好的主意。

  • 另一点是我可以让调用者传递一个 A 值(即在上面的示例中,var i interface{} = b变为var i A = b.A)。但是发生的事情是我实际上动态地迭代 B 的成员并用它们做一些事情,所以我需要更多的“派生”类型才能做到这一点。(我从问题中省略了这一点,因为这只是关于我为什么遇到这个问题的背景,并且与问题的技术答案无关。)

如果我可以像在 Java 中那样“将其转换为 A”,那就太好了。有没有更优雅的方法来做到这一点。

4

4 回答 4

2

如果你有一个未知类型 b,那么挖掘嵌入字段的唯一方法是通过反射。

它不是那么笨重:

// obviously missing various error checks
t := reflect.ValueOf(i)
fmt.Printf("%+v\n", t.FieldByName("A").Interface().(A))

结构嵌入不是继承,尝试这样使用它会继续引发这样的问题。go中实现通用多态的方法是使用接口。

我认为处理这种情况的最简洁的方法是使用一个通用接口,并为您要处理的字段使用适当的访问器方法。您将在 stdlib 中看到这样的示例,例如http.ResponseWriter,它有一个Header()用于访问实际响应标头的方法。

于 2013-10-11T21:04:40.633 回答
2

如果我可以像在 Java 中那样“将其转换为 A”,那就太好了。有没有更优雅的方法来做到这一点。

对于你的代码质量来说,盲投几乎总是坏消息,所以让它难以做到这一点实际上是相当好的。

我会走接口路线,因为它也会消除interface{}你用来存储的b. 如果您确实有多种类型并且他们只分享他们嵌入A的界面,这AVal() A对我来说似乎很好。

作为奖励,您可以直接定义AVal() AonA这样您就不需要为每个嵌入的类型实现它A

示例(播放中):

type A struct {
    MemberA string
}

func (a *A) AVal() *A {
    return a
}

type B struct {
    *A
    MemberB string
}

type AEmbedder interface {
    AVal() *A
}

func main() {
    b := B {
            A: &A { MemberA: "test1" },
            MemberB: "test2",
    }

    var i AEmbedder = b

    a := i.AVal()
    fmt.Printf("Yup, A is A: %+v\n", a)
}

请注意,我现在使用的是指针值,因此它AVal() *A不会返回副本,而是返回A.

于 2013-10-11T21:27:38.497 回答
0

这对我有用。尽管它没有像您想要的那样直接投射,但它确实提供了正确的对象实例

fmt.Printf("It's", i.(B).A)

我希望它有帮助!

于 2013-10-11T21:04:25.097 回答
0

玩了一会儿,我得到了这个工作:http ://play.golang.org/p/-bbHZr-0xx

我不是 100% 确定为什么会这样,但我最好的理论是当你打电话时

a := i.(A)

您正在尝试对存储在ito中的任何内容进行类型转换的接口版本A。所以你首先需要告诉它它实际上是B,然后你可以访问嵌套在其中的 A

a := i.(B).A
于 2013-10-11T21:05:30.160 回答