英文:
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
包中的MethodByName
和Call
方法。你还可以参考这个 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]()
}
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
请注意:
- 这种方法非常繁琐。通常,像@ANisus建议的使用映射的方式更符合Go语言的习惯。
- 最终仍然需要进行类型转换。
使用reflect
包会将你的类型化变量转换为更灵活的Value
对象,但在实践中使用起来非常繁琐。如果能找到一种不依赖反射的方式来表达你的意图,通常会更好。
还要注意,在这个例子中,我们需要使用两个Value
对象,一个用于方法的a
(指向A
的指针),一个用于字段的*a
(A
结构体)。如果尝试使用非指针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
Note however:
- How cumbersome that is. Usually, performing a map as suggested by @ANisus is a more idiomatic way of doing
- 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 Value
s, 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 Value
s and its difference with the usual typed Go, expect a lot of convenience features (such as automatic referencing/dereferencing) to be absent on Value
s.
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.
<!-- language: ruby -->
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][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>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论