英文:
nested struct - get "base" struct
问题
现在我可以使用易于编写的文字初始化一个结构体,但在我的代码中,我需要访问父结构体的成员,而不知道具体的派生类型。就像这样:
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",那就太好了。有没有更优雅的方法来做到这一点呢?
英文:
(As a followup this question: https://stackoverflow.com/questions/19325496/nested-struct-intialization-literals/19325760).
Now that I can initialize a struct with literals that are easy to write, I later in my code need access members of the parent struct, but without knowing the concrete derived type. It's like this:
type A struct {
MemberA string
}
type B struct {
A
MemberB string
}
And then I use it like this:
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
The options I see are:
-
I could use reflection to look for a member called "A" and, assuming it's the right type, use it. This would be workable but less efficient and certainly more "clunky".
-
I could require the caller to implement an interface (like
HasA { Aval() A }
or similar which returns an instance of A. So far this is the best idea I could think of. -
The other point is that I could just have the caller pass a A value (i.e. in the example above,
var i interface{} = b
becomesvar i A = b.A
). But what's happening is I actually dynamically iterate over the members of B and do stuff with them, so I need that more "derived" type in order to that. (I've omitted that from the question because it's more just background as to why I'm running into this and is not pertinent to the technical answer of the question.)
It would be great if I could just "cast it to A", as you would in Java. Is there a more elegant way to do that.
答案1
得分: 2
如果你有一个未知类型的b,唯一通过反射来获取嵌入字段的方法是:
// 显然缺少各种错误检查
t := reflect.ValueOf(i)
fmt.Printf("%+v\n", t.FieldByName("A").Interface().(A))
结构体嵌入不是继承,试图将其用作继承会继续引发此类问题。在Go中实现通用多态的方法是使用接口。
我认为处理这种情况最清晰的方法是使用一个公共接口,并为你想处理的字段提供适当的访问方法。你会在stdlib中看到这方面的例子,例如http.ResponseWriter
,它有一个用于访问实际响应头的Header()
方法。
英文:
If you have an unknown type b, the only way to dig out an embedded field is through reflection.
It's not that clunky:
// obviously missing various error checks
t := reflect.ValueOf(i)
fmt.Printf("%+v\n", t.FieldByName("A").Interface().(A))
Struct embedding is not inheritance, and trying to use it as such is going to continue to bring up issues like this. The way to achieve general polymorphism in go is to use interfaces.
I think the cleanest way to handle this situation is to use a common interface, with appropriate accessor methods for the fields you want to handle. You'll see examples of this the stdlib, e.g. http.ResponseWriter
, which has a Header()
method used to access the actual response headers.
答案2
得分: 2
如果我能像在Java中那样将其“转换为A”,那将非常好。有没有更优雅的方法来做到这一点。
盲目地进行类型转换通常对代码质量来说不是好消息,所以让它变得困难实际上是相当好的。
我会选择使用接口的方式,这样也可以消除您用于存储b
的interface{}
。如果您确实有多种类型,并且它们只共享了嵌入A
,那么对我来说,提供AVal() A
的接口似乎是个不错的选择。
作为一个额外的好处,您可以直接在A
上定义AVal() A
,这样您就不需要为每个嵌入A
的类型实现它。
示例(在play上):
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
实例。
英文:
> It would be great if I could just "cast it to A", as you would in Java. Is there a more elegant way to do that.
Blind casting is almost always bad news for your code quality so it is actually quite good
to make it difficult to do so.
I would go the interface route as it would also eliminate the interface{}
you use to store b
.
If you indeed have a high variety of types and they only share that they embed A
an interface
which offers AVal() A
seems good to me.
As a bonus, you could define AVal() A
on A
directly so you don't need to implement it for every type that embeds A
.
Example (on play):
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)
}
Note that I'm now using a pointer value so that AVal() *A
does not return a copy but
the respective instance of A
.
答案3
得分: 0
这对我有用。虽然它不像你想要的那样直接转换类型,但它确实给出了正确的对象实例。
fmt.Printf("这是", i.(B).A)
希望对你有帮助!
英文:
This worked for me. Although it's not directly casting like you wanted, it does give the correct object instance
fmt.Printf("It's", i.(B).A)
I hope it helps!
答案4
得分: 0
我玩了一下,成功让它工作了:http://play.golang.org/p/-bbHZr-0xx
我不完全确定为什么这样可以,但我最好的理论是,当你调用
a := i.(A)
你试图对存储在i
中的任何内容进行接口版本的类型转换为A
。所以你首先需要告诉它实际上是B
,然后你可以访问其中嵌套的A
a := i.(B).A
英文:
Playing around with it for a bit, I got this to work: http://play.golang.org/p/-bbHZr-0xx
I'm not 100% sure on why this works, but my best theory is that when you call
a := i.(A)
you are trying to do an interface version of a typecast of whatever is stored in i
to A
. So you first need to tell it that it is actually B
, then you can access the A that is nested in it
a := i.(B).A
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论