我正在使用Go编写的模板系统,这意味着它需要自由使用该reflect
软件包。在这种特定情况下,我需要能够在上动态调用方法interface{}
。奇怪的是,只要我的数据是已知类型,反射逻辑就可以正常工作,但是如果数据是type则不能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
当i()是时,我无法访问其地址interface{}
。我已经检查了反射包并测试了诸如和的东西reflect.Value.Addr()
,reflect.PtrTo()
但是我都无法按照我需要的方式工作。我的直觉是,这与以下事实有关interface{}
:根据定义,an
是参考对象。
感谢@Jeremy Wall,我相信我能够解决我的问题。基本问题是在上调用动态命名方法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"))
}
问题内容: 我正在学习接口,类型转换和带有指针接收器的方法。指针接收器方法背后的规则和术语使我感到困惑。让我展示我对一个程序的困惑。 这是我的Go程序。 这是输出。 如果删除最后注释的行,则会出现此错误。 如何修复该行代码,以便能够使用指针接收器调用该方法?请通过提出带有指针接收器的方法的概念来说明解决方案,以阐明为什么它不起作用。 问题答案: 您不能(在这种情况下,对于指针接收器是隐式的)获取表
11.12.1 Go 的动态类型 在经典的面向对象语言(像 C++,Java 和 C#)中数据和方法被封装为 类 的概念:类包含它们两者,并且不能剥离。 Go 没有类:数据(结构体或更一般的类型)和方法是一种松耦合的正交关系。 Go 中的接口跟 Java/C# 类似:都是必须提供一个指定方法集的实现。但是更加灵活通用:任何提供了接口方法实现代码的类型都隐式地实现了该接口,而不用显式地声明。 和其它
我想使用广播接收器在固定时间调用此方法的警报 } 删除所有消息,但没有发生,请提前帮我谢谢 /***删除所有消息。0-成功删除。1-没有要*删除的内容。*/ 我正在使用这个广播接收器 正如你所说我做了我创建了一个活动 公共类Active扩展PendingMessages实现Runnable{ } 然后打电话过去 context.start服务(新意图(上下文,Activity.class)); 但
下面是我试图做的一个非常简单的说明: 最后一个方法调用(consumer.consumer())给了我一个编译器错误 Out projected type’EventConsumer 我知道Kotlin对泛型的要求比Java严格得多,这可能就是它不起作用的原因,但我该如何正确地实现这样的东西呢?
问题内容: Java-8允许在接口内部定义静态方法,但仅通过接口名称限制其调用: 9.4:接口可以声明静态方法,这些方法在不引用特定对象的情况下被调用。 例如: 导致错误: 在JLS中,这种禁令经常有一种解释。在这种情况下,我没有发现任何详细信息。因此,我正在寻找对此规则的全面或权威的解释:为什么禁止通过特定的对象引用调用静态方法?它有什么坏处? 问题答案: 相当强烈的共识是,有关类的静态方法也不
问题内容: 目前我有这样的事情 main.go 还有我的工作包 和 我正在传递给NewJob的函数是在goroutine上每2秒执行一次,但是我想访问我传递的匿名结构…但是当我尝试访问时 编号 我越来越 t.Id未定义(类型interface {}是没有方法的接口) 但是打印t给我预期的结果 {1} 问题答案: 您必须先将其声明为兼容类型,然后才能访问其字段。