通过反射获取被遮蔽的方法。

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

Get shadowed method via reflection

问题

如何通过反射获取被隐藏的方法?

在下面的代码中,我使用MethodByName来获取类型B上的方法Test(),但我也想从b.A中获取被隐藏的方法Test(),这样我就可以调用它们两个。

package main

import (
	"fmt"
	"reflect"
)

type A struct {}
type B struct {
	A
}

func (self *A) Test() {
	fmt.Println("I'm A")
}
func (self *B) Test() {
	fmt.Println("I'm B")
}

func main() {
	b := &B{}
	b.Test()   // 这个调用会隐藏 b.A.Test()
	b.A.Test() // 但可以这样调用

	// 通过反射
	val := reflect.ValueOf(b)
	val.MethodByName("Test").Call([]reflect.Value{})
}

代码可在此处找到:http://play.golang.org/p/6YPLy2dmMb

英文:

How can I get the shadowed methods via reflection?

In the code below I'm using MethodByName to get method Test() on type B, but I would like to get the shadowed method Test() from b.A as well, so I can call both of them.

package main

import (
	"fmt"
	"reflect"
)

type A struct {}
type B struct {
	A
}

func (self *A) Test() {
	fmt.Println("I'm A")
}
func (self *B) Test() {
	fmt.Println("I'm B")
}

func main() {
	b := &B{}
	b.Test()   // This one shadows b.A.Test()
	b.A.Test() // but its ok to call like this

	// via reflection
	val := reflect.ValueOf(b)
	val.MethodByName("Test").Call([]reflect.Value{})
}

Code available here: http://play.golang.org/p/6YPLy2dmMb

答案1

得分: 2

如果你有一个嵌入的结构体和遮蔽(shadowing),你可以通过与嵌入结构体类型同名的成员变量来获取该字段,就像你的代码中的 b.A.Test() 那样。

那么我们该怎么做呢?使用 reflect.Value.FieldByName 来获取字段。在你的具体设置中,这有点棘手。你不能在指向结构体的指针上使用 FieldByName。

package main

import (
	"fmt"
	"reflect"
)

type A struct{}
type B struct {
	A
}

func (self *A) Test() {
	fmt.Println("I'm A")
}
func (self *B) Test() {
	fmt.Println("I'm B")
}

func main() {
	b := &B{}
	b.Test()
	b.A.Test()

	val := reflect.ValueOf(b)
	subVal := val.Elem().FieldByName("A").Addr()
	subVal.MethodByName("Test").Call([]reflect.Value{})
	val.MethodByName("Test").Call([]reflect.Value{})
}

如上所示,这有点丑陋。你首先需要调用 Elem 来获取值 val 指向的值,然后获取字段,然后获取字段的指针,因为 A.Test 实际上是在 (*A) 上,而不是 A 上。虽然 Go 的指针通常是透明的,但遗憾的是,在反射中并不适用,所以你必须自己进行所有显式的寻址/解引用操作,但如果你理解指针,这是相当直接的。

编辑:上述代码的 Playground 链接

英文:

If you have an embedded struct and shadowing, you can always get the field as if it were a member variable with the same name as the type of the embedded struct, this is shown by your line b.A.Test().

So what do we do? Use reflect.Value.FieldByName to get the field. With your exact setup it's a bit janky. You can't use FieldByName on a pointer to a struct.

package main

import (
	"fmt"
	"reflect"
)

type A struct{}
type B struct {
	A
}

func (self *A) Test() {
	fmt.Println("I'm A")
}
func (self *B) Test() {
	fmt.Println("I'm B")
}

func main() {
	b := &B{}
	b.Test()
	b.A.Test()

	val := reflect.ValueOf(b)
	subVal := val.Elem().FieldByName("A").Addr()
	subVal.MethodByName("Test").Call([]reflect.Value{})
	val.MethodByName("Test").Call([]reflect.Value{})
}

As seen, it's a bit ugly. You first need to call Elem to get the value val points to, then get the field, then get the pointer to the field because A.Test is actually on (*A), and not A. While Go pointers are normally transparent, it doesn't apply to reflection, unfortunately, so you have to do all explicit addressing/dereferencing yourself, but if you understand pointers it's pretty straightforward.

Edit: Playground link to the code above

答案2

得分: 2

当你将一个结构体嵌入到另一个结构体中时,比如将A嵌入到B中,你只是在结构体B中创建了一个名为A、类型为A的字段。作为一种语法上的便利,你可以直接在B上调用A的方法,但从语义上讲,并没有什么特殊的"shadowed methods";它们只是在一个结构体内部的类型为A的值上的方法。

你只需要使用反射API来模拟b.A.Test()。不好的一点是,反射不会执行纯Go语言所做的那种语法糖,所以你需要模拟的是(&((*b).A)).Test()

val.Elem(). // 转到 *B
    FieldByName("A").     // 找到名为 A 的字段
    Addr().               // 取其地址,因为 Test 的方法接收者类型是 *A
    MethodByName("Test"). // 找到它的方法 Test
    Call([]reflect.Value{})

更新后的代码:

http://play.golang.org/p/67xc66ULFz

(顺便说一句,在Go语言中,将方法接收者称为"self"并不符合惯用法。)

英文:

When you embed a struct into another, say A into B, you are only making a field in struct B named A of type A. As a syntactic convenience, you can call methods on A directly on B, but semantically there is no special thing about "shadowed methods"; they are just methods on a type A value with happens to be inside a struct.

You just need to mimick your b.A.Test() with the reflecion API. The bad thing is that reflection doesn't perform the kind of sugar that plain Go does, so the thing you need to mimick is (&((*b).A)).Test().

val.Elem(). // Go to *B
FieldByName("A").     // Find field named A
Addr().               // Take its address, since Test has a method receiver of type *A
MethodByName("Test"). // Find its method Test
Call([]reflect.Value{})

Updated code:

http://play.golang.org/p/67xc66ULFz

(By the way, calling method receivers "self" is not idiomatic in Go.)

huangapple
  • 本文由 发表于 2014年3月13日 17:17:25
  • 转载请务必保留本文链接:https://go.coder-hub.com/22373953.html
匿名

发表评论

匿名网友

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

确定