嵌入的结构体方法能否了解父/子结构体的信息?

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

Can embedded struct method have knowledge of parent/child?

问题

我已经在业余时间里断断续续地使用Go语言工作了几个月,感觉自己对它有所掌握。作为一个来自传统面向对象编程语言(如Java和PHP)的人,我在理解Go语言中的组合和嵌入方面遇到了一些困难。最近我觉得我终于理解了,并且想要更多地使用它,但是我在一个想法上遇到了障碍。

假设我有一个来自某个包的结构体,它有一个Validate方法,并且我在我当前工作的包中将其嵌入到了我的结构体中。是否有一种方式(我拥有这两个包),可以在Validate方法内部获取到进行嵌入的结构体的引用?就像一种查看调用者的方式,因为我将在“父结构体”上调用它,而不是在嵌入的结构体上调用。

我试图将其可视化...

type B struct {}
func (b B) Validate() {
  // 在这里以某种方式获取到A对象的引用
}


type A struct {
  B
}

a := A{B{}}
a.Validate()

我觉得这似乎是不可能的,但如果可以的话,那将是很酷的。我想你可能需要使A和B都成为相同的接口类型才能实现这一点。对此有什么想法或帮助将不胜感激。

英文:

I have been working with Go on and off on my free time for a few months and I feel I've been getting a hang of it. Coming from traditional OOP languages, such as Java and PHP, I had a hard time grasping composition and embedding in Go. I think it finally clicked recently and want to use it more, but I have hit a roadblock for one idea I have.

Lets say I have a struct from a package that has a Validate method and I embedded that in my struct in the package I am currently working in. Is there a way (I own both packages) that I can get a reference to the struct that is doing the embedding inside of the Validate method? Kind of like a way to see who called me since I would be calling it on the "parent" instead of the embedded.

I'll try to visualize this...

type B struct {}
func (b B) Validate() {
  // get reference somehow of A obj
}


type A struct {
  B
}

a := A{B{}}
a.Validate()

I feel as though this isn't possible, but it would be cool if you could. I'd imagine that you would need to make A and B both the same interface type for this to work. Any thoughts or help will be much appreciated.

答案1

得分: 2

这是一些不可能的事情。为了更好地理解原因,看一下你的示例的稍作修改的版本:

type B struct{}

func (b *B) Validate() {
    fmt.Printf("b:%p\n", b)
}

type A struct {
    *B
}

func main() {
    b := B{}
    a1 := A{&b}
    a2 := A{&b}

    a1.Validate()
    a2.Validate()
    a1.B.Validate()
    a2.B.Validate()

    b.Validate()
}

输出结果如预期,*B 的值在每个地方都相同(Go Playground):

b: 0x1becd8
b: 0x1becd8
b: 0x1becd8
b: 0x1becd8
b: 0x1becd8

正如你所见,我改变了方法接收者和嵌入类型为 B 的指针。

从这个示例中可以清楚地看出:你可以使用相同的 B 的值(或者更准确地说是 B 的值的相同地址)嵌入到不同的 A 类型的值中!

并且你可以在两者上调用 Validate():所以理论上的“父级”甚至对于给定的 *B 值来说都不是“常量”。 这不会成为一个破坏性因素,但是:当你拥有一个 A 的值时,你可以通过写 a.Validate() 来调用被提升的方法 B.Validate(),这是可以的,但是你也可以像这样调用它 a.B.Validate() - 这次你实际上没有一个 A 的值(有争议,但是 Validate() 是在 *B 的值上调用的,而不是 A 的值),但最后你也可以调用 b.Validate() - 这次你肯定没有一个 A 的值。

父级没有明确的类型,B(或 *B)可以嵌入到任何类型中(所以它不能是其他类型)。

所以想象一下:你有一个具体的 *B 的值,当它的 Validate() 方法被调用时,有时会有一个父级,有时则没有。那么有什么理由要有一个父级呢?

