代码之家  ›  专栏  ›  技术社区  ›  NeuronQ

Go中的函数类型-特定类型转换为更一般的类型

  •  -1
  • NeuronQ  · 技术社区  · 9 年前

    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操场链接: https://play.golang.org/p/xGzfco0IAG )

    Any -> Any 任何->任何

    2 回复  |  直到 9 年前
        1
  •  0
  •   Cerise Limón    9 年前

    要使用类型断言,必须在中枚举每个可能的类型 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

    playground example

    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())
        }
    }
    

    playground example

        2
  •  0
  •   T. Claverie    9 年前

    理论上 可能的这是因为即使编译器在编译时进行了大量检查,也有可能更改运行时。。。在运行时。所谓的运行时黑客,即动态操作不应该处理的运行时结构。

    现在,您有一个有趣的问题,其答案不包括需要使用“不安全”软件包。然而,我发现推广一个函数的方法需要大量的思考。

    here .

    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))
        }
    }
    

    我们不会使用此代码。然而,在这一点上需要注意两件重要的事情:

    • 在不关心其类型的情况下传递函数的泛型类型是接口{}。类似“func(interface{})interface{}”的内容不是函数的泛化,它已经是一个具体类型。因此,“func(interface{})interface{}”不是“func(int)int”的泛化,这是两种完全不同的函数类型。这就是为什么不能使用类型断言/转换从一种函数类型转换为另一种函数类型。
    • 接收输入数组并生成和输出数组的东西

    现在,为了调用一个函数,你必须得到它的类型,而不是它的类型 价值

    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()
    }
    

    Playground

    重要的一点是 generalize

    如果您需要任何精度,请不要犹豫!