如何向Golang中的对象发送消息? (.send()等效于go)

时间:2013-09-26 07:21:34

标签: go

我是初学者,来自Ruby的土地。

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

Time.send("now")相当于Time.now,因为您要将邮件now发送到对象Time

golang中有类似的东西吗?

5 个答案:

答案 0 :(得分: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

还可以使用反射来按名称调用方法。您可以查看reflectMethodByName的{​​{1}}包。您还可以查看this Stackoverflow问题。

答案 1 :(得分: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. 您最后仍需要执行转换。
  3. 使用reflect包将您输入的变量更改为更灵活的Value个对象,但这些非常在实践中使用起来非常麻烦。如果你能在不依赖反思的情况下找到表达意图的方法,通常会更好。

    另请注意,在这里,我们必须使用两个Value,一个用于a(指向A的指针)用于方法,一个用于*a(该领域的A结构)。尝试使用非指针Value的指针接收器定义方法(或者相反,尝试通过指针Value获取字段)将导致恐慌。更一般地说,由于反映的Value的动态特性及其与通常的类型Go的区别,期望在Value上不存在许多便利功能(例如自动引用/解除引用)。

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

    参考:the reflect package

答案 2 :(得分:1)

没有。按照http://tour.golang.org/http://golang.org/doc/effective_go.html的方式进行操作,您就可以正确理解方法调用的工作原理。

答案 3 :(得分: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)
    }
}

答案 4 :(得分:0)

我的代码基于发送的this description

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是动态类型的。通过reflect支持支持动态类型,但这是一种令人不愉快的体验,而不是一般Go代码的编写方式。