回到你的示例:为了使这个 Validate() 方法验证一些有意义的东西,它应该(必须)作为参数显式地传递给 Validate() 方法,而不是自动传递。

你可以像 captncraig 在 他的答案 中写的那样做得更简单一些(+1)。

不过,如果你在 A 中显式添加一个方法来调用 B.Validate(),你可以简化它,像这样:

func (a *A) Validate2() {
    a.Validate(a)
}

// 使用它:
a.Validate2()

对于你想象中的这种验证器来说,没有理由将其嵌入到 A 中,它应该只是 A 的一个字段,或者由于没有直接关系,它可以是一个“独立”的验证器。在这两种情况下,如果你想简化验证,你可以为 A 添加一个辅助的 Validate() 方法。

英文:

This is something not possible. To better understand why, see this slightly modified version of your example:

type B struct{}

func (b *B) Validate() {
	fmt.Printf("b:%p\n", b)
}

type A struct {
	*B
}

func main() {
	b := B{}
	a1 := A{&b}
	a2 := A{&b}

	a1.Validate()
	a2.Validate()
	a1.B.Validate()
	a2.B.Validate()

	b.Validate()
}

Output as expected, the same value of *B everywhere (Go Playground):

b: 0x1becd8
b: 0x1becd8
b: 0x1becd8
b: 0x1becd8
b: 0x1becd8

As you can see, I changed the method receiver and the embedded type to pointer of B.

From this example it is clear: you can use the same value of B (or rather the same address of a value of B) to embed in different values of type A!

And you can call Validate() on both: so the theoretical "parent" is not even "constant" for a given value of *B. This wouldn't be a deal-breaker, but: having a value of A, you can call the promoted method B.Validate() by writing a.Validate() which is OK, but you can also call it like a.B.Validate() - now this time you don't really have a value of A (arguable, but Validate() is called on a value of *B and not A), but finally you can also call b.Validate() - this time you definitely don't have a value of A.

There is no definite type for the parent, B (or *B) can be embedded in any type (so it couldn't be anything other than interface{}).

So image: you have a concrete value of *B and when its Validate() method is called, sometimes there is a parent, and sometimes there isn't. Then what would be the justification to have a parent at all?

Back to your example: for this Validate() method to validate something meaningful, it should (must) be passed to the Validate() method as a parameter - explicitly and not automatically.

The best you can do is what captncraig wrote in his answer (+1).

You can simplify it a little though if you explicitly add a method to A which would call B.Validate() like this:

func (a *A) Validate2() {
    a.Validate(a)
}

// And using it:
a.Validate2()

There is no justification for a validator of the kind you imagine to be embedded in A, it should just be a field of A, or since there is no direct relation, it can be an "independent" validator. In both of these cases you may add a helper Validate() method to A if you would like to simplify validation.

答案2

得分: 1

任何类型的“parent”引用都是不可能的。你希望用父类做什么?由于它们可能是完全不同的类型,它们之间无法进行有意义的操作。

如果你需要在不同类型之间有共同的东西,你需要一个接口,并且你需要将其传递进去:

type B struct {}
func (b B) Validate(parent MyInterface) {
}


type A struct {
  B
}
// A 实现了 MyInterface
a := A{B{}}
a.Validate(a)

如果你能解释一下你实际想要实现的目标,几乎肯定有比这更好的方法。

英文:

Any kind of parent reference is impossible. What would you hope to do with the parent? Since they would be potentially widely different types, they would be incompatible to do anything meaningful with.

If you need something in common between different types you want an interface, and you will need to pass it in:

type B struct {}
func (b B) Validate(parent MyInterface) {
}


type A struct {
  B
}
// A implements MyInterface
a := A{B{}}
a.Validate(a)

If you can explain what you are actually trying to accomplish there is almost certainly a better way than this.

huangapple
  • 本文由 发表于 2015年6月3日 22:11:13
  • 转载请务必保留本文链接:https://go.coder-hub.com/30622605.html
匿名

发表评论

匿名网友

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

确定