1

我是一名围棋初学者,来自 Ruby 大陆。

在 Ruby 中,你可以做这样的事情。

Time.send("now")相当于Time.now, 因为您正在向now对象发送消息Time

golang中是否有类似的东西?

4

5 回答 5

9

Go 中没有内置的从字符串调用任意函数的方法。

您可以通过将函数注册到 map[string] 来创建类似的东西。一个工作示例:

package main

import "fmt"

var m = map[string]func(){
    "now":  func() { fmt.Println("The time is now") },
    "then": func() { fmt.Println("Once upon a time") },
}

func main() {
    cmd := "then"
    m[cmd]()
}

play.golang.org

也可以使用反射来按名称调用方法。您可以查看 和的reflect包装。您还可以查看这个Stackoverflow 问题。MethodByNameCall

于 2013-09-26T07:37:56.710 回答
6

正如其他人建议的那样,您可以通过将字符串映射到函数来自己完成,但是 Go 的强类型特性使得.send直接翻译成 Go 变得很困难。

如果您确实需要按名称访问字段或方法,您仍然可以使用反射:

import "reflect"
import "fmt"

type A struct {
    Number int
}

func (a *A) Method(i int) int {
    return a.Number + i;
}

func main() {
    a := &A{Number: 1}
    // Direct access
    fmt.Printf("Direct -> Nb: %d, Nb + 2: %d\n", a.Number, a.Method(2));

    v := reflect.ValueOf(*a)
    vp := reflect.ValueOf(a)
    field := v.FieldByName("Number")
    meth := vp.MethodByName("Method")
    args := []reflect.Value{reflect.ValueOf(2)}
    // Reflection access
    fmt.Printf("Reflect -> Nb: %d, Nb + 2: %d\n", 
               field.Interface().(int), 
               meth.Call(args)[0].Interface().(int))
}

输出:

Direct -> Nb: 1, Nb + 2: 3
Reflect -> Nb: 1, Nb + 2: 3

play.golang.org

但请注意:

  1. 那是多么的麻烦。通常,按照@ANisus 的建议执行地图是一种更惯用的做法
  2. 最后,您仍然必须执行转换。

使用reflect包将您的类型变量更改为更灵活Value的对象,但这些在实践中使用起来非常麻烦。如果你能找到一种不依赖反思的方式来表达你的意图,通常会更好。

还要注意,在这里,我们必须使用两个Values,一个用于a(指向 的指针A)用于方法,一个用于*aA结构)用于字段。尝试使用带有非指针的指针接收器定义的方法Value(或者相反,尝试通过指针获取字段Value)将导致恐慌。更一般地说,由于反射Values 的动态特性及其与通常类型化的 Go 的区别,预计 s 上不存在许多便利功能(例如自动引用/取消引用)Value

此外,调试时会出现相当多的运行时恐慌,因为这是动态Value调用失败的唯一方法!

参考:反射包

于 2013-09-26T08:01:37.617 回答
1

不会。通过http://tour.golang.org/http://golang.org/doc/effective_go.html工作,您将正确理解方法调用的工作原理。

于 2013-09-26T07:24:31.760 回答
1

这是一个使用的工作示例reflect

package main

import (
    "fmt"
    "os"
    "reflect"
)

// Send sends a message to(calls a method of) obj, with args.
// The return value of the method call is set to ret and any error to err.
func Send(obj interface{}, method string, args ...interface{}) (ret []reflect.Value, err error) {
    defer func() {
        if e := recover(); e != nil {
            err = fmt.Errorf("%v", e)
        }
    }()
    objValue := reflect.ValueOf(obj)
    argsValue := make([]reflect.Value, 0, len(args))
    for _, arg := range args {
        argsValue = append(argsValue, reflect.ValueOf(arg))
    }
    mtd := objValue.MethodByName(method)
    if !mtd.IsValid() {
        return nil, fmt.Errorf("%v does not have a method %v", reflect.TypeOf(obj), method)
    }
    ret = mtd.Call(argsValue)
    return
}

// Then do some tests.

type A struct {
    value int
}

func (a A) Value() int {
    return a.value
}

func (a *A) SetValue(v int) {
    a.value = v
}

func main() {
    var (
        ret []reflect.Value
        err error
    )
    // StdOut.WriteString("Hello, World!\n")
    _, err = Send(os.Stdout, "WriteString", "Hello, World!\n")
    handleError(err)

    var a = &A{100}

    // ret = a.Value()
    ret, err = Send(a, "Value")
    handleError(err)
    fmt.Printf("Return value is: %v\n", ret[0].Int())

    // a.SetValue(200)
    _, err = Send(a, "SetValue", 200)
    handleError(err)

    // ret = a.Value()
    ret, err = Send(a, "Value")
    handleError(err)
    fmt.Printf("Return value is: %v", ret[0].Int())
}

func handleError(err error) {
    if err != nil {
        panic(err)
    }
}
于 2013-09-26T11:12:17.593 回答
0

我的代码基于发送的这个描述

class Klass
  def hello(*args)
    "Hello " + args.join(' ')
  end
end
k = Klass.new
k.send :hello, "gentle", "readers"   #=> "Hello gentle readers"

http://play.golang.org/p/lXlzBf_fGZ

package main

import "strings"

type Klass struct{}

func (k Klass) Hello(args ...string) string {
    return "Hello " + strings.Join(args, " ")
}

func (k Klass) Send(symbol func(Klass, ...string) string, args ...string) string {
    return symbol(k, args...)
}

func main() {
    k := new(Klass)
    k.Send(Klass.Hello, "gentle", "readers") //=> "Hello gentle readers"
}

两者最大的区别在于,Go 的Send函数仅用于Klass并且仅适用于将可变数量的字符串作为参数并返回单个字符串的方法。这是因为 Go 是一种静态类型语言,其中 Ruby 是动态类型的。Go确实通过反射库支持动态类型,但这是一种不愉快的体验,而不是一般 Go 代码的编写方式。

于 2013-09-27T07:12:26.000 回答