我能用反射在Go中创建新函数吗?

13

我有一个想法,想在Go中使用接口来定义RPC风格的接口。因此,对于给定的服务,我可能会创建如下所示的接口:

type MyService interface{
  Login(username, password string) (sessionId int, err error)
  HelloWorld(sessionId int) (hi string, err error)
}
我想做的是使用反射实现该接口,将方法调用转换为RPC调用,对输入参数进行编组,并将结果解组回方法输出。我知道如果我可以得到输入参数的[]interface{},我就可以使用反射来进行服务调用。然而,我没有看到任何使用反射来动态创建一个值以通过调用我的反射使用函数来实现接口的方法。有人知道如何做到这一点吗,即使使用不安全的方法?

以下两个答案都是准确和正确的。由于此问题有赏金,您应该将其中一个标记为您的答案。你不会得到任何其他的回答。 - Jeremy Wall
4个回答

13

通过反射无法创建带有附加方法的类型,因为要实例化该类型的对象。

你可能可以通过unsafe包中的大量黑客手段来实现此目的。但即使如此,这也将是一项巨大的痛苦。

如果您详细说明您正在尝试解决的问题,社区可能会提出替代解决方案。

编辑(2015年7月23日):从Go 1.5开始,有reflect.FuncOfreflect.MakeFunc,它们正好做到了您想要的。


问题的关键在于,Go接口可能是描述某种RPC服务的绝佳方式。然而,正如您所确认的那样,对于一个大型接口来说,今天需要创建大量的样板代码才能通过RPC调用实现接口。我意识到这些样板代码可以通过一些外部工具自动创建,但如果能够全部使用反射完成将会非常酷。 - Matt
1
不完全正确。reflect.MakeFunc 自 Go 1.1 就已经存在了。reflect.FuncOf 是在 1.5 版本中添加的,但是它不支持创建方法,而方法则是实现任意接口所必需的。我们需要的是在运行时创建新的“命名”类型并在这些类型上定义方法的能力。 - Matt

7

看起来在Go 1.1中,反射包将获得创建任意类型函数的能力:reflect.MakeFunc

(以下是为了回应@nemo而添加的)

可以创建一个结构体类型来代替接口:

type MyService struct{
  Login func(username, password string) (sessionId int, err error)
  HelloWorld func(sessionId int) (hi string, err error)
}

autorpc.ImplementService(&MyService, MyServiceURL)
session, err := MyService.Login(username, password)
stringout, err := MyService.HelloWorld(session)

1
这样做不会给你类型关联。因此,不能随意实现接口。 - nemo

1
由于Go语言的静态特性,目前无法在运行时动态实现接口。
我理解您想要达到的目标,并且还有其他场景也会受益于更高级的反射能力(例如,用于单元测试的良好模拟框架)。

话虽如此,你可以通过一种方法解决这个问题。你可以编写自己的工具,生成一个包含给定接口的RPC实现的Go源代码文件。

你需要使用AST库以及其他库来解析和处理源接口。

经过这样的尝试(gostub工具;可以作为参考),我可以说这并不是一件有趣且容易的事情。不过,最终结果还是可以接受的,因为Go提供了go:generate功能,这使得在接口发生变化后重新运行工具变得更加容易。


0
我认为如果能够完全实现 `reflect.Type` 接口(其中包含未导出方法),可能可以实现您想要的内容。然后,使用 `unsafe_New` 可能可以实例化自定义类型。
总之,这样做不是一个好主意。
你想要的下一个最好的方法可能是使用类似 gob 的东西。您可以将 gob 用作中间语言,使用反射读取接口中的方法,将其编写为 gob,并解码创建的 gob 代码以生成真正的 Go 对象。但我不确定是否值得这样做。
大多数情况下,在客户端手动实现接口并转发方法调用更安全。

1
unsafe_New是在runtime包中定义的函数,被reflect包用于创建新对象。例如,在reflectZero函数中使用。 - nemo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接