douxian6260
douxian6260
2013-09-26 07:21

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

已采纳

I am a Go beginner, coming from Ruby land.

In Ruby, you could do something like this.

Time.send("now") is equivalent to Time.now, as you are sending the message now to the object Time

Is there something similar in golang?

  • 点赞
  • 写回答
  • 关注问题
  • 收藏
  • 复制链接分享
  • 邀请回答

5条回答

  • duanben1909 duanben1909 8年前

    As other suggested, you can do it yourself by mapping strings to functions, but the strong-typing nature of Go makes it difficult to translate .send directly into Go.

    You can still use reflection if you really need to access a field or method by name:

    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
    ", 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
    ", 
                   field.Interface().(int), 
                   meth.Call(args)[0].Interface().(int))
    }
    

    Outputs:

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

    play.golang.org

    Note however:

    1. How cumbersome that is. Usually, performing a map as suggested by @ANisus is a more idiomatic way of doing
    2. You still have to perform your conversions in the end.

    Using the reflect packages changes your typed variable into more flexible Value objects, but these are very cumbersome to use in practice. It is usually better if you can find a way to express your intent without relying on reflection.

    Also note that here, we had to use two Values, one for a (a pointer to A) for the method, and one for *a (a A structure) for the field. Trying to get a method defined with a pointer receiver with a non-pointer Value (or conversely, trying to obtain a field via a pointer Value) will result in a panic. More generally, due to the dynamic nature of reflected Values and its difference with the usual typed Go, expect a lot of convenience features (such as automatic referencing/dereferencing) to be absent on Values.

    Also, expect quite a bit of runtime panics while debugging, as it is the only way for dynamic Value calls to fail !

    Reference: the reflect package

    点赞 评论 复制链接分享
  • doudang4857 doudang4857 8年前

    I based my code on this description of send.

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

    The big difference between the two is that Go's Send function is only implemented for Klass and only works on methods that take a variable number of strings as parameters and return a single string. This is because Go is a statically typed language where Ruby is dynamically typed. Go does support dynamic typing via the reflect library, but it is an unpleasant experience and not the way general Go code is meant to be written.

    点赞 评论 复制链接分享
  • douzhuo5671 douzhuo5671 8年前

    Here is a working example using 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!
    ")
        _, err = Send(os.Stdout, "WriteString", "Hello, World!
    ")
        handleError(err)
    
        var a = &A{100}
    
        // ret = a.Value()
        ret, err = Send(a, "Value")
        handleError(err)
        fmt.Printf("Return value is: %v
    ", 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)
        }
    }
    
    点赞 评论 复制链接分享
  • douluoyou9876 douluoyou9876 8年前

    There is no built in way of calling an arbitrary function from a string in Go.

    You can create something similar by registering functions to a map[string]. A working example:

    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

    There is also the possibility of using reflection in order to call a method by name. You can look at the reflect package for MethodByName and Call. You can also check this Stackoverflow question.

    点赞 评论 复制链接分享
  • doubao7287 doubao7287 8年前

    No. Work your way through http://tour.golang.org/ and http://golang.org/doc/effective_go.html and you will have a proper understanding of how method invocation works.

    点赞 评论 复制链接分享