我正在写在围棋模板系统,这意味着它需要自由的使用上reflect
包。 在这种具体情况我需要能够动态调用的方法的上interface{}
古怪的是,我的反射逻辑工作正常,只要我的数据是已知类型的,但如果该数据是类型的interface{}
在下面的例子中可以看到,在逻辑main()
和Pass()
是相同的。 唯一的区别是该数据是否是一已知类型或已知类型的内部interface{}
去发挥: 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")
}
}
在执行这个代码中,我们得到以下结果
Pass() fail
startfinish
这意味着,我的动态调用方法的方法工作得很好,除了在一个场景时,我的对象是当前处于interface{}
相反,如果我不使用指针接收器,并通过i
那么它将按预期工作。
去发挥: http://play.golang.org/p/myM0UXVYzX
这使我相信,我的问题是,我不能访问我(的地址&i
当它是一个) interface{}
我已经冲刷了反映封装和测试的东西,如reflect.Value.Addr()
和reflect.PtrTo()
但我无法得到任何工作,我需要的方式。 我的直觉是,它是与事实,即一个interface{}
是定义参考对象。
由于@Jeremy长城,我相信我能解决我的问题。 基本问题是一个在调用动态命名方法interface{}
有4例。
-
interface{}
基本数据是值和接收器是值 -
interface{}
基本数据是指针和接收器是值 -
interface{}
基本数据是值和接收器是指针 -
interface{}
基本数据是指针和接收器是指针
使用反射,我们可以判断我们的接口的下属值。 然后用另外的反射,可以生成替代数据类型,我们目前的类型。 如果传入的数据是一个值,我们需要生成一个指向它
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
}
现在,我们有两种数据类型,我们可以简单地使用各检查现有的方法
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()
}
因此,考虑到这一点,我们可以有效地调用任何方法,动态,无论是声明为*receiver
或receiver
。
理念的充分证明: 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"))
}
在你的榜样,你不叫传与支持,因为完成仅在指针定义为测试结构的完成方法的东西。 MethodByName究竟是做什么它应该在这种情况下。 *Test != Test
它们是两个不同类型的完全。 反射再多会变成一个测试成*测试。 说实在也不应该。 您可以使用PtrTo功能,以找出是否Finish方法是在指针类型定义,但不会帮助你获得一个指向实际值。
用指针调用工作通行证: http://play.golang.org/p/fICI3cqT4t
这是golang实现类似功能的一个很好的例子:
package main
import "fmt"
type Parent struct {
Attr1 string
}
type Parenter interface {
GetParent() Parent
}
type Child struct {
Parent //embed
Attr string
}
func (c Child) GetParent() Parent {
return c.Parent
}
func setf(p Parenter) {
fmt.Println(p)
}
func main() {
var ch Child
ch.Attr = "1"
ch.Attr1 = "2"
setf(ch)
}
代码: https://groups.google.com/d/msg/golang-nuts/8lK0WsGqQ-0/HJgsYW_HCDoJ