我需要在Go中执行什么强制转换/断言才能传递给期望像func(interface{}) interface{}
这样的通用函数的函数,而不是像func(int) int
这样更具体的函数?
例如,在这样的代码中,fooA
可以传递给MakeExclamer
,但不能传递给fooB
:
func MakeExclamer(foo func (interface{}) interface{}, n int) func () {
return func() {
fmt.Printf("%v!!!", foo(n))
}
}
func fooA(x interface{}) interface{} {
return x.(int)*2
}
func fooB(x int) int {
return x * 10
}
func main() {
exclamerA := MakeExclamer(fooA, 12)
exclamerA()
exclamerB := MakeExclamer(fooB, 66)
// >> cannot use fooB (type func(int) int) as type func(interface {}) interface {} in argument to MakeExclamer
exclamerB()
}
(Go Playground链接:https://play.golang.org/p/xGzfco0IAG)
我对替代代码结构模式不感兴趣,因为这是我希望它工作的方式:应该将特定函数传递给将返回的通用函数转换器(接受类型Any -> Any
的函数)另一个通用函数(Any -> Any
)。这可能不是Go中的惯用语,但它是我希望我的代码遵循的模式。
答案 0 :(得分:1)
要使用类型断言,必须在MakeExclamer
:
func MakeExclamer(fn interface{}, arg interface{}) func() {
switch fn := fn.(type) {
case func(int) int:
return func() {
fmt.Printf("%v!!!\n", fn(arg.(int)))
}
case func(interface{}) interface{}:
return func() {
fmt.Printf("%v!!!\n", fn(arg))
}
default:
panic("not supported")
}
}
要接受任何类型的函数,fn
参数将声明为类型interface{}
。该代码使用type switch来处理不同的函数类型。
Reflection可用于编写更通用的函数。
func MakeExclamer(fn interface{}, arg interface{}) func() {
fnr := reflect.ValueOf(fn)
argr := reflect.ValueOf(arg)
return func() {
resultr := fnr.Call([]reflect.Value{argr})
fmt.Printf("%v!!!\n", resultr[0].Interface())
}
}
答案 1 :(得分:0)
首先要做的事情是:在输入Go时,一切都理论上可能。这是因为即使编译器在编译时进行了大量检查,也可以在运行时更改运行时。所谓的运行时hacks,你动态操作你不应该处理的运行时结构。
现在,您有一个有趣的问题,其答案不包括使用“不安全”包的必要性。然而,我发现推广函数的方式涉及重复反思。
可以找到反射包的文档here。
因此,与Golang中的所有元素一样,函数具有Type。在不经过所有字段的情况下,函数会获取一组参数并生成一组结果。可以通过In(int)和Out(int)方法调查参数类型和结果。
func investigate(fn interface{}) {
fnType := reflect.TypeOf(fn)
for idx := 0; idx < fnType.NumIn(); idx ++ {
fmt.Printf("Input arg %d has type %v\n", idx, fnType.In(idx))
}
for idx := 0; idx < fnType.NumOut(); idx ++ {
fmt.Printf("Output arg %d has type %v\n", idx, fnType.Out(idx))
}
}
我们不会使用此代码。但是,此时需要注意两个重要的事项:
现在,为了调用函数,你必须得到的不是它的Type,而是 Value 。一旦获得其值,就可以使用参数数组来调用它,这些参数必须都是值。
原型是:
func (v Value) Call(in []Value) []Value
使用此方法,可以调用任何函数。
因此,您唯一需要的是将您拥有的任何参数数组转换为值数组,然后您就可以调用您的函数。
这是你的代码:
package main
import (
"fmt"
"reflect"
)
func MakeExclamer(foo interface{}, n int) func() {
exclamer := generalize(foo, n)
return func() {
fmt.Printf("%v!!!\n", exclamer())
}
}
func fooA(x interface{}) interface{} {
return x.(int) * 2
}
func fooB(x int) int {
return x * 10
}
func generalize(implem interface{}, args ...interface{}) func() interface{} {
valIn := make([]reflect.Value, len(args), len(args))
fnVal := reflect.ValueOf(implem)
for idx, elt := range args {
valIn[idx] = reflect.ValueOf(elt)
}
ret := func() interface{} {
res := fnVal.Call(valIn)
// We assume the function produces exactly one result
return res[0].Interface()
}
return ret
}
func main() {
exclamerA := MakeExclamer(fooA, 12)
exclamerA()
exclamerB := MakeExclamer(fooB, 66)
exclamerB()
}
重要的一点是generalize
函数,它在你的参数和一个数组之间进行转换,然后返回一个新函数,所有参数都已填充。
如果您需要任何精确度,请不要犹豫!