动态调用接口{}上的方法,而不管接收者的类型如何 [英] Dynamically call method on interface{} regardless of receiver type
问题描述
reflect
包。在这种特殊情况下,我需要能够在 interface {}
上动态调用方法。奇怪的是,只要我的数据是已知类型的数据,我的反射逻辑就能正常工作,但如果数据类型为 interface {}
,那么不是。以下示例可以看到 main()
和 Pass()
是相同的。唯一的区别是数据在界面{}
内部是已知类型还是已知类型。 : http://play.golang.org/p/FTP3wgc0sZ
包裹主要
进口(
fmt
反映
)
类型测试结构{
起始字符串
}
func(t * Test)完成()字符串{
返回t.Start +完成
}
func Pass(i interface {}){
_,ok:= reflect.TypeOf(& i).MethodByName(Finish)
if if {
fmt.Println(reflect.ValueOf(& i).MethodByName(Finish)。Call([] reflect.Value {})[0])
} else {
fmt.Println(Pass()fail)
}
}
func main(){
i:= Test {Start:start }
Pass(i)
_,ok:= reflect.TypeOf(& i).MethodByName(Finish)
if ok {
fmt .Println(reflect.ValueOf(安培;ⅰ).MethodByNam e(Finish)。Call([] reflect.Value {})[0])
} else {
fmt.Println(main()fail)
}
$ / code>
执行此代码后,我们得到以下结果:
Pass()失败
startfinish
这意味着我的动态调用方法的方法可以正常工作,除非我的对象当前位于 interface {}
中。
如果我不使用指针接收器并传递 i
,那么它可以按预期工作。
开始游戏: http://play.golang.org/p/myM0UXVYzX
这让我相信我的问题是我无法访问我的地址(& i
)它是一个界面{}
。我搜索了反射包并测试了诸如 reflect.Value.Addr()
和 reflect.PtrTo()
但我无法按照我需要的方式工作。我的直觉是,它与 interface {}
定义为引用对象有关。
感谢@Jeremy Wall,我相信我能解决我的问题。基本问题是在接口{}
上调用一个动态命名的方法。有4种情况。
-
接口{}
底层数据是值,接收者是值 -
接口{}
基础数据是指针,接收者是值
-
interface {}
基础数据是值,接收者是指针
-
接口{}
底层数据是指针而接收者是指针
使用反射我们可以确定接口的下标值。然后使用进一步的反射,我们可以生成替代数据类型到我们当前的类型。如果传入的数据是一个值,我们需要生成一个指针。
value:= reflect.ValueOf(data)
if value.Type()。Kind()== reflect.Ptr {
ptr = value
value = ptr.Elem()//获取指针引用的值
} else {
ptr = reflect.New(reflect.TypeOf(i))//创建新指针
temp:= ptr.Elem()//创建变量为指针的值
temp.Set (value)//设置变量的值给我们传入的值
}
现在,我们有两种数据类型,我们可以简单地使用它们来检查现有的方法。
var finalMethod reflect.Value
方法:= value.MethodByName(methodName)
if method.IsValid(){
finalMethod = method
}
//检查指针
method = ptr上的方法。 MethodByName(methodName)
如果method.IsValid(){
finalMethod =方法
}
if(finalMethod.IsValid()){
return finalMethod .CALL([]参考lect.Value {})[0] .String()
}
记住,我们可以动态地调用任何方法,无论是声明为 * receiver
还是
充分的概念验证: http://play.golang。 org / p / AU-Km5VjZs
包主
导入(
fmt
反映
)
类型测试结构{
开始字符串
}
//值receiver
func(t Test)Finish()string {
return t.Start +finish
}
//指针接收器
func( t * Test)Another()string {
return t.Start +another
}
func CallMethod(i interface {},methodName string)interface {} {
var ptr reflect.Value
var value reflect.Value
var finalMethod reflect.Value
value = reflect.ValueOf(i)
//如果我们从一个点开始呃,我们需要把值指向
//如果我们以一个值开始,我们需要得到一个指向该值的指针
if value.Type()。Kind()== reflect.Ptr {
ptr = value
value = ptr.Elem()
} else {
ptr = reflect.New(reflect.TypeOf(i))
temp:= ptr.Elem()
temp.Set(value)
}
//检查方法的值
method:= value.MethodByName(methodName)
if method.IsValid(){
finalMethod = method
}
//检查指针上的方法
method = ptr.MethodByName(methodName)
if method .IsValid(){
finalMethod = method
}
if(finalMethod.IsValid()){
return finalMethod.Call([] reflect.Value {} )[0] .Interface()
}
//返回或恐慌,找不到任何类型的方法
返回
}
func main(){
i:= Test {Start:start}
j:= Test {Start:st (CallMethod(i,Finish))
fmt.Println(CallMethod(& i,Finish))
fmt.Println (CallMethod(i,Another))
fmt.Println(CallMethod(& i,Another))
fmt.Println(CallMethod(j,Finish))
fmt.Println(CallMethod(& j,Finish))
fmt.Println(CallMethod(j,Another))
fmt.Println(CallMethod(& j,Another)) )
}
I'm working on a templating system written in Go, which means it requires liberal use of the reflect
package. In this specific circumstance I need to be able to dynamically call a method on an interface{}
. The oddity is that my reflection logic works fine as long as my data is of a known type, but not if the data is of type interface{}
.
The the following example you can see that the logic in main()
and Pass()
is identical. The only difference is whether the data is a known type or a known type inside an interface{}
Go Play: http://play.golang.org/p/FTP3wgc0sZ
package main
import (
"fmt"
"reflect"
)
type Test struct {
Start string
}
func (t *Test) Finish() string {
return t.Start + "finish"
}
func Pass(i interface{}) {
_, ok := reflect.TypeOf(&i).MethodByName("Finish")
if ok {
fmt.Println(reflect.ValueOf(&i).MethodByName("Finish").Call([]reflect.Value{})[0])
} else {
fmt.Println("Pass() fail")
}
}
func main() {
i := Test{Start: "start"}
Pass(i)
_, ok := reflect.TypeOf(&i).MethodByName("Finish")
if ok {
fmt.Println(reflect.ValueOf(&i).MethodByName("Finish").Call([]reflect.Value{})[0])
} else {
fmt.Println("main() fail")
}
}
Upon executing this code we get the following result
Pass() fail
startfinish
Which means that my methodology for dynamically calling a method works fine except in a scenario when my object is currently in an interface{}
.
If instead I do not use a pointer receiver and pass i
then it works as expected.
Go Play: http://play.golang.org/p/myM0UXVYzX
This leads me to believe that my problem is that I cannot access the address of i (&i
) when it is an interface{}
. I've scoured the reflect package and tested things such as reflect.Value.Addr()
and reflect.PtrTo()
but I could not get either to work the way I needed. My hunch is that it has something to do with the fact that an interface{}
is by definition a reference object.
Thanks to @Jeremy Wall I believe I was able to solve my problem. The basic issue is calling a dynamically named method on an interface{}
. There are 4 cases.
interface{}
underlying data is value and receiver is valueinterface{}
underlying data is pointer and receiver is valueinterface{}
underlying data is value and receiver is pointerinterface{}
underlying data is pointer and receiver is pointer
Using reflection we can determine the underling value of our interface. Then using further reflection we can generate the alternate data type to our current type. If the data passed in was a value we need to generate a pointer to it
value := reflect.ValueOf(data)
if value.Type().Kind() == reflect.Ptr {
ptr = value
value = ptr.Elem() // acquire value referenced by pointer
} else {
ptr = reflect.New(reflect.TypeOf(i)) // create new pointer
temp := ptr.Elem() // create variable to value of pointer
temp.Set(value) // set value of variable to our passed in value
}
Now that we have both data types we can simply use each to check for an existing method
var finalMethod reflect.Value
method := value.MethodByName(methodName)
if method.IsValid() {
finalMethod = method
}
// check for method on pointer
method = ptr.MethodByName(methodName)
if method.IsValid() {
finalMethod = method
}
if (finalMethod.IsValid()) {
return finalMethod.Call([]reflect.Value{})[0].String()
}
Therefore with this in mind we can effectively call any method, dynamically, whether declared as *receiver
or receiver
.
Full Proof of Concept: http://play.golang.org/p/AU-Km5VjZs
package main
import (
"fmt"
"reflect"
)
type Test struct {
Start string
}
// value receiver
func (t Test) Finish() string {
return t.Start + "finish"
}
// pointer receiver
func (t *Test) Another() string {
return t.Start + "another"
}
func CallMethod(i interface{}, methodName string) interface{} {
var ptr reflect.Value
var value reflect.Value
var finalMethod reflect.Value
value = reflect.ValueOf(i)
// if we start with a pointer, we need to get value pointed to
// if we start with a value, we need to get a pointer to that value
if value.Type().Kind() == reflect.Ptr {
ptr = value
value = ptr.Elem()
} else {
ptr = reflect.New(reflect.TypeOf(i))
temp := ptr.Elem()
temp.Set(value)
}
// check for method on value
method := value.MethodByName(methodName)
if method.IsValid() {
finalMethod = method
}
// check for method on pointer
method = ptr.MethodByName(methodName)
if method.IsValid() {
finalMethod = method
}
if (finalMethod.IsValid()) {
return finalMethod.Call([]reflect.Value{})[0].Interface()
}
// return or panic, method not found of either type
return ""
}
func main() {
i := Test{Start: "start"}
j := Test{Start: "start2"}
fmt.Println(CallMethod(i, "Finish"))
fmt.Println(CallMethod(&i, "Finish"))
fmt.Println(CallMethod(i, "Another"))
fmt.Println(CallMethod(&i, "Another"))
fmt.Println(CallMethod(j, "Finish"))
fmt.Println(CallMethod(&j, "Finish"))
fmt.Println(CallMethod(j, "Another"))
fmt.Println(CallMethod(&j, "Another"))
}
这篇关于动态调用接口{}上的方法,而不管接收者的类型如何的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!