如何创建一个嵌入另一个结构体的结构体切片?

huangapple go评论87阅读模式
英文:

How to create a slice of structs that embeds another?

问题

我有以下代码:

package main

import "fmt"

type A struct {
    Name    string
    Address string
}

type B struct {
    A
}

type C struct {
    A
}

type D struct {
    A
}

//....更多嵌入A的结构体

type myinterface interface {
    SetName(string)
    SetAddress(string)
}

func run() *A {
    // 遍历嵌入A的结构体切片.... 怎么做呢????
    for _, s := range []myinterface{
        &B{}, &C{}, &D{},
    } {
        s.SetName("Bob")
        s.SetAddress("Maine")
        // 进行其他一些在没有切片的情况下变得非常冗长的操作...
        return s.(*A)
    }
    return nil
}

func main() {
    a := run()
    fmt.Println(a)
}

我需要遍历所有嵌入A的结构体,但是我很难做到。上面的代码会报错:"cannot use B literal (type *B) as type *A in array or slice literal"。有什么好的方法吗?

英文:

I have the following:

https://play.golang.org/p/q2NUMzbw6-

package main

import "fmt"

type A struct {
	Name string
    Address string
}

type B struct {
	A
}

type C struct {
	A
}

type D struct {
	A
}

//....more structs that embed A

type myinterface interface {
	SetName(string)
	SetAddress(string)
}

func run() *A {
	// iterate over a slice of structs that embed A.... how????
	for _, s := range []*A{
		&B{}, &C{}, &D{},
	} {
		s.SetName("Bob")
		s.SetAddress("Maine")
        // do some other stuff that gets very verbose w/out a slice...
		return s.A
	}
}

func main() {
	a := run()
	fmt.Println(a)
}

I need to iterate through all of the structs that embed A but am having a hard time doing so. The above doesn't work "cannot use B literal (type *B) as type *A in array or slice literal". What is the best way?

答案1

得分: 1

在A上声明满足接口的方法:

func (a *A) SetName(s string) {
    a.Name = s
}

func (a *A) SetAddress(s string) {
    a.Address = s
}

在范围内使用该接口的切片:

for _, s := range []myinterface{&B{}, &C{}, &D{}} {
    ...
}

playground示例

英文:

Declare methods on A that satisfy the interface:

func (a *A) SetName(s string) {
	a.Name = s
}

func (a *A) SetAddress(s string) {
	a.Address = s
}

Use a slice of that interface in the range:

for _, s := range []myinterface{&B{}, &C{}, &D{}} {
   ...
}

playground example

答案2

得分: 1

这是一个常见的误解,认为Go语言中的类型嵌入与其他语言中的继承类似。

实际上,类型嵌入类似于其他语言中的组合。

在你的例子中,类型 BA 并没有任何关联,除了通过将 A 嵌入到 B 中,你可以直接在 B 上调用 A 的方法。

你可以在这里阅读更多信息:

https://golang.org/doc/effective_go.html#embedding

为了模拟"继承",你需要使用接口。

你应该将 myinterface 作为数组类型,以便以通用的方式处理所有这些结构体。

英文:

It's a common misconception to believe that type embedding in Go is analogous to inheritance in other languages.

In fact, type embedding is analogous to composition in other languages.

In your example, types B and A are not related in any way, other than the fact that by embedding A in B you are able to invoke A's methods directly over B.

You can read more about it here:

https://golang.org/doc/effective_go.html#embedding

In order to mimic "inheritance" you need to work with interfaces.

You should use myinterface as the array type in order to treat all of those structs in a generic way.

答案3

得分: 0

编译器告诉你问题所在:你不能将类型 B 用作类型 A(也不能将类型 A 用作类型 B);这些类型根本不相同。你应该遍历一个 myinterface 切片。

然而,这还不是完整的解决方案,因为 s.A 不起作用,因为 s 现在具有类型 myinterface(具有底层类型 *B*C*D),并且 myinterface 没有名为 A 的方法。

你可以通过向 myinterface 添加另一个方法 aSelf() *A 并使用接收者类型 *A 实现 aSelf 来解决这个问题,该方法简单地返回接收者。这样,BC 等都可以使用这个方法。请注意,在这种情况下,你不能简单地将该方法命名为 A,因为 B.A(以及 C.A 等)将是模棱两可的:.A 是指嵌入的 A 字段本身,还是指嵌入的 A 字段的 A 方法?如果该方法不会被导出,你可以将其命名为 a,否则你需要使用其他名称,如前面提到的 aSelf

以下是需要修改/添加的相关部分:

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

type myinterface interface {
    SetName(string)
    SetAddress(string)
    aSelf() *A
}

func run() *A {
    // 遍历 myinterface 切片
    for _, s := range []myinterface{
        &B{}, &C{}, &D{},
    } {
        s.SetName("Bob")
        s.SetAddress("Maine")
        // 进行一些其他操作,如果没有切片会变得非常冗长...
        return s.aSelf()
    }
    return nil
}

Playground 链接

英文:

The compiler tells you the problem: you can't use type B as type A (and you also can't use type A as type B); the types simply aren't the same. You should be ranging over a slice of myinterface.

However, that's not the entire solution because s.A won't work since s now has the type myinterface (with underlying types *B, *C, and *D), and no method named A belongs to myinterface.

You can fix this by adding another method aSelf() *A to myinterface and implementing aSelf with a receiver type *A that simply returns the receiver. This way, B, C, etc. can all use this method. Note that you can't simply name the method A in this instance because B.A (and C.A etc.) will be ambiguous: does the .A refer to the embedded A field itself or to the A method of the embedded A field? You could name it a if the method will not be exported, else you need to use a different name such as the previously mentioned aSelf.

Below are the relevant bits to alter/add:

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

type myinterface interface {
    SetName(string)
    SetAddress(string)
    aSelf() *A
}

func run() *A {
	// iterate over a slice of myinterface
	for _, s := range []myinterface{
		&B{}, &C{}, &D{},
	} {
		s.SetName("Bob")
		s.SetAddress("Maine")
		// do some other stuff that gets very verbose w/out a slice...
		return s.aSelf()
	}
	return nil
}

Playground link

huangapple
  • 本文由 发表于 2017年7月14日 10:14:27
  • 转载请务必保留本文链接:https://go.coder-hub.com/45093551.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定