How to send a message to an object in Golang? (.send() equivalent in go)

huangapple go评论84阅读模式
英文:

How to send a message to an object in Golang? (.send() equivalent in go)

问题

我是一名Go初学者,来自Ruby领域。

在Ruby中,你可以这样做。

Time.send("now") 等同于 Time.now,因为你将消息 now 发送给了对象 Time

在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?

答案1

得分: 9

在Go语言中,没有内置的方法可以通过字符串调用任意函数。

你可以通过将函数注册到map[string]func()中创建类似的功能。下面是一个可工作的示例:

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]()
}

你也可以使用反射来根据名称调用方法。你可以查看reflect包中的MethodByNameCall方法。你还可以参考这个 Stackoverflow问题。

英文:

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.

答案2

得分: 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}
    // 直接访问
    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)}
    // 反射访问
    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

请注意:

  1. 这种方法非常繁琐。通常,像@ANisus建议的使用映射的方式更符合Go语言的习惯。
  2. 最终仍然需要进行类型转换。

使用reflect包会将你的类型化变量转换为更灵活的Value对象,但在实践中使用起来非常繁琐。如果能找到一种不依赖反射的方式来表达你的意图,通常会更好。

还要注意,在这个例子中,我们需要使用两个Value对象,一个用于方法的a(指向A的指针),一个用于字段的*aA结构体)。如果尝试使用非指针Value获取使用指针接收器定义的方法(或者反过来,尝试通过指针Value获取字段),将会导致恐慌。总的来说,由于反射Value的动态特性与通常的Go类型的差异,预计在Value上会缺少许多便利功能(如自动引用/解引用)。

此外,在调试过程中,预计会出现许多运行时恐慌,因为动态Value调用失败时只有这种方式!

参考:reflect包

英文:

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\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))
}

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

答案3

得分: 1

好的,以下是翻译好的内容:

不。请按照http://tour.golang.org/和http://golang.org/doc/effective_go.html的指引逐步学习,你将会对方法调用的工作原理有一个正确的理解。

英文:

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.

答案4

得分: 1

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

package main

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

// Send向obj发送消息(调用方法),参数为args。
// 方法调用的返回值设置为ret,任何错误设置为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
}

// 然后进行一些测试。

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("返回值为:%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("返回值为:%v", ret[0].Int())
}

func handleError(err error) {
	if err != nil {
		panic(err)
	}
}

希望对你有帮助!

英文:

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!\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)
}
}

答案5

得分: 0

我基于这个描述中的send方法编写了我的代码。

class Klass
  def hello(*args)
    "Hello " + args.join(' ')
  end
end
k = Klass.new
k.send :hello, "gentle", "readers"   #=> "Hello gentle readers"
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确实通过reflect库支持动态类型,但这并不是一种愉快的体验,也不是编写一般Go代码的方式。


<details>
<summary>英文:</summary>
I based my code on [this description][1] of send.  
&lt;!-- language: ruby --&gt;
class Klass
def hello(*args)
&quot;Hello &quot; + args.join(&#39; &#39;)
end
end
k = Klass.new
k.send :hello, &quot;gentle&quot;, &quot;readers&quot;   #=&gt; &quot;Hello gentle readers&quot;
http://play.golang.org/p/lXlzBf_fGZ
package main
import &quot;strings&quot;
type Klass struct{}
func (k Klass) Hello(args ...string) string {
return &quot;Hello &quot; + strings.Join(args, &quot; &quot;)
}
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, &quot;gentle&quot;, &quot;readers&quot;) //=&gt; &quot;Hello gentle readers&quot;
}
The big difference between the two is that Go&#39;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][2] library, but it is an unpleasant experience and not the way general Go code is meant to be written.
[1]: http://ruby-doc.org/core-2.0.0/Object.html#method-i-send
[2]: http://golang.org/pkg/reflect/
</details>

huangapple
  • 本文由 发表于 2013年9月26日 15:21:34
  • 转载请务必保留本文链接:https://go.coder-hub.com/19021848.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定