golang - 传递方法的功能

时间:2016-08-11 13:17:47

标签: go methods

我很好奇Go是否可行。我有一个有多种方法的类型。是否有可能有一个函数,它接受一个方法,并可以为类型调用它?

以下是我想要的一个小例子:

package main

import (
  "fmt"
)

type Foo int

func (f Foo) A() {
    fmt.Println("A")
}
func (f Foo) B() {
    fmt.Println("B")
}
func (f Foo) C() {
    fmt.Println("C")
}

func main() {
    var f Foo
    bar := func(foo func()) {
        f.foo()
    }
    bar(A)
    bar(B)
    bar(C)
}

Go think type Foo有一个名为foo()的方法,而不是用传入的方法名替换它。

3 个答案:

答案 0 :(得分:28)

是的,这是可能的。您有2(3)个选项:

Spec: Method expressions

表达式Foo.A产生的函数等效于A,但显式接收器作为其第一个参数;它有签名func(f Foo)

var f Foo
bar := func(m func(f Foo)) {
    m(f)
}
bar(Foo.A)
bar(Foo.B)
bar(Foo.C)

这里方法接收器是显式的。您只能将方法名称(及其所属类型)传递给bar(),并且在调用时,您必须传递实际的接收者:m(f)

按预期输出(在Go Playground上试试):

A
B
C

Spec: Method values

如果fFoo类型的值,则表达式f.A会生成类型为func()的函数值,其隐式接收器值为f

var f Foo
bar := func(m func()) {
    m()
}
bar(f.A)
bar(f.B)
bar(f.C)

请注意,此处方法接收器是隐式的,它与传递给bar()的函数值一起保存,因此在未明确指定它的情况下调用它:m()

输出相同(在Go Playground上尝试)。

(完整性:reflection

不如以前的解决方案(性能和安全性"),但您可以将方法的名称作为string值传递,然后使用reflect包以通过该名称调用方法。它看起来像这样:

var f Foo
bar := func(name string) {
    reflect.ValueOf(f).MethodByName(name).Call(nil)
}
bar("A")
bar("B")
bar("C")

Go Playground上尝试此操作。

答案 1 :(得分:0)

您还可以将@icza列出的“方法值”选项用于不同的接收器。

package main

import "fmt"

type Foo int
type Goo int

func (f Foo) A() { fmt.Println("A") }
func (f Foo) B() { fmt.Println("B") }
func (g Goo) A() { fmt.Println("A") }

func main() {
    //Method values with receiver f
    var f Foo
    bar2 := func(m func()) { m() }
    bar2(f.A) //A
    bar2(f.B) //B
    //Method values with receivers f and g
    var g Goo
    bar2(f.A) //A
    bar2(g.A) //A
}

答案 2 :(得分:0)

我想要一个不使用 map[string]func() 来包含状态方法的状态机类型组件。我的代码允许运行方法 这是一个简单的 for 循环,当 pfunc == nil

时中断
type Foo struct {
    name string
    idx  int
}
type X func(*Foo) X

func (f *Foo) A() X {
    f.name += fmt.Sprintf(" A[%d]", f.idx)
    fmt.Println(f.name)
    if f.idx > 10 {
        fmt.Println("Foo is complete!")
        return nil
    } else {
        f.idx += 1
        return (*Foo).B
    }
}
func (f *Foo) B() X {
    f.name += fmt.Sprintf(" B[%d]", f.idx)
    fmt.Println(f.name)
    f.idx += 2
    return (*Foo).C
}
func (f *Foo) C() X {
    f.name += fmt.Sprintf(" C[%d]", f.idx)
    fmt.Println(f.name)
    f.idx += 3
    return (*Foo).A
}

func main() {
    bar := &Foo{"Ready!", 0}
    pfunc := (*Foo).A
    except := 1
    for pfunc != nil && except < 10 {
        pfunc = pfunc(bar)
        except += 1
    }
}    

输出:
准备好! A[0]
准备好! A[0] B[1]
准备好! A[0] B[1] C[3]
准备好! A[0] B[1] C[3] A[6]
准备好! A[0] B[1] C[3] A[6] B[7]
准备好! A[0] B[1] C[3] A[6] B[7] C[9]
准备好! A[0] B[1] C[3] A[6] B[7] C[9] A[12]
Foo 完成了!