【问题标题】:Pass method argument to function将方法参数传递给函数
【发布时间】:2016-12-18 06:16:46
【问题描述】:

我很好奇这在 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 认为类型 Foo 有一个名为 foo() 的方法,而不是用传入的方法名称替换它。

【问题讨论】:

标签: go methods


【解决方案1】:

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

Spec: Method expressions

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

var foo Foo
bar := func(m func(f Foo)) {
    m(foo)
}
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 上试试这个。

【讨论】:

  • 对于带有指针接收器的方法,你会怎么做?我试过像这个链接play.golang.org/p/mdi8htBnZ9中显示的那样。但我想不通。
  • 由于我无法编辑之前的评论,因此添加了一条新评论。我按照规范中的规定进行了尝试,并提出了play.golang.org/p/7P9mU0uhke。但我在这里还有一个问题。我期待打印的两个地址是相同的(Foo 实例的地址f
  • @rajkamal 在foo.A() 内部,您打印的不是f 指针,而是指针的地址。 f 已经是一个指针,所以&f 将是**foo 类型。如果您将其更改为仅打印 f 的值,那么您将看到打印相同的指针:Go Playground
【解决方案2】:

我想要一个不使用 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 完成了!

【讨论】:

    【解决方案3】:

    您还可以将@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
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-08-30
      • 2011-08-18
      • 2014-09-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多